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

Side by Side Diff: runtime/bin/http_parser.dart

Issue 9581011: Make the HttpParser and HttpUtil classes private (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 9 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 | « runtime/bin/http_impl.dart ('k') | runtime/bin/http_utils.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 // Global constants.
6 class _Const {
7 // Bytes for "HTTP/1.0".
8 static final HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48];
9 // Bytes for "HTTP/1.1".
10 static final HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49];
11
12 static final END_CHUNKED = const [0x30, 13, 10, 13, 10];
13 }
14
15
16 // Frequently used character codes.
17 class _CharCode {
18 static final int HT = 9;
19 static final int LF = 10;
20 static final int CR = 13;
21 static final int SP = 32;
22 static final int COLON = 58;
23 }
24
25
26 // States of the HTTP parser state machine.
27 class _State {
28 static final int START = 0;
29 static final int METHOD_OR_HTTP_VERSION = 1;
30 static final int REQUEST_LINE_METHOD = 2;
31 static final int REQUEST_LINE_URI = 3;
32 static final int REQUEST_LINE_HTTP_VERSION = 4;
33 static final int REQUEST_LINE_ENDING = 5;
34 static final int RESPONSE_LINE_STATUS_CODE = 6;
35 static final int RESPONSE_LINE_REASON_PHRASE = 7;
36 static final int RESPONSE_LINE_ENDING = 8;
37 static final int HEADER_START = 9;
38 static final int HEADER_FIELD = 10;
39 static final int HEADER_VALUE_START = 11;
40 static final int HEADER_VALUE = 12;
41 static final int HEADER_VALUE_FOLDING_OR_ENDING = 13;
42 static final int HEADER_VALUE_FOLD_OR_END = 14;
43 static final int HEADER_ENDING = 15;
44 static final int CHUNK_SIZE_STARTING_CR = 16;
45 static final int CHUNK_SIZE_STARTING_LF = 17;
46 static final int CHUNK_SIZE = 18;
47 static final int CHUNK_SIZE_ENDING = 19;
48 static final int CHUNKED_BODY_DONE_CR = 20;
49 static final int CHUNKED_BODY_DONE_LF = 21;
50 static final int BODY = 22;
51 }
52
53
54 /**
55 * HTTP parser which parses the HTTP stream as data is supplied
56 * through the writeList method. As the data is parsed the events
57 * RequestStart
58 * UriReceived
59 * HeaderReceived
60 * HeadersComplete
61 * DataReceived
62 * DataEnd
63 * are generated.
64 * Currently only HTTP requests with Content-Length header are supported.
65 */
66 class _HttpParser {
67 _HttpParser()
68 : _state = _State.START,
69 _failure = false,
70 _headerField = new StringBuffer(),
71 _headerValue = new StringBuffer(),
72 _method_or_status_code = new StringBuffer(),
73 _uri_or_reason_phrase = new StringBuffer();
74
75 // From RFC 2616.
76 // generic-message = start-line
77 // *(message-header CRLF)
78 // CRLF
79 // [ message-body ]
80 // start-line = Request-Line | Status-Line
81 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
82 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
83 // message-header = field-name ":" [ field-value ]
84 int writeList(List<int> buffer, int offset, int count) {
85 int index = offset;
86 int lastIndex = offset + count;
87 while ((index < lastIndex) && !_failure) {
88 int byte = buffer[index];
89 switch (_state) {
90 case _State.START:
91 _contentLength = 0;
92 _keepAlive = false;
93 _chunked = false;
94
95 if (byte == _Const.HTTP11[0]) {
96 // Start parsing HTTP method.
97 _httpVersionIndex = 1;
98 _state = _State.METHOD_OR_HTTP_VERSION;
99 } else {
100 // Start parsing method.
101 _method_or_status_code.addCharCode(byte);
102 _state = _State.REQUEST_LINE_METHOD;
103 }
104 break;
105
106 case _State.METHOD_OR_HTTP_VERSION:
107 if (_httpVersionIndex < _Const.HTTP11.length &&
108 byte == _Const.HTTP11[_httpVersionIndex]) {
109 // Continue parsing HTTP version.
110 _httpVersionIndex++;
111 } else if (_httpVersionIndex == _Const.HTTP11.length &&
112 byte == _CharCode.SP) {
113 // HTTP version parsed.
114 _state = _State.RESPONSE_LINE_STATUS_CODE;
115 } else {
116 // Did not parse HTTP version. Expect method instead.
117 for (int i = 0; i < _httpVersionIndex; i++) {
118 _method_or_status_code.addCharCode(_Const.HTTP11[i]);
119 }
120 _state = _State.REQUEST_LINE_URI;
121 }
122 break;
123
124 case _State.REQUEST_LINE_METHOD:
125 if (byte == _CharCode.SP) {
126 _state = _State.REQUEST_LINE_URI;
127 } else {
128 _method_or_status_code.addCharCode(byte);
129 }
130 break;
131
132 case _State.REQUEST_LINE_URI:
133 if (byte == _CharCode.SP) {
134 _state = _State.REQUEST_LINE_HTTP_VERSION;
135 _httpVersionIndex = 0;
136 } else {
137 _uri_or_reason_phrase.addCharCode(byte);
138 }
139 break;
140
141 case _State.REQUEST_LINE_HTTP_VERSION:
142 if (_httpVersionIndex < _Const.HTTP11.length) {
143 _expect(byte, _Const.HTTP11[_httpVersionIndex]);
144 _httpVersionIndex++;
145 } else {
146 _expect(byte, _CharCode.CR);
147 _state = _State.REQUEST_LINE_ENDING;
148 }
149 break;
150
151 case _State.REQUEST_LINE_ENDING:
152 _expect(byte, _CharCode.LF);
153 if (requestStart != null) {
154 requestStart(_method_or_status_code.toString(),
155 _uri_or_reason_phrase.toString());
156 }
157 _method_or_status_code.clear();
158 _uri_or_reason_phrase.clear();
159 _state = _State.HEADER_START;
160 break;
161
162 case _State.RESPONSE_LINE_STATUS_CODE:
163 if (byte == _CharCode.SP) {
164 _state = _State.RESPONSE_LINE_REASON_PHRASE;
165 } else {
166 if (byte < 0x30 && 0x39 < byte) {
167 _failure = true;
168 } else {
169 _method_or_status_code.addCharCode(byte);
170 }
171 }
172 break;
173
174 case _State.RESPONSE_LINE_REASON_PHRASE:
175 if (byte == _CharCode.CR) {
176 _state = _State.RESPONSE_LINE_ENDING;
177 } else {
178 _uri_or_reason_phrase.addCharCode(byte);
179 }
180 break;
181
182 case _State.RESPONSE_LINE_ENDING:
183 _expect(byte, _CharCode.LF);
184 // TODO(sgjesse): Check for valid status code.
185 if (responseStart != null) {
186 responseStart(Math.parseInt(_method_or_status_code.toString()),
187 _uri_or_reason_phrase.toString());
188 }
189 _method_or_status_code.clear();
190 _uri_or_reason_phrase.clear();
191 _state = _State.HEADER_START;
192 break;
193
194 case _State.HEADER_START:
195 if (byte == _CharCode.CR) {
196 _state = _State.HEADER_ENDING;
197 } else {
198 // Start of new header field.
199 _headerField.addCharCode(_toLowerCase(byte));
200 _state = _State.HEADER_FIELD;
201 }
202 break;
203
204 case _State.HEADER_FIELD:
205 if (byte == _CharCode.COLON) {
206 _state = _State.HEADER_VALUE_START;
207 } else {
208 _headerField.addCharCode(_toLowerCase(byte));
209 }
210 break;
211
212 case _State.HEADER_VALUE_START:
213 if (byte != _CharCode.SP && byte != _CharCode.HT) {
214 // Start of new header value.
215 _headerValue.addCharCode(byte);
216 _state = _State.HEADER_VALUE;
217 }
218 break;
219
220 case _State.HEADER_VALUE:
221 if (byte == _CharCode.CR) {
222 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING;
223 } else {
224 _headerValue.addCharCode(byte);
225 }
226 break;
227
228 case _State.HEADER_VALUE_FOLDING_OR_ENDING:
229 _expect(byte, _CharCode.LF);
230 _state = _State.HEADER_VALUE_FOLD_OR_END;
231 break;
232
233 case _State.HEADER_VALUE_FOLD_OR_END:
234 if (byte == _CharCode.SP || byte == _CharCode.HT) {
235 _state = _State.HEADER_VALUE_START;
236 } else {
237 String headerField = _headerField.toString();
238 String headerValue =_headerValue.toString();
239 // Ignore the Content-Length header if Transfer-Encoding
240 // is chunked (RFC 2616 section 4.4)
241 if (headerField == "content-length" && !_chunked) {
242 _contentLength = Math.parseInt(headerValue);
243 } else if (headerField == "connection" &&
244 headerValue == "keep-alive") {
245 _keepAlive = true;
246 } else if (headerField == "transfer-encoding" &&
247 headerValue == "chunked") {
248 _chunked = true;
249 _contentLength = -1;
250 }
251 if (headerReceived != null) {
252 headerReceived(headerField, headerValue);
253 }
254 _headerField.clear();
255 _headerValue.clear();
256
257 if (byte == _CharCode.CR) {
258 _state = _State.HEADER_ENDING;
259 } else {
260 // Start of new header field.
261 _headerField.addCharCode(_toLowerCase(byte));
262 _state = _State.HEADER_FIELD;
263 }
264 }
265 break;
266
267 case _State.HEADER_ENDING:
268 _expect(byte, _CharCode.LF);
269 if (headersComplete != null) headersComplete();
270
271 // If there is no data get ready to process the next request.
272 if (_chunked) {
273 _state = _State.CHUNK_SIZE;
274 _remainingContent = 0;
275 } else if (_contentLength == 0) {
276 if (dataEnd != null) dataEnd();
277 _state = _State.START;
278 } else if (_contentLength > 0) {
279 _remainingContent = _contentLength;
280 _state = _State.BODY;
281 } else {
282 // TODO(sgjesse): Error handling.
283 }
284 break;
285
286 case _State.CHUNK_SIZE_STARTING_CR:
287 _expect(byte, _CharCode.CR);
288 _state = _State.CHUNK_SIZE_STARTING_LF;
289 break;
290
291 case _State.CHUNK_SIZE_STARTING_LF:
292 _expect(byte, _CharCode.LF);
293 _state = _State.CHUNK_SIZE;
294 break;
295
296 case _State.CHUNK_SIZE:
297 if (byte == _CharCode.CR) {
298 _state = _State.CHUNK_SIZE_ENDING;
299 } else {
300 int value = _expectHexDigit(byte);
301 _remainingContent = _remainingContent * 16 + value;
302 }
303 break;
304
305 case _State.CHUNK_SIZE_ENDING:
306 _expect(byte, _CharCode.LF);
307 if (_remainingContent > 0) {
308 _state = _State.BODY;
309 } else {
310 _state = _State.CHUNKED_BODY_DONE_CR;
311 }
312 break;
313
314 case _State.CHUNKED_BODY_DONE_CR:
315 _expect(byte, _CharCode.CR);
316 _state = _State.CHUNKED_BODY_DONE_LF;
317 break;
318
319 case _State.CHUNKED_BODY_DONE_LF:
320 _expect(byte, _CharCode.LF);
321 if (dataEnd != null) dataEnd();
322 _state = _State.START;
323 break;
324
325 case _State.BODY:
326 // The body is not handled one byte at the time but in blocks.
327 int dataAvailable = lastIndex - index;
328 ByteArray data;
329 if (dataAvailable <= _remainingContent) {
330 data = new ByteArray(dataAvailable);
331 data.setRange(0, dataAvailable, buffer, index);
332 } else {
333 data = new ByteArray(_remainingContent);
334 data.setRange(0, _remainingContent, buffer, index);
335 }
336
337 if (dataReceived != null) dataReceived(data);
338 _remainingContent -= data.length;
339 index += data.length;
340 if (_remainingContent == 0) {
341 if (!_chunked) {
342 if (dataEnd != null) dataEnd();
343 _state = _State.START;
344 } else {
345 _state = _State.CHUNK_SIZE_STARTING_CR;
346 }
347 }
348
349 // Hack - as we always do index++ below.
350 index--;
351 break;
352
353 default:
354 // Should be unreachable.
355 assert(false);
356 }
357
358 // Move to the next byte.
359 index++;
360 }
361
362 // Return the number of bytes parsed.
363 return index - offset;
364 }
365
366 int get contentLength() => _contentLength;
367 bool get keepAlive() => _keepAlive;
368
369 int _toLowerCase(int byte) {
370 final int aCode = "A".charCodeAt(0);
371 final int zCode = "Z".charCodeAt(0);
372 final int delta = "a".charCodeAt(0) - aCode;
373 return (aCode <= byte && byte <= zCode) ? byte + delta : byte;
374 }
375
376 int _expect(int val1, int val2) {
377 if (val1 != val2) {
378 _failure = true;
379 }
380 }
381
382 int _expectHexDigit(int byte) {
383 if (0x30 <= byte && byte <= 0x39) {
384 return byte - 0x30; // 0 - 9
385 } else if (0x41 <= byte && byte <= 0x46) {
386 return byte - 0x41 + 10; // A - F
387 } else if (0x61 <= byte && byte <= 0x66) {
388 return byte - 0x61 + 10; // a - f
389 } else {
390 _failure = true;
391 return 0;
392 }
393 }
394
395 int _state;
396 bool _failure;
397 int _httpVersionIndex;
398 StringBuffer _method_or_status_code;
399 StringBuffer _uri_or_reason_phrase;
400 StringBuffer _headerField;
401 StringBuffer _headerValue;
402
403 int _contentLength;
404 bool _keepAlive;
405 bool _chunked;
406
407 int _remainingContent;
408
409 // Callbacks.
410 Function requestStart;
411 Function responseStart;
412 Function headerReceived;
413 Function headersComplete;
414 Function dataReceived;
415 Function dataEnd;
416 }
OLDNEW
« no previous file with comments | « runtime/bin/http_impl.dart ('k') | runtime/bin/http_utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698