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

Side by Side Diff: sdk/lib/io/mime_multipart_parser.dart

Issue 14796015: Add new HttpMultipartFormData, used for parsing a MimeMultipart and extracting either text or binar… (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Update doc comments. Created 7 years, 7 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 | « sdk/lib/io/iolib_sources.gypi ('k') | tests/standalone/io/http_auth_digest_test.dart » ('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) 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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « sdk/lib/io/iolib_sources.gypi ('k') | tests/standalone/io/http_auth_digest_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698