Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library shelf.message; | 5 library shelf.message; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 | 9 |
| 10 import 'package:http_parser/http_parser.dart'; | 10 import 'package:http_parser/http_parser.dart'; |
| 11 import 'package:stack_trace/stack_trace.dart'; | 11 import 'package:stack_trace/stack_trace.dart'; |
| 12 | 12 |
| 13 import 'shelf_unmodifiable_map.dart'; | 13 import 'shelf_unmodifiable_map.dart'; |
| 14 import 'util.dart'; | |
| 14 | 15 |
| 15 /// Represents logic shared between [Request] and [Response]. | 16 /// Represents logic shared between [Request] and [Response]. |
| 16 abstract class Message { | 17 abstract class Message { |
| 17 /// The HTTP headers. | 18 /// The HTTP headers. |
| 18 /// | 19 /// |
| 19 /// The value is immutable. | 20 /// The value is immutable. |
| 20 final Map<String, String> headers; | 21 final Map<String, String> headers; |
| 21 | 22 |
| 22 /// Extra context that can be used by for middleware and handlers. | 23 /// Extra context that can be used by for middleware and handlers. |
| 23 /// | 24 /// |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 39 | 40 |
| 40 /// This boolean indicates whether [_body] has been read. | 41 /// This boolean indicates whether [_body] has been read. |
| 41 /// | 42 /// |
| 42 /// After calling [read], or [readAsString] (which internally calls [read]), | 43 /// After calling [read], or [readAsString] (which internally calls [read]), |
| 43 /// this will be `true`. | 44 /// this will be `true`. |
| 44 bool _bodyWasRead = false; | 45 bool _bodyWasRead = false; |
| 45 | 46 |
| 46 /// Creates a new [Message]. | 47 /// Creates a new [Message]. |
| 47 /// | 48 /// |
| 48 /// If [headers] is `null`, it is treated as empty. | 49 /// If [headers] is `null`, it is treated as empty. |
| 49 Message(this._body, | 50 Message(body, {Encoding encoding, Map<String, String> headers, |
|
nweiz
2015/01/21 01:51:00
Document what types [body] can be.
kevmoo
2015/01/21 02:15:25
Done.
| |
| 50 {Map<String, String> headers, Map<String, Object> context}) | 51 Map<String, Object> context}) |
| 51 : this.headers = new ShelfUnmodifiableMap<String>(headers, | 52 : this._body = _bodyToStream(body, encoding), |
| 52 ignoreKeyCase: true), | 53 this.headers = new ShelfUnmodifiableMap<String>( |
| 54 _adjustHeaders(headers, encoding), ignoreKeyCase: true), | |
| 53 this.context = new ShelfUnmodifiableMap<Object>(context, | 55 this.context = new ShelfUnmodifiableMap<Object>(context, |
| 54 ignoreKeyCase: false); | 56 ignoreKeyCase: false); |
| 55 | 57 |
| 56 /// The contents of the content-length field in [headers]. | 58 /// The contents of the content-length field in [headers]. |
| 57 /// | 59 /// |
| 58 /// If not set, `null`. | 60 /// If not set, `null`. |
| 59 int get contentLength { | 61 int get contentLength { |
| 60 if (_contentLengthCache != null) return _contentLengthCache; | 62 if (_contentLengthCache != null) return _contentLengthCache; |
| 61 if (!headers.containsKey('content-length')) return null; | 63 if (!headers.containsKey('content-length')) return null; |
| 62 _contentLengthCache = int.parse(headers['content-length']); | 64 _contentLengthCache = int.parse(headers['content-length']); |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 123 Future<String> readAsString([Encoding encoding]) { | 125 Future<String> readAsString([Encoding encoding]) { |
| 124 if (encoding == null) encoding = this.encoding; | 126 if (encoding == null) encoding = this.encoding; |
| 125 if (encoding == null) encoding = UTF8; | 127 if (encoding == null) encoding = UTF8; |
| 126 return Chain.track(encoding.decodeStream(read())); | 128 return Chain.track(encoding.decodeStream(read())); |
| 127 } | 129 } |
| 128 | 130 |
| 129 /// Creates a new [Message] by copying existing values and applying specified | 131 /// Creates a new [Message] by copying existing values and applying specified |
| 130 /// changes. | 132 /// changes. |
| 131 Message change({Map<String, String> headers, Map<String, Object> context}); | 133 Message change({Map<String, String> headers, Map<String, Object> context}); |
| 132 } | 134 } |
| 135 | |
| 136 /// Converts [body] to a byte stream. | |
| 137 /// | |
| 138 /// [body] may be either a [String], a [Stream<List<int>>], or `null`. If it's a | |
| 139 /// [String], [encoding] will be used to convert it to a [Stream<List<int>>]. | |
| 140 Stream<List<int>> _bodyToStream(body, Encoding encoding) { | |
| 141 if (encoding == null) encoding = UTF8; | |
| 142 if (body == null) return new Stream.fromIterable([]); | |
| 143 if (body is String) return new Stream.fromIterable([encoding.encode(body)]); | |
| 144 if (body is Stream) return body; | |
| 145 | |
| 146 throw new ArgumentError('Response body "$body" must be a String or a ' | |
| 147 'Stream.'); | |
| 148 } | |
| 149 | |
| 150 /// Adds information about [encoding] to [headers]. | |
| 151 /// | |
| 152 /// Returns a new map without modifying [headers]. | |
| 153 Map<String, String> _adjustHeaders( | |
| 154 Map<String, String> headers, Encoding encoding) { | |
| 155 if (headers == null) headers = const {}; | |
| 156 if (encoding == null) return headers; | |
| 157 if (headers['content-type'] == null) { | |
| 158 return addHeader(headers, 'content-type', | |
| 159 'application/octet-stream; charset=${encoding.name}'); | |
| 160 } | |
| 161 | |
| 162 var contentType = new MediaType.parse(headers['content-type']).change( | |
| 163 parameters: {'charset': encoding.name}); | |
| 164 return addHeader(headers, 'content-type', contentType.toString()); | |
| 165 } | |
| OLD | NEW |