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

Unified Diff: samples/chat/http_impl.dart

Issue 9488008: Include HTTP library in the standalone VM (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Minor update Created 8 years, 10 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
« no previous file with comments | « samples/chat/http.dart ('k') | samples/tests/samples/src/chat/ChatServerTest.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: samples/chat/http_impl.dart
diff --git a/samples/chat/http_impl.dart b/samples/chat/http_impl.dart
deleted file mode 100644
index 3124a3d115a4731c94537b1b08d0dd8f1cc60f8b..0000000000000000000000000000000000000000
--- a/samples/chat/http_impl.dart
+++ /dev/null
@@ -1,1579 +0,0 @@
-// 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.
-
-// Global constants.
-class _Const {
- // Bytes for "HTTP/1.0".
- static final HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48];
- // Bytes for "HTTP/1.1".
- static final HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49];
-
- static final END_CHUNKED = const [0x30, 13, 10, 13, 10];
-}
-
-// Frequently used character codes.
-class _CharCode {
- static final int HT = 9;
- static final int LF = 10;
- static final int CR = 13;
- static final int SP = 32;
- static final int COLON = 58;
-}
-
-
-// States of the HTTP parser state machine.
-class _State {
- static final int START = 0;
- static final int METHOD_OR_HTTP_VERSION = 1;
- static final int REQUEST_LINE_METHOD = 2;
- static final int REQUEST_LINE_URI = 3;
- static final int REQUEST_LINE_HTTP_VERSION = 4;
- static final int REQUEST_LINE_ENDING = 5;
- static final int RESPONSE_LINE_STATUS_CODE = 6;
- static final int RESPONSE_LINE_REASON_PHRASE = 7;
- static final int RESPONSE_LINE_ENDING = 8;
- static final int HEADER_START = 9;
- static final int HEADER_FIELD = 10;
- static final int HEADER_VALUE_START = 11;
- static final int HEADER_VALUE = 12;
- static final int HEADER_VALUE_FOLDING_OR_ENDING = 13;
- static final int HEADER_VALUE_FOLD_OR_END = 14;
- static final int HEADER_ENDING = 15;
- static final int CHUNK_SIZE_STARTING_CR = 16;
- static final int CHUNK_SIZE_STARTING_LF = 17;
- static final int CHUNK_SIZE = 18;
- static final int CHUNK_SIZE_ENDING = 19;
- static final int CHUNKED_BODY_DONE_CR = 20;
- static final int CHUNKED_BODY_DONE_LF = 21;
- static final int BODY = 22;
-}
-
-
-/**
- * HTTP parser which parses the HTTP stream as data is supplied
- * through the writeList method. As the data is parsed the events
- * RequestStart
- * UriReceived
- * HeaderReceived
- * HeadersComplete
- * DataReceived
- * DataEnd
- * are generated.
- * Currently only HTTP requests with Content-Length header are supported.
- */
-class HttpParser {
- HttpParser()
- : _state = _State.START,
- _failure = false,
- _headerField = new StringBuffer(),
- _headerValue = new StringBuffer(),
- _method_or_status_code = new StringBuffer(),
- _uri_or_reason_phrase = new StringBuffer();
-
- // From RFC 2616.
- // generic-message = start-line
- // *(message-header CRLF)
- // CRLF
- // [ message-body ]
- // start-line = Request-Line | Status-Line
- // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
- // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
- // message-header = field-name ":" [ field-value ]
- int writeList(List<int> buffer, int offset, int count) {
- int index = offset;
- int lastIndex = offset + count;
- while ((index < lastIndex) && !_failure) {
- int byte = buffer[index];
- switch (_state) {
- case _State.START:
- _contentLength = 0;
- _keepAlive = false;
- _chunked = false;
-
- if (byte == _Const.HTTP11[0]) {
- // Start parsing HTTP method.
- _httpVersionIndex = 1;
- _state = _State.METHOD_OR_HTTP_VERSION;
- } else {
- // Start parsing method.
- _method_or_status_code.addCharCode(byte);
- _state = _State.REQUEST_LINE_METHOD;
- }
- break;
-
- case _State.METHOD_OR_HTTP_VERSION:
- if (_httpVersionIndex < _Const.HTTP11.length &&
- byte == _Const.HTTP11[_httpVersionIndex]) {
- // Continue parsing HTTP version.
- _httpVersionIndex++;
- } else if (_httpVersionIndex == _Const.HTTP11.length &&
- byte == _CharCode.SP) {
- // HTTP version parsed.
- _state = _State.RESPONSE_LINE_STATUS_CODE;
- } else {
- // Did not parse HTTP version. Expect method instead.
- for (int i = 0; i < _httpVersionIndex; i++) {
- _method_or_status_code.addCharCode(_Const.HTTP11[i]);
- }
- _state = _State.REQUEST_LINE_URI;
- }
- break;
-
- case _State.REQUEST_LINE_METHOD:
- if (byte == _CharCode.SP) {
- _state = _State.REQUEST_LINE_URI;
- } else {
- _method_or_status_code.addCharCode(byte);
- }
- break;
-
- case _State.REQUEST_LINE_URI:
- if (byte == _CharCode.SP) {
- _state = _State.REQUEST_LINE_HTTP_VERSION;
- _httpVersionIndex = 0;
- } else {
- _uri_or_reason_phrase.addCharCode(byte);
- }
- break;
-
- case _State.REQUEST_LINE_HTTP_VERSION:
- if (_httpVersionIndex < _Const.HTTP11.length) {
- _expect(byte, _Const.HTTP11[_httpVersionIndex]);
- _httpVersionIndex++;
- } else {
- _expect(byte, _CharCode.CR);
- _state = _State.REQUEST_LINE_ENDING;
- }
- break;
-
- case _State.REQUEST_LINE_ENDING:
- _expect(byte, _CharCode.LF);
- if (requestStart != null) {
- requestStart(_method_or_status_code.toString(),
- _uri_or_reason_phrase.toString());
- }
- _method_or_status_code.clear();
- _uri_or_reason_phrase.clear();
- _state = _State.HEADER_START;
- break;
-
- case _State.RESPONSE_LINE_STATUS_CODE:
- if (byte == _CharCode.SP) {
- _state = _State.RESPONSE_LINE_REASON_PHRASE;
- } else {
- if (byte < 0x30 && 0x39 < byte) {
- _failure = true;
- } else {
- _method_or_status_code.addCharCode(byte);
- }
- }
- break;
-
- case _State.RESPONSE_LINE_REASON_PHRASE:
- if (byte == _CharCode.CR) {
- _state = _State.RESPONSE_LINE_ENDING;
- } else {
- _uri_or_reason_phrase.addCharCode(byte);
- }
- break;
-
- case _State.RESPONSE_LINE_ENDING:
- _expect(byte, _CharCode.LF);
- // TODO(sgjesse): Check for valid status code.
- if (responseStart != null) {
- responseStart(Math.parseInt(_method_or_status_code.toString()),
- _uri_or_reason_phrase.toString());
- }
- _method_or_status_code.clear();
- _uri_or_reason_phrase.clear();
- _state = _State.HEADER_START;
- break;
-
- case _State.HEADER_START:
- if (byte == _CharCode.CR) {
- _state = _State.HEADER_ENDING;
- } else {
- // Start of new header field.
- _headerField.addCharCode(_toLowerCase(byte));
- _state = _State.HEADER_FIELD;
- }
- break;
-
- case _State.HEADER_FIELD:
- if (byte == _CharCode.COLON) {
- _state = _State.HEADER_VALUE_START;
- } else {
- _headerField.addCharCode(_toLowerCase(byte));
- }
- break;
-
- case _State.HEADER_VALUE_START:
- if (byte != _CharCode.SP && byte != _CharCode.HT) {
- // Start of new header value.
- _headerValue.addCharCode(byte);
- _state = _State.HEADER_VALUE;
- }
- break;
-
- case _State.HEADER_VALUE:
- if (byte == _CharCode.CR) {
- _state = _State.HEADER_VALUE_FOLDING_OR_ENDING;
- } else {
- _headerValue.addCharCode(byte);
- }
- break;
-
- case _State.HEADER_VALUE_FOLDING_OR_ENDING:
- _expect(byte, _CharCode.LF);
- _state = _State.HEADER_VALUE_FOLD_OR_END;
- break;
-
- case _State.HEADER_VALUE_FOLD_OR_END:
- if (byte == _CharCode.SP || byte == _CharCode.HT) {
- _state = _State.HEADER_VALUE_START;
- } else {
- String headerField = _headerField.toString();
- String headerValue =_headerValue.toString();
- // Ignore the Content-Length header if Transfer-Encoding
- // is chunked (RFC 2616 section 4.4)
- if (headerField == "content-length" && !_chunked) {
- _contentLength = Math.parseInt(headerValue);
- } else if (headerField == "connection" &&
- headerValue == "keep-alive") {
- _keepAlive = true;
- } else if (headerField == "transfer-encoding" &&
- headerValue == "chunked") {
- _chunked = true;
- _contentLength = -1;
- }
- if (headerReceived != null) {
- headerReceived(headerField, headerValue);
- }
- _headerField.clear();
- _headerValue.clear();
-
- if (byte == _CharCode.CR) {
- _state = _State.HEADER_ENDING;
- } else {
- // Start of new header field.
- _headerField.addCharCode(_toLowerCase(byte));
- _state = _State.HEADER_FIELD;
- }
- }
- break;
-
- case _State.HEADER_ENDING:
- _expect(byte, _CharCode.LF);
- if (headersComplete != null) headersComplete();
-
- // If there is no data get ready to process the next request.
- if (_chunked) {
- _state = _State.CHUNK_SIZE;
- _remainingContent = 0;
- } else if (_contentLength == 0) {
- if (dataEnd != null) dataEnd();
- _state = _State.START;
- } else if (_contentLength > 0) {
- _remainingContent = _contentLength;
- _state = _State.BODY;
- } else {
- // TODO(sgjesse): Error handling.
- }
- break;
-
- case _State.CHUNK_SIZE_STARTING_CR:
- _expect(byte, _CharCode.CR);
- _state = _State.CHUNK_SIZE_STARTING_LF;
- break;
-
- case _State.CHUNK_SIZE_STARTING_LF:
- _expect(byte, _CharCode.LF);
- _state = _State.CHUNK_SIZE;
- break;
-
- case _State.CHUNK_SIZE:
- if (byte == _CharCode.CR) {
- _state = _State.CHUNK_SIZE_ENDING;
- } else {
- int value = _expectHexDigit(byte);
- _remainingContent = _remainingContent * 16 + value;
- }
- break;
-
- case _State.CHUNK_SIZE_ENDING:
- _expect(byte, _CharCode.LF);
- if (_remainingContent > 0) {
- _state = _State.BODY;
- } else {
- _state = _State.CHUNKED_BODY_DONE_CR;
- }
- break;
-
- case _State.CHUNKED_BODY_DONE_CR:
- _expect(byte, _CharCode.CR);
- _state = _State.CHUNKED_BODY_DONE_LF;
- break;
-
- case _State.CHUNKED_BODY_DONE_LF:
- _expect(byte, _CharCode.LF);
- if (dataEnd != null) dataEnd();
- _state = _State.START;
- break;
-
- case _State.BODY:
- // The body is not handled one byte at the time but in blocks.
- int dataAvailable = lastIndex - index;
- ByteArray data;
- if (dataAvailable <= _remainingContent) {
- data = new ByteArray(dataAvailable);
- data.setRange(0, dataAvailable, buffer, index);
- } else {
- data = new ByteArray(_remainingContent);
- data.setRange(0, _remainingContent, buffer, index);
- }
-
- if (dataReceived != null) dataReceived(data);
- _remainingContent -= data.length;
- index += data.length;
- if (_remainingContent == 0) {
- if (!_chunked) {
- if (dataEnd != null) dataEnd();
- _state = _State.START;
- } else {
- _state = _State.CHUNK_SIZE_STARTING_CR;
- }
- }
-
- // Hack - as we always do index++ below.
- index--;
- break;
-
- default:
- // Should be unreachable.
- assert(false);
- }
-
- // Move to the next byte.
- index++;
- }
-
- // Return the number of bytes parsed.
- return index - offset;
- }
-
- int get contentLength() => _contentLength;
- bool get keepAlive() => _keepAlive;
-
- 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;
- }
-
- int _expect(int val1, int val2) {
- if (val1 != val2) {
- _failure = true;
- }
- }
-
- int _expectHexDigit(int byte) {
- if (0x30 <= byte && byte <= 0x39) {
- return byte - 0x30; // 0 - 9
- } else if (0x41 <= byte && byte <= 0x46) {
- return byte - 0x41 + 10; // A - F
- } else if (0x61 <= byte && byte <= 0x66) {
- return byte - 0x61 + 10; // a - f
- } else {
- _failure = true;
- return 0;
- }
- }
-
- int _state;
- bool _failure;
- int _httpVersionIndex;
- StringBuffer _method_or_status_code;
- StringBuffer _uri_or_reason_phrase;
- StringBuffer _headerField;
- StringBuffer _headerValue;
-
- int _contentLength;
- bool _keepAlive;
- bool _chunked;
-
- int _remainingContent;
-
- // Callbacks.
- Function requestStart;
- Function responseStart;
- Function headerReceived;
- Function headersComplete;
- Function dataReceived;
- Function dataEnd;
-}
-
-
-// Utility class for encoding a string into UTF-8 byte stream.
-class _UTF8Encoder {
- static List<int> encodeString(String string) {
- int size = _encodingSize(string);
- ByteArray result = new ByteArray(size);
- _encodeString(string, result);
- return result;
- }
-
- static int _encodingSize(String string) => _encodeString(string, null);
-
- static int _encodeString(String string, List<int> buffer) {
- int pos = 0;
- int length = string.length;
- for (int i = 0; i < length; i++) {
- int additionalBytes;
- int charCode = string.charCodeAt(i);
- if (charCode <= 0x007F) {
- additionalBytes = 0;
- if (buffer != null) buffer[pos] = charCode;
- } else if (charCode <= 0x07FF) {
- // 110xxxxx (xxxxx is top 5 bits).
- if (buffer != null) buffer[pos] = ((charCode >> 6) & 0x1F) | 0xC0;
- additionalBytes = 1;
- } else if (charCode <= 0xFFFF) {
- // 1110xxxx (xxxx is top 4 bits)
- if (buffer != null) buffer[pos] = ((charCode >> 12) & 0x0F)| 0xE0;
- additionalBytes = 2;
- } else {
- // 11110xxx (xxx is top 3 bits)
- if (buffer != null) buffer[pos] = ((charCode >> 18) & 0x07) | 0xF0;
- additionalBytes = 3;
- }
- pos++;
- if (buffer != null) {
- for (int i = additionalBytes; i > 0; i--) {
- // 10xxxxxx (xxxxxx is next 6 bits from the top).
- buffer[pos++] = ((charCode >> (6 * (i - 1))) & 0x3F) | 0x80;
- }
- } else {
- pos += additionalBytes;
- }
- }
- return pos;
- }
-}
-
-
-class _HttpRequestResponseBase {
- _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection)
- : _contentLength = -1,
- _keepAlive = false,
- _headers = new Map();
-
- int get contentLength() => _contentLength;
- bool get keepAlive() => _keepAlive;
-
- void _setHeader(String name, String value) {
- _headers[name] = value;
- }
-
- bool _write(List<int> data, bool copyBuffer) {
- bool allWritten = true;
- if (data.length > 0) {
- if (_contentLength < 0) {
- // Write chunk size if transfer encoding is chunked.
- _writeHexString(data.length);
- _writeCRLF();
- _httpConnection.outputStream.write(data, copyBuffer);
- allWritten = _writeCRLF();
- } else {
- allWritten = _httpConnection.outputStream.write(data, copyBuffer);
- }
- }
- return allWritten;
- }
-
- bool _writeList(List<int> data, int offset, int count) {
- bool allWritten = true;
- if (count > 0) {
- if (_contentLength < 0) {
- // Write chunk size if transfer encoding is chunked.
- _writeHexString(count);
- _writeCRLF();
- _httpConnection.outputStream.writeFrom(data, offset, count);
- allWritten = _writeCRLF();
- } else {
- allWritten = _httpConnection.outputStream.writeFrom(data, offset, count);
- }
- }
- return allWritten;
- }
-
- bool _writeString(String string) {
- bool allWritten = true;
- if (string.length > 0) {
- // Encode as UTF-8 and write data.
- List<int> data = _UTF8Encoder.encodeString(string);
- allWritten = _writeList(data, 0, data.length);
- }
- return allWritten;
- }
-
- bool _writeDone() {
- bool allWritten = true;
- if (_contentLength < 0) {
- // Terminate the content if transfer encoding is chunked.
- allWritten = _httpConnection.outputStream.write(_Const.END_CHUNKED);
- }
- return allWritten;
- }
-
- bool _writeHeaders() {
- List<int> data;
-
- // Format headers.
- _headers.forEach((String name, String value) {
- data = name.charCodes();
- _httpConnection.outputStream.write(data);
- data = ": ".charCodes();
- _httpConnection.outputStream.write(data);
- data = value.charCodes();
- _httpConnection.outputStream.write(data);
- _writeCRLF();
- });
- // Terminate header.
- return _writeCRLF();
- }
-
- bool _writeHexString(int x) {
- final List<int> hexDigits = [0x30, 0x31, 0x32, 0x33, 0x34,
- 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x41, 0x42, 0x43, 0x44, 0x45, 0x46];
- ByteArray hex = new ByteArray(10);
- int index = hex.length;
- while (x > 0) {
- index--;
- hex[index] = hexDigits[x % 16];
- x = x >> 4;
- }
- return _httpConnection.outputStream.writeFrom(hex, index, hex.length - index);
- }
-
- bool _writeCRLF() {
- final CRLF = const [_CharCode.CR, _CharCode.LF];
- return _httpConnection.outputStream.write(CRLF);
- }
-
- bool _writeSP() {
- final SP = const [_CharCode.SP];
- return _httpConnection.outputStream.write(SP);
- }
-
- _HttpConnectionBase _httpConnection;
- Map<String, String> _headers;
-
- // Length of the content body. If this is set to -1 (default value)
- // when starting to send data chunked transfer encoding will be
- // used.
- int _contentLength;
- bool _keepAlive;
-}
-
-
-// Parsed HTTP request providing information on the HTTP headers.
-class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest {
- _HttpRequest(_HttpConnection connection) : super(connection);
-
- String get method() => _method;
- String get uri() => _uri;
- String get path() => _path;
- Map get headers() => _headers;
- String get queryString() => _queryString;
- Map get queryParameters() => _queryParameters;
-
- InputStream get inputStream() {
- if (_inputStream == null) {
- _inputStream = new _HttpInputStream(this);
- }
- return _inputStream;
- }
-
- void _requestStartHandler(String method, String uri) {
- _method = method;
- _uri = uri;
- _parseRequestUri(uri);
- }
-
- void _headerReceivedHandler(String name, String value) {
- _setHeader(name, value);
- }
-
- void _headersCompleteHandler() {
- // Prepare for receiving data.
- _buffer = new _BufferList();
- }
-
- void _dataReceivedHandler(List<int> data) {
- _buffer.add(data);
- if (_inputStream != null) _inputStream._dataReceived();
- }
-
- void _dataEndHandler() {
- if (_inputStream != null) _inputStream._closeReceived();
- }
-
- // Escaped characters in uri are expected to have been parsed.
- void _parseRequestUri(String uri) {
- int position;
- position = uri.indexOf("?", 0);
- if (position == -1) {
- _path = HttpUtil.decodeUrlEncodedString(_uri);
- _queryString = null;
- _queryParameters = new Map();
- } else {
- _path = HttpUtil.decodeUrlEncodedString(_uri.substring(0, position));
- _queryString = _uri.substring(position + 1);
- _queryParameters = HttpUtil.splitQueryString(_queryString);
- }
- }
-
- // Delegate functions for the HttpInputStream implementation.
- int _streamAvailable() {
- return _buffer.length;
- }
-
- List<int> _streamRead(int bytesToRead) {
- return _buffer.readBytes(bytesToRead);
- }
-
- int _streamReadInto(List<int> buffer, int offset, int len) {
- List<int> data = _buffer.readBytes(len);
- buffer.setRange(offset, data.length, data);
- }
-
- String _method;
- String _uri;
- String _path;
- String _queryString;
- Map<String, String> _queryParameters;
- _HttpInputStream _inputStream;
- _BufferList _buffer;
-}
-
-
-// HTTP response object for sending a HTTP response.
-class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse {
- static final int START = 0;
- static final int HEADERS_SENT = 1;
- static final int DONE = 2;
-
- _HttpResponse(_HttpConnection httpConnection)
- : super(httpConnection),
- statusCode = HttpStatus.OK,
- _state = START;
-
- void set contentLength(int contentLength) {
- if (_outputStream != null) return new HttpException("Header already sent");
- _contentLength = contentLength;
- }
- void set keepAlive(bool keepAlive) {
- if (_outputStream != null) return new HttpException("Header already sent");
- _keepAlive = keepAlive;
- }
-
- // Set a header on the response. NOTE: If the same header is set
- // more than once only the last one will be part of the response.
- void setHeader(String name, String value) {
- if (_outputStream != null) return new HttpException("Header already sent");
- _setHeader(name, value);
- }
-
- OutputStream get outputStream() {
- if (_state == DONE) throw new HttpException("Response closed");
- if (_outputStream == null) {
- // Ensure that headers are written.
- if (_state == START) {
- _writeHeader();
- }
- _outputStream = new _HttpOutputStream(this);
- }
- return _outputStream;
- }
-
- bool writeString(String string) {
- // Invoke the output stream getter to make sure the header is sent.
- outputStream;
- return _writeString(string);
- }
-
- // Delegate functions for the HttpOutputStream implementation.
- bool _streamWrite(List<int> buffer, bool copyBuffer) {
- return _write(buffer, copyBuffer);
- }
-
- bool _streamWriteFrom(List<int> buffer, int offset, int len) {
- return _writeList(buffer, offset, len);
- }
-
- void _streamClose() {
- _state = DONE;
- // Stop tracking no pending write events.
- _httpConnection.outputStream.noPendingWriteHandler = null;
- // Ensure that any trailing data is written.
- _writeDone();
- // If the connection is closing then close the output stream to
- // fully close the socket.
- if (_httpConnection._closing) {
- _httpConnection.outputStream.close();
- }
- }
-
- void _streamSetNoPendingWriteHandler(callback()) {
- if (_state != DONE) {
- _httpConnection.outputStream.noPendingWriteHandler = callback;
- }
- }
-
- void _streamSetCloseHandler(callback()) {
- // TODO(sgjesse): Handle this.
- }
-
- void _streamSetErrorHandler(callback()) {
- // TODO(sgjesse): Handle this.
- }
-
- String _findReasonPhrase(int statusCode) {
- if (reasonPhrase != null) {
- return reasonPhrase;
- }
-
- switch (statusCode) {
- case HttpStatus.CONTINUE: return "Continue";
- case HttpStatus.SWITCHING_PROTOCOLS: return "Switching Protocols";
- case HttpStatus.OK: return "OK";
- case HttpStatus.CREATED: return "Created";
- case HttpStatus.ACCEPTED: return "Accepted";
- case HttpStatus.NON_AUTHORITATIVE_INFORMATION:
- return "Non-Authoritative Information";
- case HttpStatus.NO_CONTENT: return "No Content";
- case HttpStatus.RESET_CONTENT: return "Reset Content";
- case HttpStatus.PARTIAL_CONTENT: return "Partial Content";
- case HttpStatus.MULTIPLE_CHOICES: return "Multiple Choices";
- case HttpStatus.MOVED_PERMANENTLY: return "Moved Permanently";
- case HttpStatus.FOUND: return "Found";
- case HttpStatus.SEE_OTHER: return "See Other";
- case HttpStatus.NOT_MODIFIED: return "Not Modified";
- case HttpStatus.USE_PROXY: return "Use Proxy";
- case HttpStatus.TEMPORARY_REDIRECT: return "Temporary Redirect";
- case HttpStatus.BAD_REQUEST: return "Bad Request";
- case HttpStatus.UNAUTHORIZED: return "Unauthorized";
- case HttpStatus.PAYMENT_REQUIRED: return "Payment Required";
- case HttpStatus.FORBIDDEN: return "Forbidden";
- case HttpStatus.NOT_FOUND: return "Not Found";
- case HttpStatus.METHOD_NOT_ALLOWED: return "Method Not Allowed";
- case HttpStatus.NOT_ACCEPTABLE: return "Not Acceptable";
- case HttpStatus.PROXY_AUTHENTICATION_REQUIRED:
- return "Proxy Authentication Required";
- case HttpStatus.REQUEST_TIMEOUT: return "Request Time-out";
- case HttpStatus.CONFLICT: return "Conflict";
- case HttpStatus.GONE: return "Gone";
- case HttpStatus.LENGTH_REQUIRED: return "Length Required";
- case HttpStatus.PRECONDITION_FAILED: return "Precondition Failed";
- case HttpStatus.REQUEST_ENTITY_TOO_LARGE:
- return "Request Entity Too Large";
- case HttpStatus.REQUEST_URI_TOO_LONG: return "Request-URI Too Large";
- case HttpStatus.UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type";
- case HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE:
- return "Requested range not satisfiable";
- case HttpStatus.EXPECTATION_FAILED: return "Expectation Failed";
- case HttpStatus.INTERNAL_SERVER_ERROR: return "Internal Server Error";
- case HttpStatus.NOT_IMPLEMENTED: return "Not Implemented";
- case HttpStatus.BAD_GATEWAY: return "Bad Gateway";
- case HttpStatus.SERVICE_UNAVAILABLE: return "Service Unavailable";
- case HttpStatus.GATEWAY_TIMEOUT: return "Gateway Time-out";
- case HttpStatus.HTTP_VERSION_NOT_SUPPORTED:
- return "Http Version not supported";
- default: return "Status " + statusCode.toString();
- }
- }
-
- bool _writeHeader() {
- List<int> data;
- OutputStream stream = _httpConnection.outputStream;
-
- // Write status line.
- stream.write(_Const.HTTP11);
- _writeSP();
- data = statusCode.toString().charCodes();
- stream.write(data);
- _writeSP();
- data = _findReasonPhrase(statusCode).charCodes();
- stream.write(data);
- _writeCRLF();
-
- // Determine the value of the "Connection" header
- // based on the keep alive state.
- setHeader("Connection", keepAlive ? "keep-alive" : "close");
- // Determine the value of the "Transfer-Encoding" header based on
- // whether the content length is known.
- if (_contentLength >= 0) {
- setHeader("Content-Length", _contentLength.toString());
- } else {
- setHeader("Transfer-Encoding", "chunked");
- }
-
- // Write headers.
- bool allWritten = _writeHeaders();
- _state = HEADERS_SENT;
- return allWritten;
- }
-
- // Response status code.
- int statusCode;
- String reasonPhrase;
- _HttpOutputStream _outputStream;
- int _state;
-}
-
-
-class _HttpInputStream extends _BaseDataInputStream implements InputStream {
- _HttpInputStream(_HttpRequestResponseBase this._requestOrResponse) {
- _checkScheduleCallbacks();
- }
-
- int available() {
- return _requestOrResponse._streamAvailable();
- }
-
- void pipe(OutputStream output, [bool close = true]) {
- _pipe(this, output, close: close);
- }
-
- List<int> _read(int bytesToRead) {
- List<int> result = _requestOrResponse._streamRead(bytesToRead);
- _checkScheduleCallbacks();
- return result;
- }
-
- int _readInto(List<int> buffer, int offset, int len) {
- int result = _requestOrResponse._streamReadInto(buffer, offset, len);
- _checkScheduleCallbacks();
- return result;
- }
-
- void _close() {
- // TODO(sgjesse): Handle this.
- }
-
- void _dataReceived() {
- super._dataReceived();
- }
-
- _HttpRequestResponseBase _requestOrResponse;
-}
-
-
-class _HttpOutputStream implements OutputStream {
- _HttpOutputStream(_HttpRequestResponseBase this._requestOrResponse);
-
- bool write(List<int> buffer, [bool copyBuffer = true]) {
- return _requestOrResponse._streamWrite(buffer, copyBuffer);
- }
-
- bool writeFrom(List<int> buffer, [int offset = 0, int len]) {
- return _requestOrResponse._streamWriteFrom(buffer, offset, len);
- }
-
- void close() {
- _requestOrResponse._streamClose();
- }
-
- void destroy() {
- throw "Not implemented";
- }
-
- void set noPendingWriteHandler(void callback()) {
- _requestOrResponse._streamSetNoPendingWriteHandler(callback);
- }
-
- void set closeHandler(void callback()) {
- _requestOrResponse._streamSetCloseHandler(callback);
- }
-
- void set errorHandler(void callback()) {
- _requestOrResponse._streamSetErrorHandler(callback);
- }
-
- _HttpRequestResponseBase _requestOrResponse;
-}
-
-
-class _HttpConnectionBase {
- _HttpConnectionBase() : _sendBuffers = new Queue(),
- _httpParser = new HttpParser();
-
- void _connectionEstablished(Socket socket) {
- _socket = socket;
- // Register handler for socket events.
- _socket.dataHandler = _dataHandler;
- _socket.closeHandler = _closeHandler;
- _socket.errorHandler = _errorHandler;
- }
-
- OutputStream get outputStream() {
- return _socket.outputStream;
- }
-
- void _dataHandler() {
- int available = _socket.available();
- if (available == 0) {
- return;
- }
-
- ByteArray buffer = new ByteArray(available);
- int bytesRead = _socket.readList(buffer, 0, available);
- if (bytesRead > 0) {
- int parsed = _httpParser.writeList(buffer, 0, bytesRead);
- if (parsed != bytesRead) {
- print("Failed to parse HTTP data $parsed $bytesRead");
- _socket.close();
- }
- }
- }
-
- void _closeHandler() {
- // Client closed socket for writing. Socket should still be open
- // for writing the response.
- _closing = true;
- if (_disconnectHandlerCallback != null) _disconnectHandlerCallback();
- }
-
- void _errorHandler() {
- // If an error occours, treat the socket as closed.
- _closeHandler();
- if (_errorHandlerCallback != null) {
- _errorHandlerCallback("Connection closed while sending data to client.");
- }
- }
-
- void set disconnectHandler(void callback()) {
- _disconnectHandlerCallback = callback;
- }
-
- void set errorHandler(void callback(String errorMessage)) {
- _errorHandlerCallback = callback;
- }
-
- Socket _socket;
- bool _closing = false; // Is the socket closed by the client?
- HttpParser _httpParser;
-
- Queue _sendBuffers;
-
- Function _disconnectHandlerCallback;
- Function _errorHandlerCallback;
-}
-
-
-// HTTP server connection over a socket.
-class _HttpConnection extends _HttpConnectionBase {
- _HttpConnection() {
- // Register HTTP parser callbacks.
- _httpParser.requestStart =
- (method, uri) => _requestStartHandler(method, uri);
- _httpParser.responseStart =
- (statusCode, reasonPhrase) =>
- _responseStartHandler(statusCode, reasonPhrase);
- _httpParser.headerReceived =
- (name, value) => _headerReceivedHandler(name, value);
- _httpParser.headersComplete = () => _headersCompleteHandler();
- _httpParser.dataReceived = (data) => _dataReceivedHandler(data);
- _httpParser.dataEnd = () => _dataEndHandler();
- }
-
- void _requestStartHandler(String method, String uri) {
- // Create new request and response objects for this request.
- _request = new _HttpRequest(this);
- _response = new _HttpResponse(this);
- _request._requestStartHandler(method, uri);
- }
-
- void _responseStartHandler(int statusCode, String reasonPhrase) {
- // TODO(sgjesse): Error handling.
- }
-
- void _headerReceivedHandler(String name, String value) {
- _request._headerReceivedHandler(name, value);
- }
-
- void _headersCompleteHandler() {
- _request._headersCompleteHandler();
- _response.keepAlive = _httpParser.keepAlive;
- if (requestReceived != null) {
- requestReceived(_request, _response);
- }
- }
-
- void _dataReceivedHandler(List<int> data) {
- _request._dataReceivedHandler(data);
- }
-
- void _dataEndHandler() {
- _request._dataEndHandler();
- }
-
- HttpRequest _request;
- HttpResponse _response;
-
- // Callbacks.
- var requestReceived;
-}
-
-
-// HTTP server waiting for socket connections. The connections are
-// managed by the server and as requests are received the request.
-class _HttpServer implements HttpServer {
- _HttpServer () : _debugTrace = false;
-
- void listen(String host, int port, [int backlog = 5]) {
-
- void connectionHandler(Socket socket) {
- // Accept the client connection.
- _HttpConnection connection = new _HttpConnection();
- connection._connectionEstablished(socket);
- connection.requestReceived = _requestHandler;
- _connections.add(connection);
- if (_debugTrace) {
- print("New connection (total ${_connections.length} connections)");
- }
- void disconnectHandler() {
- for (int i = 0; i < _connections.length; i++) {
- if (_connections[i] == connection) {
- _connections.removeRange(i, 1);
- break;
- }
- }
- if (_debugTrace) {
- print("Closed connection (total ${_connections.length} connections)");
- }
- }
- connection.disconnectHandler = disconnectHandler;
- void errorHandler(String errorMessage) {
- if (_errorHandler != null) _errorHandler(errorMessage);
- }
- connection.errorHandler = errorHandler;
- }
-
- // TODO(ajohnsen): Use Set once Socket is Hashable.
- _connections = new List<_HttpConnection>();
- _server = new ServerSocket(host, port, backlog);
- _server.connectionHandler = connectionHandler;
- }
-
- void close() => _server.close();
- int get port() => _server.port;
-
- void set errorHandler(void handler(String errorMessage)) {
- _errorHandler = handler;
- }
-
- void set requestHandler(void handler(HttpRequest, HttpResponse)) {
- _requestHandler = handler;
- }
-
- ServerSocket _server; // The server listen socket.
- List<_HttpConnection> _connections; // List of currently connected clients.
- Function _requestHandler;
- Function _errorHandler;
- bool _debugTrace;
-}
-
-
-class _HttpClientRequest
- extends _HttpRequestResponseBase implements HttpClientRequest {
- static final int START = 0;
- static final int HEADERS_SENT = 1;
- static final int DONE = 2;
-
- _HttpClientRequest(String this._method,
- String this._uri,
- _HttpClientConnection connection)
- : super(connection),
- _state = START {
- _connection = connection;
- // Default GET requests to have no content.
- if (_method == "GET") {
- _contentLength = 0;
- }
- }
-
- void set contentLength(int contentLength) => _contentLength = contentLength;
- void set keepAlive(bool keepAlive) => _keepAlive = keepAlive;
- int get statusCode() { return _statusCode; }
- String get reasonPhrase() { return _reasonPhrase; }
-
- void setHeader(String name, String value) {
- _setHeader(name, value);
- }
-
- bool writeString(String string) {
- outputStream;
- return _writeString(string);
- }
-
- OutputStream get outputStream() {
- if (_state == DONE) throw new HttpException("Request closed");
- if (_outputStream == null) {
- // Ensure that headers are written.
- if (_state == START) {
- _writeHeader();
- }
- _outputStream = new _HttpOutputStream(this);
- }
- return _outputStream;
- }
-
- // Delegate functions for the HttpOutputStream implementation.
- bool _streamWrite(List<int> buffer, bool copyBuffer) {
- return _write(buffer, copyBuffer);
- }
-
- bool _streamWriteFrom(List<int> buffer, int offset, int len) {
- return _writeList(buffer, offset, len);
- }
-
- void _streamClose() {
- _state = DONE;
- // Stop tracking no pending write events.
- _httpConnection.outputStream.noPendingWriteHandler = null;
- // Ensure that any trailing data is written.
- _writeDone();
- // If the connection is closing then close the output stream to
- // fully close the socket.
- if (_httpConnection._closing) {
- _httpConnection.outputStream.close();
- }
- }
-
- void _streamSetNoPendingWriteHandler(callback()) {
- if (_state != DONE) {
- _httpConnection.outputStream.noPendingWriteHandler = callback;
- }
- }
-
- void _streamSetCloseHandler(callback()) {
- // TODO(sgjesse): Handle this.
- }
-
- void _streamSetErrorHandler(callback()) {
- // TODO(sgjesse): Handle this.
- }
-
- void _writeHeader() {
- List<int> data;
- OutputStream stream = _httpConnection.outputStream;
-
- // Write request line.
- data = _method.toString().charCodes();
- stream.write(data);
- _writeSP();
- data = _uri.toString().charCodes();
- stream.write(data);
- _writeSP();
- stream.write(_Const.HTTP11);
- _writeCRLF();
-
- // Determine the value of the "Connection" header
- // based on the keep alive state.
- setHeader("Connection", keepAlive ? "keep-alive" : "close");
- // Determine the value of the "Transfer-Encoding" header based on
- // whether the content length is known.
- if (_contentLength >= 0) {
- setHeader("Content-Length", _contentLength.toString());
- } else {
- setHeader("Transfer-Encoding", "chunked");
- }
-
- // Write headers.
- _writeHeaders();
- _state = HEADERS_SENT;
- }
-
- String _method;
- String _uri;
- _HttpClientConnection _connection;
- _HttpOutputStream _outputStream;
- int _state;
-}
-
-
-class _HttpClientResponse
- extends _HttpRequestResponseBase implements HttpClientResponse {
- _HttpClientResponse(_HttpClientConnection connection)
- : super(connection) {
- _connection = connection;
- }
-
- int get statusCode() { return _statusCode; }
- int get reasonPhrase() { return _reasonPhrase; }
- Map get headers() => _headers;
-
- InputStream get inputStream() {
- if (_inputStream == null) {
- _inputStream = new _HttpInputStream(this);
- }
- return _inputStream;
- }
-
- void _requestStartHandler(String method, String uri) {
- // TODO(sgjesse): Error handling
- }
-
- void _responseStartHandler(int statusCode, String reasonPhrase) {
- _statusCode = statusCode;
- _reasonPhrase = reasonPhrase;
- }
-
- void _headerReceivedHandler(String name, String value) {
- _setHeader(name, value);
- }
-
- void _headersCompleteHandler() {
- _buffer = new _BufferList();
- if (_connection._responseHandler != null) {
- _connection._responseHandler(this);
- }
- }
-
- void _dataReceivedHandler(List<int> data) {
- _buffer.add(data);
- if (_inputStream != null) _inputStream._dataReceived();
- }
-
- void _dataEndHandler() {
- if (_inputStream != null) _inputStream._closeReceived();
- }
-
- // Delegate functions for the HttpInputStream implementation.
- int _streamAvailable() {
- return _buffer.length;
- }
-
- List<int> _streamRead(int bytesToRead) {
- return _buffer.readBytes(bytesToRead);
- }
-
- int _streamReadInto(List<int> buffer, int offset, int len) {
- List<int> data = _buffer.readBytes(len);
- buffer.setRange(offset, data.length, data);
- return data.length;
- }
-
- int _statusCode;
- String _reasonPhrase;
-
- _HttpClientConnection _connection;
- _HttpInputStream _inputStream;
- _BufferList _buffer;
-}
-
-
-class _HttpClientConnection
- extends _HttpConnectionBase implements HttpClientConnection {
- _HttpClientConnection(_HttpClient this._client);
-
- void _connectionEstablished(_SocketConnection socketConn) {
- super._connectionEstablished(socketConn._socket);
- _socketConn = socketConn;
- // Register HTTP parser callbacks.
- _httpParser.requestStart =
- (method, uri) => _requestStartHandler(method, uri);
- _httpParser.responseStart =
- (statusCode, reasonPhrase) =>
- _responseStartHandler(statusCode, reasonPhrase);
- _httpParser.headerReceived =
- (name, value) => _headerReceivedHandler(name, value);
- _httpParser.headersComplete = () => _headersCompleteHandler();
- _httpParser.dataReceived = (data) => _dataReceivedHandler(data);
- _httpParser.dataEnd = () => _dataEndHandler();
- }
-
- HttpClientRequest open(String method, String uri) {
- _request = new _HttpClientRequest(method, uri, this);
- _request.keepAlive = true;
- _response = new _HttpClientResponse(this);
- return _request;
- }
-
- void _requestStartHandler(String method, String uri) {
- // TODO(sgjesse): Error handling.
- }
-
- void _responseStartHandler(int statusCode, String reasonPhrase) {
- _response._responseStartHandler(statusCode, reasonPhrase);
- }
-
- void _headerReceivedHandler(String name, String value) {
- _response._headerReceivedHandler(name, value);
- }
-
- void _headersCompleteHandler() {
- _response._headersCompleteHandler();
- }
-
- void _dataReceivedHandler(List<int> data) {
- _response._dataReceivedHandler(data);
- }
-
- void _dataEndHandler() {
- if (_response.headers["connection"] == "close") {
- _socket.close();
- } else {
- _client._returnSocketConnection(_socketConn);
- _socket = null;
- _socketConn = null;
- }
- _response._dataEndHandler();
- }
-
- void set requestHandler(void handler(HttpClientRequest request)) {
- _requestHandler = handler;
- }
-
- void set responseHandler(void handler(HttpClientResponse response)) {
- _responseHandler = handler;
- }
-
- Function _requestHandler;
- Function _responseHandler;
-
- _HttpClient _client;
- _SocketConnection _socketConn;
- HttpClientRequest _request;
- HttpClientResponse _response;
-
- // Callbacks.
- var requestReceived;
-
-}
-
-
-// Class for holding keep-alive sockets in the cache for the HTTP
-// client together with the connection information.
-class _SocketConnection {
- _SocketConnection(String this._host,
- int this._port,
- Socket this._socket);
-
- void _markReturned() {
- _socket.dataHandler = null;
- _socket.closeHandler = null;
- _socket.errorHandler = null;
- _returnTime = new Date.now();
- }
-
- Duration _idleTime(Date now) => now.difference(_returnTime);
-
- String _host;
- int _port;
- Socket _socket;
- Date _returnTime;
-}
-
-
-class _HttpClient implements HttpClient {
- static final int DEFAULT_EVICTION_TIMEOUT = 60000;
-
- _HttpClient() : _openSockets = new Map(), _shutdown = false;
-
- HttpClientConnection open(
- String method, String host, int port, String path) {
- if (_shutdown) throw new HttpException("HttpClient shutdown");
- return _prepareHttpClientConnection(host, port, method, path);
- }
-
- HttpClientConnection get(String host, int port, String path) {
- return open("GET", host, port, path);
- }
-
- HttpClientConnection post(String host, int port, String path) {
- return open("POST", host, port, path);
- }
-
- void shutdown() {
- _openSockets.forEach(
- void _(String key, Queue<_SocketConnection> connections) {
- while (!connections.isEmpty()) {
- var socketConn = connections.removeFirst();
- socketConn._socket.close();
- }
- });
- if (_evictionTimer != null) {
- _evictionTimer.cancel();
- }
- _shutdown = true;
- }
-
- String _connectionKey(String host, int port) {
- return "$host:$port";
- }
-
- HttpClientConnection _prepareHttpClientConnection(
- String host, int port, String method, String path) {
-
- void _connectionOpened(_SocketConnection socketConn,
- _HttpClientConnection connection) {
- connection._connectionEstablished(socketConn);
- HttpClientRequest request = connection.open(method, path);
- if (connection._requestHandler != null) {
- connection._requestHandler(request);
- } else {
- request.outputStream.close();
- }
- }
-
- _HttpClientConnection connection = new _HttpClientConnection(this);
-
- // If there are active connections for this key get the first one
- // otherwise create a new one.
- Queue socketConnections = _openSockets[_connectionKey(host, port)];
- if (socketConnections == null || socketConnections.isEmpty()) {
- Socket socket = new Socket(host, port);
- socket.connectHandler = () {
- socket.errorHandler = null;
- _SocketConnection socketConn =
- new _SocketConnection(host, port, socket);
- _connectionOpened(socketConn, connection);
- };
- socket.errorHandler = () {
- if (_errorHandler !== null) {
- _errorHandler(HttpStatus.NETWORK_CONNECT_TIMEOUT_ERROR);
- }
- };
- } else {
- _SocketConnection socketConn = socketConnections.removeFirst();
- new Timer((ignored) => _connectionOpened(socketConn, connection), 0);
-
- // Get rid of eviction timer if there are no more active connections.
- if (socketConnections.isEmpty()) {
- _evictionTimer.cancel();
- _evictionTimer = null;
- }
- }
-
- return connection;
- }
-
- void _returnSocketConnection(_SocketConnection socketConn) {
- // If the HTTP client is beeing shutdown don't return the connection.
- if (_shutdown) {
- socketConn._socket.close();
- return;
- };
-
- String key = _connectionKey(socketConn._host, socketConn._port);
-
- // Get or create the connection list for this key.
- Queue sockets = _openSockets[key];
- if (sockets == null) {
- sockets = new Queue();
- _openSockets[key] = sockets;
- }
-
- // If there is currently no eviction timer start one.
- if (_evictionTimer == null) {
- void _handleEviction(Timer timer) {
- Date now = new Date.now();
- _openSockets.forEach(
- void _(String key, Queue<_SocketConnection> connections) {
- // As returned connections are added at the head of the
- // list remove from the tail.
- while (!connections.isEmpty()) {
- _SocketConnection socketConn = connections.last();
- if (socketConn._idleTime(now).inMilliseconds >
- DEFAULT_EVICTION_TIMEOUT) {
- connections.removeLast();
- } else {
- break;
- }
- }
- });
- }
- _evictionTimer = new Timer.repeating(_handleEviction, 10000);
- }
-
- // Return connection.
- sockets.addFirst(socketConn);
- socketConn._markReturned();
- }
-
- void set errorHandler(void callback(int status)) {
- _errorHandler = callback;
- }
-
- Function _openHandler;
- Function _errorHandler;
- Map<String, Queue<_SocketConnection>> _openSockets;
- Timer _evictionTimer;
- bool _shutdown; // Has this HTTP client been shutdown?
-}
-
-
-class HttpUtil {
- static String decodeUrlEncodedString(String urlEncoded) {
- void invalidEscape() {
- // TODO(sgjesse): Handle the error.
- print("Invalid escape code.");
- }
-
- StringBuffer result = new StringBuffer();
- for (int ii = 0; urlEncoded.length > ii; ++ii) {
- if ('+' == urlEncoded[ii]) {
- result.add(' ');
- } else if ('%' == urlEncoded[ii] &&
- urlEncoded.length - 2 > ii) {
- try {
- int charCode =
- Math.parseInt('0x' + urlEncoded.substring(ii + 1, ii + 3));
- if (charCode <= 0x7f) {
- result.add(new String.fromCharCodes([charCode]));
- ii += 2;
- } else {
- invalidEscape();
- return '';
- }
- } catch (BadNumberFormatException ignored) {
- invalidEscape();
- return '';
- }
- } else {
- result.add(urlEncoded[ii]);
- }
- }
- return result.toString();
- }
-
- static Map<String, String> splitQueryString(String queryString) {
- Map<String, String> result = new Map<String, String>();
- int currentPosition = 0;
- while (currentPosition < queryString.length) {
- int position = queryString.indexOf("=", currentPosition);
- if (position == -1) {
- break;
- }
- String name = queryString.substring(currentPosition, position);
- currentPosition = position + 1;
- position = queryString.indexOf("&", currentPosition);
- String value;
- if (position == -1) {
- value = queryString.substring(currentPosition);
- currentPosition = queryString.length;
- } else {
- value = queryString.substring(currentPosition, position);
- currentPosition = position + 1;
- }
- result[HttpUtil.decodeUrlEncodedString(name)] =
- HttpUtil.decodeUrlEncodedString(value);
- }
- return result;
- }
-}
« no previous file with comments | « samples/chat/http.dart ('k') | samples/tests/samples/src/chat/ChatServerTest.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698