Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library mime.bound_multipart_stream; | 4 library mime.bound_multipart_stream; |
| 5 | 5 |
| 6 import 'dart:async'; | 6 import 'dart:async'; |
| 7 import 'dart:convert'; | 7 import 'dart:convert'; |
| 8 | 8 |
| 9 import 'mime_shared.dart'; | 9 import 'mime_shared.dart'; |
| 10 import 'char_code.dart'; | 10 import 'char_code.dart'; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 67 static const int _LAST_BOUNDARY_DASH2 = 11; | 67 static const int _LAST_BOUNDARY_DASH2 = 11; |
| 68 static const int _LAST_BOUNDARY_ENDING = 12; | 68 static const int _LAST_BOUNDARY_ENDING = 12; |
| 69 static const int _LAST_BOUNDARY_END = 13; | 69 static const int _LAST_BOUNDARY_END = 13; |
| 70 static const int _DONE = 14; | 70 static const int _DONE = 14; |
| 71 static const int _FAIL = 15; | 71 static const int _FAIL = 15; |
| 72 | 72 |
| 73 final List<int> _boundary; | 73 final List<int> _boundary; |
| 74 final List<int> _headerField = []; | 74 final List<int> _headerField = []; |
| 75 final List<int> _headerValue = []; | 75 final List<int> _headerValue = []; |
| 76 | 76 |
| 77 // The following states belong to `_controller`, state changes will not be | |
| 78 // immediately acted upon but rather only after the current | |
| 79 // `_multipartController` is done. | |
| 80 static const int _CONTROLLER_STATE_IDLE = 0; | |
| 81 static const int _CONTROLLER_STATE_ACTIVE = 1; | |
| 82 static const int _CONTROLLER_STATE_PAUSED = 2; | |
| 83 static const int _CONTROLLER_STATE_CANCELLED = 3; | |
|
Lasse Reichstein Nielsen
2015/02/12 13:40:04
The US version is CANCELED with one L (as I have r
kustermann
2015/02/12 13:51:24
Good to know :)
Done.
| |
| 84 | |
| 85 int _controllerState = _CONTROLLER_STATE_IDLE; | |
| 86 | |
| 77 StreamController _controller; | 87 StreamController _controller; |
| 78 | 88 |
| 79 Stream<MimeMultipart> get stream => _controller.stream; | 89 Stream<MimeMultipart> get stream => _controller.stream; |
| 80 | 90 |
| 81 StreamSubscription _subscription; | 91 StreamSubscription _subscription; |
| 82 | 92 |
| 83 StreamController _multipartController; | 93 StreamController _multipartController; |
| 84 Map<String, String> _headers; | 94 Map<String, String> _headers; |
| 85 | 95 |
| 86 int _state = _START; | 96 int _state = _START; |
| 87 int _boundaryIndex = 2; | 97 int _boundaryIndex = 2; |
| 88 | 98 |
| 89 // Current index in the data buffer. If index is negative then it | 99 // Current index in the data buffer. If index is negative then it |
| 90 // is the index into the artificial prefix of the boundary string. | 100 // is the index into the artificial prefix of the boundary string. |
| 91 int _index; | 101 int _index; |
| 92 List<int> _buffer; | 102 List<int> _buffer; |
| 93 | 103 |
| 94 BoundMultipartStream(this._boundary, Stream<List<int>> stream) { | 104 BoundMultipartStream(this._boundary, Stream<List<int>> stream) { |
| 95 _controller = new StreamController( | 105 _controller = new StreamController( |
| 96 sync: true, | 106 sync: true, |
| 97 onPause: _pauseStream, | 107 onPause: _pauseStream, |
| 98 onResume:_resumeStream, | 108 onResume:_resumeStream, |
| 99 onCancel: () { | 109 onCancel: () { |
| 100 _subscription.cancel(); | 110 _controllerState = _CONTROLLER_STATE_CANCELLED; |
| 111 _tryPropagateControllerState(); | |
| 101 }, | 112 }, |
| 102 onListen: () { | 113 onListen: () { |
| 114 _controllerState = _CONTROLLER_STATE_ACTIVE; | |
| 103 _subscription = stream.listen( | 115 _subscription = stream.listen( |
| 104 (data) { | 116 (data) { |
| 105 assert(_buffer == null); | 117 assert(_buffer == null); |
| 106 _pauseStream(); | 118 _subscription.pause(); |
| 107 _buffer = data; | 119 _buffer = data; |
| 108 _index = 0; | 120 _index = 0; |
| 109 _parse(); | 121 _parse(); |
| 110 }, | 122 }, |
| 111 onDone: () { | 123 onDone: () { |
| 112 if (_state != _DONE) { | 124 if (_state != _DONE) { |
| 113 _controller.addError( | 125 _controller.addError( |
| 114 new MimeMultipartException("Bad multipart ending")); | 126 new MimeMultipartException("Bad multipart ending")); |
| 115 } | 127 } |
| 116 _controller.close(); | 128 _controller.close(); |
| 117 }, | 129 }, |
| 118 onError: _controller.addError); | 130 onError: _controller.addError); |
| 119 }); | 131 }); |
| 120 } | 132 } |
| 121 | 133 |
| 122 void _resumeStream() { | 134 void _resumeStream() { |
| 123 _subscription.resume(); | 135 assert (_controllerState == _CONTROLLER_STATE_PAUSED); |
| 136 _controllerState = _CONTROLLER_STATE_ACTIVE; | |
| 137 _tryPropagateControllerState(); | |
| 124 } | 138 } |
| 125 | 139 |
| 126 void _pauseStream() { | 140 void _pauseStream() { |
| 127 _subscription.pause(); | 141 _controllerState = _CONTROLLER_STATE_PAUSED; |
| 142 _tryPropagateControllerState(); | |
| 128 } | 143 } |
| 129 | 144 |
| 145 void _tryPropagateControllerState() { | |
| 146 if (_multipartController == null) { | |
| 147 switch (_controllerState) { | |
| 148 case _CONTROLLER_STATE_ACTIVE: | |
| 149 if (_subscription.isPaused) _subscription.resume(); | |
| 150 break; | |
| 151 case _CONTROLLER_STATE_PAUSED: | |
| 152 if (!_subscription.isPaused) _subscription.pause(); | |
| 153 break; | |
| 154 case _CONTROLLER_STATE_CANCELLED: | |
| 155 _subscription.cancel(); | |
| 156 break; | |
| 157 default: | |
| 158 throw new Exception("This code should never be reached."); | |
|
Lasse Reichstein Nielsen
2015/02/12 13:40:04
This should be an Error, not an Exception.
I sugge
kustermann
2015/02/12 13:51:24
Done.
I was looking for something like `throw new
| |
| 159 } | |
| 160 } | |
| 161 } | |
| 130 | 162 |
| 131 void _parse() { | 163 void _parse() { |
| 132 // Number of boundary bytes to artificially place before the supplied data. | 164 // Number of boundary bytes to artificially place before the supplied data. |
| 133 int boundaryPrefix = 0; | 165 int boundaryPrefix = 0; |
| 134 // Position where content starts. Will be null if no known content | 166 // Position where content starts. Will be null if no known content |
| 135 // start exists. Will be negative of the content starts in the | 167 // start exists. Will be negative of the content starts in the |
| 136 // boundary prefix. Will be zero or position if the content starts | 168 // boundary prefix. Will be zero or position if the content starts |
| 137 // in the current buffer. | 169 // in the current buffer. |
| 138 int contentStartIndex; | 170 int contentStartIndex; |
| 139 | 171 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 203 } else { | 235 } else { |
| 204 _expectWhitespace(byte); | 236 _expectWhitespace(byte); |
| 205 } | 237 } |
| 206 break; | 238 break; |
| 207 | 239 |
| 208 case _BOUNDARY_END: | 240 case _BOUNDARY_END: |
| 209 _expectByteValue(byte, CharCode.LF); | 241 _expectByteValue(byte, CharCode.LF); |
| 210 if (_multipartController != null) { | 242 if (_multipartController != null) { |
| 211 _multipartController.close(); | 243 _multipartController.close(); |
| 212 _multipartController = null; | 244 _multipartController = null; |
| 245 _tryPropagateControllerState(); | |
| 213 } | 246 } |
| 214 _state = _HEADER_START; | 247 _state = _HEADER_START; |
| 215 break; | 248 break; |
| 216 | 249 |
| 217 case _HEADER_START: | 250 case _HEADER_START: |
| 218 _headers = new Map<String, String>(); | 251 _headers = new Map<String, String>(); |
| 219 if (byte == CharCode.CR) { | 252 if (byte == CharCode.CR) { |
| 220 _state = _HEADER_ENDING; | 253 _state = _HEADER_ENDING; |
| 221 } else { | 254 } else { |
| 222 // Start of new header field. | 255 // Start of new header field. |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 275 _headerField.add(_toLowerCase(byte)); | 308 _headerField.add(_toLowerCase(byte)); |
| 276 _state = _HEADER_FIELD; | 309 _state = _HEADER_FIELD; |
| 277 } | 310 } |
| 278 } | 311 } |
| 279 break; | 312 break; |
| 280 | 313 |
| 281 case _HEADER_ENDING: | 314 case _HEADER_ENDING: |
| 282 _expectByteValue(byte, CharCode.LF); | 315 _expectByteValue(byte, CharCode.LF); |
| 283 _multipartController = new StreamController( | 316 _multipartController = new StreamController( |
| 284 sync: true, | 317 sync: true, |
| 285 onPause: () { | 318 onPause: _subscription.pause, |
| 286 _pauseStream(); | |
| 287 }, | |
| 288 onResume: () { | 319 onResume: () { |
| 289 _resumeStream(); | 320 _subscription.resume(); |
| 290 _parse(); | 321 _parse(); |
| 291 }); | 322 }); |
| 292 _controller.add( | 323 _controller.add( |
| 293 new _MimeMultipart(_headers, _multipartController.stream)); | 324 new _MimeMultipart(_headers, _multipartController.stream)); |
| 294 _headers = null; | 325 _headers = null; |
| 295 _state = _CONTENT; | 326 _state = _CONTENT; |
| 296 contentStartIndex = _index + 1; | 327 contentStartIndex = _index + 1; |
| 297 break; | 328 break; |
| 298 | 329 |
| 299 case _CONTENT: | 330 case _CONTENT: |
| 300 if (byte == _boundary[_boundaryIndex]) { | 331 if (byte == _boundary[_boundaryIndex]) { |
| 301 _boundaryIndex++; | 332 _boundaryIndex++; |
| 302 if (_boundaryIndex == _boundary.length) { | 333 if (_boundaryIndex == _boundary.length) { |
| 303 if (contentStartIndex != null) { | 334 if (contentStartIndex != null) { |
| 304 _index++; | 335 _index++; |
| 305 reportData(); | 336 reportData(); |
| 306 _index--; | 337 _index--; |
| 307 } | 338 } |
| 308 _multipartController.close(); | 339 _multipartController.close(); |
| 340 _multipartController = null; | |
| 341 _tryPropagateControllerState(); | |
| 309 _boundaryIndex = 0; | 342 _boundaryIndex = 0; |
| 310 _state = _BOUNDARY_ENDING; | 343 _state = _BOUNDARY_ENDING; |
| 311 } | 344 } |
| 312 } else { | 345 } else { |
| 313 // Restart matching of the boundary. | 346 // Restart matching of the boundary. |
| 314 _index = _index - _boundaryIndex; | 347 _index = _index - _boundaryIndex; |
| 315 if (contentStartIndex == null) contentStartIndex = _index; | 348 if (contentStartIndex == null) contentStartIndex = _index; |
| 316 _boundaryIndex = 0; | 349 _boundaryIndex = 0; |
| 317 } | 350 } |
| 318 break; | 351 break; |
| 319 | 352 |
| 320 case _LAST_BOUNDARY_DASH2: | 353 case _LAST_BOUNDARY_DASH2: |
| 321 _expectByteValue(byte, CharCode.DASH); | 354 _expectByteValue(byte, CharCode.DASH); |
| 322 _state = _LAST_BOUNDARY_ENDING; | 355 _state = _LAST_BOUNDARY_ENDING; |
| 323 break; | 356 break; |
| 324 | 357 |
| 325 case _LAST_BOUNDARY_ENDING: | 358 case _LAST_BOUNDARY_ENDING: |
| 326 if (byte == CharCode.CR) { | 359 if (byte == CharCode.CR) { |
| 327 _state = _LAST_BOUNDARY_END; | 360 _state = _LAST_BOUNDARY_END; |
| 328 } else { | 361 } else { |
| 329 _expectWhitespace(byte); | 362 _expectWhitespace(byte); |
| 330 } | 363 } |
| 331 break; | 364 break; |
| 332 | 365 |
| 333 case _LAST_BOUNDARY_END: | 366 case _LAST_BOUNDARY_END: |
| 334 _expectByteValue(byte, CharCode.LF); | 367 _expectByteValue(byte, CharCode.LF); |
| 335 if (_multipartController != null) { | 368 if (_multipartController != null) { |
| 336 _multipartController.close(); | 369 _multipartController.close(); |
| 337 _multipartController = null; | 370 _multipartController = null; |
| 371 _tryPropagateControllerState(); | |
| 338 } | 372 } |
| 339 _state = _DONE; | 373 _state = _DONE; |
| 340 break; | 374 break; |
| 341 | 375 |
| 342 default: | 376 default: |
| 343 // Should be unreachable. | 377 // Should be unreachable. |
| 344 assert(false); | 378 assert(false); |
| 345 break; | 379 break; |
| 346 } | 380 } |
| 347 | 381 |
| 348 // Move to the next byte. | 382 // Move to the next byte. |
| 349 _index++; | 383 _index++; |
| 350 } | 384 } |
| 351 | 385 |
| 352 // Report any known content. | 386 // Report any known content. |
| 353 if (_state == _CONTENT && contentStartIndex != null) { | 387 if (_state == _CONTENT && contentStartIndex != null) { |
| 354 reportData(); | 388 reportData(); |
| 355 } | 389 } |
| 356 | 390 |
| 357 // Resume if at end. | 391 // Resume if at end. |
| 358 if (_index == _buffer.length) { | 392 if (_index == _buffer.length) { |
| 359 _buffer = null; | 393 _buffer = null; |
| 360 _index = null; | 394 _index = null; |
| 361 _resumeStream(); | 395 _subscription.resume(); |
| 362 } | 396 } |
| 363 } | 397 } |
| 364 } | 398 } |
| OLD | NEW |