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

Unified 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, 9 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/shelf/lib/src/request.dart ('k') | pkg/shelf/lib/src/stack.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/shelf/lib/src/response.dart
diff --git a/pkg/shelf/lib/src/response.dart b/pkg/shelf/lib/src/response.dart
new file mode 100644
index 0000000000000000000000000000000000000000..54fe48bfd85f59ca93e3c6cb206efe733a2467f1
--- /dev/null
+++ b/pkg/shelf/lib/src/response.dart
@@ -0,0 +1,273 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library shelf.response;
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:collection/wrappers.dart';
+
+import 'media_type.dart';
+import 'message.dart';
+import 'util.dart';
+
+/// The response returned by a [Handler].
+class Response extends Message {
+ /// The HTTP status code of the response.
+ final int statusCode;
+
+ /// The date and time after which the response's data should be considered
+ /// stale.
+ ///
+ /// This is parsed from the Expires header in [headers]. If [headers] doesn't
+ /// have an Expires header, this will be `null`.
+ DateTime get expires {
+ if (_expiresCache != null) return _expiresCache;
+ if (!headers.containsKey('expires')) return null;
+ _expiresCache = parseHttpDate(headers['expires']);
+ return _expiresCache;
+ }
+ DateTime _expiresCache;
+
+ /// The date and time the source of the response's data was last modified.
+ ///
+ /// This is parsed from the Last-Modified header in [headers]. If [headers]
+ /// doesn't have a Last-Modified header, this will be `null`.
+ DateTime get lastModified {
+ if (_lastModifiedCache != null) return _lastModifiedCache;
+ if (!headers.containsKey('last-modified')) return null;
+ _lastModifiedCache = parseHttpDate(headers['last-modified']);
+ return _lastModifiedCache;
+ }
+ DateTime _lastModifiedCache;
+
+ /// Constructs a 200 OK response.
+ ///
+ /// This indicates that the request has succeeded.
+ ///
+ /// [body] is the response body. It may be either a [String], a
+ /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
+ /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
+ /// UTF-8.
+ ///
+ /// If [encoding] is passed, the "encoding" field of the Content-Type header
+ /// in [headers] will be set appropriately. If there is no existing
+ /// Content-Type header, it will be set to "application/octet-stream".
+ Response.ok(body, {Map<String, String> headers, Encoding encoding})
+ : this(200, body: body, headers: headers, encoding: encoding);
+
+ /// Constructs a 301 Moved Permanently response.
+ ///
+ /// This indicates that the requested resource has moved permanently to a new
+ /// URI. [location] is that URI; it can be either a [String] or a [Uri]. It's
+ /// automatically set as the Location header in [headers].
+ ///
+ /// [body] is the response body. It may be either a [String], a
+ /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
+ /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
+ /// UTF-8.
+ ///
+ /// If [encoding] is passed, the "encoding" field of the Content-Type header
+ /// in [headers] will be set appropriately. If there is no existing
+ /// Content-Type header, it will be set to "application/octet-stream".
+ Response.movedPermanently(location, {body, Map<String, String> headers,
+ Encoding encoding})
+ : this._redirect(301, location, body, headers, encoding);
+
+ /// Constructs a 302 Found response.
+ ///
+ /// This indicates that the requested resource has moved temporarily to a new
+ /// URI. [location] is that URI; it can be either a [String] or a [Uri]. It's
+ /// automatically set as the Location header in [headers].
+ ///
+ /// [body] is the response body. It may be either a [String], a
+ /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
+ /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
+ /// UTF-8.
+ ///
+ /// If [encoding] is passed, the "encoding" field of the Content-Type header
+ /// in [headers] will be set appropriately. If there is no existing
+ /// Content-Type header, it will be set to "application/octet-stream".
+ Response.found(location, {body, Map<String, String> headers,
+ Encoding encoding})
+ : this._redirect(302, location, body, headers, encoding);
+
+ /// Constructs a 303 See Other response.
+ ///
+ /// This indicates that the response to the request should be retrieved using
+ /// a GET request to a new URI. [location] is that URI; it can be either a
+ /// [String] or a [Uri]. It's automatically set as the Location header in
+ /// [headers].
+ ///
+ /// [body] is the response body. It may be either a [String], a
+ /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
+ /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
+ /// UTF-8.
+ ///
+ /// If [encoding] is passed, the "encoding" field of the Content-Type header
+ /// in [headers] will be set appropriately. If there is no existing
+ /// Content-Type header, it will be set to "application/octet-stream".
+ Response.seeOther(location, {body, Map<String, String> headers,
+ Encoding encoding})
+ : this._redirect(303, location, body, headers, encoding);
+
+ /// Constructs a helper constructor for redirect responses.
+ Response._redirect(int statusCode, location, body,
+ Map<String, String> headers, Encoding encoding)
+ : this(statusCode,
+ body: body,
+ encoding: encoding,
+ headers: _addHeader(
+ headers, 'location', _locationToString(location)));
+
+ /// Constructs a 304 Not Modified response.
+ ///
+ /// This is used to respond to a conditional GET request that provided
+ /// information used to determine whether the requested resource has changed
+ /// since the last request. It indicates that the resource has not changed and
+ /// the old value should be used.
+ Response.notModified({Map<String, String> headers})
+ : this(304, headers: _addHeader(
+ headers, 'date', formatHttpDate(new DateTime.now())));
+
+ /// Constructs a 403 Forbidden response.
+ ///
+ /// This indicates that the server is refusing to fulfill the request.
+ ///
+ /// [body] is the response body. It may be either a [String], a
+ /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
+ /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
+ /// UTF-8.
+ ///
+ /// If [encoding] is passed, the "encoding" field of the Content-Type header
+ /// in [headers] will be set appropriately. If there is no existing
+ /// Content-Type header, it will be set to "application/octet-stream".
+ Response.forbidden(body, {Map<String, String> headers,
+ Encoding encoding})
+ : this(403, body: body, headers: headers);
+
+ /// Constructs a 404 Not Found response.
+ ///
+ /// This indicates that the server didn't find any resource matching the
+ /// requested URI.
+ ///
+ /// [body] is the response body. It may be either a [String], a
+ /// [Stream<List<int>>], or `null` to indicate no body. If it's a [String],
+ /// [encoding] is used to encode it to a [Stream<List<int>>]. It defaults to
+ /// UTF-8.
+ ///
+ /// If [encoding] is passed, the "encoding" field of the Content-Type header
+ /// in [headers] will be set appropriately. If there is no existing
+ /// Content-Type header, it will be set to "application/octet-stream".
+ Response.notFound(body, {Map<String, String> headers, Encoding encoding})
+ : this(404, body: body, headers: headers);
+
+ /// Constructs a 500 Internal Server Error response.
+ ///
+ /// This indicates that the server had an internal error that prevented it
+ /// from fulfilling the request.
+ ///
+ /// [body] is the response body. It may be either a [String], a
+ /// [Stream<List<int>>], or `null` to indicate no body. If it's `null` or not
+ /// passed, a default error message is used. If it's a [String], [encoding] is
+ /// used to encode it to a [Stream<List<int>>]. It defaults to UTF-8.
+ ///
+ /// If [encoding] is passed, the "encoding" field of the Content-Type header
+ /// in [headers] will be set appropriately. If there is no existing
+ /// Content-Type header, it will be set to "application/octet-stream".
+ Response.internalServerError({body, Map<String, String> headers,
+ Encoding encoding})
+ : this(500,
+ headers: body == null ? _adjust500Headers(headers) : headers,
+ body: body == null ? 'Internal Server Error' : body);
+
+ /// Constructs an HTTP response with the given [statusCode].
+ ///
+ /// [statusCode] must be greater than or equal to 100.
+ ///
+ /// [body] is the response body. It may be either a [String], a
+ /// [Stream<List<int>>], or `null` to indicate no body. If it's `null` or not
+ /// passed, a default error message is used. If it's a [String], [encoding] is
+ /// used to encode it to a [Stream<List<int>>]. It defaults to UTF-8.
+ ///
+ /// If [encoding] is passed, the "encoding" field of the Content-Type header
+ /// in [headers] will be set appropriately. If there is no existing
+ /// Content-Type header, it will be set to "application/octet-stream".
+ Response(this.statusCode, {body, Map<String, String> headers,
+ Encoding encoding})
+ : super(_adjustHeaders(headers, encoding),
+ _bodyToStream(body, encoding)) {
+ if (statusCode < 100) {
+ throw new ArgumentError("Invalid status code: $statusCode.");
+ }
+ }
+}
+
+/// Converts [body] to a byte stream.
+///
+/// [body] may be either a [String], a [Stream<List<int>>], or `null`. If it's a
+/// [String], [encoding] will be used to convert it to a [Stream<List<int>>].
+Stream<List<int>> _bodyToStream(body, Encoding encoding) {
+ if (encoding == null) encoding = UTF8;
+ if (body == null) return new Stream.fromIterable([]);
+ if (body is String) return new Stream.fromIterable([encoding.encode(body)]);
+ if (body is Stream) return body;
+
+ throw new ArgumentError('Response body "$body" must be a String or a '
+ 'Stream.');
+}
+
+/// Adds information about [encoding] to [headers].
+///
+/// Returns a new map without modifying [headers].
+UnmodifiableMapView<String, String> _adjustHeaders(
+ Map<String, String> headers, Encoding encoding) {
+ if (headers == null) headers = const {};
+ if (encoding == null) return new UnmodifiableMapView(headers);
+ if (headers['content-type'] == null) {
+ return new UnmodifiableMapView(_addHeader(headers, 'content-type',
+ 'application/octet-stream; charset=${encoding.name}'));
+ }
+
+ var contentType = new MediaType.parse(headers['content-type'])
+ .change(parameters: {'charset': encoding.name});
+ return new UnmodifiableMapView(
+ _addHeader(headers, 'content-type', contentType.toString()));
+}
+
+/// Adds a header with [name] and [value] to [headers], which may be null.
+///
+/// Returns a new map without modifying [headers].
+Map<String, String> _addHeader(Map<String, String> headers, String name,
+ String value) {
+ headers = headers == null ? {} : new Map.from(headers);
+ headers[name] = value;
+ return headers;
+}
+
+/// Adds content-type information to [headers].
+///
+/// Returns a new map without modifying [headers]. This is used to add
+/// content-type information when creating a 500 response with a default body.
+Map<String, String> _adjust500Headers(Map<String, String> headers) {
+ if (headers == null || headers['content-type'] == null) {
+ return _addHeader(headers, 'content-type', 'text/plain');
+ }
+
+ var contentType = new MediaType.parse(headers['content-type'])
+ .change(mimeType: 'text/plain');
+ return _addHeader(headers, 'content-type', contentType.toString());
+}
+
+/// Converts [location], which may be a [String] or a [Uri], to a [String].
+///
+/// Throws an [ArgumentError] if [location] isn't a [String] or a [Uri].
+String _locationToString(location) {
+ if (location is String) return location;
+ if (location is Uri) return location.toString();
+
+ throw new ArgumentError('Response location must be a String or Uri, was '
+ '"$location".');
+}
« 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