Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1077)

Side by Side Diff: pkg/shelf/lib/src/response.dart

Issue 814113004: Pull args, intl, logging, shelf, and source_maps out of the SDK. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Also csslib. Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/shelf/lib/src/request.dart ('k') | pkg/shelf/lib/src/shelf_unmodifiable_map.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library shelf.response;
6
7 import 'dart:async';
8 import 'dart:convert';
9
10 import 'package:http_parser/http_parser.dart';
11
12 import 'message.dart';
13 import 'util.dart';
14
15 /// The response returned by a [Handler].
16 class Response extends Message {
17 /// The HTTP status code of the response.
18 final int statusCode;
19
20 /// The date and time after which the response's data should be considered
21 /// stale.
22 ///
23 /// This is parsed from the Expires header in [headers]. If [headers] doesn't
24 /// have an Expires header, this will be `null`.
25 DateTime get expires {
26 if (_expiresCache != null) return _expiresCache;
27 if (!headers.containsKey('expires')) return null;
28 _expiresCache = parseHttpDate(headers['expires']);
29 return _expiresCache;
30 }
31 DateTime _expiresCache;
32
33 /// The date and time the source of the response's data was last modified.
34 ///
35 /// This is parsed from the Last-Modified header in [headers]. If [headers]
36 /// doesn't have a Last-Modified header, this will be `null`.
37 DateTime get lastModified {
38 if (_lastModifiedCache != null) return _lastModifiedCache;
39 if (!headers.containsKey('last-modified')) return null;
40 _lastModifiedCache = parseHttpDate(headers['last-modified']);
41 return _lastModifiedCache;
42 }
43 DateTime _lastModifiedCache;
44
45 /// Constructs a 200 OK response.
46 ///
47 /// This indicates that the request has succeeded.
48 ///
49 /// [body] is the response body. It may be either a [String], a
50 /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
51 /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
52 /// UTF-8.
53 ///
54 /// If [encoding] is passed, the "encoding" field of the Content-Type header
55 /// in [headers] will be set appropriately. If there is no existing
56 /// Content-Type header, it will be set to "application/octet-stream".
57 Response.ok(body, {Map<String, String> headers, Encoding encoding,
58 Map<String, Object> context})
59 : this(200, body: body, headers: headers, encoding: encoding,
60 context: context);
61
62 /// Constructs a 301 Moved Permanently response.
63 ///
64 /// This indicates that the requested resource has moved permanently to a new
65 /// URI. [location] is that URI; it can be either a [String] or a [Uri]. It's
66 /// automatically set as the Location header in [headers].
67 ///
68 /// [body] is the response body. It may be either a [String], a
69 /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
70 /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
71 /// UTF-8.
72 ///
73 /// If [encoding] is passed, the "encoding" field of the Content-Type header
74 /// in [headers] will be set appropriately. If there is no existing
75 /// Content-Type header, it will be set to "application/octet-stream".
76 Response.movedPermanently(location, {body, Map<String, String> headers,
77 Encoding encoding, Map<String, Object> context})
78 : this._redirect(301, location, body, headers, encoding,
79 context: context);
80
81 /// Constructs a 302 Found response.
82 ///
83 /// This indicates that the requested resource has moved temporarily to a new
84 /// URI. [location] is that URI; it can be either a [String] or a [Uri]. It's
85 /// automatically set as the Location header in [headers].
86 ///
87 /// [body] is the response body. It may be either a [String], a
88 /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
89 /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
90 /// UTF-8.
91 ///
92 /// If [encoding] is passed, the "encoding" field of the Content-Type header
93 /// in [headers] will be set appropriately. If there is no existing
94 /// Content-Type header, it will be set to "application/octet-stream".
95 Response.found(location, {body, Map<String, String> headers,
96 Encoding encoding, Map<String, Object> context})
97 : this._redirect(302, location, body, headers, encoding,
98 context: context);
99
100 /// Constructs a 303 See Other response.
101 ///
102 /// This indicates that the response to the request should be retrieved using
103 /// a GET request to a new URI. [location] is that URI; it can be either a
104 /// [String] or a [Uri]. It's automatically set as the Location header in
105 /// [headers].
106 ///
107 /// [body] is the response body. It may be either a [String], a
108 /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
109 /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
110 /// UTF-8.
111 ///
112 /// If [encoding] is passed, the "encoding" field of the Content-Type header
113 /// in [headers] will be set appropriately. If there is no existing
114 /// Content-Type header, it will be set to "application/octet-stream".
115 Response.seeOther(location, {body, Map<String, String> headers,
116 Encoding encoding, Map<String, Object> context})
117 : this._redirect(303, location, body, headers, encoding,
118 context: context);
119
120 /// Constructs a helper constructor for redirect responses.
121 Response._redirect(int statusCode, location, body,
122 Map<String, String> headers, Encoding encoding,
123 { Map<String, Object> context })
124 : this(statusCode,
125 body: body,
126 encoding: encoding,
127 headers: _addHeader(
128 headers, 'location', _locationToString(location)),
129 context: context);
130
131 /// Constructs a 304 Not Modified response.
132 ///
133 /// This is used to respond to a conditional GET request that provided
134 /// information used to determine whether the requested resource has changed
135 /// since the last request. It indicates that the resource has not changed and
136 /// the old value should be used.
137 Response.notModified({Map<String, String> headers,
138 Map<String, Object> context})
139 : this(304, headers: _addHeader(
140 headers, 'date', formatHttpDate(new DateTime.now())),
141 context: context);
142
143 /// Constructs a 403 Forbidden response.
144 ///
145 /// This indicates that the server is refusing to fulfill the request.
146 ///
147 /// [body] is the response body. It may be a [String], a [Stream<List<int>>],
148 /// or `null`. If it's a [String], [encoding] is used to encode it to a
149 /// [Stream<List<int>>]. The default encoding is UTF-8. If it's `null` or not
150 /// passed, a default error message is used.
151 ///
152 /// If [encoding] is passed, the "encoding" field of the Content-Type header
153 /// in [headers] will be set appropriately. If there is no existing
154 /// Content-Type header, it will be set to "application/octet-stream".
155 Response.forbidden(body, {Map<String, String> headers,
156 Encoding encoding, Map<String, Object> context})
157 : this(403,
158 headers: body == null ? _adjustErrorHeaders(headers) : headers,
159 body: body == null ? 'Forbidden' : body,
160 context: context);
161
162 /// Constructs a 404 Not Found response.
163 ///
164 /// This indicates that the server didn't find any resource matching the
165 /// requested URI.
166 ///
167 /// [body] is the response body. It may be a [String], a [Stream<List<int>>],
168 /// or `null`. If it's a [String], [encoding] is used to encode it to a
169 /// [Stream<List<int>>]. The default encoding is UTF-8. If it's `null` or not
170 /// passed, a default error message is used.
171 ///
172 /// If [encoding] is passed, the "encoding" field of the Content-Type header
173 /// in [headers] will be set appropriately. If there is no existing
174 /// Content-Type header, it will be set to "application/octet-stream".
175 Response.notFound(body, {Map<String, String> headers, Encoding encoding,
176 Map<String, Object> context})
177 : this(404,
178 headers: body == null ? _adjustErrorHeaders(headers) : headers,
179 body: body == null ? 'Not Found' : body,
180 context: context);
181
182 /// Constructs a 500 Internal Server Error response.
183 ///
184 /// This indicates that the server had an internal error that prevented it
185 /// from fulfilling the request.
186 ///
187 /// [body] is the response body. It may be a [String], a [Stream<List<int>>],
188 /// or `null`. If it's a [String], [encoding] is used to encode it to a
189 /// [Stream<List<int>>]. The default encoding is UTF-8. If it's `null` or not
190 /// passed, a default error message is used.
191 ///
192 /// If [encoding] is passed, the "encoding" field of the Content-Type header
193 /// in [headers] will be set appropriately. If there is no existing
194 /// Content-Type header, it will be set to "application/octet-stream".
195 Response.internalServerError({body, Map<String, String> headers,
196 Encoding encoding, Map<String, Object> context})
197 : this(500,
198 headers: body == null ? _adjustErrorHeaders(headers) : headers,
199 body: body == null ? 'Internal Server Error' : body,
200 context: context);
201
202 /// Constructs an HTTP response with the given [statusCode].
203 ///
204 /// [statusCode] must be greater than or equal to 100.
205 ///
206 /// [body] is the response body. It may be either a [String], a
207 /// [Stream<List<int>>], or `null` to indicate no body.
208 /// If it's a [String], [encoding] is used to encode it to a
209 /// [Stream<List<int>>]. The default encoding is UTF-8.
210 ///
211 /// If [encoding] is passed, the "encoding" field of the Content-Type header
212 /// in [headers] will be set appropriately. If there is no existing
213 /// Content-Type header, it will be set to "application/octet-stream".
214 Response(this.statusCode, {body, Map<String, String> headers,
215 Encoding encoding, Map<String, Object> context})
216 : super(_bodyToStream(body, encoding),
217 headers: _adjustHeaders(headers, encoding),
218 context: context) {
219 if (statusCode < 100) {
220 throw new ArgumentError("Invalid status code: $statusCode.");
221 }
222 }
223
224 /// Creates a new [Response] by copying existing values and applying specified
225 /// changes.
226 ///
227 /// New key-value pairs in [context] and [headers] will be added to the copied
228 /// [Response].
229 ///
230 /// If [context] or [headers] includes a key that already exists, the
231 /// key-value pair will replace the corresponding entry in the copied
232 /// [Response].
233 ///
234 /// All other context and header values from the [Response] will be included
235 /// in the copied [Response] unchanged.
236 Response change({Map<String, String> headers, Map<String, Object> context}) {
237 headers = updateMap(this.headers, headers);
238 context = updateMap(this.context, context);
239
240 return new Response(this.statusCode, body: this.read(), headers: headers,
241 context: context);
242 }
243 }
244
245 /// Converts [body] to a byte stream.
246 ///
247 /// [body] may be either a [String], a [Stream<List<int>>], or `null`. If it's a
248 /// [String], [encoding] will be used to convert it to a [Stream<List<int>>].
249 Stream<List<int>> _bodyToStream(body, Encoding encoding) {
250 if (encoding == null) encoding = UTF8;
251 if (body == null) return new Stream.fromIterable([]);
252 if (body is String) return new Stream.fromIterable([encoding.encode(body)]);
253 if (body is Stream) return body;
254
255 throw new ArgumentError('Response body "$body" must be a String or a '
256 'Stream.');
257 }
258
259 /// Adds information about [encoding] to [headers].
260 ///
261 /// Returns a new map without modifying [headers].
262 Map<String, String> _adjustHeaders(
263 Map<String, String> headers, Encoding encoding) {
264 if (headers == null) headers = const {};
265 if (encoding == null) return headers;
266 if (headers['content-type'] == null) {
267 return _addHeader(headers, 'content-type',
268 'application/octet-stream; charset=${encoding.name}');
269 }
270
271 var contentType = new MediaType.parse(headers['content-type'])
272 .change(parameters: {'charset': encoding.name});
273 return _addHeader(headers, 'content-type', contentType.toString());
274 }
275
276 /// Adds a header with [name] and [value] to [headers], which may be null.
277 ///
278 /// Returns a new map without modifying [headers].
279 Map<String, String> _addHeader(Map<String, String> headers, String name,
280 String value) {
281 headers = headers == null ? {} : new Map.from(headers);
282 headers[name] = value;
283 return headers;
284 }
285
286 /// Adds content-type information to [headers].
287 ///
288 /// Returns a new map without modifying [headers]. This is used to add
289 /// content-type information when creating a 500 response with a default body.
290 Map<String, String> _adjustErrorHeaders(Map<String, String> headers) {
291 if (headers == null || headers['content-type'] == null) {
292 return _addHeader(headers, 'content-type', 'text/plain');
293 }
294
295 var contentType = new MediaType.parse(headers['content-type'])
296 .change(mimeType: 'text/plain');
297 return _addHeader(headers, 'content-type', contentType.toString());
298 }
299
300 /// Converts [location], which may be a [String] or a [Uri], to a [String].
301 ///
302 /// Throws an [ArgumentError] if [location] isn't a [String] or a [Uri].
303 String _locationToString(location) {
304 if (location is String) return location;
305 if (location is Uri) return location.toString();
306
307 throw new ArgumentError('Response location must be a String or Uri, was '
308 '"$location".');
309 }
OLDNEW
« no previous file with comments | « pkg/shelf/lib/src/request.dart ('k') | pkg/shelf/lib/src/shelf_unmodifiable_map.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698