| 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 |