Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(996)

Side by Side Diff: dart/pkg/mime/lib/src/bound_multipart_stream.dart

Issue 874223002: Fix multipart transformer in package:mime (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Addressed comments, updated CHANGELOG.md & pubspec.yaml Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « dart/pkg/mime/CHANGELOG.md ('k') | dart/pkg/mime/pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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 }
OLDNEW
« no previous file with comments | « dart/pkg/mime/CHANGELOG.md ('k') | dart/pkg/mime/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698