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

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

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

Powered by Google App Engine
This is Rietveld 408576698