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

Unified Diff: runtime/bin/mime_multipart_parser.dart

Issue 10441021: Implement a MIME multipart parser (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 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 side-by-side diff with in-line comments
Download patch
Index: runtime/bin/mime_multipart_parser.dart
diff --git a/runtime/bin/mime_multipart_parser.dart b/runtime/bin/mime_multipart_parser.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d8becaf5478c93e6d5c5f853a3677fbb3d7525ec
--- /dev/null
+++ b/runtime/bin/mime_multipart_parser.dart
@@ -0,0 +1,299 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class _MimeMultipartParser {
+ final int _START = 0;
+ final int _FIRST_BOUNDARY_ENDING = 111;
+ final int _FIRST_BOUNDARY_END = 112;
+ final int _BOUNDARY_ENDING = 1;
+ final int _BOUNDARY_END = 2;
+ final int _HEADER_START = 3;
+ final int _HEADER_FIELD = 4;
+ final int _HEADER_VALUE_START = 5;
+ final int _HEADER_VALUE = 6;
+ final int _HEADER_VALUE_FOLDING_OR_ENDING = 7;
+ final int _HEADER_VALUE_FOLD_OR_END = 8;
+ final int _HEADER_ENDING = 9;
+ final int _CONTENT = 10;
+ final int _LAST_BOUNDARY_DASH2 = 11;
+ final int _LAST_BOUNDARY_ENDING = 12;
+ final int _LAST_BOUNDARY_END = 13;
+ final int _DONE = 14;
+ final int _FAILURE = 15;
+
+ _MimeMultipartParser(String boundary) {
+ List<int> charCodes = boundary.charCodes();
+ _boundary = new List<int>(4 + charCodes.length);
+ // Set-up the matching boundary preceding it with CRLF and two
+ // dashes.
+ _boundary[0] = _CharCode.CR;
+ _boundary[1] = _CharCode.LF;
+ _boundary[2] = _CharCode.DASH;
+ _boundary[3] = _CharCode.DASH;
+ _boundary.setRange(4, charCodes.length, charCodes);
+ _state = _START;
+ _headerField = new StringBuffer();
+ _headerValue = new StringBuffer();
+ }
+
+ void update(List<int> buffer, int offset, int count) {
+ int index = offset;
+ int lastIndex = offset + count;
+ if (_state == _CONTENT && _boundaryIndex == 0) {
+ _contentStartIndex = 0;
+ } else {
+ _contentStartIndex = null;
+ }
+ // The data to parse might be "artificially" prefixed with a
+ // partial match of the boundary.
+ _boundaryPrefix = _boundaryIndex;
+
+ void reportData() {
Mads Ager (google) 2012/05/25 08:11:58 Having this local function defined here breaks the
Søren Gjesse 2012/05/25 11:57:06 Moved the function to the top. I still have to dec
+ if (partDataReceived == null) return;
+
+ var contentLength = _boundaryPrefix + index - _boundaryIndex;
+ if (_contentStartIndex < 0) {
+ if (contentLength <= _boundaryPrefix) {
+ partDataReceived(
+ _boundary.getRange(0, contentLength));
+ } else {
+ partDataReceived(
+ _boundary.getRange(0, _boundaryPrefix));
+ partDataReceived(
+ buffer.getRange(0, contentLength - _boundaryPrefix));
+ }
+ } else {
+ contentLength -= _contentStartIndex;
Mads Ager (google) 2012/05/25 08:11:58 So contentLength is the full length of contents se
Søren Gjesse 2012/05/25 11:57:06 Re-arranged the state variables. Most are now loca
+ partDataReceived(
+ buffer.getRange(_contentStartIndex, contentLength));
+ }
+ }
+
+ while ((index < lastIndex) && _state != _FAILURE && _state != _DONE) {
+ int byte;
+ if (index < 0) {
+ byte = _boundary[_boundaryPrefix + index];
+ } else {
+ byte = buffer[index];
+ }
+ switch (_state) {
+ case _START:
+ if (_toLowerCase(byte) == _toLowerCase(_boundary[_boundaryIndex])) {
+ _boundaryIndex++;
+ if (_boundaryIndex == _boundary.length) {
+ _state = _FIRST_BOUNDARY_ENDING;
+ _boundaryIndex = 0;
+ }
+ } else {
+ // Restart matching of the boundary.
+ index = index - _boundaryIndex;
+ _boundaryIndex = 0;
+ }
+ break;
+
+ case _FIRST_BOUNDARY_ENDING:
+ if (byte == _CharCode.CR) {
+ _state = _FIRST_BOUNDARY_END;
+ } else {
+ _expectWS(byte);
+ }
+ break;
+
+ case _FIRST_BOUNDARY_END:
+ _expect(byte, _CharCode.LF);
+ _state = _HEADER_START;
+ break;
+
+ case _BOUNDARY_ENDING:
+ if (byte == _CharCode.CR) {
+ _state = _BOUNDARY_END;
+ } else if (byte == _CharCode.DASH) {
+ _state = _LAST_BOUNDARY_DASH2;
+ } else {
+ _expectWS(byte);
+ }
+ break;
+
+ case _BOUNDARY_END:
+ _expect(byte, _CharCode.LF);
+ if (partEnd != null) {
+ partEnd(false);
+ }
+ _state = _HEADER_START;
+ break;
+
+ case _HEADER_START:
+ if (byte == _CharCode.CR) {
+ _state = _HEADER_ENDING;
+ } else {
+ // Start of new header field.
+ _headerField.addCharCode(_toLowerCase(byte));
+ _state = _HEADER_FIELD;
+ }
+ break;
+
+ case _HEADER_FIELD:
+ if (byte == _CharCode.COLON) {
+ _state = _HEADER_VALUE_START;
+ } else {
+ if (!_isTokenChar(byte)) {
+ throw new HttpParserException("Invalid header field name");
+ }
+ _headerField.addCharCode(_toLowerCase(byte));
+ }
+ break;
+
+ case _HEADER_VALUE_START:
+ if (byte == _CharCode.CR) {
+ _state = _HEADER_VALUE_FOLDING_OR_ENDING;
+ } else if (byte != _CharCode.SP && byte != _CharCode.HT) {
+ // Start of new header value.
+ _headerValue.addCharCode(byte);
+ _state = _HEADER_VALUE;
+ }
+ break;
+
+ case _HEADER_VALUE:
+ if (byte == _CharCode.CR) {
+ _state = _HEADER_VALUE_FOLDING_OR_ENDING;
+ } else {
+ _headerValue.addCharCode(byte);
+ }
+ break;
+
+ case _HEADER_VALUE_FOLDING_OR_ENDING:
+ _expect(byte, _CharCode.LF);
+ _state = _HEADER_VALUE_FOLD_OR_END;
+ break;
+
+ case _HEADER_VALUE_FOLD_OR_END:
+ if (byte == _CharCode.SP || byte == _CharCode.HT) {
+ _state = _HEADER_VALUE_START;
+ } else {
+ String headerField = _headerField.toString();
+ String headerValue =_headerValue.toString();
+ if (headerReceived != null) {
+ headerReceived(headerField, headerValue);
+ }
+ _headerField.clear();
+ _headerValue.clear();
+ if (byte == _CharCode.CR) {
+ _state = _HEADER_ENDING;
+ } else {
+ // Start of new header field.
+ _headerField.addCharCode(_toLowerCase(byte));
+ _state = _HEADER_FIELD;
+ }
+ }
+ break;
+
+ case _HEADER_ENDING:
+ _expect(byte, _CharCode.LF);
+ if (headersComplete != null) headersComplete();
+ _state = _CONTENT;
+ _contentStartIndex = index + 1;
+ break;
+
+ case _CONTENT:
+ if (_toLowerCase(byte) == _toLowerCase(_boundary[_boundaryIndex])) {
+ _boundaryIndex++;
+ if (_boundaryIndex == _boundary.length) {
+ if (_contentStartIndex != null) {
+ reportData();
+ }
+ _boundaryIndex = 0;
+ _state = _BOUNDARY_ENDING;
+ }
+ } else {
+ // Restart matching of the boundary.
+ index = index - _boundaryIndex;
+ if (_contentStartIndex == null) _contentStartIndex = index;
+ _boundaryIndex = 0;
+ }
+ break;
+
+ case _LAST_BOUNDARY_DASH2:
+ _expect(byte, _CharCode.DASH);
+ _state = _LAST_BOUNDARY_ENDING;
+ break;
+
+ case _LAST_BOUNDARY_ENDING:
+ if (byte == _CharCode.CR) {
+ _state = _LAST_BOUNDARY_END;
+ } else {
+ _expectWS(byte);
+ }
+ break;
+
+ case _LAST_BOUNDARY_END:
+ _expect(byte, _CharCode.LF);
+ if (partEnd != null) {
+ partEnd(true);
+ }
+ _state = _DONE;
+ break;
+
+ default:
+ // Should be unreachable.
+ assert(false);
+ break;
+ }
+
+ // Move to the next byte.
+ index++;
+ }
+
+ // Report any known content.
+ if (_state == _CONTENT && _contentStartIndex != null) {
+ reportData();
+ }
+ return index - offset;
+ }
+
+ bool _isTokenChar(int byte) {
+ return byte > 31 && byte < 128 && _Const.SEPARATORS.indexOf(byte) == -1;
+ }
+
+ int _toLowerCase(int byte) {
+ final int aCode = "A".charCodeAt(0);
+ final int zCode = "Z".charCodeAt(0);
+ final int delta = "a".charCodeAt(0) - aCode;
+ return (aCode <= byte && byte <= zCode) ? byte + delta : byte;
+ }
+
+ void _expect(int val1, int val2) {
+ if (val1 != val2) {
+ throw new MimeParserException("Failed to parse multipart mime 1");
+ }
+ }
+
+ void _expectWS(int byte) {
+ if (byte != _CharCode.SP && byte != _CharCode.HT) {
+ throw new MimeParserException("Failed to parse multipart mime 2");
+ }
+ }
+
+ List<int> _boundary;
+ int _state;
+ int _boundaryIndex = 0;
+ // Number of boundary bytes to artificially place before the supplied data.
+ int _boundaryPrefix = 0;
+ int _contentStartIndex = -1;
+
+ StringBuffer _headerField;
+ StringBuffer _headerValue;
+
+ Function partStart;
+ Function headerReceived;
+ Function headersComplete;
+ Function partDataReceived;
+ Function partEnd;
+}
+
+
+class MimeParserException implements Exception {
+ const MimeParserException([String this.message = ""]);
+ String toString() => "MimeParserException: $message";
+ final String message;
+}

Powered by Google App Engine
This is Rietveld 408576698