Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of dart.io; | 5 part of http_server; |
| 6 | 6 |
| 7 class _HttpBodyHandlerTransformer | 7 class _HttpBodyHandlerTransformer |
| 8 extends StreamEventTransformer<HttpRequest, HttpRequestBody> { | 8 extends StreamEventTransformer<HttpRequest, HttpRequestBody> { |
| 9 final Encoding _defaultEncoding; | 9 final Encoding _defaultEncoding; |
| 10 _HttpBodyHandlerTransformer(this._defaultEncoding); | 10 _HttpBodyHandlerTransformer(this._defaultEncoding); |
| 11 | 11 |
| 12 void handleData(HttpRequest request, EventSink<HttpRequestBody> sink) { | 12 void handleData(HttpRequest request, EventSink<HttpRequestBody> sink) { |
| 13 _HttpBodyHandler.processRequest(request, _defaultEncoding) | 13 _HttpBodyHandler.processRequest(request, _defaultEncoding) |
| 14 .then(sink.add, onError: sink.addError); | 14 .then(sink.add, onError: sink.addError); |
| 15 } | 15 } |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 113 switch (contentType.subType) { | 113 switch (contentType.subType) { |
| 114 case "json": | 114 case "json": |
| 115 return asText(Encoding.UTF_8) | 115 return asText(Encoding.UTF_8) |
| 116 .then((body) => new _HttpBody(contentType, | 116 .then((body) => new _HttpBody(contentType, |
| 117 "json", | 117 "json", |
| 118 JSON.parse(body.body))); | 118 JSON.parse(body.body))); |
| 119 | 119 |
| 120 case "x-www-form-urlencoded": | 120 case "x-www-form-urlencoded": |
| 121 return asText(Encoding.ASCII) | 121 return asText(Encoding.ASCII) |
| 122 .then((body) { | 122 .then((body) { |
| 123 var map = _HttpUtils.splitQueryString( | 123 var map = _splitQueryString( |
| 124 body.body, encoding: defaultEncoding); | 124 body.body, encoding: defaultEncoding); |
| 125 var result = {}; | 125 var result = {}; |
| 126 for (var key in map.keys) { | 126 for (var key in map.keys) { |
| 127 result[key] = map[key]; | 127 result[key] = map[key]; |
| 128 } | 128 } |
| 129 return new _HttpBody(contentType, "form", result); | 129 return new _HttpBody(contentType, "form", result); |
| 130 }); | 130 }); |
| 131 | 131 |
| 132 default: | 132 default: |
| 133 break; | 133 break; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 _HttpClientResponseBody(HttpClientResponse response, HttpBody body) | 190 _HttpClientResponseBody(HttpClientResponse response, HttpBody body) |
| 191 : super(body.contentType, body.type, body.body), | 191 : super(body.contentType, body.type, body.body), |
| 192 this.response = response; | 192 this.response = response; |
| 193 | 193 |
| 194 int get statusCode => response.statusCode; | 194 int get statusCode => response.statusCode; |
| 195 | 195 |
| 196 String get reasonPhrase => response.reasonPhrase; | 196 String get reasonPhrase => response.reasonPhrase; |
| 197 | 197 |
| 198 HttpHeaders get headers => response.headers; | 198 HttpHeaders get headers => response.headers; |
| 199 } | 199 } |
| 200 | |
| 201 | |
| 202 class _CharCode { | |
| 203 static const int AMPERSAND = 38; | |
| 204 static const int SEMI_COLON = 59; | |
| 205 static const int EQUAL = 61; | |
| 206 } | |
| 207 | |
| 208 | |
| 209 // 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.
| |
| 210 Map<String, String> _splitQueryString(String queryString, | |
| 211 {Encoding encoding: Encoding.UTF_8}) { | |
| 212 Map<String, String> result = new Map<String, String>(); | |
| 213 int currentPosition = 0; | |
| 214 int length = queryString.length; | |
| 215 | |
| 216 while (currentPosition < length) { | |
| 217 | |
| 218 // Find the first equals character between current position and | |
| 219 // 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.
| |
| 220 int indexOfEquals(int end) { | |
| 221 int index = currentPosition; | |
| 222 while (index < end) { | |
| 223 if (queryString.codeUnitAt(index) == _CharCode.EQUAL) return index; | |
| 224 index++; | |
| 225 } | |
| 226 return -1; | |
| 227 } | |
| 228 | |
| 229 // Find the next separator (either & or ;), see | |
| 230 // http://www.w3.org/TR/REC-html40/appendix/notes.html#ampersands-in-uris | |
| 231 // relating the ; separator. If no separator is found returns | |
| 232 // the length of the query string. | |
| 233 int indexOfSeparator() { | |
| 234 int end = length; | |
| 235 int index = currentPosition; | |
| 236 while (index < end) { | |
| 237 int codeUnit = queryString.codeUnitAt(index); | |
| 238 if (codeUnit == _CharCode.AMPERSAND || | |
| 239 codeUnit == _CharCode.SEMI_COLON) { | |
| 240 return index; | |
| 241 } | |
| 242 index++; | |
| 243 } | |
| 244 return end; | |
| 245 } | |
| 246 | |
| 247 int seppos = indexOfSeparator(); | |
| 248 int equalspos = indexOfEquals(seppos); | |
| 249 String name; | |
| 250 String value; | |
| 251 if (equalspos == -1) { | |
| 252 name = queryString.substring(currentPosition, seppos); | |
| 253 value = ''; | |
| 254 } else { | |
| 255 name = queryString.substring(currentPosition, equalspos); | |
| 256 value = queryString.substring(equalspos + 1, seppos); | |
| 257 } | |
| 258 currentPosition = seppos + 1; // This also works when seppos == length. | |
| 259 if (name == '') continue; | |
| 260 result[_decodeUrlEncodedString(name, encoding: encoding)] = | |
| 261 _decodeUrlEncodedString(value, encoding: encoding); | |
| 262 } | |
| 263 return result; | |
| 264 } | |
| 265 | |
| 266 | |
| 267 // TODO(ajohnsen): Provide in dart:io? | |
| 268 String _decodeUrlEncodedString(String urlEncoded, | |
| 269 {Encoding encoding: Encoding.UTF_8}) { | |
| 270 // First check the string for any encoding. | |
| 271 int index = 0; | |
| 272 bool encoded = false; | |
| 273 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.
| |
| 274 encoded = urlEncoded[index] == "+" || urlEncoded[index] == "%"; | |
| 275 index++; | |
| 276 } | |
| 277 if (!encoded) return urlEncoded; | |
| 278 index--; | |
| 279 | |
| 280 // Start decoding from the first encoded character. | |
| 281 List<int> bytes = new List<int>(); | |
| 282 for (int i = 0; i < index; i++) bytes.add(urlEncoded.codeUnitAt(i)); | |
| 283 for (int i = index; i < urlEncoded.length; i++) { | |
| 284 if (urlEncoded[i] == "+") { | |
| 285 bytes.add(32); | |
| 286 } else if (urlEncoded[i] == "%") { | |
| 287 if (urlEncoded.length - i < 2) { | |
| 288 throw new HttpException("Invalid URL encoding"); | |
| 289 } | |
| 290 int byte = 0; | |
| 291 for (int j = 0; j < 2; j++) { | |
| 292 var charCode = urlEncoded.codeUnitAt(i + j + 1); | |
| 293 if (0x30 <= charCode && charCode <= 0x39) { | |
| 294 byte = byte * 16 + charCode - 0x30; | |
| 295 } else { | |
| 296 // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66). | |
| 297 charCode |= 0x20; | |
| 298 if (0x61 <= charCode && charCode <= 0x66) { | |
| 299 byte = byte * 16 + charCode - 0x57; | |
| 300 } else { | |
| 301 throw new ArgumentError("Invalid URL encoding"); | |
| 302 } | |
| 303 } | |
| 304 } | |
| 305 bytes.add(byte); | |
| 306 i += 2; | |
| 307 } else { | |
| 308 bytes.add(urlEncoded.codeUnitAt(i)); | |
| 309 } | |
| 310 } | |
| 311 return _decodeString(bytes, encoding); | |
| 312 } | |
| 313 | |
| 314 // Utility function to synchronously decode a list of bytes. | |
| 315 // TODO(ajohnsen): Provide in dart:?. | |
| 316 String _decodeString(List<int> bytes, [Encoding encoding = Encoding.UTF_8]) { | |
| 317 if (bytes.length == 0) return ""; | |
| 318 var string; | |
| 319 var error; | |
| 320 var controller = new StreamController(sync: true); | |
| 321 controller.stream | |
| 322 .transform(new StringDecoder(encoding)) | |
| 323 .listen((data) => string = data, | |
| 324 onError: (e) => error = e); | |
| 325 controller.add(bytes); | |
| 326 controller.close(); | |
| 327 if (error != null) throw error; | |
| 328 assert(string != null); | |
| 329 return string; | |
| 330 } | |
| 331 | |
| 332 /** | |
| 333 * Utility class that can fast concatenate [List<int>]s of bytes. Use | |
| 334 * [readBytes] to get the final buffer. | |
| 335 */ | |
| 336 // TODO(ajohnsen): Provide in dart:io? | |
| 337 class _BufferList { | |
| 338 const int _INIT_SIZE = 1 * 1024; | |
| 339 | |
| 340 _BufferList() { | |
| 341 clear(); | |
| 342 } | |
| 343 | |
| 344 int pow2roundup(int x) { | |
| 345 --x; | |
| 346 x |= x >> 1; | |
| 347 x |= x >> 2; | |
| 348 x |= x >> 4; | |
| 349 x |= x >> 8; | |
| 350 x |= x >> 16; | |
| 351 return x + 1; | |
| 352 } | |
| 353 | |
| 354 /** | |
| 355 * Adds a new buffer to the list. | |
| 356 */ | |
| 357 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.
| |
| 358 int bufferLength = buffer.length; | |
| 359 int required = _length + bufferLength; | |
| 360 if (_buffer == null) { | |
| 361 int size = pow2roundup(required); | |
| 362 if (size < _INIT_SIZE) size = _INIT_SIZE; | |
| 363 _buffer = new Uint8List(size); | |
| 364 } else if (_buffer.length < required) { | |
| 365 // This will give is a list in the range of 2-4 times larger than | |
| 366 // required. | |
| 367 int size = pow2roundup(required) * 2; | |
| 368 Uint8List newBuffer = new Uint8List(size); | |
| 369 newBuffer.setRange(0, _buffer.length, _buffer); | |
| 370 _buffer = newBuffer; | |
| 371 } | |
| 372 assert(_buffer.length >= required); | |
| 373 if (buffer is Uint8List) { | |
| 374 _buffer.setRange(_length, required, buffer); | |
| 375 } else { | |
| 376 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.
| |
| 377 _buffer[_length + i] = buffer[i]; | |
| 378 } | |
| 379 } | |
| 380 _length = required; | |
| 381 } | |
| 382 | |
| 383 /** | |
| 384 * Same as [add]. | |
| 385 */ | |
| 386 void write(List<int> buffer) { | |
| 387 add(buffer); | |
| 388 } | |
| 389 | |
| 390 /** | |
| 391 * Read all the bytes from the buffer list. If it's empty, an empty list | |
| 392 * is returned. A call to [readBytes] will clear the buffer. | |
| 393 */ | |
| 394 List<int> readBytes() { | |
| 395 if (_buffer == null) return new Uint8List(0); | |
| 396 var buffer = new Uint8List.view(_buffer.buffer, 0, _length); | |
| 397 clear(); | |
| 398 return buffer; | |
| 399 } | |
| 400 | |
| 401 /** | |
| 402 * Returns the total number of bytes in the buffer. | |
| 403 */ | |
| 404 int get length => _length; | |
| 405 | |
| 406 /** | |
| 407 * Returns whether the buffer list is empty. | |
| 408 */ | |
| 409 bool get isEmpty => _length == 0; | |
| 410 | |
| 411 /** | |
| 412 * Returns whether the buffer list is not empty. | |
| 413 */ | |
| 414 bool get isNotEmpty => !isEmpty; | |
| 415 | |
| 416 /** | |
| 417 * Clears the content of the buffer list. | |
| 418 */ | |
| 419 void clear() { | |
| 420 _length = 0; | |
| 421 _buffer = null; | |
| 422 } | |
| 423 | |
| 424 int _length; // Total number of bytes in the buffer. | |
| 425 Uint8List _buffer; // Internal buffer. | |
| 426 } | |
| OLD | NEW |