| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:math'; | 6 import 'dart:math'; |
| 7 import 'dart:scalarlist'; | 7 import 'dart:scalarlist'; |
| 8 import 'dart:isolate'; |
| 9 import 'dart:uri'; |
| 8 | 10 |
| 11 part '../../../sdk/lib/io/io_stream_consumer.dart'; |
| 9 part '../../../sdk/lib/io/http.dart'; | 12 part '../../../sdk/lib/io/http.dart'; |
| 13 part '../../../sdk/lib/io/http_impl.dart'; |
| 10 part '../../../sdk/lib/io/http_headers.dart'; | 14 part '../../../sdk/lib/io/http_headers.dart'; |
| 11 part '../../../sdk/lib/io/http_parser.dart'; | 15 part '../../../sdk/lib/io/http_parser.dart'; |
| 16 part '../../../sdk/lib/io/socket.dart'; |
| 12 | 17 |
| 13 class HttpParserTest { | 18 class HttpParserTest { |
| 14 static void runAllTests() { | 19 static void runAllTests() { |
| 15 testParseRequest(); | 20 testParseRequest(); |
| 16 testParseResponse(); | 21 testParseResponse(); |
| 17 testParseInvalidRequest(); | 22 testParseInvalidRequest(); |
| 18 testParseInvalidResponse(); | 23 testParseInvalidResponse(); |
| 19 } | 24 } |
| 20 | 25 |
| 21 static void _testParseRequest(String request, | 26 static void _testParseRequest(String request, |
| 22 String expectedMethod, | 27 String expectedMethod, |
| 23 String expectedUri, | 28 String expectedUri, |
| 24 {int expectedContentLength: 0, | 29 {int expectedTransferLength: 0, |
| 25 int expectedBytesReceived: 0, | 30 int expectedBytesReceived: 0, |
| 26 Map expectedHeaders: null, | 31 Map expectedHeaders: null, |
| 27 bool chunked: false, | 32 bool chunked: false, |
| 28 bool upgrade: false, | 33 bool upgrade: false, |
| 29 int unparsedLength: 0, | 34 int unparsedLength: 0, |
| 30 bool connectionClose: false, | 35 bool connectionClose: false, |
| 31 String expectedVersion: "1.1"}) { | 36 String expectedVersion: "1.1"}) { |
| 32 _HttpParser httpParser; | 37 StreamController controller; |
| 33 bool headersCompleteCalled; | 38 void reset() { |
| 34 bool dataEndCalled; | 39 _HttpParser httpParser = new _HttpParser.requestParser(); |
| 35 String method; | 40 controller = new StreamController(); |
| 36 String uri; | 41 var port1 = new ReceivePort(); |
| 37 String version; | 42 var port2 = new ReceivePort(); |
| 38 HttpHeaders headers; | |
| 39 int contentLength; | |
| 40 int bytesReceived; | |
| 41 | 43 |
| 42 void reset() { | 44 String method; |
| 43 httpParser = new _HttpParser.requestParser(); | 45 Uri uri; |
| 44 httpParser.requestStart = (m, u, v, h, b) { | 46 HttpHeaders headers; |
| 45 method = m; | 47 int contentLength; |
| 46 uri = u; | 48 int bytesReceived; |
| 47 version = v; | 49 int unparsedBytesReceived; |
| 48 headers = h; | 50 bool upgraded; |
| 49 headersCompleteCalled = true; | 51 |
| 52 controller.stream.pipe(httpParser); |
| 53 var subscription = httpParser.listen((incoming) { |
| 54 method = incoming.method; |
| 55 uri = incoming.uri; |
| 56 headers = incoming.headers; |
| 57 upgraded = incoming.upgraded; |
| 58 Expect.equals(upgrade, upgraded); |
| 59 |
| 50 if (!chunked) { | 60 if (!chunked) { |
| 51 Expect.equals(expectedContentLength, httpParser.contentLength); | 61 Expect.equals(expectedTransferLength, incoming.transferLength); |
| 52 } else { | 62 } else { |
| 53 Expect.equals(-1, httpParser.contentLength); | 63 Expect.equals(-1, incoming.transferLength); |
| 54 } | 64 } |
| 55 if (expectedHeaders != null) { | 65 if (expectedHeaders != null) { |
| 56 expectedHeaders.forEach( | 66 expectedHeaders.forEach( |
| 57 (String name, String value) => | 67 (String name, String value) => |
| 58 Expect.equals(value, headers[name][0])); | 68 Expect.equals(value, headers[name][0])); |
| 59 } | 69 } |
| 60 Expect.equals(upgrade, httpParser.upgrade); | 70 incoming.listen( |
| 61 Expect.equals(connectionClose, !httpParser.persistentConnection); | 71 (List<int> data) { |
| 62 headersCompleteCalled = true; | 72 Expect.isFalse(upgraded); |
| 63 }; | 73 bytesReceived += data.length; |
| 64 httpParser.responseStart = (s, r, v, h, b) { | 74 }, |
| 65 Expect.fail("Expected request"); | 75 onDone: () { |
| 66 }; | 76 Expect.isFalse(upgraded); |
| 67 httpParser.dataReceived = (List<int> data) { | 77 port2.close(); |
| 68 Expect.isTrue(headersCompleteCalled); | 78 }); |
| 69 bytesReceived += data.length; | |
| 70 }; | |
| 71 httpParser.dataEnd = (close) { | |
| 72 Expect.isFalse(close); | |
| 73 dataEndCalled = true; | |
| 74 }; | |
| 75 httpParser.closed = () { }; | |
| 76 | 79 |
| 77 headersCompleteCalled = false; | 80 if (upgraded) { |
| 78 dataEndCalled = false; | 81 port1.close(); |
| 82 httpParser.detachIncoming().listen( |
| 83 (List<int> data) { |
| 84 unparsedBytesReceived += data.length; |
| 85 }, |
| 86 onDone: () { |
| 87 Expect.equals(unparsedLength, unparsedBytesReceived); |
| 88 port2.close(); |
| 89 }); |
| 90 } |
| 91 |
| 92 incoming.dataDone.then((_) { |
| 93 port1.close(); |
| 94 Expect.isFalse(upgraded); |
| 95 Expect.equals(expectedMethod, method); |
| 96 Expect.stringEquals(expectedUri, uri.toString()); |
| 97 Expect.equals(expectedVersion, headers.protocolVersion); |
| 98 if (upgrade) { |
| 99 Expect.equals(0, bytesReceived); |
| 100 // port1 is closed by the listener on the detached data. |
| 101 } else { |
| 102 Expect.equals(expectedBytesReceived, bytesReceived); |
| 103 } |
| 104 }); |
| 105 }); |
| 106 |
| 79 method = null; | 107 method = null; |
| 80 uri = null; | 108 uri = null; |
| 81 headers = null; | 109 headers = null; |
| 82 bytesReceived = 0; | 110 bytesReceived = 0; |
| 111 unparsedBytesReceived = 0; |
| 112 upgraded = false; |
| 83 } | 113 } |
| 84 | 114 |
| 85 void testWrite(List<int> requestData, [int chunkSize = -1]) { | 115 void testWrite(List<int> requestData, [int chunkSize = -1]) { |
| 86 if (chunkSize == -1) chunkSize = requestData.length; | 116 if (chunkSize == -1) chunkSize = requestData.length; |
| 87 reset(); | 117 reset(); |
| 88 int written = 0; | |
| 89 int unparsed; | |
| 90 for (int pos = 0; pos < requestData.length; pos += chunkSize) { | 118 for (int pos = 0; pos < requestData.length; pos += chunkSize) { |
| 91 int remaining = requestData.length - pos; | 119 int remaining = requestData.length - pos; |
| 92 int writeLength = min(chunkSize, remaining); | 120 int writeLength = min(chunkSize, remaining); |
| 93 written += writeLength; | 121 controller.add(requestData.getRange(pos, writeLength)); |
| 94 httpParser.streamData(requestData.getRange(pos, writeLength)); | |
| 95 unparsed = httpParser.readUnparsedData().length; | |
| 96 if (httpParser.upgrade) { | |
| 97 unparsed += requestData.length - written; | |
| 98 break; | |
| 99 } else { | |
| 100 Expect.equals(0, unparsed); | |
| 101 } | |
| 102 } | 122 } |
| 103 Expect.equals(expectedMethod, method); | 123 controller.close(); |
| 104 Expect.equals(expectedUri, uri); | |
| 105 Expect.equals(expectedVersion, version); | |
| 106 Expect.isTrue(headersCompleteCalled); | |
| 107 Expect.equals(expectedBytesReceived, bytesReceived); | |
| 108 if (!upgrade) Expect.isTrue(dataEndCalled); | |
| 109 if (unparsedLength == 0) { | |
| 110 Expect.equals(0, unparsed); | |
| 111 } else { | |
| 112 Expect.equals(unparsedLength, unparsed); | |
| 113 } | |
| 114 } | 124 } |
| 115 | 125 |
| 116 // Test parsing the request three times delivering the data in | 126 // Test parsing the request three times delivering the data in |
| 117 // different chunks. | 127 // different chunks. |
| 118 List<int> requestData = request.charCodes; | 128 List<int> requestData = request.charCodes; |
| 119 testWrite(requestData); | 129 testWrite(requestData); |
| 120 testWrite(requestData, 10); | 130 testWrite(requestData, 10); |
| 121 testWrite(requestData, 1); | 131 testWrite(requestData, 1); |
| 122 } | 132 } |
| 123 | 133 |
| 124 static void _testParseInvalidRequest(String request) { | 134 static void _testParseInvalidRequest(String request) { |
| 125 _HttpParser httpParser; | 135 _HttpParser httpParser; |
| 126 bool errorCalled; | 136 bool errorCalled; |
| 137 StreamController controller; |
| 127 | 138 |
| 128 void reset() { | 139 void reset() { |
| 129 httpParser = new _HttpParser.requestParser(); | 140 httpParser = new _HttpParser.requestParser(); |
| 130 httpParser.responseStart = (s, r, v, h, b) { | 141 controller = new StreamController(); |
| 142 var port = new ReceivePort(); |
| 143 controller.stream.pipe(httpParser); |
| 144 var subscription = httpParser.listen((incoming) { |
| 131 Expect.fail("Expected request"); | 145 Expect.fail("Expected request"); |
| 132 }; | 146 }); |
| 133 httpParser.error = (e) { | 147 subscription.onError((e) { |
| 134 errorCalled = true; | 148 errorCalled = true; |
| 135 }; | 149 }); |
| 136 httpParser.closed = () { }; | 150 subscription.onDone(() { |
| 137 | 151 port.close(); |
| 152 Expect.isTrue(errorCalled); |
| 153 }); |
| 138 errorCalled = false; | 154 errorCalled = false; |
| 139 } | 155 } |
| 140 | 156 |
| 141 void testWrite(List<int> requestData, [int chunkSize = -1]) { | 157 void testWrite(List<int> requestData, [int chunkSize = -1]) { |
| 142 if (chunkSize == -1) chunkSize = requestData.length; | 158 if (chunkSize == -1) chunkSize = requestData.length; |
| 143 reset(); | 159 reset(); |
| 144 for (int pos = 0; | 160 for (int pos = 0; |
| 145 pos < requestData.length && !errorCalled; | 161 pos < requestData.length && !errorCalled; |
| 146 pos += chunkSize) { | 162 pos += chunkSize) { |
| 147 int remaining = requestData.length - pos; | 163 int remaining = requestData.length - pos; |
| 148 int writeLength = min(chunkSize, remaining); | 164 int writeLength = min(chunkSize, remaining); |
| 149 httpParser.streamData(requestData.getRange(pos, writeLength)); | 165 controller.add(requestData.getRange(pos, writeLength)); |
| 150 } | 166 } |
| 151 Expect.isTrue(errorCalled); | 167 controller.close(); |
| 152 } | 168 } |
| 153 | 169 |
| 154 // Test parsing the request three times delivering the data in | 170 // Test parsing the request three times delivering the data in |
| 155 // different chunks. | 171 // different chunks. |
| 156 List<int> requestData = request.charCodes; | 172 List<int> requestData = request.charCodes; |
| 157 testWrite(requestData); | 173 testWrite(requestData); |
| 158 testWrite(requestData, 10); | 174 testWrite(requestData, 10); |
| 159 testWrite(requestData, 1); | 175 testWrite(requestData, 1); |
| 160 } | 176 } |
| 161 | 177 |
| 162 static void _testParseResponse(String response, | 178 static void _testParseResponse(String response, |
| 163 int expectedStatusCode, | 179 int expectedStatusCode, |
| 164 String expectedReasonPhrase, | 180 String expectedReasonPhrase, |
| 165 {int expectedContentLength: -1, | 181 {int expectedTransferLength: 0, |
| 166 int expectedBytesReceived: 0, | 182 int expectedBytesReceived: 0, |
| 167 Map expectedHeaders: null, | 183 Map expectedHeaders: null, |
| 168 bool chunked: false, | 184 bool chunked: false, |
| 169 bool close: false, | 185 bool close: false, |
| 170 String responseToMethod: null, | 186 String responseToMethod: null, |
| 171 bool connectionClose: false, | 187 bool connectionClose: false, |
| 172 bool upgrade: false, | 188 bool upgrade: false, |
| 173 int unparsedLength: 0, | 189 int unparsedLength: 0, |
| 174 String expectedVersion: "1.1"}) { | 190 String expectedVersion: "1.1"}) { |
| 175 _HttpParser httpParser; | 191 _HttpParser httpParser; |
| 176 bool headersCompleteCalled; | 192 bool headersCompleteCalled; |
| 177 bool dataEndCalled; | 193 bool dataEndCalled; |
| 178 bool dataEndClose; | 194 bool dataEndClose; |
| 179 int statusCode; | 195 int statusCode; |
| 180 String reasonPhrase; | 196 String reasonPhrase; |
| 181 String version; | |
| 182 HttpHeaders headers; | 197 HttpHeaders headers; |
| 183 int contentLength; | 198 int contentLength; |
| 184 int bytesReceived; | 199 int bytesReceived; |
| 200 StreamController controller; |
| 201 bool upgraded; |
| 185 | 202 |
| 186 void reset() { | 203 void reset() { |
| 187 httpParser = new _HttpParser.responseParser(); | 204 httpParser = new _HttpParser.responseParser(); |
| 188 if (responseToMethod != null) { | 205 controller = new StreamController(); |
| 189 httpParser.responseToMethod = responseToMethod; | 206 var port = new ReceivePort(); |
| 190 } | 207 controller.stream.pipe(httpParser); |
| 191 httpParser.requestStart = (m, u, v, h, b) { | 208 var subscription = httpParser.listen((incoming) { |
| 192 Expect.fail("Expected response"); | 209 port.close(); |
| 193 }; | 210 statusCode = incoming.statusCode; |
| 194 httpParser.responseStart = (s, r, v, h, b) { | 211 reasonPhrase = incoming.reasonPhrase; |
| 195 statusCode = s; | 212 headers = incoming.headers; |
| 196 reasonPhrase = r; | |
| 197 version = v; | |
| 198 headers = h; | |
| 199 Expect.isFalse(headersCompleteCalled); | 213 Expect.isFalse(headersCompleteCalled); |
| 200 if (!chunked && !close) { | 214 if (!chunked && !close) { |
| 201 Expect.equals(expectedContentLength, httpParser.contentLength); | 215 Expect.equals(expectedTransferLength, incoming.transferLength); |
| 202 } else { | 216 } else { |
| 203 Expect.equals(-1, httpParser.contentLength); | 217 Expect.equals(-1, incoming.transferLength); |
| 204 } | 218 } |
| 205 if (expectedHeaders != null) { | 219 if (expectedHeaders != null) { |
| 206 expectedHeaders.forEach((String name, String value) { | 220 expectedHeaders.forEach((String name, String value) { |
| 207 Expect.equals(value, headers[name][0]); | 221 Expect.equals(value, headers[name][0]); |
| 208 }); | 222 }); |
| 209 } | 223 } |
| 210 Expect.equals(upgrade, httpParser.upgrade); | 224 Expect.equals(upgrade, httpParser.upgrade); |
| 211 headersCompleteCalled = true; | 225 headersCompleteCalled = true; |
| 212 }; | 226 incoming.listen( |
| 213 httpParser.dataReceived = (List<int> data) { | 227 (List<int> data) { |
| 228 Expect.isTrue(headersCompleteCalled); |
| 229 bytesReceived += data.length; |
| 230 }, |
| 231 onDone: () { |
| 232 dataEndCalled = true; |
| 233 dataEndClose = close; |
| 234 }); |
| 235 }); |
| 236 |
| 237 subscription.onDone(() { |
| 238 Expect.equals(expectedVersion, headers.protocolVersion); |
| 239 Expect.equals(expectedStatusCode, statusCode); |
| 240 Expect.equals(expectedReasonPhrase, reasonPhrase); |
| 214 Expect.isTrue(headersCompleteCalled); | 241 Expect.isTrue(headersCompleteCalled); |
| 215 bytesReceived += data.length; | 242 Expect.equals(expectedBytesReceived, bytesReceived); |
| 216 }; | 243 if (!upgrade) { |
| 217 httpParser.dataEnd = (close) { | 244 Expect.isTrue(dataEndCalled); |
| 218 dataEndCalled = true; | 245 if (close) Expect.isTrue(dataEndClose); |
| 219 dataEndClose = close; | 246 Expect.equals(dataEndClose, connectionClose); |
| 220 }; | 247 } |
| 221 httpParser.closed = () { }; | 248 }); |
| 222 | 249 |
| 223 headersCompleteCalled = false; | 250 headersCompleteCalled = false; |
| 224 dataEndCalled = false; | 251 dataEndCalled = false; |
| 225 dataEndClose = null; | 252 dataEndClose = null; |
| 226 statusCode = -1; | 253 statusCode = -1; |
| 227 reasonPhrase = null; | 254 reasonPhrase = null; |
| 228 headers = null; | 255 headers = null; |
| 229 bytesReceived = 0; | 256 bytesReceived = 0; |
| 230 } | 257 } |
| 231 | 258 |
| 232 void testWrite(List<int> requestData, [int chunkSize = -1]) { | 259 void testWrite(List<int> requestData, [int chunkSize = -1]) { |
| 233 if (chunkSize == -1) chunkSize = requestData.length; | 260 if (chunkSize == -1) chunkSize = requestData.length; |
| 234 reset(); | 261 reset(); |
| 235 int written = 0; | |
| 236 int unparsed; | |
| 237 for (int pos = 0; pos < requestData.length; pos += chunkSize) { | 262 for (int pos = 0; pos < requestData.length; pos += chunkSize) { |
| 238 int remaining = requestData.length - pos; | 263 int remaining = requestData.length - pos; |
| 239 int writeLength = min(chunkSize, remaining); | 264 int writeLength = min(chunkSize, remaining); |
| 240 written += writeLength; | 265 controller.add(requestData.getRange(pos, writeLength)); |
| 241 httpParser.streamData(requestData.getRange(pos, writeLength)); | 266 |
| 242 unparsed = httpParser.readUnparsedData().length; | |
| 243 if (httpParser.upgrade) { | |
| 244 unparsed += requestData.length - written; | |
| 245 break; | |
| 246 } else { | |
| 247 Expect.equals(0, unparsed); | |
| 248 } | |
| 249 } | 267 } |
| 250 if (close) httpParser.streamDone(); | 268 if (close) controller.close(); |
| 251 Expect.equals(expectedVersion, version); | |
| 252 Expect.equals(expectedStatusCode, statusCode); | |
| 253 Expect.equals(expectedReasonPhrase, reasonPhrase); | |
| 254 Expect.isTrue(headersCompleteCalled); | |
| 255 Expect.equals(expectedBytesReceived, bytesReceived); | |
| 256 if (!upgrade) { | |
| 257 Expect.isTrue(dataEndCalled); | |
| 258 if (close) Expect.isTrue(dataEndClose); | |
| 259 Expect.equals(dataEndClose, connectionClose); | |
| 260 } | |
| 261 if (unparsedLength == 0) { | |
| 262 Expect.equals(0, unparsed); | |
| 263 } else { | |
| 264 Expect.equals(unparsedLength, unparsed); | |
| 265 } | |
| 266 } | 269 } |
| 267 | 270 |
| 268 // Test parsing the request three times delivering the data in | 271 // Test parsing the request three times delivering the data in |
| 269 // different chunks. | 272 // different chunks. |
| 270 List<int> responseData = response.charCodes; | 273 List<int> responseData = response.charCodes; |
| 271 testWrite(responseData); | 274 testWrite(responseData); |
| 272 testWrite(responseData, 10); | 275 testWrite(responseData, 10); |
| 273 testWrite(responseData, 1); | 276 testWrite(responseData, 1); |
| 274 } | 277 } |
| 275 | 278 |
| 276 static void _testParseInvalidResponse(String response, [bool close = false]) { | 279 static void _testParseInvalidResponse(String response, [bool close = false]) { |
| 277 _HttpParser httpParser; | 280 _HttpParser httpParser; |
| 278 bool errorCalled; | 281 bool errorCalled; |
| 282 StreamController controller; |
| 279 | 283 |
| 280 void reset() { | 284 void reset() { |
| 281 httpParser = new _HttpParser.responseParser(); | 285 httpParser = new _HttpParser.responseParser(); |
| 282 httpParser.requestStart = (m, u, v, h, b) { | 286 controller = new StreamController(); |
| 283 Expect.fail("Expected response"); | 287 var port = new ReceivePort(); |
| 284 }; | 288 controller.stream.pipe(httpParser); |
| 285 httpParser.error = (e) => errorCalled = true; | 289 var subscription = httpParser.listen((incoming) { |
| 286 httpParser.closed = () { }; | 290 incoming.listen( |
| 287 | 291 (data) {}, |
| 292 onError: (e) { |
| 293 Expect.isFalse(errorCalled); |
| 294 errorCalled = true; |
| 295 }); |
| 296 }); |
| 297 subscription.onError((e) { |
| 298 Expect.isFalse(errorCalled); |
| 299 errorCalled = true; |
| 300 }); |
| 301 subscription.onDone(() { |
| 302 port.close(); |
| 303 Expect.isTrue(errorCalled); |
| 304 }); |
| 288 errorCalled = false; | 305 errorCalled = false; |
| 289 } | 306 } |
| 290 | 307 |
| 291 void testWrite(List<int> requestData, [int chunkSize = -1]) { | 308 void testWrite(List<int> requestData, [int chunkSize = -1]) { |
| 292 if (chunkSize == -1) chunkSize = requestData.length; | 309 if (chunkSize == -1) chunkSize = requestData.length; |
| 293 reset(); | 310 reset(); |
| 294 for (int pos = 0; | 311 for (int pos = 0; |
| 295 pos < requestData.length && !errorCalled; | 312 pos < requestData.length && !errorCalled; |
| 296 pos += chunkSize) { | 313 pos += chunkSize) { |
| 297 int remaining = requestData.length - pos; | 314 int remaining = requestData.length - pos; |
| 298 int writeLength = min(chunkSize, remaining); | 315 int writeLength = min(chunkSize, remaining); |
| 299 httpParser.streamData(requestData.getRange(pos, writeLength)); | 316 controller.add(requestData.getRange(pos, writeLength)); |
| 300 } | 317 } |
| 301 if (close) httpParser.streamDone(); | 318 controller.close(); |
| 302 Expect.isTrue(errorCalled); | |
| 303 } | 319 } |
| 304 | 320 |
| 305 // Test parsing the request three times delivering the data in | 321 // Test parsing the request three times delivering the data in |
| 306 // different chunks. | 322 // different chunks. |
| 307 List<int> responseData = response.charCodes; | 323 List<int> responseData = response.charCodes; |
| 308 testWrite(responseData); | 324 testWrite(responseData); |
| 309 testWrite(responseData, 10); | 325 testWrite(responseData, 10); |
| 310 testWrite(responseData, 1); | 326 testWrite(responseData, 1); |
| 311 } | 327 } |
| 312 | 328 |
| 313 static void testParseRequest() { | 329 static void testParseRequest() { |
| 314 String request; | 330 String request; |
| 315 Map headers; | 331 Map headers; |
| 316 var methods = [ | 332 var methods = [ |
| 317 // RFC 2616 methods. | 333 // RFC 2616 methods. |
| 318 "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", | 334 "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", |
| 319 // WebDAV methods from RFC 4918. | 335 // WebDAV methods from RFC 4918. |
| 320 "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", | 336 "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", |
| 321 // WebDAV methods from RFC 5323. | 337 // WebDAV methods from RFC 5323. |
| 322 "SEARCH", | 338 "SEARCH", |
| 323 // Methods with HTTP prefix. | 339 // Methods with HTTP prefix. |
| 324 "H", "HT", "HTT", "HTTP", "HX", "HTX", "HTTX", "HTTPX"]; | 340 "H", "HT", "HTT", "HTTP", "HX", "HTX", "HTTX", "HTTPX"]; |
| 325 methods.forEach((method) { | 341 methods.forEach((method) { |
| 326 request = "$method / HTTP/1.1\r\n\r\n"; | 342 request = "$method / HTTP/1.1\r\n\r\n"; |
| 327 _testParseRequest(request, method, "/"); | 343 _testParseRequest(request, method, "/"); |
| 328 request = "$method /index.html HTTP/1.1\r\n\r\n"; | 344 request = "$method /index.html HTTP/1.1\r\n\r\n"; |
| 329 _testParseRequest(request, method, "/index.html"); | 345 _testParseRequest(request, method, "/index.html"); |
| 330 }); | 346 }); |
| 331 | 347 |
| 348 |
| 332 request = "GET / HTTP/1.0\r\n\r\n"; | 349 request = "GET / HTTP/1.0\r\n\r\n"; |
| 333 _testParseRequest(request, "GET", "/", | 350 _testParseRequest(request, "GET", "/", |
| 334 expectedVersion: "1.0", | 351 expectedVersion: "1.0", |
| 335 connectionClose: true); | 352 connectionClose: true); |
| 336 | 353 |
| 337 request = "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"; | 354 request = "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"; |
| 338 _testParseRequest(request, "GET", "/", expectedVersion: "1.0"); | 355 _testParseRequest(request, "GET", "/", expectedVersion: "1.0"); |
| 339 | 356 |
| 340 request = """ | 357 request = """ |
| 341 POST /test HTTP/1.1\r | 358 POST /test HTTP/1.1\r |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 399 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); | 416 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); |
| 400 | 417 |
| 401 request = """ | 418 request = """ |
| 402 POST /test HTTP/1.1\r | 419 POST /test HTTP/1.1\r |
| 403 Content-Length: 10\r | 420 Content-Length: 10\r |
| 404 \r | 421 \r |
| 405 0123456789"""; | 422 0123456789"""; |
| 406 _testParseRequest(request, | 423 _testParseRequest(request, |
| 407 "POST", | 424 "POST", |
| 408 "/test", | 425 "/test", |
| 409 expectedContentLength: 10, | 426 expectedTransferLength: 10, |
| 410 expectedBytesReceived: 10); | 427 expectedBytesReceived: 10); |
| 411 | 428 |
| 412 // Test connection close header. | 429 // Test connection close header. |
| 413 request = "GET /test HTTP/1.1\r\nConnection: close\r\n\r\n"; | 430 request = "GET /test HTTP/1.1\r\nConnection: close\r\n\r\n"; |
| 414 _testParseRequest(request, "GET", "/test", connectionClose: true); | 431 _testParseRequest(request, "GET", "/test", connectionClose: true); |
| 415 | 432 |
| 416 // Test chunked encoding. | 433 // Test chunked encoding. |
| 417 request = """ | 434 request = """ |
| 418 POST /test HTTP/1.1\r | 435 POST /test HTTP/1.1\r |
| 419 Transfer-Encoding: chunked\r | 436 Transfer-Encoding: chunked\r |
| 420 \r | 437 \r |
| 421 5\r | 438 5\r |
| 422 01234\r | 439 01234\r |
| 423 5\r | 440 5\r |
| 424 56789\r | 441 56789\r |
| 425 0\r\n\r\n"""; | 442 0\r\n\r\n"""; |
| 426 _testParseRequest(request, | 443 _testParseRequest(request, |
| 427 "POST", | 444 "POST", |
| 428 "/test", | 445 "/test", |
| 429 expectedContentLength: -1, | 446 expectedTransferLength: -1, |
| 430 expectedBytesReceived: 10, | 447 expectedBytesReceived: 10, |
| 431 chunked: true); | 448 chunked: true); |
| 432 | 449 |
| 433 // Test mixing chunked encoding and content length (content length | 450 // Test mixing chunked encoding and content length (content length |
| 434 // is ignored). | 451 // is ignored). |
| 435 request = """ | 452 request = """ |
| 436 POST /test HTTP/1.1\r | 453 POST /test HTTP/1.1\r |
| 437 Content-Length: 7\r | 454 Content-Length: 7\r |
| 438 Transfer-Encoding: chunked\r | 455 Transfer-Encoding: chunked\r |
| 439 \r | 456 \r |
| 440 5\r | 457 5\r |
| 441 01234\r | 458 01234\r |
| 442 5\r | 459 5\r |
| 443 56789\r | 460 56789\r |
| 444 0\r\n\r\n"""; | 461 0\r\n\r\n"""; |
| 445 _testParseRequest(request, | 462 _testParseRequest(request, |
| 446 "POST", | 463 "POST", |
| 447 "/test", | 464 "/test", |
| 448 expectedContentLength: -1, | 465 expectedTransferLength: -1, |
| 449 expectedBytesReceived: 10, | 466 expectedBytesReceived: 10, |
| 450 chunked: true); | 467 chunked: true); |
| 451 | 468 |
| 452 // Test mixing chunked encoding and content length (content length | 469 // Test mixing chunked encoding and content length (content length |
| 453 // is ignored). | 470 // is ignored). |
| 454 request = """ | 471 request = """ |
| 455 POST /test HTTP/1.1\r | 472 POST /test HTTP/1.1\r |
| 456 Transfer-Encoding: chunked\r | 473 Transfer-Encoding: chunked\r |
| 457 Content-Length: 3\r | 474 Content-Length: 3\r |
| 458 \r | 475 \r |
| 459 5\r | 476 5\r |
| 460 01234\r | 477 01234\r |
| 461 5\r | 478 5\r |
| 462 56789\r | 479 56789\r |
| 463 0\r\n\r\n"""; | 480 0\r\n\r\n"""; |
| 464 _testParseRequest(request, | 481 _testParseRequest(request, |
| 465 "POST", | 482 "POST", |
| 466 "/test", | 483 "/test", |
| 467 expectedContentLength: -1, | 484 expectedTransferLength: -1, |
| 468 expectedBytesReceived: 10, | 485 expectedBytesReceived: 10, |
| 469 chunked: true); | 486 chunked: true); |
| 470 | 487 |
| 471 // Test upper and lower case hex digits in chunked encoding. | 488 // Test upper and lower case hex digits in chunked encoding. |
| 472 request = """ | 489 request = """ |
| 473 POST /test HTTP/1.1\r | 490 POST /test HTTP/1.1\r |
| 474 Transfer-Encoding: chunked\r | 491 Transfer-Encoding: chunked\r |
| 475 \r | 492 \r |
| 476 1E\r | 493 1E\r |
| 477 012345678901234567890123456789\r | 494 012345678901234567890123456789\r |
| 478 1e\r | 495 1e\r |
| 479 012345678901234567890123456789\r | 496 012345678901234567890123456789\r |
| 480 0\r\n\r\n"""; | 497 0\r\n\r\n"""; |
| 481 _testParseRequest(request, | 498 _testParseRequest(request, |
| 482 "POST", | 499 "POST", |
| 483 "/test", | 500 "/test", |
| 484 expectedContentLength: -1, | 501 expectedTransferLength: -1, |
| 485 expectedBytesReceived: 60, | 502 expectedBytesReceived: 60, |
| 486 chunked: true); | 503 chunked: true); |
| 487 | 504 |
| 488 // Test chunk extensions in chunked encoding. | 505 // Test chunk extensions in chunked encoding. |
| 489 request = """ | 506 request = """ |
| 490 POST /test HTTP/1.1\r | 507 POST /test HTTP/1.1\r |
| 491 Transfer-Encoding: chunked\r | 508 Transfer-Encoding: chunked\r |
| 492 \r | 509 \r |
| 493 1E;xxx\r | 510 1E;xxx\r |
| 494 012345678901234567890123456789\r | 511 012345678901234567890123456789\r |
| 495 1E;yyy=zzz\r | 512 1E;yyy=zzz\r |
| 496 012345678901234567890123456789\r | 513 012345678901234567890123456789\r |
| 497 0\r\n\r\n"""; | 514 0\r\n\r\n"""; |
| 498 _testParseRequest(request, | 515 _testParseRequest(request, |
| 499 "POST", | 516 "POST", |
| 500 "/test", | 517 "/test", |
| 501 expectedContentLength: -1, | 518 expectedTransferLength: -1, |
| 502 expectedBytesReceived: 60, | 519 expectedBytesReceived: 60, |
| 503 chunked: true); | 520 chunked: true); |
| 504 | 521 |
| 505 // Test HTTP upgrade. | 522 // Test HTTP upgrade. |
| 506 request = """ | 523 request = """ |
| 507 GET /irc HTTP/1.1\r | 524 GET /irc HTTP/1.1\r |
| 508 Upgrade: irc/1.2\r | 525 Upgrade: irc/1.2\r |
| 509 Connection: Upgrade\r | 526 Connection: Upgrade\r |
| 510 \r\n\x01\x01\x01\x01\x01\x02\x02\x02\x02\xFF"""; | 527 \r\n\x01\x01\x01\x01\x01\x02\x02\x02\x02\xFF"""; |
| 511 headers = new Map(); | 528 headers = new Map(); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 579 "/chat", | 596 "/chat", |
| 580 expectedHeaders: headers, | 597 expectedHeaders: headers, |
| 581 upgrade: true, | 598 upgrade: true, |
| 582 unparsedLength: 7); | 599 unparsedLength: 7); |
| 583 } | 600 } |
| 584 | 601 |
| 585 static void testParseResponse() { | 602 static void testParseResponse() { |
| 586 String response; | 603 String response; |
| 587 Map headers; | 604 Map headers; |
| 588 response = "HTTP/1.1 100 Continue\r\nContent-Length: 0\r\n\r\n"; | 605 response = "HTTP/1.1 100 Continue\r\nContent-Length: 0\r\n\r\n"; |
| 589 _testParseResponse(response, 100, "Continue", expectedContentLength: 0); | 606 _testParseResponse(response, 100, "Continue"); |
| 607 |
| 608 response = "HTTP/1.1 100 Continue\r\nContent-Length: 0\r\n\r\n"; |
| 609 _testParseResponse(response, 100, "Continue"); |
| 590 | 610 |
| 591 response = "HTTP/1.1 100 Continue\r\nContent-Length: 10\r\n\r\n"; | 611 response = "HTTP/1.1 100 Continue\r\nContent-Length: 10\r\n\r\n"; |
| 592 _testParseResponse(response, | 612 _testParseResponse(response, |
| 593 100, | 613 100, |
| 594 "Continue", | 614 "Continue", |
| 595 expectedContentLength: 10, | 615 expectedTransferLength: 10, |
| 596 expectedBytesReceived: 0); | 616 expectedBytesReceived: 0); |
| 597 | 617 |
| 598 response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n" | 618 response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n" |
| 599 "Connection: Close\r\n\r\n"; | 619 "Connection: Close\r\n\r\n"; |
| 600 _testParseResponse(response, | 620 _testParseResponse(response, |
| 601 200, | 621 200, |
| 602 "OK", | 622 "OK", |
| 603 expectedContentLength: 0, | |
| 604 connectionClose: true); | 623 connectionClose: true); |
| 605 | 624 |
| 606 response = "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"; | 625 response = "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"; |
| 607 _testParseResponse(response, | 626 _testParseResponse(response, |
| 608 200, | 627 200, |
| 609 "OK", | 628 "OK", |
| 610 expectedContentLength: 0, | |
| 611 expectedVersion: "1.0", | 629 expectedVersion: "1.0", |
| 612 connectionClose: true); | 630 connectionClose: true); |
| 613 | 631 |
| 614 response = "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n" | 632 response = "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n" |
| 615 "Connection: Keep-Alive\r\n\r\n"; | 633 "Connection: Keep-Alive\r\n\r\n"; |
| 616 _testParseResponse(response, | 634 _testParseResponse(response, |
| 617 200, | 635 200, |
| 618 "OK", | 636 "OK", |
| 619 expectedContentLength: 0, | |
| 620 expectedVersion: "1.0"); | 637 expectedVersion: "1.0"); |
| 621 | 638 |
| 622 response = "HTTP/1.1 204 No Content\r\nContent-Length: 11\r\n\r\n"; | 639 response = "HTTP/1.1 204 No Content\r\nContent-Length: 11\r\n\r\n"; |
| 623 _testParseResponse(response, | 640 _testParseResponse(response, |
| 624 204, | 641 204, |
| 625 "No Content", | 642 "No Content", |
| 626 expectedContentLength: 11, | 643 expectedTransferLength: 11, |
| 627 expectedBytesReceived: 0); | 644 expectedBytesReceived: 0); |
| 628 | 645 |
| 629 response = "HTTP/1.1 304 Not Modified\r\nContent-Length: 12\r\n\r\n"; | 646 response = "HTTP/1.1 304 Not Modified\r\nContent-Length: 12\r\n\r\n"; |
| 630 _testParseResponse(response, | 647 _testParseResponse(response, |
| 631 304, | 648 304, |
| 632 "Not Modified", | 649 "Not Modified", |
| 633 expectedContentLength: 12, | 650 expectedTransferLength: 12, |
| 634 expectedBytesReceived: 0); | 651 expectedBytesReceived: 0); |
| 635 | 652 |
| 636 response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; | 653 response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; |
| 637 _testParseResponse(response, 200, "OK", expectedContentLength: 0); | 654 _testParseResponse(response, 200, "OK"); |
| 638 | 655 |
| 639 response = "HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"; | 656 response = "HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"; |
| 640 _testParseResponse(response, 404, "Not found", expectedContentLength: 0); | 657 _testParseResponse(response, 404, "Not found"); |
| 641 | 658 |
| 642 response = "HTTP/1.1 500 Server error\r\nContent-Length: 0\r\n\r\n"; | 659 response = "HTTP/1.1 500 Server error\r\nContent-Length: 0\r\n\r\n"; |
| 643 _testParseResponse(response, 500, "Server error", expectedContentLength: 0); | 660 _testParseResponse(response, 500, "Server error"); |
| 644 | 661 |
| 645 // Test response to HEAD request. | 662 // Test response to HEAD request. |
| 646 response = """ | 663 response = """ |
| 647 HTTP/1.1 200 OK\r | 664 HTTP/1.1 200 OK\r |
| 648 Content-Length: 20\r | 665 Content-Length: 20\r |
| 649 Content-Type: text/html\r | 666 Content-Type: text/html\r |
| 650 \r\n"""; | 667 \r\n"""; |
| 651 headers = new Map(); | 668 headers = new Map(); |
| 652 headers["content-length"] = "20"; | 669 headers["content-length"] = "20"; |
| 653 headers["content-type"] = "text/html"; | 670 headers["content-type"] = "text/html"; |
| 654 _testParseResponse(response, | 671 _testParseResponse(response, |
| 655 200, | 672 200, |
| 656 "OK", | 673 "OK", |
| 657 responseToMethod: "HEAD", | 674 responseToMethod: "HEAD", |
| 658 expectedContentLength: 20, | 675 expectedTransferLength: 20, |
| 659 expectedBytesReceived: 0, | 676 expectedBytesReceived: 0, |
| 660 expectedHeaders: headers); | 677 expectedHeaders: headers); |
| 661 | 678 |
| 662 // Test content. | 679 // Test content. |
| 663 response = """ | 680 response = """ |
| 664 HTTP/1.1 200 OK\r | 681 HTTP/1.1 200 OK\r |
| 665 Content-Length: 20\r | 682 Content-Length: 20\r |
| 666 \r | 683 \r |
| 667 01234567890123456789"""; | 684 01234567890123456789"""; |
| 668 _testParseResponse(response, | 685 _testParseResponse(response, |
| 669 200, | 686 200, |
| 670 "OK", | 687 "OK", |
| 671 expectedContentLength: 20, | 688 expectedTransferLength: 20, |
| 672 expectedBytesReceived: 20); | 689 expectedBytesReceived: 20); |
| 673 | 690 |
| 674 // Test upper and lower case hex digits in chunked encoding. | 691 // Test upper and lower case hex digits in chunked encoding. |
| 675 response = """ | 692 response = """ |
| 676 HTTP/1.1 200 OK\r | 693 HTTP/1.1 200 OK\r |
| 677 Transfer-Encoding: chunked\r | 694 Transfer-Encoding: chunked\r |
| 678 \r | 695 \r |
| 679 1A\r | 696 1A\r |
| 680 01234567890123456789012345\r | 697 01234567890123456789012345\r |
| 681 1f\r | 698 1f\r |
| 682 0123456789012345678901234567890\r | 699 0123456789012345678901234567890\r |
| 683 0\r\n\r\n"""; | 700 0\r\n\r\n"""; |
| 684 _testParseResponse(response, | 701 _testParseResponse(response, |
| 685 200, | 702 200, |
| 686 "OK", | 703 "OK", |
| 687 expectedContentLength: -1, | 704 expectedTransferLength: -1, |
| 688 expectedBytesReceived: 57, | 705 expectedBytesReceived: 57, |
| 689 chunked: true); | 706 chunked: true); |
| 690 | 707 |
| 691 // Test connection close header. | 708 // Test connection close header. |
| 692 response = """ | 709 response = """ |
| 693 HTTP/1.1 200 OK\r | 710 HTTP/1.1 200 OK\r |
| 694 Content-Length: 0\r | 711 Content-Length: 0\r |
| 695 Connection: close\r | 712 Connection: close\r |
| 696 \r\n"""; | 713 \r\n"""; |
| 697 _testParseResponse(response, | 714 _testParseResponse(response, |
| 698 200, | 715 200, |
| 699 "OK", | 716 "OK", |
| 700 expectedContentLength: 0, | |
| 701 connectionClose: true); | 717 connectionClose: true); |
| 702 | 718 |
| 703 // Test HTTP response without any transfer length indications | 719 // Test HTTP response without any transfer length indications |
| 704 // where closing the connections indicates end of body. | 720 // where closing the connections indicates end of body. |
| 705 response = """ | 721 response = """ |
| 706 HTTP/1.1 200 OK\r | 722 HTTP/1.1 200 OK\r |
| 707 \r | 723 \r |
| 708 01234567890123456789012345 | 724 01234567890123456789012345 |
| 709 0123456789012345678901234567890 | 725 0123456789012345678901234567890 |
| 710 """; | 726 """; |
| 711 _testParseResponse(response, | 727 _testParseResponse(response, |
| 712 200, | 728 200, |
| 713 "OK", | 729 "OK", |
| 714 expectedContentLength: -1, | 730 expectedTransferLength: -1, |
| 715 expectedBytesReceived: 59, | 731 expectedBytesReceived: 59, |
| 716 close: true, | 732 close: true, |
| 717 connectionClose: true); | 733 connectionClose: true); |
| 718 | 734 |
| 719 // Test HTTP upgrade. | 735 // Test HTTP upgrade. |
| 720 response = """ | 736 response = """ |
| 721 HTTP/1.1 101 Switching Protocols\r | 737 HTTP/1.1 101 Switching Protocols\r |
| 722 Upgrade: irc/1.2\r | 738 Upgrade: irc/1.2\r |
| 723 Connection: Upgrade\r | 739 Connection: Upgrade\r |
| 724 \r\n"""; | 740 \r\n"""; |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 858 0123456789012345678901234567890\r | 874 0123456789012345678901234567890\r |
| 859 0\r\n\r\n"""; | 875 0\r\n\r\n"""; |
| 860 _testParseInvalidResponse(response); | 876 _testParseInvalidResponse(response); |
| 861 } | 877 } |
| 862 } | 878 } |
| 863 | 879 |
| 864 | 880 |
| 865 void main() { | 881 void main() { |
| 866 HttpParserTest.runAllTests(); | 882 HttpParserTest.runAllTests(); |
| 867 } | 883 } |
| OLD | NEW |