Index: mojo/public/dart/third_party/shelf/lib/src/response.dart |
diff --git a/mojo/public/dart/third_party/shelf/lib/src/response.dart b/mojo/public/dart/third_party/shelf/lib/src/response.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..79f8994e444a89ba9f08e5cca26be17328027791 |
--- /dev/null |
+++ b/mojo/public/dart/third_party/shelf/lib/src/response.dart |
@@ -0,0 +1,271 @@ |
+// 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:convert'; |
+ |
+import 'package:http_parser/http_parser.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, |
+ Map<String, Object> context}) |
+ : this(200, body: body, headers: headers, encoding: encoding, |
+ context: context); |
+ |
+ /// 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, Map<String, Object> context}) |
+ : this._redirect(301, location, body, headers, encoding, |
+ context: context); |
+ |
+ /// 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, Map<String, Object> context}) |
+ : this._redirect(302, location, body, headers, encoding, |
+ context: context); |
+ |
+ /// 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, Map<String, Object> context}) |
+ : this._redirect(303, location, body, headers, encoding, |
+ context: context); |
+ |
+ /// Constructs a helper constructor for redirect responses. |
+ Response._redirect(int statusCode, location, body, |
+ Map<String, String> headers, Encoding encoding, |
+ { Map<String, Object> context }) |
+ : this(statusCode, |
+ body: body, |
+ encoding: encoding, |
+ headers: addHeader( |
+ headers, 'location', _locationToString(location)), |
+ context: context); |
+ |
+ /// 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, |
+ Map<String, Object> context}) |
+ : this(304, headers: addHeader( |
+ headers, 'date', formatHttpDate(new DateTime.now())), |
+ context: context); |
+ |
+ /// Constructs a 403 Forbidden response. |
+ /// |
+ /// This indicates that the server is refusing to fulfill the request. |
+ /// |
+ /// [body] is the response body. It may be a [String], a [Stream<List<int>>], |
+ /// or `null`. If it's a [String], [encoding] is used to encode it to a |
+ /// [Stream<List<int>>]. The default encoding is UTF-8. If it's `null` or not |
+ /// passed, a default error message is used. |
+ /// |
+ /// 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, Map<String, Object> context}) |
+ : this(403, |
+ headers: body == null ? _adjustErrorHeaders(headers) : headers, |
+ body: body == null ? 'Forbidden' : body, |
+ context: context); |
+ |
+ /// 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 a [String], a [Stream<List<int>>], |
+ /// or `null`. If it's a [String], [encoding] is used to encode it to a |
+ /// [Stream<List<int>>]. The default encoding is UTF-8. If it's `null` or not |
+ /// passed, a default error message is used. |
+ /// |
+ /// 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, |
+ Map<String, Object> context}) |
+ : this(404, |
+ headers: body == null ? _adjustErrorHeaders(headers) : headers, |
+ body: body == null ? 'Not Found' : body, |
+ context: context); |
+ |
+ /// 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 a [String], a [Stream<List<int>>], |
+ /// or `null`. If it's a [String], [encoding] is used to encode it to a |
+ /// [Stream<List<int>>]. The default encoding is UTF-8. If it's `null` or not |
+ /// passed, a default error message is used. |
+ /// |
+ /// 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, Map<String, Object> context}) |
+ : this(500, |
+ headers: body == null ? _adjustErrorHeaders(headers) : headers, |
+ body: body == null ? 'Internal Server Error' : body, |
+ context: context); |
+ |
+ /// 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 a [String], [encoding] is used to encode it to a |
+ /// [Stream<List<int>>]. The default encoding is 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, Map<String, Object> context}) |
+ : super(body, encoding: encoding, headers: headers, context: context) { |
+ if (statusCode < 100) { |
+ throw new ArgumentError("Invalid status code: $statusCode."); |
+ } |
+ } |
+ |
+ /// Creates a new [Response] by copying existing values and applying specified |
+ /// changes. |
+ /// |
+ /// New key-value pairs in [context] and [headers] will be added to the copied |
+ /// [Response]. |
+ /// |
+ /// If [context] or [headers] includes a key that already exists, the |
+ /// key-value pair will replace the corresponding entry in the copied |
+ /// [Response]. |
+ /// |
+ /// All other context and header values from the [Response] will be included |
+ /// in the copied [Response] unchanged. |
+ /// |
+ /// [body] is the request body. It may be either a [String] or a |
+ /// [Stream<List<int>>]. |
+ Response change( |
+ {Map<String, String> headers, Map<String, Object> context, body}) { |
+ headers = updateMap(this.headers, headers); |
+ context = updateMap(this.context, context); |
+ |
+ if (body == null) body = getBody(this); |
+ |
+ return new Response(this.statusCode, body: body, headers: headers, |
+ context: context); |
+ } |
+} |
+ |
+/// 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> _adjustErrorHeaders(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".'); |
+} |