| Index: pkg/shelf/lib/src/request.dart
|
| diff --git a/pkg/shelf/lib/src/request.dart b/pkg/shelf/lib/src/request.dart
|
| index 75ef8653e1c23c6126ddc5de822bcfa3396d4fb8..71c4e98200ae070092fbc202380b15bb372610ca 100644
|
| --- a/pkg/shelf/lib/src/request.dart
|
| +++ b/pkg/shelf/lib/src/request.dart
|
| @@ -5,31 +5,24 @@
|
| library shelf.request;
|
|
|
| import 'dart:async';
|
| -import 'dart:collection';
|
|
|
| -// TODO(kevmoo): use UnmodifiableMapView from SDK once 1.4 ships
|
| -import 'package:collection/wrappers.dart' as pc;
|
| import 'package:http_parser/http_parser.dart';
|
| -import 'package:path/path.dart' as p;
|
|
|
| import 'message.dart';
|
|
|
| /// Represents an HTTP request to be processed by a Shelf application.
|
| class Request extends Message {
|
| - /// The remainder of the [requestedUri] path designating the virtual
|
| + /// The remainder of the [requestedUri] path and query designating the virtual
|
| /// "location" of the request's target within the handler.
|
| ///
|
| - /// [pathInfo] may be an empty string, if [requestedUri ]targets the handler
|
| + /// [url] may be an empty, if [requestedUri]targets the handler
|
| /// root and does not have a trailing slash.
|
| ///
|
| - /// [pathInfo] is never null. If it is not empty, it will start with `/`.
|
| + /// [url] is never null. If it is not empty, it will start with `/`.
|
| ///
|
| - /// [scriptName] and [pathInfo] combine to create a valid path that should
|
| + /// [scriptName] and [url] combine to create a valid path that should
|
| /// correspond to the [requestedUri] path.
|
| - final String pathInfo;
|
| -
|
| - /// The portion of the request URL that follows the "?", if any.
|
| - final String queryString;
|
| + final Uri url;
|
|
|
| /// The HTTP request method, such as "GET" or "POST".
|
| final String method;
|
| @@ -42,7 +35,7 @@ class Request extends Message {
|
| /// If the handler corresponds to the "root" of a server, it will be an
|
| /// empty string, otherwise it will start with a `/`
|
| ///
|
| - /// [scriptName] and [pathInfo] combine to create a valid path that should
|
| + /// [scriptName] and [url] combine to create a valid path that should
|
| /// correspond to the [requestedUri] path.
|
| final String scriptName;
|
|
|
| @@ -66,43 +59,95 @@ class Request extends Message {
|
| }
|
| DateTime _ifModifiedSinceCache;
|
|
|
| - Request(this.pathInfo, String queryString, this.method,
|
| - this.scriptName, this.protocolVersion, this.requestedUri,
|
| - Map<String, String> headers, {Stream<List<int>> body})
|
| - : this.queryString = queryString == null ? '' : queryString,
|
| - super(new pc.UnmodifiableMapView(new HashMap.from(headers)),
|
| - body == null ? new Stream.fromIterable([]) : body) {
|
| + /// Creates a new [Request].
|
| + ///
|
| + /// If [url] and [scriptName] are omitted, they are inferred from
|
| + /// [requestedUri].
|
| + ///
|
| + /// Setting one of [url] or [scriptName] and not the other will throw an
|
| + /// [ArgumentError].
|
| + ///
|
| + /// The default value for [protocolVersion] is '1.1'.
|
| + // TODO(kevmoo) finish documenting the rest of the arguments.
|
| + Request(this.method, Uri requestedUri, {String protocolVersion,
|
| + Map<String, String> headers, Uri url, String scriptName,
|
| + Stream<List<int>> body})
|
| + : this.requestedUri = requestedUri,
|
| + this.protocolVersion = protocolVersion == null ?
|
| + '1.1' : protocolVersion,
|
| + this.url = _computeUrl(requestedUri, url, scriptName),
|
| + this.scriptName = _computeScriptName(requestedUri, url, scriptName),
|
| + super(body == null ? new Stream.fromIterable([]) : body,
|
| + headers: headers) {
|
| if (method.isEmpty) throw new ArgumentError('method cannot be empty.');
|
|
|
| - if (scriptName.isNotEmpty && !scriptName.startsWith('/')) {
|
| + // TODO(kevmoo) use isAbsolute property on Uri once Issue 18053 is fixed
|
| + if (requestedUri.scheme.isEmpty) {
|
| + throw new ArgumentError('requstedUri must be an absolute URI.');
|
| + }
|
| +
|
| + if (this.scriptName.isNotEmpty && !this.scriptName.startsWith('/')) {
|
| throw new ArgumentError('scriptName must be empty or start with "/".');
|
| }
|
|
|
| - if (scriptName == '/') {
|
| + if (this.scriptName == '/') {
|
| throw new ArgumentError(
|
| 'scriptName can never be "/". It should be empty instead.');
|
| }
|
|
|
| - if (scriptName.endsWith('/')) {
|
| + if (this.scriptName.endsWith('/')) {
|
| throw new ArgumentError('scriptName must not end with "/".');
|
| }
|
|
|
| - if (pathInfo.isNotEmpty && !pathInfo.startsWith('/')) {
|
| - throw new ArgumentError('pathInfo must be empty or start with "/".');
|
| + if (this.url.path.isNotEmpty && !this.url.path.startsWith('/')) {
|
| + throw new ArgumentError('url must be empty or start with "/".');
|
| }
|
|
|
| - if (scriptName.isEmpty && pathInfo.isEmpty) {
|
| - throw new ArgumentError('scriptName and pathInfo cannot both be empty.');
|
| + if (this.scriptName.isEmpty && this.url.path.isEmpty) {
|
| + throw new ArgumentError('scriptName and url cannot both be empty.');
|
| }
|
| }
|
| +}
|
|
|
| - /// Convenience property to access [pathInfo] data as a [List].
|
| - List<String> get pathSegments {
|
| - var segs = p.url.split(pathInfo);
|
| - if (segs.length > 0) {
|
| - assert(segs.first == p.url.separator);
|
| - segs.removeAt(0);
|
| - }
|
| - return segs;
|
| +/// Computes `url` from the provided [Request] constructor arguments.
|
| +///
|
| +/// If [url] and [scriptName] are `null`, infer value from [requestedUrl],
|
| +/// otherwise return [url].
|
| +///
|
| +/// If [url] is provided, but [scriptName] is omitted, throws an
|
| +/// [ArgumentError].
|
| +Uri _computeUrl(Uri requestedUri, Uri url, String scriptName) {
|
| + if (url == null && scriptName == null) {
|
| + return new Uri(path: requestedUri.path, query: requestedUri.query,
|
| + fragment: requestedUri.fragment);
|
| }
|
| +
|
| + if (url != null && scriptName != null) {
|
| + // TODO(kevmoo) use isAbsolute property on Uri once Issue 18053 is fixed
|
| + if (url.scheme.isNotEmpty) throw new ArgumentError('url must be relative.');
|
| + return url;
|
| + }
|
| +
|
| + throw new ArgumentError(
|
| + 'url and scriptName must both be null or both be set.');
|
| +}
|
| +
|
| +/// Computes `scriptName` from the provided [Request] constructor arguments.
|
| +///
|
| +/// If [url] and [scriptName] are `null` it returns an empty string, otherwise
|
| +/// [scriptName] is returned.
|
| +///
|
| +/// If [script] is provided, but [url] is omitted, throws an
|
| +/// [ArgumentError].
|
| +String _computeScriptName(Uri requstedUri, Uri url, String scriptName) {
|
| + if (url == null && scriptName == null) {
|
| + return '';
|
| + }
|
| +
|
| + if (url != null && scriptName != null) {
|
| + return scriptName;
|
| + }
|
| +
|
| + throw new ArgumentError(
|
| + 'url and scriptName must both be null or both be set.');
|
| }
|
|
|