Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library shelf.request; | 5 library shelf.request; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | |
| 9 | 8 |
| 10 // TODO(kevmoo): use UnmodifiableMapView from SDK once 1.4 ships | |
| 11 import 'package:collection/wrappers.dart' as pc; | |
| 12 import 'package:http_parser/http_parser.dart'; | 9 import 'package:http_parser/http_parser.dart'; |
| 13 import 'package:path/path.dart' as p; | 10 import 'package:path/path.dart' as p; |
| 14 | 11 |
| 15 import 'message.dart'; | 12 import 'message.dart'; |
| 16 | 13 |
| 17 /// Represents an HTTP request to be processed by a Shelf application. | 14 /// Represents an HTTP request to be processed by a Shelf application. |
| 18 class Request extends Message { | 15 class Request extends Message { |
| 16 /// Represents the path, query string, and fragment of the request. | |
| 17 final Uri uri; | |
|
kevmoo
2014/04/08 02:21:05
open for discussion on the name here...
nweiz
2014/04/08 20:23:09
I prefer "url".
Also, this needs a lot more docum
kevmoo
2014/04/08 21:41:13
Done.
| |
| 18 | |
| 19 /// The remainder of the [requestedUri] path designating the virtual | 19 /// The remainder of the [requestedUri] path designating the virtual |
| 20 /// "location" of the request's target within the handler. | 20 /// "location" of the request's target within the handler. |
| 21 /// | 21 /// |
| 22 /// [pathInfo] may be an empty string, if [requestedUri ]targets the handler | 22 /// [pathInfo] may be an empty string, if [requestedUri ]targets the handler |
| 23 /// root and does not have a trailing slash. | 23 /// root and does not have a trailing slash. |
| 24 /// | 24 /// |
| 25 /// [pathInfo] is never null. If it is not empty, it will start with `/`. | 25 /// [pathInfo] is never null. If it is not empty, it will start with `/`. |
| 26 /// | 26 /// |
| 27 /// [scriptName] and [pathInfo] combine to create a valid path that should | 27 /// [scriptName] and [pathInfo] combine to create a valid path that should |
| 28 /// correspond to the [requestedUri] path. | 28 /// correspond to the [requestedUri] path. |
| 29 final String pathInfo; | 29 String get pathInfo => uri.path; |
| 30 | 30 |
| 31 /// The portion of the request URL that follows the "?", if any. | 31 /// The portion of the request URL that follows the "?", if any. |
| 32 final String queryString; | 32 String get queryString => uri.query; |
|
nweiz
2014/04/08 20:23:09
I thought you said you were getting rid of these.
kevmoo
2014/04/08 21:41:13
Kept them around for convenience. Worth while?
nweiz
2014/04/08 22:52:44
I think they're more confusing than they're worth.
| |
| 33 | 33 |
| 34 /// The HTTP request method, such as "GET" or "POST". | 34 /// The HTTP request method, such as "GET" or "POST". |
| 35 final String method; | 35 final String method; |
| 36 | 36 |
| 37 /// The initial portion of the [requestedUri] path that corresponds to the | 37 /// The initial portion of the [requestedUri] path that corresponds to the |
| 38 /// handler. | 38 /// handler. |
| 39 /// | 39 /// |
| 40 /// [scriptName] allows a handler to know its virtual "location". | 40 /// [scriptName] allows a handler to know its virtual "location". |
| 41 /// | 41 /// |
| 42 /// If the handler corresponds to the "root" of a server, it will be an | 42 /// If the handler corresponds to the "root" of a server, it will be an |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 59 /// This is parsed from the If-Modified-Since header in [headers]. If | 59 /// This is parsed from the If-Modified-Since header in [headers]. If |
| 60 /// [headers] doesn't have an If-Modified-Since header, this will be `null`. | 60 /// [headers] doesn't have an If-Modified-Since header, this will be `null`. |
| 61 DateTime get ifModifiedSince { | 61 DateTime get ifModifiedSince { |
| 62 if (_ifModifiedSinceCache != null) return _ifModifiedSinceCache; | 62 if (_ifModifiedSinceCache != null) return _ifModifiedSinceCache; |
| 63 if (!headers.containsKey('if-modified-since')) return null; | 63 if (!headers.containsKey('if-modified-since')) return null; |
| 64 _ifModifiedSinceCache = parseHttpDate(headers['if-modified-since']); | 64 _ifModifiedSinceCache = parseHttpDate(headers['if-modified-since']); |
| 65 return _ifModifiedSinceCache; | 65 return _ifModifiedSinceCache; |
| 66 } | 66 } |
| 67 DateTime _ifModifiedSinceCache; | 67 DateTime _ifModifiedSinceCache; |
| 68 | 68 |
| 69 Request(this.pathInfo, String queryString, this.method, | 69 /// Creats a new [Request]. |
|
nweiz
2014/04/08 20:23:09
"Creats" -> "Creates"
kevmoo
2014/04/08 21:41:13
Done.
| |
| 70 this.scriptName, this.protocolVersion, this.requestedUri, | 70 /// |
| 71 Map<String, String> headers, {Stream<List<int>> body}) | 71 /// If [uri] and [scriptName] are omitted, they are inferred from |
| 72 : this.queryString = queryString == null ? '' : queryString, | 72 /// [requestedUri]. |
| 73 super(new pc.UnmodifiableMapView(new HashMap.from(headers)), | 73 /// |
| 74 body == null ? new Stream.fromIterable([]) : body) { | 74 /// Setting one of [uri] or [scriptName] and not the other will throw an |
| 75 /// [ArgumentError]. | |
| 76 // TODO(kevmoo) finish documenting the rest of the arguments. | |
|
kevmoo
2014/04/08 02:21:05
Yes, there needs to be more here -- but this is an
| |
| 77 Request(this.protocolVersion, this.method, Map<String, String> headers, | |
|
nweiz
2014/04/08 20:23:09
I feel like [protocolVersion] should be named as w
kevmoo
2014/04/08 21:41:13
Done.
| |
| 78 Uri requestedUri, {Uri uri, String scriptName, Stream<List<int>> body}) | |
| 79 : this.requestedUri = requestedUri, | |
| 80 this.uri = _getUri(requestedUri, uri, scriptName), | |
|
nweiz
2014/04/08 20:23:09
Nit: I'd use "compute" rather than "get" here and
kevmoo
2014/04/08 21:41:13
Done.
| |
| 81 this.scriptName = _getScriptName(requestedUri, uri, scriptName), | |
| 82 super(headers, body == null ? new Stream.fromIterable([]) : body) { | |
| 75 if (method.isEmpty) throw new ArgumentError('method cannot be empty.'); | 83 if (method.isEmpty) throw new ArgumentError('method cannot be empty.'); |
| 76 | 84 |
| 77 if (scriptName.isNotEmpty && !scriptName.startsWith('/')) { | 85 // TODO(kevmoo) use isAbsolute property on Uri once Issue 18053 is fixed |
| 86 if (requestedUri.scheme.isEmpty) { | |
| 87 throw new ArgumentError('requstedUri must be an absolute URI'); | |
|
nweiz
2014/04/08 20:23:09
Period at the end of the error message, here and b
kevmoo
2014/04/08 21:41:13
Done.
| |
| 88 } | |
| 89 | |
| 90 if (this.scriptName.isNotEmpty && !this.scriptName.startsWith('/')) { | |
| 78 throw new ArgumentError('scriptName must be empty or start with "/".'); | 91 throw new ArgumentError('scriptName must be empty or start with "/".'); |
| 79 } | 92 } |
| 80 | 93 |
| 81 if (scriptName == '/') { | 94 if (this.scriptName == '/') { |
| 82 throw new ArgumentError( | 95 throw new ArgumentError( |
| 83 'scriptName can never be "/". It should be empty instead.'); | 96 'scriptName can never be "/". It should be empty instead.'); |
| 84 } | 97 } |
| 85 | 98 |
| 86 if (scriptName.endsWith('/')) { | 99 if (this.scriptName.endsWith('/')) { |
| 87 throw new ArgumentError('scriptName must not end with "/".'); | 100 throw new ArgumentError('scriptName must not end with "/".'); |
| 88 } | 101 } |
| 89 | 102 |
| 90 if (pathInfo.isNotEmpty && !pathInfo.startsWith('/')) { | 103 if (this.pathInfo.isNotEmpty && !this.pathInfo.startsWith('/')) { |
| 91 throw new ArgumentError('pathInfo must be empty or start with "/".'); | 104 throw new ArgumentError('pathInfo must be empty or start with "/".'); |
| 92 } | 105 } |
| 93 | 106 |
| 94 if (scriptName.isEmpty && pathInfo.isEmpty) { | 107 if (this.scriptName.isEmpty && this.pathInfo.isEmpty) { |
| 95 throw new ArgumentError('scriptName and pathInfo cannot both be empty.'); | 108 throw new ArgumentError('scriptName and pathInfo cannot both be empty.'); |
| 96 } | 109 } |
| 97 } | 110 } |
| 98 | 111 |
| 99 /// Convenience property to access [pathInfo] data as a [List]. | 112 /// Convenience property to access [pathInfo] data as a [List]. |
| 100 List<String> get pathSegments { | 113 List<String> get pathSegments { |
| 101 var segs = p.url.split(pathInfo); | 114 var segs = p.url.split(pathInfo); |
| 102 if (segs.length > 0) { | 115 if (segs.length > 0) { |
| 103 assert(segs.first == p.url.separator); | 116 assert(segs.first == p.url.separator); |
| 104 segs.removeAt(0); | 117 segs.removeAt(0); |
| 105 } | 118 } |
| 106 return segs; | 119 return segs; |
| 107 } | 120 } |
| 108 } | 121 } |
| 122 | |
| 123 Uri _getUri(Uri requestedUri, Uri uri, String scriptName) { | |
|
nweiz
2014/04/08 20:23:09
Document these.
kevmoo
2014/04/08 21:41:13
Done.
| |
| 124 if (uri == null && scriptName == null) { | |
| 125 return new Uri(path: requestedUri.path, query: requestedUri.query, | |
| 126 fragment: requestedUri.fragment); | |
| 127 } else if (uri != null && scriptName != null) { | |
| 128 // TODO(kevmoo) use isAbsolute property on Uri once Issue 18053 is fixed | |
| 129 if (uri.scheme.isNotEmpty) throw new ArgumentError('uri must be relative'); | |
| 130 return uri; | |
| 131 } | |
| 132 throw new ArgumentError( | |
| 133 'pathInfo and scriptName must both be null or both be set'); | |
|
nweiz
2014/04/08 20:23:09
Nit: It's confusing that you start out using if/el
kevmoo
2014/04/08 21:41:13
Done.
| |
| 134 } | |
| 135 | |
| 136 String _getScriptName(Uri requstedUri, Uri pathInfo, String scriptName) { | |
| 137 if (pathInfo == null && scriptName == null) { | |
| 138 return ''; | |
| 139 } else if (pathInfo != null && scriptName != null) { | |
| 140 return scriptName; | |
| 141 } | |
| 142 throw new ArgumentError( | |
| 143 'pathInfo and scriptName must both be null or both be set'); | |
| 144 } | |
| OLD | NEW |