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 |