| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #source("../../../../runtime/bin/http_parser.dart"); | 5 #source("../../../../runtime/bin/http_parser.dart"); |
| 6 | 6 |
| 7 class HttpParserTest { | 7 class HttpParserTest { |
| 8 static void runAllTests() { | 8 static void runAllTests() { |
| 9 testParseRequest(); | 9 testParseRequest(); |
| 10 testParseResponse(); | 10 testParseResponse(); |
| 11 } | 11 } |
| 12 | 12 |
| 13 static void _testParseRequest(String request, | 13 static void _testParseRequest(String request, |
| 14 String expectedMethod, | 14 String expectedMethod, |
| 15 String expectedUri, | 15 String expectedUri, |
| 16 [int expectedContentLength = 0, | 16 [int expectedContentLength = -1, |
| 17 int expectedBytesReceived = 0, | 17 int expectedBytesReceived = 0, |
| 18 Map expectedHeaders = null, | 18 Map expectedHeaders = null, |
| 19 bool chunked = false]) { | 19 bool chunked = false]) { |
| 20 _HttpParser httpParser; | 20 _HttpParser httpParser; |
| 21 bool headersCompleteCalled; | 21 bool headersCompleteCalled; |
| 22 bool dataEndCalled; | 22 bool dataEndCalled; |
| 23 String method; | 23 String method; |
| 24 String uri; | 24 String uri; |
| 25 Map headers; | 25 Map headers; |
| 26 int contentLength; | 26 int contentLength; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 if (chunkSize == -1) chunkSize = requestData.length; | 74 if (chunkSize == -1) chunkSize = requestData.length; |
| 75 reset(); | 75 reset(); |
| 76 for (int pos = 0; pos < requestData.length; pos += chunkSize) { | 76 for (int pos = 0; pos < requestData.length; pos += chunkSize) { |
| 77 int remaining = requestData.length - pos; | 77 int remaining = requestData.length - pos; |
| 78 int writeLength = Math.min(chunkSize, remaining); | 78 int writeLength = Math.min(chunkSize, remaining); |
| 79 httpParser.writeList(requestData, pos, writeLength); | 79 httpParser.writeList(requestData, pos, writeLength); |
| 80 } | 80 } |
| 81 checkExpectations(); | 81 checkExpectations(); |
| 82 } | 82 } |
| 83 | 83 |
| 84 void testWriteAll(List<int> requestData) { | |
| 85 reset(); | |
| 86 httpParser.writeList(requestData, 0, requestData.length); | |
| 87 checkExpectations(); | |
| 88 } | |
| 89 | |
| 90 // Test parsing the request three times delivering the data in | 84 // Test parsing the request three times delivering the data in |
| 91 // different chunks. | 85 // different chunks. |
| 92 List<int> requestData = request.charCodes(); | 86 List<int> requestData = request.charCodes(); |
| 93 testWrite(requestData); | 87 testWrite(requestData); |
| 94 testWrite(requestData, 10); | 88 testWrite(requestData, 10); |
| 95 testWrite(requestData, 1); | 89 testWrite(requestData, 1); |
| 96 } | 90 } |
| 97 | 91 |
| 98 static void _testParseResponse(String response, | 92 static void _testParseResponse(String response, |
| 99 int expectedStatusCode, | 93 int expectedStatusCode, |
| 100 String expectedReasonPhrase, | 94 String expectedReasonPhrase, |
| 101 [int expectedContentLength = 0, | 95 [int expectedContentLength = -1, |
| 102 int expectedBytesReceived = 0, | 96 int expectedBytesReceived = 0, |
| 103 Map expectedHeaders = null, | 97 Map expectedHeaders = null, |
| 104 bool chunked = false, | 98 bool chunked = false, |
| 105 bool close = false]) { | 99 bool close = false, |
| 100 String responseToMethod = null]) { |
| 106 _HttpParser httpParser; | 101 _HttpParser httpParser; |
| 107 bool headersCompleteCalled; | 102 bool headersCompleteCalled; |
| 108 bool dataEndCalled; | 103 bool dataEndCalled; |
| 109 int statusCode; | 104 int statusCode; |
| 110 String reasonPhrase; | 105 String reasonPhrase; |
| 111 Map headers; | 106 Map headers; |
| 112 int contentLength; | 107 int contentLength; |
| 113 int bytesReceived; | 108 int bytesReceived; |
| 114 | 109 |
| 115 void reset() { | 110 void reset() { |
| 116 httpParser = new _HttpParser(); | 111 httpParser = new _HttpParser(); |
| 112 if (responseToMethod != null) { |
| 113 httpParser.responseToMethod = responseToMethod; |
| 114 } |
| 117 httpParser.requestStart = (m, u) => Expect.fail("Expected response"); | 115 httpParser.requestStart = (m, u) => Expect.fail("Expected response"); |
| 118 httpParser.responseStart = (s, r) { | 116 httpParser.responseStart = (s, r) { |
| 119 statusCode = s; | 117 statusCode = s; |
| 120 reasonPhrase = r; | 118 reasonPhrase = r; |
| 121 }; | 119 }; |
| 122 httpParser.headerReceived = (f, v) { | 120 httpParser.headerReceived = (f, v) { |
| 123 Expect.isFalse(headersCompleteCalled); | 121 Expect.isFalse(headersCompleteCalled); |
| 124 headers[f] = v; | 122 headers[f] = v; |
| 125 }; | 123 }; |
| 126 httpParser.headersComplete = () { | 124 httpParser.headersComplete = () { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 reset(); | 162 reset(); |
| 165 for (int pos = 0; pos < requestData.length; pos += chunkSize) { | 163 for (int pos = 0; pos < requestData.length; pos += chunkSize) { |
| 166 int remaining = requestData.length - pos; | 164 int remaining = requestData.length - pos; |
| 167 int writeLength = Math.min(chunkSize, remaining); | 165 int writeLength = Math.min(chunkSize, remaining); |
| 168 httpParser.writeList(requestData, pos, writeLength); | 166 httpParser.writeList(requestData, pos, writeLength); |
| 169 } | 167 } |
| 170 if (close) httpParser.connectionClosed(); | 168 if (close) httpParser.connectionClosed(); |
| 171 checkExpectations(); | 169 checkExpectations(); |
| 172 } | 170 } |
| 173 | 171 |
| 174 void testWriteAll(List<int> requestData) { | |
| 175 reset(); | |
| 176 httpParser.writeList(requestData, 0, requestData.length); | |
| 177 checkExpectations(); | |
| 178 } | |
| 179 | |
| 180 // Test parsing the request three times delivering the data in | 172 // Test parsing the request three times delivering the data in |
| 181 // different chunks. | 173 // different chunks. |
| 182 List<int> responseData = response.charCodes(); | 174 List<int> responseData = response.charCodes(); |
| 183 testWrite(responseData); | 175 testWrite(responseData); |
| 184 testWrite(responseData, 10); | 176 testWrite(responseData, 10); |
| 185 testWrite(responseData, 1); | 177 testWrite(responseData, 1); |
| 186 } | 178 } |
| 187 | 179 |
| 188 static void testParseRequest() { | 180 static void testParseRequest() { |
| 189 String request; | 181 String request; |
| 190 Map headers; | 182 Map headers; |
| 191 request = "GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; | 183 request = "GET / HTTP/1.1\r\n\r\n"; |
| 192 _testParseRequest(request, "GET", "/"); | 184 _testParseRequest(request, "GET", "/"); |
| 193 | 185 |
| 194 request = "POST / HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; | 186 request = "POST / HTTP/1.1\r\n\r\n"; |
| 195 _testParseRequest(request, "POST", "/"); | 187 _testParseRequest(request, "POST", "/"); |
| 196 | 188 |
| 197 request = "GET /index.html HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; | 189 request = "GET /index.html HTTP/1.1\r\n\r\n"; |
| 198 _testParseRequest(request, "GET", "/index.html"); | 190 _testParseRequest(request, "GET", "/index.html"); |
| 199 | 191 |
| 200 request = "POST /index.html HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; | 192 request = "POST /index.html HTTP/1.1\r\n\r\n"; |
| 201 _testParseRequest(request, "POST", "/index.html"); | 193 _testParseRequest(request, "POST", "/index.html"); |
| 202 | 194 |
| 203 request = "H /index.html HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; | 195 request = "H /index.html HTTP/1.1\r\n\r\n"; |
| 204 _testParseRequest(request, "H", "/index.html"); | 196 _testParseRequest(request, "H", "/index.html"); |
| 205 | 197 |
| 206 request = "HT /index.html HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; | 198 request = "HT /index.html HTTP/1.1\r\n\r\n"; |
| 207 _testParseRequest(request, "HT", "/index.html"); | 199 _testParseRequest(request, "HT", "/index.html"); |
| 208 | 200 |
| 209 request = "HTT /index.html HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; | 201 request = "HTT /index.html HTTP/1.1\r\n\r\n"; |
| 210 _testParseRequest(request, "HTT", "/index.html"); | 202 _testParseRequest(request, "HTT", "/index.html"); |
| 211 | 203 |
| 212 request = "HTTP /index.html HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; | 204 request = "HTTP /index.html HTTP/1.1\r\n\r\n"; |
| 213 _testParseRequest(request, "HTTP", "/index.html"); | 205 _testParseRequest(request, "HTTP", "/index.html"); |
| 214 | 206 |
| 215 request = """ | 207 request = """ |
| 216 POST /test HTTP/1.1\r | 208 POST /test HTTP/1.1\r |
| 217 AAA: AAA\r | 209 AAA: AAA\r |
| 218 Content-Length: 0\r | |
| 219 \r | 210 \r |
| 220 """; | 211 """; |
| 221 _testParseRequest(request, "POST", "/test"); | 212 _testParseRequest(request, "POST", "/test"); |
| 222 | 213 |
| 223 request = """ | 214 request = """ |
| 224 POST /test HTTP/1.1\r | 215 POST /test HTTP/1.1\r |
| 225 content-length: 0\r | |
| 226 \r | 216 \r |
| 227 """; | 217 """; |
| 228 _testParseRequest(request, "POST", "/test"); | 218 _testParseRequest(request, "POST", "/test"); |
| 229 | 219 |
| 230 request = """ | 220 request = """ |
| 231 POST /test HTTP/1.1\r | 221 POST /test HTTP/1.1\r |
| 232 Content-Length: 0\r | |
| 233 Header-A: AAA\r | 222 Header-A: AAA\r |
| 234 X-Header-B: bbb\r | 223 X-Header-B: bbb\r |
| 235 \r | 224 \r |
| 236 """; | 225 """; |
| 237 headers = new Map(); | 226 headers = new Map(); |
| 238 headers["header-a"] = "AAA"; | 227 headers["header-a"] = "AAA"; |
| 239 headers["x-header-b"] = "bbb"; | 228 headers["x-header-b"] = "bbb"; |
| 240 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); | 229 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); |
| 241 | 230 |
| 242 request = """ | 231 request = """ |
| 243 POST /test HTTP/1.1\r | 232 POST /test HTTP/1.1\r |
| 244 Content-Length: 0\r | |
| 245 Header-A: AAA\r | 233 Header-A: AAA\r |
| 246 X-Header-B:\t \t bbb\r | 234 X-Header-B:\t \t bbb\r |
| 247 \r | 235 \r |
| 248 """; | 236 """; |
| 249 headers = new Map(); | 237 headers = new Map(); |
| 250 headers["header-a"] = "AAA"; | 238 headers["header-a"] = "AAA"; |
| 251 headers["x-header-b"] = "bbb"; | 239 headers["x-header-b"] = "bbb"; |
| 252 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); | 240 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); |
| 253 | 241 |
| 254 request = """ | 242 request = """ |
| 255 POST /test HTTP/1.1\r | 243 POST /test HTTP/1.1\r |
| 256 Content-Length: 0\r | |
| 257 Header-A: AA\r | 244 Header-A: AA\r |
| 258 A\r | 245 A\r |
| 259 X-Header-B: b\r | 246 X-Header-B: b\r |
| 260 b\r | 247 b\r |
| 261 \t b\r | 248 \t b\r |
| 262 \r | 249 \r |
| 263 """; | 250 """; |
| 264 headers = new Map(); | 251 headers = new Map(); |
| 265 headers["header-a"] = "AAA"; | 252 headers["header-a"] = "AAA"; |
| 266 headers["x-header-b"] = "bbb"; | 253 headers["x-header-b"] = "bbb"; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 012345678901234567890123456789\r | 328 012345678901234567890123456789\r |
| 342 1e\r | 329 1e\r |
| 343 012345678901234567890123456789\r | 330 012345678901234567890123456789\r |
| 344 0\r\n\r\n"""; | 331 0\r\n\r\n"""; |
| 345 _testParseRequest(request, | 332 _testParseRequest(request, |
| 346 "POST", | 333 "POST", |
| 347 "/test", | 334 "/test", |
| 348 expectedContentLength: -1, | 335 expectedContentLength: -1, |
| 349 expectedBytesReceived: 60, | 336 expectedBytesReceived: 60, |
| 350 chunked: true); | 337 chunked: true); |
| 338 |
| 339 // Test chunk extensions in chunked encoding. |
| 340 request = """ |
| 341 POST /test HTTP/1.1\r |
| 342 Transfer-Encoding: chunked\r |
| 343 \r |
| 344 1E;xxx\r |
| 345 012345678901234567890123456789\r |
| 346 1E;yyy=zzz\r |
| 347 012345678901234567890123456789\r |
| 348 0\r\n\r\n"""; |
| 349 _testParseRequest(request, |
| 350 "POST", |
| 351 "/test", |
| 352 expectedContentLength: -1, |
| 353 expectedBytesReceived: 60, |
| 354 chunked: true); |
| 351 } | 355 } |
| 352 | 356 |
| 353 static void testParseResponse() { | 357 static void testParseResponse() { |
| 354 String response; | 358 String response; |
| 355 Map headers; | 359 Map headers; |
| 360 response = "HTTP/1.1 100 Continue\r\nContent-Length: 0\r\n\r\n"; |
| 361 _testParseResponse(response, 100, "Continue", expectedContentLength: 0); |
| 362 |
| 363 response = "HTTP/1.1 100 Continue\r\nContent-Length: 10\r\n\r\n"; |
| 364 _testParseResponse(response, |
| 365 100, |
| 366 "Continue", |
| 367 expectedContentLength: 10, |
| 368 expectedBytesReceived: 0); |
| 369 |
| 370 response = "HTTP/1.1 204 No Content\r\nContent-Length: 11\r\n\r\n"; |
| 371 _testParseResponse(response, |
| 372 204, |
| 373 "No Content", |
| 374 expectedContentLength: 11, |
| 375 expectedBytesReceived: 0); |
| 376 |
| 377 response = "HTTP/1.1 304 Not Modified\r\nContent-Length: 12\r\n\r\n"; |
| 378 _testParseResponse(response, |
| 379 304, |
| 380 "Not Modified", |
| 381 expectedContentLength: 12, |
| 382 expectedBytesReceived: 0); |
| 383 |
| 356 response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; | 384 response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; |
| 357 _testParseResponse(response, 200, "OK"); | 385 _testParseResponse(response, 200, "OK", expectedContentLength: 0); |
| 358 | 386 |
| 359 response = "HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"; | 387 response = "HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"; |
| 360 _testParseResponse(response, 404, "Not found"); | 388 _testParseResponse(response, 404, "Not found", expectedContentLength: 0); |
| 361 | 389 |
| 362 response = "HTTP/1.1 500 Server error\r\nContent-Length: 0\r\n\r\n"; | 390 response = "HTTP/1.1 500 Server error\r\nContent-Length: 0\r\n\r\n"; |
| 363 _testParseResponse(response, 500, "Server error"); | 391 _testParseResponse(response, 500, "Server error", expectedContentLength: 0); |
| 392 |
| 393 // Test response to HEAD request. |
| 394 response = """ |
| 395 HTTP/1.1 200 OK\r |
| 396 Content-Length: 20\r |
| 397 Content-Type: text/html\r |
| 398 \r\n"""; |
| 399 headers = new Map(); |
| 400 headers["content-length"] = "20"; |
| 401 headers["content-type"] = "text/html"; |
| 402 _testParseResponse(response, |
| 403 200, |
| 404 "OK", |
| 405 responseToMethod: "HEAD", |
| 406 expectedContentLength: 20, |
| 407 expectedBytesReceived: 0, |
| 408 expectedHeaders: headers); |
| 364 | 409 |
| 365 // Test content. | 410 // Test content. |
| 366 response = """ | 411 response = """ |
| 367 HTTP/1.1 200 OK\r | 412 HTTP/1.1 200 OK\r |
| 368 Content-Length: 20\r | 413 Content-Length: 20\r |
| 369 \r | 414 \r |
| 370 01234567890123456789"""; | 415 01234567890123456789"""; |
| 371 | |
| 372 _testParseResponse(response, | 416 _testParseResponse(response, |
| 373 200, | 417 200, |
| 374 "OK", | 418 "OK", |
| 375 expectedContentLength: 20, | 419 expectedContentLength: 20, |
| 376 expectedBytesReceived: 20); | 420 expectedBytesReceived: 20); |
| 377 | 421 |
| 378 // Test upper and lower case hex digits in chunked encoding. | 422 // Test upper and lower case hex digits in chunked encoding. |
| 379 response = """ | 423 response = """ |
| 380 HTTP/1.1 200 OK\r | 424 HTTP/1.1 200 OK\r |
| 381 Transfer-Encoding: chunked\r | 425 Transfer-Encoding: chunked\r |
| (...skipping 24 matching lines...) Expand all Loading... |
| 406 expectedContentLength: -1, | 450 expectedContentLength: -1, |
| 407 expectedBytesReceived: 59, | 451 expectedBytesReceived: 59, |
| 408 close: true); | 452 close: true); |
| 409 } | 453 } |
| 410 } | 454 } |
| 411 | 455 |
| 412 | 456 |
| 413 void main() { | 457 void main() { |
| 414 HttpParserTest.runAllTests(); | 458 HttpParserTest.runAllTests(); |
| 415 } | 459 } |
| OLD | NEW |