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.response; | 5 library shelf.response; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:convert'; | 8 import 'dart:convert'; |
9 | 9 |
10 import 'package:collection/wrappers.dart'; | |
11 import 'package:http_parser/http_parser.dart'; | 10 import 'package:http_parser/http_parser.dart'; |
12 | 11 |
13 import 'message.dart'; | 12 import 'message.dart'; |
14 | 13 |
15 /// The response returned by a [Handler]. | 14 /// The response returned by a [Handler]. |
16 class Response extends Message { | 15 class Response extends Message { |
17 /// The HTTP status code of the response. | 16 /// The HTTP status code of the response. |
18 final int statusCode; | 17 final int statusCode; |
19 | 18 |
20 /// The date and time after which the response's data should be considered | 19 /// The date and time after which the response's data should be considered |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 /// [body] is the response body. It may be either a [String], a | 188 /// [body] is the response body. It may be either a [String], a |
190 /// [Stream<List<int>>], or `null` to indicate no body. If it's `null` or not | 189 /// [Stream<List<int>>], or `null` to indicate no body. If it's `null` or not |
191 /// passed, a default error message is used. If it's a [String], [encoding] is | 190 /// passed, a default error message is used. If it's a [String], [encoding] is |
192 /// used to encode it to a [Stream<List<int>>]. It defaults to UTF-8. | 191 /// used to encode it to a [Stream<List<int>>]. It defaults to UTF-8. |
193 /// | 192 /// |
194 /// If [encoding] is passed, the "encoding" field of the Content-Type header | 193 /// If [encoding] is passed, the "encoding" field of the Content-Type header |
195 /// in [headers] will be set appropriately. If there is no existing | 194 /// in [headers] will be set appropriately. If there is no existing |
196 /// Content-Type header, it will be set to "application/octet-stream". | 195 /// Content-Type header, it will be set to "application/octet-stream". |
197 Response(this.statusCode, {body, Map<String, String> headers, | 196 Response(this.statusCode, {body, Map<String, String> headers, |
198 Encoding encoding}) | 197 Encoding encoding}) |
199 : super(_adjustHeaders(headers, encoding), | 198 : super(_bodyToStream(body, encoding), |
200 _bodyToStream(body, encoding)) { | 199 headers: _adjustHeaders(headers, encoding)) { |
201 if (statusCode < 100) { | 200 if (statusCode < 100) { |
202 throw new ArgumentError("Invalid status code: $statusCode."); | 201 throw new ArgumentError("Invalid status code: $statusCode."); |
203 } | 202 } |
204 } | 203 } |
205 } | 204 } |
206 | 205 |
207 /// Converts [body] to a byte stream. | 206 /// Converts [body] to a byte stream. |
208 /// | 207 /// |
209 /// [body] may be either a [String], a [Stream<List<int>>], or `null`. If it's a | 208 /// [body] may be either a [String], a [Stream<List<int>>], or `null`. If it's a |
210 /// [String], [encoding] will be used to convert it to a [Stream<List<int>>]. | 209 /// [String], [encoding] will be used to convert it to a [Stream<List<int>>]. |
211 Stream<List<int>> _bodyToStream(body, Encoding encoding) { | 210 Stream<List<int>> _bodyToStream(body, Encoding encoding) { |
212 if (encoding == null) encoding = UTF8; | 211 if (encoding == null) encoding = UTF8; |
213 if (body == null) return new Stream.fromIterable([]); | 212 if (body == null) return new Stream.fromIterable([]); |
214 if (body is String) return new Stream.fromIterable([encoding.encode(body)]); | 213 if (body is String) return new Stream.fromIterable([encoding.encode(body)]); |
215 if (body is Stream) return body; | 214 if (body is Stream) return body; |
216 | 215 |
217 throw new ArgumentError('Response body "$body" must be a String or a ' | 216 throw new ArgumentError('Response body "$body" must be a String or a ' |
218 'Stream.'); | 217 'Stream.'); |
219 } | 218 } |
220 | 219 |
221 /// Adds information about [encoding] to [headers]. | 220 /// Adds information about [encoding] to [headers]. |
222 /// | 221 /// |
223 /// Returns a new map without modifying [headers]. | 222 /// Returns a new map without modifying [headers]. |
224 UnmodifiableMapView<String, String> _adjustHeaders( | 223 Map<String, String> _adjustHeaders( |
225 Map<String, String> headers, Encoding encoding) { | 224 Map<String, String> headers, Encoding encoding) { |
226 if (headers == null) headers = const {}; | 225 if (headers == null) headers = const {}; |
227 if (encoding == null) return new UnmodifiableMapView(headers); | 226 if (encoding == null) return headers; |
228 if (headers['content-type'] == null) { | 227 if (headers['content-type'] == null) { |
229 return new UnmodifiableMapView(_addHeader(headers, 'content-type', | 228 return _addHeader(headers, 'content-type', |
230 'application/octet-stream; charset=${encoding.name}')); | 229 'application/octet-stream; charset=${encoding.name}'); |
231 } | 230 } |
232 | 231 |
233 var contentType = new MediaType.parse(headers['content-type']) | 232 var contentType = new MediaType.parse(headers['content-type']) |
234 .change(parameters: {'charset': encoding.name}); | 233 .change(parameters: {'charset': encoding.name}); |
235 return new UnmodifiableMapView( | 234 return _addHeader(headers, 'content-type', contentType.toString()); |
236 _addHeader(headers, 'content-type', contentType.toString())); | |
237 } | 235 } |
238 | 236 |
239 /// Adds a header with [name] and [value] to [headers], which may be null. | 237 /// Adds a header with [name] and [value] to [headers], which may be null. |
240 /// | 238 /// |
241 /// Returns a new map without modifying [headers]. | 239 /// Returns a new map without modifying [headers]. |
242 Map<String, String> _addHeader(Map<String, String> headers, String name, | 240 Map<String, String> _addHeader(Map<String, String> headers, String name, |
243 String value) { | 241 String value) { |
244 headers = headers == null ? {} : new Map.from(headers); | 242 headers = headers == null ? {} : new Map.from(headers); |
245 headers[name] = value; | 243 headers[name] = value; |
246 return headers; | 244 return headers; |
(...skipping 16 matching lines...) Expand all Loading... |
263 /// Converts [location], which may be a [String] or a [Uri], to a [String]. | 261 /// Converts [location], which may be a [String] or a [Uri], to a [String]. |
264 /// | 262 /// |
265 /// Throws an [ArgumentError] if [location] isn't a [String] or a [Uri]. | 263 /// Throws an [ArgumentError] if [location] isn't a [String] or a [Uri]. |
266 String _locationToString(location) { | 264 String _locationToString(location) { |
267 if (location is String) return location; | 265 if (location is String) return location; |
268 if (location is Uri) return location.toString(); | 266 if (location is Uri) return location.toString(); |
269 | 267 |
270 throw new ArgumentError('Response location must be a String or Uri, was ' | 268 throw new ArgumentError('Response location must be a String or Uri, was ' |
271 '"$location".'); | 269 '"$location".'); |
272 } | 270 } |
OLD | NEW |