Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 class _MimeMultipartParser { | |
| 6 final int _START = 0; | |
| 7 final int _FIRST_BOUNDARY_ENDING = 111; | |
| 8 final int _FIRST_BOUNDARY_END = 112; | |
| 9 final int _BOUNDARY_ENDING = 1; | |
| 10 final int _BOUNDARY_END = 2; | |
| 11 final int _HEADER_START = 3; | |
| 12 final int _HEADER_FIELD = 4; | |
| 13 final int _HEADER_VALUE_START = 5; | |
| 14 final int _HEADER_VALUE = 6; | |
| 15 final int _HEADER_VALUE_FOLDING_OR_ENDING = 7; | |
| 16 final int _HEADER_VALUE_FOLD_OR_END = 8; | |
| 17 final int _HEADER_ENDING = 9; | |
| 18 final int _CONTENT = 10; | |
| 19 final int _LAST_BOUNDARY_DASH2 = 11; | |
| 20 final int _LAST_BOUNDARY_ENDING = 12; | |
| 21 final int _LAST_BOUNDARY_END = 13; | |
| 22 final int _DONE = 14; | |
| 23 final int _FAILURE = 15; | |
| 24 | |
| 25 _MimeMultipartParser(String boundary) { | |
| 26 List<int> charCodes = boundary.charCodes(); | |
| 27 _boundary = new List<int>(4 + charCodes.length); | |
| 28 // Set-up the matching boundary preceding it with CRLF and two | |
| 29 // dashes. | |
| 30 _boundary[0] = _CharCode.CR; | |
| 31 _boundary[1] = _CharCode.LF; | |
| 32 _boundary[2] = _CharCode.DASH; | |
| 33 _boundary[3] = _CharCode.DASH; | |
| 34 _boundary.setRange(4, charCodes.length, charCodes); | |
| 35 _state = _START; | |
| 36 _headerField = new StringBuffer(); | |
| 37 _headerValue = new StringBuffer(); | |
| 38 } | |
| 39 | |
| 40 void update(List<int> buffer, int offset, int count) { | |
| 41 int index = offset; | |
| 42 int lastIndex = offset + count; | |
| 43 if (_state == _CONTENT && _boundaryIndex == 0) { | |
| 44 _contentStartIndex = 0; | |
| 45 } else { | |
| 46 _contentStartIndex = null; | |
| 47 } | |
| 48 // The data to parse might be "artificially" prefixed with a | |
| 49 // partial match of the boundary. | |
| 50 _boundaryPrefix = _boundaryIndex; | |
| 51 | |
| 52 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
| |
| 53 if (partDataReceived == null) return; | |
| 54 | |
| 55 var contentLength = _boundaryPrefix + index - _boundaryIndex; | |
| 56 if (_contentStartIndex < 0) { | |
| 57 if (contentLength <= _boundaryPrefix) { | |
| 58 partDataReceived( | |
| 59 _boundary.getRange(0, contentLength)); | |
| 60 } else { | |
| 61 partDataReceived( | |
| 62 _boundary.getRange(0, _boundaryPrefix)); | |
| 63 partDataReceived( | |
| 64 buffer.getRange(0, contentLength - _boundaryPrefix)); | |
| 65 } | |
| 66 } else { | |
| 67 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
| |
| 68 partDataReceived( | |
| 69 buffer.getRange(_contentStartIndex, contentLength)); | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 while ((index < lastIndex) && _state != _FAILURE && _state != _DONE) { | |
| 74 int byte; | |
| 75 if (index < 0) { | |
| 76 byte = _boundary[_boundaryPrefix + index]; | |
| 77 } else { | |
| 78 byte = buffer[index]; | |
| 79 } | |
| 80 switch (_state) { | |
| 81 case _START: | |
| 82 if (_toLowerCase(byte) == _toLowerCase(_boundary[_boundaryIndex])) { | |
| 83 _boundaryIndex++; | |
| 84 if (_boundaryIndex == _boundary.length) { | |
| 85 _state = _FIRST_BOUNDARY_ENDING; | |
| 86 _boundaryIndex = 0; | |
| 87 } | |
| 88 } else { | |
| 89 // Restart matching of the boundary. | |
| 90 index = index - _boundaryIndex; | |
| 91 _boundaryIndex = 0; | |
| 92 } | |
| 93 break; | |
| 94 | |
| 95 case _FIRST_BOUNDARY_ENDING: | |
| 96 if (byte == _CharCode.CR) { | |
| 97 _state = _FIRST_BOUNDARY_END; | |
| 98 } else { | |
| 99 _expectWS(byte); | |
| 100 } | |
| 101 break; | |
| 102 | |
| 103 case _FIRST_BOUNDARY_END: | |
| 104 _expect(byte, _CharCode.LF); | |
| 105 _state = _HEADER_START; | |
| 106 break; | |
| 107 | |
| 108 case _BOUNDARY_ENDING: | |
| 109 if (byte == _CharCode.CR) { | |
| 110 _state = _BOUNDARY_END; | |
| 111 } else if (byte == _CharCode.DASH) { | |
| 112 _state = _LAST_BOUNDARY_DASH2; | |
| 113 } else { | |
| 114 _expectWS(byte); | |
| 115 } | |
| 116 break; | |
| 117 | |
| 118 case _BOUNDARY_END: | |
| 119 _expect(byte, _CharCode.LF); | |
| 120 if (partEnd != null) { | |
| 121 partEnd(false); | |
| 122 } | |
| 123 _state = _HEADER_START; | |
| 124 break; | |
| 125 | |
| 126 case _HEADER_START: | |
| 127 if (byte == _CharCode.CR) { | |
| 128 _state = _HEADER_ENDING; | |
| 129 } else { | |
| 130 // Start of new header field. | |
| 131 _headerField.addCharCode(_toLowerCase(byte)); | |
| 132 _state = _HEADER_FIELD; | |
| 133 } | |
| 134 break; | |
| 135 | |
| 136 case _HEADER_FIELD: | |
| 137 if (byte == _CharCode.COLON) { | |
| 138 _state = _HEADER_VALUE_START; | |
| 139 } else { | |
| 140 if (!_isTokenChar(byte)) { | |
| 141 throw new HttpParserException("Invalid header field name"); | |
| 142 } | |
| 143 _headerField.addCharCode(_toLowerCase(byte)); | |
| 144 } | |
| 145 break; | |
| 146 | |
| 147 case _HEADER_VALUE_START: | |
| 148 if (byte == _CharCode.CR) { | |
| 149 _state = _HEADER_VALUE_FOLDING_OR_ENDING; | |
| 150 } else if (byte != _CharCode.SP && byte != _CharCode.HT) { | |
| 151 // Start of new header value. | |
| 152 _headerValue.addCharCode(byte); | |
| 153 _state = _HEADER_VALUE; | |
| 154 } | |
| 155 break; | |
| 156 | |
| 157 case _HEADER_VALUE: | |
| 158 if (byte == _CharCode.CR) { | |
| 159 _state = _HEADER_VALUE_FOLDING_OR_ENDING; | |
| 160 } else { | |
| 161 _headerValue.addCharCode(byte); | |
| 162 } | |
| 163 break; | |
| 164 | |
| 165 case _HEADER_VALUE_FOLDING_OR_ENDING: | |
| 166 _expect(byte, _CharCode.LF); | |
| 167 _state = _HEADER_VALUE_FOLD_OR_END; | |
| 168 break; | |
| 169 | |
| 170 case _HEADER_VALUE_FOLD_OR_END: | |
| 171 if (byte == _CharCode.SP || byte == _CharCode.HT) { | |
| 172 _state = _HEADER_VALUE_START; | |
| 173 } else { | |
| 174 String headerField = _headerField.toString(); | |
| 175 String headerValue =_headerValue.toString(); | |
| 176 if (headerReceived != null) { | |
| 177 headerReceived(headerField, headerValue); | |
| 178 } | |
| 179 _headerField.clear(); | |
| 180 _headerValue.clear(); | |
| 181 if (byte == _CharCode.CR) { | |
| 182 _state = _HEADER_ENDING; | |
| 183 } else { | |
| 184 // Start of new header field. | |
| 185 _headerField.addCharCode(_toLowerCase(byte)); | |
| 186 _state = _HEADER_FIELD; | |
| 187 } | |
| 188 } | |
| 189 break; | |
| 190 | |
| 191 case _HEADER_ENDING: | |
| 192 _expect(byte, _CharCode.LF); | |
| 193 if (headersComplete != null) headersComplete(); | |
| 194 _state = _CONTENT; | |
| 195 _contentStartIndex = index + 1; | |
| 196 break; | |
| 197 | |
| 198 case _CONTENT: | |
| 199 if (_toLowerCase(byte) == _toLowerCase(_boundary[_boundaryIndex])) { | |
| 200 _boundaryIndex++; | |
| 201 if (_boundaryIndex == _boundary.length) { | |
| 202 if (_contentStartIndex != null) { | |
| 203 reportData(); | |
| 204 } | |
| 205 _boundaryIndex = 0; | |
| 206 _state = _BOUNDARY_ENDING; | |
| 207 } | |
| 208 } else { | |
| 209 // Restart matching of the boundary. | |
| 210 index = index - _boundaryIndex; | |
| 211 if (_contentStartIndex == null) _contentStartIndex = index; | |
| 212 _boundaryIndex = 0; | |
| 213 } | |
| 214 break; | |
| 215 | |
| 216 case _LAST_BOUNDARY_DASH2: | |
| 217 _expect(byte, _CharCode.DASH); | |
| 218 _state = _LAST_BOUNDARY_ENDING; | |
| 219 break; | |
| 220 | |
| 221 case _LAST_BOUNDARY_ENDING: | |
| 222 if (byte == _CharCode.CR) { | |
| 223 _state = _LAST_BOUNDARY_END; | |
| 224 } else { | |
| 225 _expectWS(byte); | |
| 226 } | |
| 227 break; | |
| 228 | |
| 229 case _LAST_BOUNDARY_END: | |
| 230 _expect(byte, _CharCode.LF); | |
| 231 if (partEnd != null) { | |
| 232 partEnd(true); | |
| 233 } | |
| 234 _state = _DONE; | |
| 235 break; | |
| 236 | |
| 237 default: | |
| 238 // Should be unreachable. | |
| 239 assert(false); | |
| 240 break; | |
| 241 } | |
| 242 | |
| 243 // Move to the next byte. | |
| 244 index++; | |
| 245 } | |
| 246 | |
| 247 // Report any known content. | |
| 248 if (_state == _CONTENT && _contentStartIndex != null) { | |
| 249 reportData(); | |
| 250 } | |
| 251 return index - offset; | |
| 252 } | |
| 253 | |
| 254 bool _isTokenChar(int byte) { | |
| 255 return byte > 31 && byte < 128 && _Const.SEPARATORS.indexOf(byte) == -1; | |
| 256 } | |
| 257 | |
| 258 int _toLowerCase(int byte) { | |
| 259 final int aCode = "A".charCodeAt(0); | |
| 260 final int zCode = "Z".charCodeAt(0); | |
| 261 final int delta = "a".charCodeAt(0) - aCode; | |
| 262 return (aCode <= byte && byte <= zCode) ? byte + delta : byte; | |
| 263 } | |
| 264 | |
| 265 void _expect(int val1, int val2) { | |
| 266 if (val1 != val2) { | |
| 267 throw new MimeParserException("Failed to parse multipart mime 1"); | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 void _expectWS(int byte) { | |
| 272 if (byte != _CharCode.SP && byte != _CharCode.HT) { | |
| 273 throw new MimeParserException("Failed to parse multipart mime 2"); | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 List<int> _boundary; | |
| 278 int _state; | |
| 279 int _boundaryIndex = 0; | |
| 280 // Number of boundary bytes to artificially place before the supplied data. | |
| 281 int _boundaryPrefix = 0; | |
| 282 int _contentStartIndex = -1; | |
| 283 | |
| 284 StringBuffer _headerField; | |
| 285 StringBuffer _headerValue; | |
| 286 | |
| 287 Function partStart; | |
| 288 Function headerReceived; | |
| 289 Function headersComplete; | |
| 290 Function partDataReceived; | |
| 291 Function partEnd; | |
| 292 } | |
| 293 | |
| 294 | |
| 295 class MimeParserException implements Exception { | |
| 296 const MimeParserException([String this.message = ""]); | |
| 297 String toString() => "MimeParserException: $message"; | |
| 298 final String message; | |
| 299 } | |
| OLD | NEW |