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:convert'; |
8 | 9 |
9 import 'package:http_parser/http_parser.dart'; | 10 import 'package:http_parser/http_parser.dart'; |
10 | 11 |
11 import 'hijack_exception.dart'; | 12 import 'hijack_exception.dart'; |
12 import 'message.dart'; | 13 import 'message.dart'; |
13 import 'util.dart'; | 14 import 'util.dart'; |
14 | 15 |
15 /// A callback provided by a Shelf handler that's passed to [Request.hijack]. | 16 /// A callback provided by a Shelf handler that's passed to [Request.hijack]. |
16 typedef void HijackCallback( | 17 typedef void HijackCallback( |
17 Stream<List<int>> stream, StreamSink<List<int>> sink); | 18 Stream<List<int>> stream, StreamSink<List<int>> sink); |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
81 DateTime _ifModifiedSinceCache; | 82 DateTime _ifModifiedSinceCache; |
82 | 83 |
83 /// Creates a new [Request]. | 84 /// Creates a new [Request]. |
84 /// | 85 /// |
85 /// If [url] and [scriptName] are omitted, they are inferred from | 86 /// If [url] and [scriptName] are omitted, they are inferred from |
86 /// [requestedUri]. | 87 /// [requestedUri]. |
87 /// | 88 /// |
88 /// Setting one of [url] or [scriptName] and not the other will throw an | 89 /// Setting one of [url] or [scriptName] and not the other will throw an |
89 /// [ArgumentError]. | 90 /// [ArgumentError]. |
90 /// | 91 /// |
| 92 /// [body] is the request body. It may be either a [String], a |
| 93 /// [Stream<List<int>>], or `null` to indicate no body. |
| 94 /// If it's a [String], [encoding] is used to encode it to a |
| 95 /// [Stream<List<int>>]. The default encoding is UTF-8. |
| 96 /// |
| 97 /// If [encoding] is passed, the "encoding" field of the Content-Type header |
| 98 /// in [headers] will be set appropriately. If there is no existing |
| 99 /// Content-Type header, it will be set to "application/octet-stream". |
| 100 /// |
91 /// The default value for [protocolVersion] is '1.1'. | 101 /// The default value for [protocolVersion] is '1.1'. |
92 /// | 102 /// |
93 /// ## `onHijack` | 103 /// ## `onHijack` |
94 /// | 104 /// |
95 /// [onHijack] allows handlers to take control of the underlying socket for | 105 /// [onHijack] allows handlers to take control of the underlying socket for |
96 /// the request. It should be passed by adapters that can provide access to | 106 /// the request. It should be passed by adapters that can provide access to |
97 /// the bidirectional socket underlying the HTTP connection stream. | 107 /// the bidirectional socket underlying the HTTP connection stream. |
98 /// | 108 /// |
99 /// The [onHijack] callback will only be called once per request. It will be | 109 /// The [onHijack] callback will only be called once per request. It will be |
100 /// passed another callback which takes a byte stream and a byte sink. | 110 /// passed another callback which takes a byte stream and a byte sink. |
101 /// [onHijack] must pass the stream and sink for the connection stream to this | 111 /// [onHijack] must pass the stream and sink for the connection stream to this |
102 /// callback, although it may do so asynchronously. Both parameters may be the | 112 /// callback, although it may do so asynchronously. Both parameters may be the |
103 /// same object. If the user closes the sink, the adapter should ensure that | 113 /// same object. If the user closes the sink, the adapter should ensure that |
104 /// the stream is closed as well. | 114 /// the stream is closed as well. |
105 /// | 115 /// |
106 /// If a request is hijacked, the adapter should expect to receive a | 116 /// If a request is hijacked, the adapter should expect to receive a |
107 /// [HijackException] from the handler. This is a special exception used to | 117 /// [HijackException] from the handler. This is a special exception used to |
108 /// indicate that hijacking has occurred. The adapter should avoid either | 118 /// indicate that hijacking has occurred. The adapter should avoid either |
109 /// sending a response or notifying the user of an error if a | 119 /// sending a response or notifying the user of an error if a |
110 /// [HijackException] is caught. | 120 /// [HijackException] is caught. |
111 /// | 121 /// |
112 /// An adapter can check whether a request was hijacked using [canHijack], | 122 /// An adapter can check whether a request was hijacked using [canHijack], |
113 /// which will be `false` for a hijacked request. The adapter may throw an | 123 /// which will be `false` for a hijacked request. The adapter may throw an |
114 /// error if a [HijackException] is received for a non-hijacked request, or if | 124 /// error if a [HijackException] is received for a non-hijacked request, or if |
115 /// no [HijackException] is received for a hijacked request. | 125 /// no [HijackException] is received for a hijacked request. |
116 /// | 126 /// |
117 /// See also [hijack]. | 127 /// See also [hijack]. |
118 // TODO(kevmoo) finish documenting the rest of the arguments. | 128 // TODO(kevmoo) finish documenting the rest of the arguments. |
119 Request(String method, Uri requestedUri, {String protocolVersion, | 129 Request(String method, Uri requestedUri, {String protocolVersion, |
120 Map<String, String> headers, Uri url, String scriptName, | 130 Map<String, String> headers, Uri url, String scriptName, body, |
121 Stream<List<int>> body, Map<String, Object> context, | 131 Encoding encoding, Map<String, Object> context, |
122 OnHijackCallback onHijack}) | 132 OnHijackCallback onHijack}) |
123 : this._(method, requestedUri, | 133 : this._(method, requestedUri, |
124 protocolVersion: protocolVersion, | 134 protocolVersion: protocolVersion, |
125 headers: headers, | 135 headers: headers, |
126 url: url, | 136 url: url, |
127 scriptName: scriptName, | 137 scriptName: scriptName, |
128 body: body, | 138 body: body, |
| 139 encoding: encoding, |
129 context: context, | 140 context: context, |
130 onHijack: onHijack == null ? null : new _OnHijack(onHijack)); | 141 onHijack: onHijack == null ? null : new _OnHijack(onHijack)); |
131 | 142 |
132 /// This constructor has the same signature as [new Request] except that | 143 /// This constructor has the same signature as [new Request] except that |
133 /// accepts [onHijack] as [_OnHijack]. | 144 /// accepts [onHijack] as [_OnHijack]. |
134 /// | 145 /// |
135 /// Any [Request] created by calling [change] will pass [_onHijack] from the | 146 /// Any [Request] created by calling [change] will pass [_onHijack] from the |
136 /// source [Request] to ensure that [hijack] can only be called once, even | 147 /// source [Request] to ensure that [hijack] can only be called once, even |
137 /// from a changed [Request]. | 148 /// from a changed [Request]. |
138 Request._(this.method, Uri requestedUri, {String protocolVersion, | 149 Request._(this.method, Uri requestedUri, {String protocolVersion, |
139 Map<String, String> headers, Uri url, String scriptName, | 150 Map<String, String> headers, Uri url, String scriptName, body, |
140 Stream<List<int>> body, Map<String, Object> context, _OnHijack onHijack}) | 151 Encoding encoding, Map<String, Object> context, _OnHijack onHijack}) |
141 : this.requestedUri = requestedUri, | 152 : this.requestedUri = requestedUri, |
142 this.protocolVersion = protocolVersion == null | 153 this.protocolVersion = protocolVersion == null |
143 ? '1.1' | 154 ? '1.1' |
144 : protocolVersion, | 155 : protocolVersion, |
145 this.url = _computeUrl(requestedUri, url, scriptName), | 156 this.url = _computeUrl(requestedUri, url, scriptName), |
146 this.scriptName = _computeScriptName(requestedUri, url, scriptName), | 157 this.scriptName = _computeScriptName(requestedUri, url, scriptName), |
147 this._onHijack = onHijack, | 158 this._onHijack = onHijack, |
148 super(body == null ? new Stream.fromIterable([]) : body, | 159 super(body, encoding: encoding, headers: headers, context: context) { |
149 headers: headers, context: context) { | |
150 if (method.isEmpty) throw new ArgumentError('method cannot be empty.'); | 160 if (method.isEmpty) throw new ArgumentError('method cannot be empty.'); |
151 | 161 |
152 if (!requestedUri.isAbsolute) { | 162 if (!requestedUri.isAbsolute) { |
153 throw new ArgumentError('requstedUri must be an absolute URI.'); | 163 throw new ArgumentError('requstedUri must be an absolute URI.'); |
154 } | 164 } |
155 | 165 |
156 // TODO(kevmoo) if defined, check that scriptName is a fully-encoded, valid | 166 // TODO(kevmoo) if defined, check that scriptName is a fully-encoded, valid |
157 // path component | 167 // path component |
158 if (this.scriptName.isNotEmpty && !this.scriptName.startsWith('/')) { | 168 if (this.scriptName.isNotEmpty && !this.scriptName.startsWith('/')) { |
159 throw new ArgumentError('scriptName must be empty or start with "/".'); | 169 throw new ArgumentError('scriptName must be empty or start with "/".'); |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
303 return ''; | 313 return ''; |
304 } | 314 } |
305 | 315 |
306 if (url != null && scriptName != null) { | 316 if (url != null && scriptName != null) { |
307 return scriptName; | 317 return scriptName; |
308 } | 318 } |
309 | 319 |
310 throw new ArgumentError( | 320 throw new ArgumentError( |
311 'url and scriptName must both be null or both be set.'); | 321 'url and scriptName must both be null or both be set.'); |
312 } | 322 } |
OLD | NEW |