| 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 part of dart.io; | 5 part of dart.io; |
| 6 | 6 |
| 7 | 7 |
| 8 /** | 8 /** |
| 9 * A Mime Multipart class representing each part parsed by | 9 * A Mime Multipart class representing each part parsed by |
| 10 * [MimeMultipartTransformer]. The data is streamed in as it become available. | 10 * [MimeMultipartTransformer]. The data is streamed in as it become available. |
| 11 */ | 11 */ |
| 12 abstract class MimeMultipart extends Stream<List<int>> { | 12 abstract class MimeMultipart extends Stream<List<int>> { |
| 13 Map<String, String> get headers; | 13 Map<String, String> get headers; |
| 14 } | 14 } |
| 15 | 15 |
| 16 class _MimeMultipartImpl extends MimeMultipart { | 16 class _MimeMultipart extends MimeMultipart { |
| 17 final Map<String, String> headers; | 17 final Map<String, String> headers; |
| 18 final Stream<List<int>> _stream; | 18 final Stream<List<int>> _stream; |
| 19 | 19 |
| 20 _MimeMultipartImpl(this.headers, this._stream); | 20 _MimeMultipart(this.headers, this._stream); |
| 21 | 21 |
| 22 StreamSubscription<List<int>> listen(void onData(List<int> data), | 22 StreamSubscription<List<int>> listen(void onData(List<int> data), |
| 23 {void onDone(), | 23 {void onDone(), |
| 24 void onError(error), | 24 void onError(error), |
| 25 bool cancelOnError}) { | 25 bool cancelOnError}) { |
| 26 return _stream.listen(onData, | 26 return _stream.listen(onData, |
| 27 onDone: onDone, | 27 onDone: onDone, |
| 28 onError: onError, | 28 onError: onError, |
| 29 cancelOnError: cancelOnError); | 29 cancelOnError: cancelOnError); |
| 30 } | 30 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 57 static const int _FAILURE = 15; | 57 static const int _FAILURE = 15; |
| 58 | 58 |
| 59 StreamController _controller; | 59 StreamController _controller; |
| 60 StreamSubscription _subscription; | 60 StreamSubscription _subscription; |
| 61 | 61 |
| 62 StreamController _multipartController; | 62 StreamController _multipartController; |
| 63 Map<String, String> _headers; | 63 Map<String, String> _headers; |
| 64 | 64 |
| 65 List<int> _boundary; | 65 List<int> _boundary; |
| 66 int _state = _START; | 66 int _state = _START; |
| 67 int _boundaryIndex = 0; | 67 int _boundaryIndex = 2; |
| 68 | 68 |
| 69 // Current index in the data buffer. If index is negative then it | 69 // Current index in the data buffer. If index is negative then it |
| 70 // is the index into the artificial prefix of the boundary string. | 70 // is the index into the artificial prefix of the boundary string. |
| 71 int _index; | 71 int _index; |
| 72 List<int> _buffer; | 72 List<int> _buffer; |
| 73 | 73 |
| 74 StringBuffer _headerField = new StringBuffer(); | 74 StringBuffer _headerField = new StringBuffer(); |
| 75 StringBuffer _headerValue = new StringBuffer(); | 75 StringBuffer _headerValue = new StringBuffer(); |
| 76 | 76 |
| 77 /** | 77 /** |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 (data) { | 112 (data) { |
| 113 assert(_buffer == null); | 113 assert(_buffer == null); |
| 114 _pauseStream(); | 114 _pauseStream(); |
| 115 _buffer = data; | 115 _buffer = data; |
| 116 _index = 0; | 116 _index = 0; |
| 117 _parse(); | 117 _parse(); |
| 118 }, | 118 }, |
| 119 onDone: () { | 119 onDone: () { |
| 120 if (_state != _DONE) { | 120 if (_state != _DONE) { |
| 121 _controller.addError( | 121 _controller.addError( |
| 122 new MimeParserException("Bad multipart ending")); | 122 new MimeMultipartException("Bad multipart ending")); |
| 123 } | 123 } |
| 124 _controller.close(); | 124 _controller.close(); |
| 125 }, | 125 }, |
| 126 onError: (error) { | 126 onError: (error) { |
| 127 _controller.addError(error); | 127 _controller.addError(error); |
| 128 }); | 128 }); |
| 129 }, | 129 }, |
| 130 onCancel: () { | 130 onCancel: () { |
| 131 _subscription.cancel(); | 131 _subscription.cancel(); |
| 132 }); | 132 }); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 180 return; | 180 return; |
| 181 } | 181 } |
| 182 int byte; | 182 int byte; |
| 183 if (_index < 0) { | 183 if (_index < 0) { |
| 184 byte = _boundary[boundaryPrefix + _index]; | 184 byte = _boundary[boundaryPrefix + _index]; |
| 185 } else { | 185 } else { |
| 186 byte = _buffer[_index]; | 186 byte = _buffer[_index]; |
| 187 } | 187 } |
| 188 switch (_state) { | 188 switch (_state) { |
| 189 case _START: | 189 case _START: |
| 190 if (_toLowerCase(byte) == _toLowerCase(_boundary[_boundaryIndex])) { | 190 if (byte == _boundary[_boundaryIndex]) { |
| 191 _boundaryIndex++; | 191 _boundaryIndex++; |
| 192 if (_boundaryIndex == _boundary.length) { | 192 if (_boundaryIndex == _boundary.length) { |
| 193 _state = _FIRST_BOUNDARY_ENDING; | 193 _state = _FIRST_BOUNDARY_ENDING; |
| 194 _boundaryIndex = 0; | 194 _boundaryIndex = 0; |
| 195 } | 195 } |
| 196 } else { | 196 } else { |
| 197 // Restart matching of the boundary. | 197 // Restart matching of the boundary. |
| 198 _index = _index - _boundaryIndex; | 198 _index = _index - _boundaryIndex; |
| 199 _boundaryIndex = 0; | 199 _boundaryIndex = 0; |
| 200 } | 200 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 239 _headerField.writeCharCode(_toLowerCase(byte)); | 239 _headerField.writeCharCode(_toLowerCase(byte)); |
| 240 _state = _HEADER_FIELD; | 240 _state = _HEADER_FIELD; |
| 241 } | 241 } |
| 242 break; | 242 break; |
| 243 | 243 |
| 244 case _HEADER_FIELD: | 244 case _HEADER_FIELD: |
| 245 if (byte == _CharCode.COLON) { | 245 if (byte == _CharCode.COLON) { |
| 246 _state = _HEADER_VALUE_START; | 246 _state = _HEADER_VALUE_START; |
| 247 } else { | 247 } else { |
| 248 if (!_isTokenChar(byte)) { | 248 if (!_isTokenChar(byte)) { |
| 249 throw new MimeParserException("Invalid header field name"); | 249 throw new MimeMultipartException("Invalid header field name"); |
| 250 } | 250 } |
| 251 _headerField.writeCharCode(_toLowerCase(byte)); | 251 _headerField.writeCharCode(_toLowerCase(byte)); |
| 252 } | 252 } |
| 253 break; | 253 break; |
| 254 | 254 |
| 255 case _HEADER_VALUE_START: | 255 case _HEADER_VALUE_START: |
| 256 if (byte == _CharCode.CR) { | 256 if (byte == _CharCode.CR) { |
| 257 _state = _HEADER_VALUE_FOLDING_OR_ENDING; | 257 _state = _HEADER_VALUE_FOLDING_OR_ENDING; |
| 258 } else if (byte != _CharCode.SP && byte != _CharCode.HT) { | 258 } else if (byte != _CharCode.SP && byte != _CharCode.HT) { |
| 259 // Start of new header value. | 259 // Start of new header value. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 274 _expect(byte, _CharCode.LF); | 274 _expect(byte, _CharCode.LF); |
| 275 _state = _HEADER_VALUE_FOLD_OR_END; | 275 _state = _HEADER_VALUE_FOLD_OR_END; |
| 276 break; | 276 break; |
| 277 | 277 |
| 278 case _HEADER_VALUE_FOLD_OR_END: | 278 case _HEADER_VALUE_FOLD_OR_END: |
| 279 if (byte == _CharCode.SP || byte == _CharCode.HT) { | 279 if (byte == _CharCode.SP || byte == _CharCode.HT) { |
| 280 _state = _HEADER_VALUE_START; | 280 _state = _HEADER_VALUE_START; |
| 281 } else { | 281 } else { |
| 282 String headerField = _headerField.toString(); | 282 String headerField = _headerField.toString(); |
| 283 String headerValue =_headerValue.toString(); | 283 String headerValue =_headerValue.toString(); |
| 284 _headers[headerField] = headerValue; | 284 _headers[headerField.toLowerCase()] = headerValue; |
| 285 _headerField = new StringBuffer(); | 285 _headerField = new StringBuffer(); |
| 286 _headerValue = new StringBuffer(); | 286 _headerValue = new StringBuffer(); |
| 287 if (byte == _CharCode.CR) { | 287 if (byte == _CharCode.CR) { |
| 288 _state = _HEADER_ENDING; | 288 _state = _HEADER_ENDING; |
| 289 } else { | 289 } else { |
| 290 // Start of new header field. | 290 // Start of new header field. |
| 291 _headerField.writeCharCode(_toLowerCase(byte)); | 291 _headerField.writeCharCode(_toLowerCase(byte)); |
| 292 _state = _HEADER_FIELD; | 292 _state = _HEADER_FIELD; |
| 293 } | 293 } |
| 294 } | 294 } |
| 295 break; | 295 break; |
| 296 | 296 |
| 297 case _HEADER_ENDING: | 297 case _HEADER_ENDING: |
| 298 _expect(byte, _CharCode.LF); | 298 _expect(byte, _CharCode.LF); |
| 299 _multipartController = new StreamController( | 299 _multipartController = new StreamController( |
| 300 onPause: () { | 300 onPause: () { |
| 301 _pauseStream(); | 301 _pauseStream(); |
| 302 }, | 302 }, |
| 303 onResume: () { | 303 onResume: () { |
| 304 _resumeStream(); | 304 _resumeStream(); |
| 305 _parse(); | 305 _parse(); |
| 306 }); | 306 }); |
| 307 _controller.add( | 307 _controller.add( |
| 308 new _MimeMultipartImpl(_headers, _multipartController.stream)); | 308 new _MimeMultipart(_headers, _multipartController.stream)); |
| 309 _headers = null; | 309 _headers = null; |
| 310 _state = _CONTENT; | 310 _state = _CONTENT; |
| 311 contentStartIndex = _index + 1; | 311 contentStartIndex = _index + 1; |
| 312 break; | 312 break; |
| 313 | 313 |
| 314 case _CONTENT: | 314 case _CONTENT: |
| 315 if (byte == _boundary[_boundaryIndex]) { | 315 if (byte == _boundary[_boundaryIndex]) { |
| 316 _boundaryIndex++; | 316 _boundaryIndex++; |
| 317 if (_boundaryIndex == _boundary.length) { | 317 if (_boundaryIndex == _boundary.length) { |
| 318 if (contentStartIndex != null) { | 318 if (contentStartIndex != null) { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 381 | 381 |
| 382 int _toLowerCase(int byte) { | 382 int _toLowerCase(int byte) { |
| 383 final int aCode = "A".codeUnitAt(0); | 383 final int aCode = "A".codeUnitAt(0); |
| 384 final int zCode = "Z".codeUnitAt(0); | 384 final int zCode = "Z".codeUnitAt(0); |
| 385 final int delta = "a".codeUnitAt(0) - aCode; | 385 final int delta = "a".codeUnitAt(0) - aCode; |
| 386 return (aCode <= byte && byte <= zCode) ? byte + delta : byte; | 386 return (aCode <= byte && byte <= zCode) ? byte + delta : byte; |
| 387 } | 387 } |
| 388 | 388 |
| 389 void _expect(int val1, int val2) { | 389 void _expect(int val1, int val2) { |
| 390 if (val1 != val2) { | 390 if (val1 != val2) { |
| 391 throw new MimeParserException("Failed to parse multipart mime 1"); | 391 throw new MimeMultipartException("Failed to parse multipart mime 1"); |
| 392 } | 392 } |
| 393 } | 393 } |
| 394 | 394 |
| 395 void _expectWS(int byte) { | 395 void _expectWS(int byte) { |
| 396 if (byte != _CharCode.SP && byte != _CharCode.HT) { | 396 if (byte != _CharCode.SP && byte != _CharCode.HT) { |
| 397 throw new MimeParserException("Failed to parse multipart mime 2"); | 397 throw new MimeMultipartException("Failed to parse multipart mime 2"); |
| 398 } | 398 } |
| 399 } | 399 } |
| 400 } | 400 } |
| 401 | 401 |
| 402 | 402 |
| 403 class MimeParserException implements Exception { | 403 class MimeMultipartException implements Exception { |
| 404 const MimeParserException([String this.message = ""]); | 404 const MimeMultipartException([String this.message = ""]); |
| 405 String toString() => "MimeParserException: $message"; | 405 String toString() => "MimeMultipartException: $message"; |
| 406 final String message; | 406 final String message; |
| 407 } | 407 } |
| OLD | NEW |