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 |