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

Side by Side Diff: runtime/bin/http_impl.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 | « no previous file | runtime/bin/http_parser.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 // 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 // Frequently used character codes.
16 class _CharCode {
17 static final int HT = 9;
18 static final int LF = 10;
19 static final int CR = 13;
20 static final int SP = 32;
21 static final int COLON = 58;
22 }
23
24
25 // States of the HTTP parser state machine.
26 class _State {
27 static final int START = 0;
28 static final int METHOD_OR_HTTP_VERSION = 1;
29 static final int REQUEST_LINE_METHOD = 2;
30 static final int REQUEST_LINE_URI = 3;
31 static final int REQUEST_LINE_HTTP_VERSION = 4;
32 static final int REQUEST_LINE_ENDING = 5;
33 static final int RESPONSE_LINE_STATUS_CODE = 6;
34 static final int RESPONSE_LINE_REASON_PHRASE = 7;
35 static final int RESPONSE_LINE_ENDING = 8;
36 static final int HEADER_START = 9;
37 static final int HEADER_FIELD = 10;
38 static final int HEADER_VALUE_START = 11;
39 static final int HEADER_VALUE = 12;
40 static final int HEADER_VALUE_FOLDING_OR_ENDING = 13;
41 static final int HEADER_VALUE_FOLD_OR_END = 14;
42 static final int HEADER_ENDING = 15;
43 static final int CHUNK_SIZE_STARTING_CR = 16;
44 static final int CHUNK_SIZE_STARTING_LF = 17;
45 static final int CHUNK_SIZE = 18;
46 static final int CHUNK_SIZE_ENDING = 19;
47 static final int CHUNKED_BODY_DONE_CR = 20;
48 static final int CHUNKED_BODY_DONE_LF = 21;
49 static final int BODY = 22;
50 }
51
52
53 /**
54 * HTTP parser which parses the HTTP stream as data is supplied
55 * through the writeList method. As the data is parsed the events
56 * RequestStart
57 * UriReceived
58 * HeaderReceived
59 * HeadersComplete
60 * DataReceived
61 * DataEnd
62 * are generated.
63 * Currently only HTTP requests with Content-Length header are supported.
64 */
65 class HttpParser {
66 HttpParser()
67 : _state = _State.START,
68 _failure = false,
69 _headerField = new StringBuffer(),
70 _headerValue = new StringBuffer(),
71 _method_or_status_code = new StringBuffer(),
72 _uri_or_reason_phrase = new StringBuffer();
73
74 // From RFC 2616.
75 // generic-message = start-line
76 // *(message-header CRLF)
77 // CRLF
78 // [ message-body ]
79 // start-line = Request-Line | Status-Line
80 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
81 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
82 // message-header = field-name ":" [ field-value ]
83 int writeList(List<int> buffer, int offset, int count) {
84 int index = offset;
85 int lastIndex = offset + count;
86 while ((index < lastIndex) && !_failure) {
87 int byte = buffer[index];
88 switch (_state) {
89 case _State.START:
90 _contentLength = 0;
91 _keepAlive = false;
92 _chunked = false;
93
94 if (byte == _Const.HTTP11[0]) {
95 // Start parsing HTTP method.
96 _httpVersionIndex = 1;
97 _state = _State.METHOD_OR_HTTP_VERSION;
98 } else {
99 // Start parsing method.
100 _method_or_status_code.addCharCode(byte);
101 _state = _State.REQUEST_LINE_METHOD;
102 }
103 break;
104
105 case _State.METHOD_OR_HTTP_VERSION:
106 if (_httpVersionIndex < _Const.HTTP11.length &&
107 byte == _Const.HTTP11[_httpVersionIndex]) {
108 // Continue parsing HTTP version.
109 _httpVersionIndex++;
110 } else if (_httpVersionIndex == _Const.HTTP11.length &&
111 byte == _CharCode.SP) {
112 // HTTP version parsed.
113 _state = _State.RESPONSE_LINE_STATUS_CODE;
114 } else {
115 // Did not parse HTTP version. Expect method instead.
116 for (int i = 0; i < _httpVersionIndex; i++) {
117 _method_or_status_code.addCharCode(_Const.HTTP11[i]);
118 }
119 _state = _State.REQUEST_LINE_URI;
120 }
121 break;
122
123 case _State.REQUEST_LINE_METHOD:
124 if (byte == _CharCode.SP) {
125 _state = _State.REQUEST_LINE_URI;
126 } else {
127 _method_or_status_code.addCharCode(byte);
128 }
129 break;
130
131 case _State.REQUEST_LINE_URI:
132 if (byte == _CharCode.SP) {
133 _state = _State.REQUEST_LINE_HTTP_VERSION;
134 _httpVersionIndex = 0;
135 } else {
136 _uri_or_reason_phrase.addCharCode(byte);
137 }
138 break;
139
140 case _State.REQUEST_LINE_HTTP_VERSION:
141 if (_httpVersionIndex < _Const.HTTP11.length) {
142 _expect(byte, _Const.HTTP11[_httpVersionIndex]);
143 _httpVersionIndex++;
144 } else {
145 _expect(byte, _CharCode.CR);
146 _state = _State.REQUEST_LINE_ENDING;
147 }
148 break;
149
150 case _State.REQUEST_LINE_ENDING:
151 _expect(byte, _CharCode.LF);
152 if (requestStart != null) {
153 requestStart(_method_or_status_code.toString(),
154 _uri_or_reason_phrase.toString());
155 }
156 _method_or_status_code.clear();
157 _uri_or_reason_phrase.clear();
158 _state = _State.HEADER_START;
159 break;
160
161 case _State.RESPONSE_LINE_STATUS_CODE:
162 if (byte == _CharCode.SP) {
163 _state = _State.RESPONSE_LINE_REASON_PHRASE;
164 } else {
165 if (byte < 0x30 && 0x39 < byte) {
166 _failure = true;
167 } else {
168 _method_or_status_code.addCharCode(byte);
169 }
170 }
171 break;
172
173 case _State.RESPONSE_LINE_REASON_PHRASE:
174 if (byte == _CharCode.CR) {
175 _state = _State.RESPONSE_LINE_ENDING;
176 } else {
177 _uri_or_reason_phrase.addCharCode(byte);
178 }
179 break;
180
181 case _State.RESPONSE_LINE_ENDING:
182 _expect(byte, _CharCode.LF);
183 // TODO(sgjesse): Check for valid status code.
184 if (responseStart != null) {
185 responseStart(Math.parseInt(_method_or_status_code.toString()),
186 _uri_or_reason_phrase.toString());
187 }
188 _method_or_status_code.clear();
189 _uri_or_reason_phrase.clear();
190 _state = _State.HEADER_START;
191 break;
192
193 case _State.HEADER_START:
194 if (byte == _CharCode.CR) {
195 _state = _State.HEADER_ENDING;
196 } else {
197 // Start of new header field.
198 _headerField.addCharCode(_toLowerCase(byte));
199 _state = _State.HEADER_FIELD;
200 }
201 break;
202
203 case _State.HEADER_FIELD:
204 if (byte == _CharCode.COLON) {
205 _state = _State.HEADER_VALUE_START;
206 } else {
207 _headerField.addCharCode(_toLowerCase(byte));
208 }
209 break;
210
211 case _State.HEADER_VALUE_START:
212 if (byte != _CharCode.SP && byte != _CharCode.HT) {
213 // Start of new header value.
214 _headerValue.addCharCode(byte);
215 _state = _State.HEADER_VALUE;
216 }
217 break;
218
219 case _State.HEADER_VALUE:
220 if (byte == _CharCode.CR) {
221 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING;
222 } else {
223 _headerValue.addCharCode(byte);
224 }
225 break;
226
227 case _State.HEADER_VALUE_FOLDING_OR_ENDING:
228 _expect(byte, _CharCode.LF);
229 _state = _State.HEADER_VALUE_FOLD_OR_END;
230 break;
231
232 case _State.HEADER_VALUE_FOLD_OR_END:
233 if (byte == _CharCode.SP || byte == _CharCode.HT) {
234 _state = _State.HEADER_VALUE_START;
235 } else {
236 String headerField = _headerField.toString();
237 String headerValue =_headerValue.toString();
238 // Ignore the Content-Length header if Transfer-Encoding
239 // is chunked (RFC 2616 section 4.4)
240 if (headerField == "content-length" && !_chunked) {
241 _contentLength = Math.parseInt(headerValue);
242 } else if (headerField == "connection" &&
243 headerValue == "keep-alive") {
244 _keepAlive = true;
245 } else if (headerField == "transfer-encoding" &&
246 headerValue == "chunked") {
247 _chunked = true;
248 _contentLength = -1;
249 }
250 if (headerReceived != null) {
251 headerReceived(headerField, headerValue);
252 }
253 _headerField.clear();
254 _headerValue.clear();
255
256 if (byte == _CharCode.CR) {
257 _state = _State.HEADER_ENDING;
258 } else {
259 // Start of new header field.
260 _headerField.addCharCode(_toLowerCase(byte));
261 _state = _State.HEADER_FIELD;
262 }
263 }
264 break;
265
266 case _State.HEADER_ENDING:
267 _expect(byte, _CharCode.LF);
268 if (headersComplete != null) headersComplete();
269
270 // If there is no data get ready to process the next request.
271 if (_chunked) {
272 _state = _State.CHUNK_SIZE;
273 _remainingContent = 0;
274 } else if (_contentLength == 0) {
275 if (dataEnd != null) dataEnd();
276 _state = _State.START;
277 } else if (_contentLength > 0) {
278 _remainingContent = _contentLength;
279 _state = _State.BODY;
280 } else {
281 // TODO(sgjesse): Error handling.
282 }
283 break;
284
285 case _State.CHUNK_SIZE_STARTING_CR:
286 _expect(byte, _CharCode.CR);
287 _state = _State.CHUNK_SIZE_STARTING_LF;
288 break;
289
290 case _State.CHUNK_SIZE_STARTING_LF:
291 _expect(byte, _CharCode.LF);
292 _state = _State.CHUNK_SIZE;
293 break;
294
295 case _State.CHUNK_SIZE:
296 if (byte == _CharCode.CR) {
297 _state = _State.CHUNK_SIZE_ENDING;
298 } else {
299 int value = _expectHexDigit(byte);
300 _remainingContent = _remainingContent * 16 + value;
301 }
302 break;
303
304 case _State.CHUNK_SIZE_ENDING:
305 _expect(byte, _CharCode.LF);
306 if (_remainingContent > 0) {
307 _state = _State.BODY;
308 } else {
309 _state = _State.CHUNKED_BODY_DONE_CR;
310 }
311 break;
312
313 case _State.CHUNKED_BODY_DONE_CR:
314 _expect(byte, _CharCode.CR);
315 _state = _State.CHUNKED_BODY_DONE_LF;
316 break;
317
318 case _State.CHUNKED_BODY_DONE_LF:
319 _expect(byte, _CharCode.LF);
320 if (dataEnd != null) dataEnd();
321 _state = _State.START;
322 break;
323
324 case _State.BODY:
325 // The body is not handled one byte at the time but in blocks.
326 int dataAvailable = lastIndex - index;
327 ByteArray data;
328 if (dataAvailable <= _remainingContent) {
329 data = new ByteArray(dataAvailable);
330 data.setRange(0, dataAvailable, buffer, index);
331 } else {
332 data = new ByteArray(_remainingContent);
333 data.setRange(0, _remainingContent, buffer, index);
334 }
335
336 if (dataReceived != null) dataReceived(data);
337 _remainingContent -= data.length;
338 index += data.length;
339 if (_remainingContent == 0) {
340 if (!_chunked) {
341 if (dataEnd != null) dataEnd();
342 _state = _State.START;
343 } else {
344 _state = _State.CHUNK_SIZE_STARTING_CR;
345 }
346 }
347
348 // Hack - as we always do index++ below.
349 index--;
350 break;
351
352 default:
353 // Should be unreachable.
354 assert(false);
355 }
356
357 // Move to the next byte.
358 index++;
359 }
360
361 // Return the number of bytes parsed.
362 return index - offset;
363 }
364
365 int get contentLength() => _contentLength;
366 bool get keepAlive() => _keepAlive;
367
368 int _toLowerCase(int byte) {
369 final int aCode = "A".charCodeAt(0);
370 final int zCode = "Z".charCodeAt(0);
371 final int delta = "a".charCodeAt(0) - aCode;
372 return (aCode <= byte && byte <= zCode) ? byte + delta : byte;
373 }
374
375 int _expect(int val1, int val2) {
376 if (val1 != val2) {
377 _failure = true;
378 }
379 }
380
381 int _expectHexDigit(int byte) {
382 if (0x30 <= byte && byte <= 0x39) {
383 return byte - 0x30; // 0 - 9
384 } else if (0x41 <= byte && byte <= 0x46) {
385 return byte - 0x41 + 10; // A - F
386 } else if (0x61 <= byte && byte <= 0x66) {
387 return byte - 0x61 + 10; // a - f
388 } else {
389 _failure = true;
390 return 0;
391 }
392 }
393
394 int _state;
395 bool _failure;
396 int _httpVersionIndex;
397 StringBuffer _method_or_status_code;
398 StringBuffer _uri_or_reason_phrase;
399 StringBuffer _headerField;
400 StringBuffer _headerValue;
401
402 int _contentLength;
403 bool _keepAlive;
404 bool _chunked;
405
406 int _remainingContent;
407
408 // Callbacks.
409 Function requestStart;
410 Function responseStart;
411 Function headerReceived;
412 Function headersComplete;
413 Function dataReceived;
414 Function dataEnd;
415 }
416
417
418 // Utility class for encoding a string into UTF-8 byte stream. 5 // Utility class for encoding a string into UTF-8 byte stream.
419 class _UTF8Encoder { 6 class _UTF8Encoder {
420 static List<int> encodeString(String string) { 7 static List<int> encodeString(String string) {
421 int size = _encodingSize(string); 8 int size = _encodingSize(string);
422 ByteArray result = new ByteArray(size); 9 ByteArray result = new ByteArray(size);
423 _encodeString(string, result); 10 _encodeString(string, result);
424 return result; 11 return result;
425 } 12 }
426 13
427 static int _encodingSize(String string) => _encodeString(string, null); 14 static int _encodingSize(String string) => _encodeString(string, null);
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
619 206
620 void _onDataEnd() { 207 void _onDataEnd() {
621 if (_inputStream != null) _inputStream._closeReceived(); 208 if (_inputStream != null) _inputStream._closeReceived();
622 } 209 }
623 210
624 // Escaped characters in uri are expected to have been parsed. 211 // Escaped characters in uri are expected to have been parsed.
625 void _parseRequestUri(String uri) { 212 void _parseRequestUri(String uri) {
626 int position; 213 int position;
627 position = uri.indexOf("?", 0); 214 position = uri.indexOf("?", 0);
628 if (position == -1) { 215 if (position == -1) {
629 _path = HttpUtil.decodeUrlEncodedString(_uri); 216 _path = _HttpUtils.decodeUrlEncodedString(_uri);
630 _queryString = null; 217 _queryString = null;
631 _queryParameters = new Map(); 218 _queryParameters = new Map();
632 } else { 219 } else {
633 _path = HttpUtil.decodeUrlEncodedString(_uri.substring(0, position)); 220 _path = _HttpUtils.decodeUrlEncodedString(_uri.substring(0, position));
634 _queryString = _uri.substring(position + 1); 221 _queryString = _uri.substring(position + 1);
635 _queryParameters = HttpUtil.splitQueryString(_queryString); 222 _queryParameters = _HttpUtils.splitQueryString(_queryString);
636 } 223 }
637 } 224 }
638 225
639 // Delegate functions for the HttpInputStream implementation. 226 // Delegate functions for the HttpInputStream implementation.
640 int _streamAvailable() { 227 int _streamAvailable() {
641 return _buffer.length; 228 return _buffer.length;
642 } 229 }
643 230
644 List<int> _streamRead(int bytesToRead) { 231 List<int> _streamRead(int bytesToRead) {
645 return _buffer.readBytes(bytesToRead); 232 return _buffer.readBytes(bytesToRead);
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after
912 void set onError(void callback()) { 499 void set onError(void callback()) {
913 _requestOrResponse._streamSetErrorHandler(callback); 500 _requestOrResponse._streamSetErrorHandler(callback);
914 } 501 }
915 502
916 _HttpRequestResponseBase _requestOrResponse; 503 _HttpRequestResponseBase _requestOrResponse;
917 } 504 }
918 505
919 506
920 class _HttpConnectionBase { 507 class _HttpConnectionBase {
921 _HttpConnectionBase() : _sendBuffers = new Queue(), 508 _HttpConnectionBase() : _sendBuffers = new Queue(),
922 _httpParser = new HttpParser(); 509 _httpParser = new _HttpParser();
923 510
924 void _connectionEstablished(Socket socket) { 511 void _connectionEstablished(Socket socket) {
925 _socket = socket; 512 _socket = socket;
926 // Register handler for socket events. 513 // Register handler for socket events.
927 _socket.onData = _onData; 514 _socket.onData = _onData;
928 _socket.onClosed = _onClosed; 515 _socket.onClosed = _onClosed;
929 _socket.onError = _onError; 516 _socket.onError = _onError;
930 } 517 }
931 518
932 OutputStream get outputStream() { 519 OutputStream get outputStream() {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
968 void set onDisconnect(void callback()) { 555 void set onDisconnect(void callback()) {
969 _onDisconnectCallback = callback; 556 _onDisconnectCallback = callback;
970 } 557 }
971 558
972 void set onError(void callback(String errorMessage)) { 559 void set onError(void callback(String errorMessage)) {
973 _onErrorCallback = callback; 560 _onErrorCallback = callback;
974 } 561 }
975 562
976 Socket _socket; 563 Socket _socket;
977 bool _closing = false; // Is the socket closed by the client? 564 bool _closing = false; // Is the socket closed by the client?
978 HttpParser _httpParser; 565 _HttpParser _httpParser;
979 566
980 Queue _sendBuffers; 567 Queue _sendBuffers;
981 568
982 Function _onDisconnectCallback; 569 Function _onDisconnectCallback;
983 Function _onErrorCallback; 570 Function _onErrorCallback;
984 } 571 }
985 572
986 573
987 // HTTP server connection over a socket. 574 // HTTP server connection over a socket.
988 class _HttpConnection extends _HttpConnectionBase { 575 class _HttpConnection extends _HttpConnectionBase {
(...skipping 519 matching lines...) Expand 10 before | Expand all | Expand 10 after
1508 void set onError(void callback(int status)) { 1095 void set onError(void callback(int status)) {
1509 _onError = callback; 1096 _onError = callback;
1510 } 1097 }
1511 1098
1512 Function _onOpen; 1099 Function _onOpen;
1513 Function _onError; 1100 Function _onError;
1514 Map<String, Queue<_SocketConnection>> _openSockets; 1101 Map<String, Queue<_SocketConnection>> _openSockets;
1515 Timer _evictionTimer; 1102 Timer _evictionTimer;
1516 bool _shutdown; // Has this HTTP client been shutdown? 1103 bool _shutdown; // Has this HTTP client been shutdown?
1517 } 1104 }
1518
1519
1520 class HttpUtil {
1521 static String decodeUrlEncodedString(String urlEncoded) {
1522 void invalidEscape() {
1523 // TODO(sgjesse): Handle the error.
1524 }
1525
1526 StringBuffer result = new StringBuffer();
1527 for (int ii = 0; urlEncoded.length > ii; ++ii) {
1528 if ('+' == urlEncoded[ii]) {
1529 result.add(' ');
1530 } else if ('%' == urlEncoded[ii] &&
1531 urlEncoded.length - 2 > ii) {
1532 try {
1533 int charCode =
1534 Math.parseInt('0x' + urlEncoded.substring(ii + 1, ii + 3));
1535 if (charCode <= 0x7f) {
1536 result.add(new String.fromCharCodes([charCode]));
1537 ii += 2;
1538 } else {
1539 invalidEscape();
1540 return '';
1541 }
1542 } catch (BadNumberFormatException ignored) {
1543 invalidEscape();
1544 return '';
1545 }
1546 } else {
1547 result.add(urlEncoded[ii]);
1548 }
1549 }
1550 return result.toString();
1551 }
1552
1553 static Map<String, String> splitQueryString(String queryString) {
1554 Map<String, String> result = new Map<String, String>();
1555 int currentPosition = 0;
1556 while (currentPosition < queryString.length) {
1557 int position = queryString.indexOf("=", currentPosition);
1558 if (position == -1) {
1559 break;
1560 }
1561 String name = queryString.substring(currentPosition, position);
1562 currentPosition = position + 1;
1563 position = queryString.indexOf("&", currentPosition);
1564 String value;
1565 if (position == -1) {
1566 value = queryString.substring(currentPosition);
1567 currentPosition = queryString.length;
1568 } else {
1569 value = queryString.substring(currentPosition, position);
1570 currentPosition = position + 1;
1571 }
1572 result[HttpUtil.decodeUrlEncodedString(name)] =
1573 HttpUtil.decodeUrlEncodedString(value);
1574 }
1575 return result;
1576 }
1577 }
OLDNEW
« no previous file with comments | « no previous file | runtime/bin/http_parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698