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 // Global constants. | 5 // Global constants. |
6 class _Const { | 6 class _Const { |
7 // Bytes for "HTTP". | 7 // Bytes for "HTTP". |
8 static const HTTP = const [72, 84, 84, 80]; | 8 static const HTTP = const [72, 84, 84, 80]; |
9 // Bytes for "HTTP/1.". | 9 // Bytes for "HTTP/1.". |
10 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46]; | 10 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46]; |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
163 case _State.START: | 163 case _State.START: |
164 if (byte == _Const.HTTP[0]) { | 164 if (byte == _Const.HTTP[0]) { |
165 // Start parsing method or HTTP version. | 165 // Start parsing method or HTTP version. |
166 _httpVersionIndex = 1; | 166 _httpVersionIndex = 1; |
167 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION; | 167 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION; |
168 } else { | 168 } else { |
169 // Start parsing method. | 169 // Start parsing method. |
170 if (!_isTokenChar(byte)) { | 170 if (!_isTokenChar(byte)) { |
171 throw new HttpParserException("Invalid request method"); | 171 throw new HttpParserException("Invalid request method"); |
172 } | 172 } |
173 _method_or_status_code.addCharCode(byte); | 173 _method_or_status_code.add(byte); |
174 if (!_requestParser) { | 174 if (!_requestParser) { |
175 throw new HttpParserException("Invalid response line"); | 175 throw new HttpParserException("Invalid response line"); |
176 } | 176 } |
177 _state = _State.REQUEST_LINE_METHOD; | 177 _state = _State.REQUEST_LINE_METHOD; |
178 } | 178 } |
179 break; | 179 break; |
180 | 180 |
181 case _State.METHOD_OR_RESPONSE_HTTP_VERSION: | 181 case _State.METHOD_OR_RESPONSE_HTTP_VERSION: |
182 if (_httpVersionIndex < _Const.HTTP.length && | 182 if (_httpVersionIndex < _Const.HTTP.length && |
183 byte == _Const.HTTP[_httpVersionIndex]) { | 183 byte == _Const.HTTP[_httpVersionIndex]) { |
184 // Continue parsing HTTP version. | 184 // Continue parsing HTTP version. |
185 _httpVersionIndex++; | 185 _httpVersionIndex++; |
186 } else if (_httpVersionIndex == _Const.HTTP.length && | 186 } else if (_httpVersionIndex == _Const.HTTP.length && |
187 byte == _CharCode.SLASH) { | 187 byte == _CharCode.SLASH) { |
188 // HTTP/ parsed. As method is a token this cannot be a | 188 // HTTP/ parsed. As method is a token this cannot be a |
189 // method anymore. | 189 // method anymore. |
190 _httpVersionIndex++; | 190 _httpVersionIndex++; |
191 if (_requestParser) { | 191 if (_requestParser) { |
192 throw new HttpParserException("Invalid request line"); | 192 throw new HttpParserException("Invalid request line"); |
193 } | 193 } |
194 _state = _State.RESPONSE_HTTP_VERSION; | 194 _state = _State.RESPONSE_HTTP_VERSION; |
195 } else { | 195 } else { |
196 // Did not parse HTTP version. Expect method instead. | 196 // Did not parse HTTP version. Expect method instead. |
197 for (int i = 0; i < _httpVersionIndex; i++) { | 197 for (int i = 0; i < _httpVersionIndex; i++) { |
198 _method_or_status_code.addCharCode(_Const.HTTP[i]); | 198 _method_or_status_code.add(_Const.HTTP[i]); |
199 } | 199 } |
200 if (byte == _CharCode.SP) { | 200 if (byte == _CharCode.SP) { |
201 _state = _State.REQUEST_LINE_URI; | 201 _state = _State.REQUEST_LINE_URI; |
202 } else { | 202 } else { |
203 _method_or_status_code.addCharCode(byte); | 203 _method_or_status_code.add(byte); |
204 _httpVersion = _HttpVersion.UNDETERMINED; | 204 _httpVersion = _HttpVersion.UNDETERMINED; |
205 if (!_requestParser) { | 205 if (!_requestParser) { |
206 throw new HttpParserException("Invalid response line"); | 206 throw new HttpParserException("Invalid response line"); |
207 } | 207 } |
208 _state = _State.REQUEST_LINE_METHOD; | 208 _state = _State.REQUEST_LINE_METHOD; |
209 } | 209 } |
210 } | 210 } |
211 break; | 211 break; |
212 | 212 |
213 case _State.RESPONSE_HTTP_VERSION: | 213 case _State.RESPONSE_HTTP_VERSION: |
(...skipping 22 matching lines...) Expand all Loading... |
236 } | 236 } |
237 break; | 237 break; |
238 | 238 |
239 case _State.REQUEST_LINE_METHOD: | 239 case _State.REQUEST_LINE_METHOD: |
240 if (byte == _CharCode.SP) { | 240 if (byte == _CharCode.SP) { |
241 _state = _State.REQUEST_LINE_URI; | 241 _state = _State.REQUEST_LINE_URI; |
242 } else { | 242 } else { |
243 if (_Const.SEPARATORS_AND_CR_LF.indexOf(byte) != -1) { | 243 if (_Const.SEPARATORS_AND_CR_LF.indexOf(byte) != -1) { |
244 throw new HttpParserException("Invalid request method"); | 244 throw new HttpParserException("Invalid request method"); |
245 } | 245 } |
246 _method_or_status_code.addCharCode(byte); | 246 _method_or_status_code.add(byte); |
247 } | 247 } |
248 break; | 248 break; |
249 | 249 |
250 case _State.REQUEST_LINE_URI: | 250 case _State.REQUEST_LINE_URI: |
251 if (byte == _CharCode.SP) { | 251 if (byte == _CharCode.SP) { |
252 if (_uri_or_reason_phrase.length == 0) { | 252 if (_uri_or_reason_phrase.length == 0) { |
253 throw new HttpParserException("Invalid request URI"); | 253 throw new HttpParserException("Invalid request URI"); |
254 } | 254 } |
255 _state = _State.REQUEST_LINE_HTTP_VERSION; | 255 _state = _State.REQUEST_LINE_HTTP_VERSION; |
256 _httpVersionIndex = 0; | 256 _httpVersionIndex = 0; |
257 } else { | 257 } else { |
258 if (byte == _CharCode.CR || byte == _CharCode.LF) { | 258 if (byte == _CharCode.CR || byte == _CharCode.LF) { |
259 throw new HttpParserException("Invalid request URI"); | 259 throw new HttpParserException("Invalid request URI"); |
260 } | 260 } |
261 _uri_or_reason_phrase.addCharCode(byte); | 261 _uri_or_reason_phrase.add(byte); |
262 } | 262 } |
263 break; | 263 break; |
264 | 264 |
265 case _State.REQUEST_LINE_HTTP_VERSION: | 265 case _State.REQUEST_LINE_HTTP_VERSION: |
266 if (_httpVersionIndex < _Const.HTTP1DOT.length) { | 266 if (_httpVersionIndex < _Const.HTTP1DOT.length) { |
267 _expect(byte, _Const.HTTP11[_httpVersionIndex]); | 267 _expect(byte, _Const.HTTP11[_httpVersionIndex]); |
268 _httpVersionIndex++; | 268 _httpVersionIndex++; |
269 } else if (_httpVersionIndex == _Const.HTTP1DOT.length) { | 269 } else if (_httpVersionIndex == _Const.HTTP1DOT.length) { |
270 if (byte == _CharCode.ONE) { | 270 if (byte == _CharCode.ONE) { |
271 // HTTP/1.1 parsed. | 271 // HTTP/1.1 parsed. |
(...skipping 10 matching lines...) Expand all Loading... |
282 } | 282 } |
283 } else { | 283 } else { |
284 _expect(byte, _CharCode.CR); | 284 _expect(byte, _CharCode.CR); |
285 _state = _State.REQUEST_LINE_ENDING; | 285 _state = _State.REQUEST_LINE_ENDING; |
286 } | 286 } |
287 break; | 287 break; |
288 | 288 |
289 case _State.REQUEST_LINE_ENDING: | 289 case _State.REQUEST_LINE_ENDING: |
290 _expect(byte, _CharCode.LF); | 290 _expect(byte, _CharCode.LF); |
291 _messageType = _MessageType.REQUEST; | 291 _messageType = _MessageType.REQUEST; |
292 requestStart(_method_or_status_code.toString(), | 292 requestStart(new String.fromCharCodes(_method_or_status_code), |
293 _uri_or_reason_phrase.toString(), | 293 new String.fromCharCodes(_uri_or_reason_phrase), |
294 version); | 294 version); |
295 _method_or_status_code.clear(); | 295 _method_or_status_code.clear(); |
296 _uri_or_reason_phrase.clear(); | 296 _uri_or_reason_phrase.clear(); |
297 _state = _State.HEADER_START; | 297 _state = _State.HEADER_START; |
298 break; | 298 break; |
299 | 299 |
300 case _State.RESPONSE_LINE_STATUS_CODE: | 300 case _State.RESPONSE_LINE_STATUS_CODE: |
301 if (byte == _CharCode.SP) { | 301 if (byte == _CharCode.SP) { |
302 if (_method_or_status_code.length != 3) { | 302 if (_method_or_status_code.length != 3) { |
303 throw new HttpParserException("Invalid response status code"); | 303 throw new HttpParserException("Invalid response status code"); |
304 } | 304 } |
305 _state = _State.RESPONSE_LINE_REASON_PHRASE; | 305 _state = _State.RESPONSE_LINE_REASON_PHRASE; |
306 } else { | 306 } else { |
307 if (byte < 0x30 && 0x39 < byte) { | 307 if (byte < 0x30 && 0x39 < byte) { |
308 throw new HttpParserException("Invalid response status code"); | 308 throw new HttpParserException("Invalid response status code"); |
309 } else { | 309 } else { |
310 _method_or_status_code.addCharCode(byte); | 310 _method_or_status_code.add(byte); |
311 } | 311 } |
312 } | 312 } |
313 break; | 313 break; |
314 | 314 |
315 case _State.RESPONSE_LINE_REASON_PHRASE: | 315 case _State.RESPONSE_LINE_REASON_PHRASE: |
316 if (byte == _CharCode.CR) { | 316 if (byte == _CharCode.CR) { |
317 if (_uri_or_reason_phrase.length == 0) { | 317 if (_uri_or_reason_phrase.length == 0) { |
318 throw new HttpParserException("Invalid response reason phrase"); | 318 throw new HttpParserException("Invalid response reason phrase"); |
319 } | 319 } |
320 _state = _State.RESPONSE_LINE_ENDING; | 320 _state = _State.RESPONSE_LINE_ENDING; |
321 } else { | 321 } else { |
322 if (byte == _CharCode.CR || byte == _CharCode.LF) { | 322 if (byte == _CharCode.CR || byte == _CharCode.LF) { |
323 throw new HttpParserException("Invalid response reason phrase"); | 323 throw new HttpParserException("Invalid response reason phrase"); |
324 } | 324 } |
325 _uri_or_reason_phrase.addCharCode(byte); | 325 _uri_or_reason_phrase.add(byte); |
326 } | 326 } |
327 break; | 327 break; |
328 | 328 |
329 case _State.RESPONSE_LINE_ENDING: | 329 case _State.RESPONSE_LINE_ENDING: |
330 _expect(byte, _CharCode.LF); | 330 _expect(byte, _CharCode.LF); |
331 _messageType == _MessageType.RESPONSE; | 331 _messageType == _MessageType.RESPONSE; |
332 int statusCode = parseInt(_method_or_status_code.toString()); | 332 int statusCode = parseInt(new String.fromCharCodes(_method_or_status
_code)); |
333 if (statusCode < 100 || statusCode > 599) { | 333 if (statusCode < 100 || statusCode > 599) { |
334 throw new HttpParserException("Invalid response status code"); | 334 throw new HttpParserException("Invalid response status code"); |
335 } else { | 335 } else { |
336 // Check whether this response will never have a body. | 336 // Check whether this response will never have a body. |
337 _noMessageBody = | 337 _noMessageBody = |
338 statusCode <= 199 || statusCode == 204 || statusCode == 304; | 338 statusCode <= 199 || statusCode == 204 || statusCode == 304; |
339 } | 339 } |
340 responseStart(statusCode, | 340 responseStart(statusCode, |
341 _uri_or_reason_phrase.toString(), | 341 new String.fromCharCodes(_uri_or_reason_phrase), |
342 version); | 342 version); |
343 _method_or_status_code.clear(); | 343 _method_or_status_code.clear(); |
344 _uri_or_reason_phrase.clear(); | 344 _uri_or_reason_phrase.clear(); |
345 _state = _State.HEADER_START; | 345 _state = _State.HEADER_START; |
346 break; | 346 break; |
347 | 347 |
348 case _State.HEADER_START: | 348 case _State.HEADER_START: |
349 if (byte == _CharCode.CR) { | 349 if (byte == _CharCode.CR) { |
350 _state = _State.HEADER_ENDING; | 350 _state = _State.HEADER_ENDING; |
351 } else { | 351 } else { |
352 // Start of new header field. | 352 // Start of new header field. |
353 _headerField.addCharCode(_toLowerCase(byte)); | 353 _headerField.add(_toLowerCase(byte)); |
354 _state = _State.HEADER_FIELD; | 354 _state = _State.HEADER_FIELD; |
355 } | 355 } |
356 break; | 356 break; |
357 | 357 |
358 case _State.HEADER_FIELD: | 358 case _State.HEADER_FIELD: |
359 if (byte == _CharCode.COLON) { | 359 if (byte == _CharCode.COLON) { |
360 _state = _State.HEADER_VALUE_START; | 360 _state = _State.HEADER_VALUE_START; |
361 } else { | 361 } else { |
362 if (!_isTokenChar(byte)) { | 362 if (!_isTokenChar(byte)) { |
363 throw new HttpParserException("Invalid header field name"); | 363 throw new HttpParserException("Invalid header field name"); |
364 } | 364 } |
365 _headerField.addCharCode(_toLowerCase(byte)); | 365 _headerField.add(_toLowerCase(byte)); |
366 } | 366 } |
367 break; | 367 break; |
368 | 368 |
369 case _State.HEADER_VALUE_START: | 369 case _State.HEADER_VALUE_START: |
370 if (byte == _CharCode.CR) { | 370 if (byte == _CharCode.CR) { |
371 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING; | 371 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING; |
372 } else if (byte != _CharCode.SP && byte != _CharCode.HT) { | 372 } else if (byte != _CharCode.SP && byte != _CharCode.HT) { |
373 // Start of new header value. | 373 // Start of new header value. |
374 _headerValue.addCharCode(byte); | 374 _headerValue.add(byte); |
375 _state = _State.HEADER_VALUE; | 375 _state = _State.HEADER_VALUE; |
376 } | 376 } |
377 break; | 377 break; |
378 | 378 |
379 case _State.HEADER_VALUE: | 379 case _State.HEADER_VALUE: |
380 if (byte == _CharCode.CR) { | 380 if (byte == _CharCode.CR) { |
381 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING; | 381 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING; |
382 } else { | 382 } else { |
383 _headerValue.addCharCode(byte); | 383 _headerValue.add(byte); |
384 } | 384 } |
385 break; | 385 break; |
386 | 386 |
387 case _State.HEADER_VALUE_FOLDING_OR_ENDING: | 387 case _State.HEADER_VALUE_FOLDING_OR_ENDING: |
388 _expect(byte, _CharCode.LF); | 388 _expect(byte, _CharCode.LF); |
389 _state = _State.HEADER_VALUE_FOLD_OR_END; | 389 _state = _State.HEADER_VALUE_FOLD_OR_END; |
390 break; | 390 break; |
391 | 391 |
392 case _State.HEADER_VALUE_FOLD_OR_END: | 392 case _State.HEADER_VALUE_FOLD_OR_END: |
393 if (byte == _CharCode.SP || byte == _CharCode.HT) { | 393 if (byte == _CharCode.SP || byte == _CharCode.HT) { |
394 _state = _State.HEADER_VALUE_START; | 394 _state = _State.HEADER_VALUE_START; |
395 } else { | 395 } else { |
396 String headerField = _headerField.toString(); | 396 String headerField = new String.fromCharCodes(_headerField); |
397 String headerValue =_headerValue.toString(); | 397 String headerValue = new String.fromCharCodes(_headerValue); |
398 bool reportHeader = true; | 398 bool reportHeader = true; |
399 if (headerField == "content-length" && !_chunked) { | 399 if (headerField == "content-length" && !_chunked) { |
400 // Ignore the Content-Length header if Transfer-Encoding | 400 // Ignore the Content-Length header if Transfer-Encoding |
401 // is chunked (RFC 2616 section 4.4) | 401 // is chunked (RFC 2616 section 4.4) |
402 _contentLength = parseInt(headerValue); | 402 _contentLength = parseInt(headerValue); |
403 } else if (headerField == "connection") { | 403 } else if (headerField == "connection") { |
404 List<String> tokens = _tokenizeFieldValue(headerValue); | 404 List<String> tokens = _tokenizeFieldValue(headerValue); |
405 for (int i = 0; i < tokens.length; i++) { | 405 for (int i = 0; i < tokens.length; i++) { |
406 String token = tokens[i].toLowerCase(); | 406 String token = tokens[i].toLowerCase(); |
407 if (token == "keep-alive") { | 407 if (token == "keep-alive") { |
(...skipping 16 matching lines...) Expand all Loading... |
424 if (reportHeader) { | 424 if (reportHeader) { |
425 headerReceived(headerField, headerValue); | 425 headerReceived(headerField, headerValue); |
426 } | 426 } |
427 _headerField.clear(); | 427 _headerField.clear(); |
428 _headerValue.clear(); | 428 _headerValue.clear(); |
429 | 429 |
430 if (byte == _CharCode.CR) { | 430 if (byte == _CharCode.CR) { |
431 _state = _State.HEADER_ENDING; | 431 _state = _State.HEADER_ENDING; |
432 } else { | 432 } else { |
433 // Start of new header field. | 433 // Start of new header field. |
434 _headerField.addCharCode(_toLowerCase(byte)); | 434 _headerField.add(_toLowerCase(byte)); |
435 _state = _State.HEADER_FIELD; | 435 _state = _State.HEADER_FIELD; |
436 } | 436 } |
437 } | 437 } |
438 break; | 438 break; |
439 | 439 |
440 case _State.HEADER_ENDING: | 440 case _State.HEADER_ENDING: |
441 _expect(byte, _CharCode.LF); | 441 _expect(byte, _CharCode.LF); |
442 // If a request message has neither Content-Length nor | 442 // If a request message has neither Content-Length nor |
443 // Transfer-Encoding the message must not have a body (RFC | 443 // Transfer-Encoding the message must not have a body (RFC |
444 // 2616 section 4.3). | 444 // 2616 section 4.3). |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
651 return result; | 651 return result; |
652 } | 652 } |
653 | 653 |
654 void _bodyEnd() { | 654 void _bodyEnd() { |
655 dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection); | 655 dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection); |
656 } | 656 } |
657 | 657 |
658 _reset() { | 658 _reset() { |
659 _state = _State.START; | 659 _state = _State.START; |
660 _messageType = _MessageType.UNDETERMINED; | 660 _messageType = _MessageType.UNDETERMINED; |
661 _headerField = new StringBuffer(); | 661 _headerField = new List(); |
662 _headerValue = new StringBuffer(); | 662 _headerValue = new List(); |
663 _method_or_status_code = new StringBuffer(); | 663 _method_or_status_code = new List(); |
664 _uri_or_reason_phrase = new StringBuffer(); | 664 _uri_or_reason_phrase = new List(); |
665 | 665 |
666 _httpVersion = _HttpVersion.UNDETERMINED; | 666 _httpVersion = _HttpVersion.UNDETERMINED; |
667 _contentLength = -1; | 667 _contentLength = -1; |
668 _persistentConnection = false; | 668 _persistentConnection = false; |
669 _connectionUpgrade = false; | 669 _connectionUpgrade = false; |
670 _chunked = false; | 670 _chunked = false; |
671 | 671 |
672 _noMessageBody = false; | 672 _noMessageBody = false; |
673 _responseToMethod = null; | 673 _responseToMethod = null; |
674 _remainingContent = null; | 674 _remainingContent = null; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
728 | 728 |
729 // The data that is currently being parsed. | 729 // The data that is currently being parsed. |
730 List<int> _buffer; | 730 List<int> _buffer; |
731 int _index; | 731 int _index; |
732 int _lastIndex; | 732 int _lastIndex; |
733 | 733 |
734 bool _requestParser; | 734 bool _requestParser; |
735 int _state; | 735 int _state; |
736 int _httpVersionIndex; | 736 int _httpVersionIndex; |
737 int _messageType; | 737 int _messageType; |
738 StringBuffer _method_or_status_code; | 738 List _method_or_status_code; |
739 StringBuffer _uri_or_reason_phrase; | 739 List _uri_or_reason_phrase; |
740 StringBuffer _headerField; | 740 List _headerField; |
741 StringBuffer _headerValue; | 741 List _headerValue; |
742 | 742 |
743 int _httpVersion; | 743 int _httpVersion; |
744 int _contentLength; | 744 int _contentLength; |
745 bool _persistentConnection; | 745 bool _persistentConnection; |
746 bool _connectionUpgrade; | 746 bool _connectionUpgrade; |
747 bool _chunked; | 747 bool _chunked; |
748 | 748 |
749 bool _noMessageBody; | 749 bool _noMessageBody; |
750 String _responseToMethod; // Indicates the method used for the request. | 750 String _responseToMethod; // Indicates the method used for the request. |
751 int _remainingContent; | 751 int _remainingContent; |
752 | 752 |
753 // Callbacks. | 753 // Callbacks. |
754 Function requestStart; | 754 Function requestStart; |
755 Function responseStart; | 755 Function responseStart; |
756 Function headerReceived; | 756 Function headerReceived; |
757 Function headersComplete; | 757 Function headersComplete; |
758 Function dataReceived; | 758 Function dataReceived; |
759 Function dataEnd; | 759 Function dataEnd; |
760 Function error; | 760 Function error; |
761 Function closed; | 761 Function closed; |
762 } | 762 } |
763 | 763 |
764 | 764 |
765 class HttpParserException implements Exception { | 765 class HttpParserException implements Exception { |
766 const HttpParserException([String this.message = ""]); | 766 const HttpParserException([String this.message = ""]); |
767 String toString() => "HttpParserException: $message"; | 767 String toString() => "HttpParserException: $message"; |
768 final String message; | 768 final String message; |
769 } | 769 } |
OLD | NEW |