Chromium Code Reviews| Index: pkg/http_server/lib/src/http_body_impl.dart |
| diff --git a/sdk/lib/io/http_body_impl.dart b/pkg/http_server/lib/src/http_body_impl.dart |
| similarity index 50% |
| rename from sdk/lib/io/http_body_impl.dart |
| rename to pkg/http_server/lib/src/http_body_impl.dart |
| index 900dff51e9304d8349993e36a0972c2b38ad8584..1ccf49291d00642006515bd1194e539b1b820da9 100644 |
| --- a/sdk/lib/io/http_body_impl.dart |
| +++ b/pkg/http_server/lib/src/http_body_impl.dart |
| @@ -2,7 +2,7 @@ |
| // 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. |
| -part of dart.io; |
| +part of http_server; |
| class _HttpBodyHandlerTransformer |
| extends StreamEventTransformer<HttpRequest, HttpRequestBody> { |
| @@ -120,7 +120,7 @@ class _HttpBodyHandler { |
| case "x-www-form-urlencoded": |
| return asText(Encoding.ASCII) |
| .then((body) { |
| - var map = _HttpUtils.splitQueryString( |
| + var map = _splitQueryString( |
| body.body, encoding: defaultEncoding); |
| var result = {}; |
| for (var key in map.keys) { |
| @@ -197,3 +197,230 @@ class _HttpClientResponseBody |
| HttpHeaders get headers => response.headers; |
| } |
| + |
| + |
| +class _CharCode { |
| + static const int AMPERSAND = 38; |
| + static const int SEMI_COLON = 59; |
| + static const int EQUAL = 61; |
| +} |
| + |
| + |
| +// TODO(ajohnsen): Provide in dart:io? |
|
Bill Hesse
2013/07/03 11:30:27
Do you now mean "Provide in pkg/http_server?" Do
Anders Johnsen
2013/07/12 10:45:57
Removed.
|
| +Map<String, String> _splitQueryString(String queryString, |
| + {Encoding encoding: Encoding.UTF_8}) { |
| + Map<String, String> result = new Map<String, String>(); |
| + int currentPosition = 0; |
| + int length = queryString.length; |
| + |
| + while (currentPosition < length) { |
| + |
| + // Find the first equals character between current position and |
| + // the provided end. |
|
Bill Hesse
2013/07/03 11:30:27
I don't think you need this function. You can, in
Anders Johnsen
2013/07/12 10:45:57
This is gone.
|
| + int indexOfEquals(int end) { |
| + int index = currentPosition; |
| + while (index < end) { |
| + if (queryString.codeUnitAt(index) == _CharCode.EQUAL) return index; |
| + index++; |
| + } |
| + return -1; |
| + } |
| + |
| + // Find the next separator (either & or ;), see |
| + // http://www.w3.org/TR/REC-html40/appendix/notes.html#ampersands-in-uris |
| + // relating the ; separator. If no separator is found returns |
| + // the length of the query string. |
| + int indexOfSeparator() { |
| + int end = length; |
| + int index = currentPosition; |
| + while (index < end) { |
| + int codeUnit = queryString.codeUnitAt(index); |
| + if (codeUnit == _CharCode.AMPERSAND || |
| + codeUnit == _CharCode.SEMI_COLON) { |
| + return index; |
| + } |
| + index++; |
| + } |
| + return end; |
| + } |
| + |
| + int seppos = indexOfSeparator(); |
| + int equalspos = indexOfEquals(seppos); |
| + String name; |
| + String value; |
| + if (equalspos == -1) { |
| + name = queryString.substring(currentPosition, seppos); |
| + value = ''; |
| + } else { |
| + name = queryString.substring(currentPosition, equalspos); |
| + value = queryString.substring(equalspos + 1, seppos); |
| + } |
| + currentPosition = seppos + 1; // This also works when seppos == length. |
| + if (name == '') continue; |
| + result[_decodeUrlEncodedString(name, encoding: encoding)] = |
| + _decodeUrlEncodedString(value, encoding: encoding); |
| + } |
| + return result; |
| +} |
| + |
| + |
| +// TODO(ajohnsen): Provide in dart:io? |
| +String _decodeUrlEncodedString(String urlEncoded, |
| + {Encoding encoding: Encoding.UTF_8}) { |
| + // First check the string for any encoding. |
| + int index = 0; |
| + bool encoded = false; |
| + while (!encoded && index < urlEncoded.length) { |
|
Bill Hesse
2013/07/03 11:30:27
.indexOf(RegExp('[%+]'))?
Anders Johnsen
2013/07/12 10:45:57
This is gone.
|
| + encoded = urlEncoded[index] == "+" || urlEncoded[index] == "%"; |
| + index++; |
| + } |
| + if (!encoded) return urlEncoded; |
| + index--; |
| + |
| + // Start decoding from the first encoded character. |
| + List<int> bytes = new List<int>(); |
| + for (int i = 0; i < index; i++) bytes.add(urlEncoded.codeUnitAt(i)); |
| + for (int i = index; i < urlEncoded.length; i++) { |
| + if (urlEncoded[i] == "+") { |
| + bytes.add(32); |
| + } else if (urlEncoded[i] == "%") { |
| + if (urlEncoded.length - i < 2) { |
| + throw new HttpException("Invalid URL encoding"); |
| + } |
| + int byte = 0; |
| + for (int j = 0; j < 2; j++) { |
| + var charCode = urlEncoded.codeUnitAt(i + j + 1); |
| + if (0x30 <= charCode && charCode <= 0x39) { |
| + byte = byte * 16 + charCode - 0x30; |
| + } else { |
| + // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66). |
| + charCode |= 0x20; |
| + if (0x61 <= charCode && charCode <= 0x66) { |
| + byte = byte * 16 + charCode - 0x57; |
| + } else { |
| + throw new ArgumentError("Invalid URL encoding"); |
| + } |
| + } |
| + } |
| + bytes.add(byte); |
| + i += 2; |
| + } else { |
| + bytes.add(urlEncoded.codeUnitAt(i)); |
| + } |
| + } |
| + return _decodeString(bytes, encoding); |
| +} |
| + |
| +// Utility function to synchronously decode a list of bytes. |
| +// TODO(ajohnsen): Provide in dart:?. |
| +String _decodeString(List<int> bytes, [Encoding encoding = Encoding.UTF_8]) { |
| + if (bytes.length == 0) return ""; |
| + var string; |
| + var error; |
| + var controller = new StreamController(sync: true); |
| + controller.stream |
| + .transform(new StringDecoder(encoding)) |
| + .listen((data) => string = data, |
| + onError: (e) => error = e); |
| + controller.add(bytes); |
| + controller.close(); |
| + if (error != null) throw error; |
| + assert(string != null); |
| + return string; |
| +} |
| + |
| +/** |
| + * Utility class that can fast concatenate [List<int>]s of bytes. Use |
| + * [readBytes] to get the final buffer. |
| + */ |
| +// TODO(ajohnsen): Provide in dart:io? |
| +class _BufferList { |
| + const int _INIT_SIZE = 1 * 1024; |
| + |
| + _BufferList() { |
| + clear(); |
| + } |
| + |
| + int pow2roundup(int x) { |
| + --x; |
| + x |= x >> 1; |
| + x |= x >> 2; |
| + x |= x >> 4; |
| + x |= x >> 8; |
| + x |= x >> 16; |
| + return x + 1; |
| + } |
| + |
| + /** |
| + * Adds a new buffer to the list. |
| + */ |
| + void add(List<int> buffer) { |
|
Bill Hesse
2013/07/03 11:30:27
buffer and _buffer in the same function is confusi
Anders Johnsen
2013/07/12 10:45:57
Also gone.
|
| + int bufferLength = buffer.length; |
| + int required = _length + bufferLength; |
| + if (_buffer == null) { |
| + int size = pow2roundup(required); |
| + if (size < _INIT_SIZE) size = _INIT_SIZE; |
| + _buffer = new Uint8List(size); |
| + } else if (_buffer.length < required) { |
| + // This will give is a list in the range of 2-4 times larger than |
| + // required. |
| + int size = pow2roundup(required) * 2; |
| + Uint8List newBuffer = new Uint8List(size); |
| + newBuffer.setRange(0, _buffer.length, _buffer); |
| + _buffer = newBuffer; |
| + } |
| + assert(_buffer.length >= required); |
| + if (buffer is Uint8List) { |
| + _buffer.setRange(_length, required, buffer); |
| + } else { |
| + for (int i = 0; i < bufferLength; i++) { |
|
Bill Hesse
2013/07/03 11:30:27
Why aren't we using setRange for both cases?
Anders Johnsen
2013/07/12 10:45:57
We are now.
|
| + _buffer[_length + i] = buffer[i]; |
| + } |
| + } |
| + _length = required; |
| + } |
| + |
| + /** |
| + * Same as [add]. |
| + */ |
| + void write(List<int> buffer) { |
| + add(buffer); |
| + } |
| + |
| + /** |
| + * Read all the bytes from the buffer list. If it's empty, an empty list |
| + * is returned. A call to [readBytes] will clear the buffer. |
| + */ |
| + List<int> readBytes() { |
| + if (_buffer == null) return new Uint8List(0); |
| + var buffer = new Uint8List.view(_buffer.buffer, 0, _length); |
| + clear(); |
| + return buffer; |
| + } |
| + |
| + /** |
| + * Returns the total number of bytes in the buffer. |
| + */ |
| + int get length => _length; |
| + |
| + /** |
| + * Returns whether the buffer list is empty. |
| + */ |
| + bool get isEmpty => _length == 0; |
| + |
| + /** |
| + * Returns whether the buffer list is not empty. |
| + */ |
| + bool get isNotEmpty => !isEmpty; |
| + |
| + /** |
| + * Clears the content of the buffer list. |
| + */ |
| + void clear() { |
| + _length = 0; |
| + _buffer = null; |
| + } |
| + |
| + int _length; // Total number of bytes in the buffer. |
| + Uint8List _buffer; // Internal buffer. |
| +} |