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. |
+} |