OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of dart.io; | 5 part of dart.io; |
6 | 6 |
7 const int _OUTGOING_BUFFER_SIZE = 8 * 1024; | 7 const int _OUTGOING_BUFFER_SIZE = 8 * 1024; |
8 | 8 |
9 typedef void _BytesConsumer(List<int> bytes); | 9 typedef void _BytesConsumer(List<int> bytes); |
10 | 10 |
(...skipping 20 matching lines...) Expand all Loading... |
31 | 31 |
32 // The transfer length if the length of the message body as it | 32 // The transfer length if the length of the message body as it |
33 // appears in the message (RFC 2616 section 4.4). This can be -1 if | 33 // appears in the message (RFC 2616 section 4.4). This can be -1 if |
34 // the length of the massage body is not known due to transfer | 34 // the length of the massage body is not known due to transfer |
35 // codings. | 35 // codings. |
36 int get transferLength => _transferLength; | 36 int get transferLength => _transferLength; |
37 | 37 |
38 _HttpIncoming(this.headers, this._transferLength, this._stream); | 38 _HttpIncoming(this.headers, this._transferLength, this._stream); |
39 | 39 |
40 StreamSubscription<List<int>> listen(void onData(List<int> event), | 40 StreamSubscription<List<int>> listen(void onData(List<int> event), |
41 {Function onError, | 41 {Function onError, void onDone(), bool cancelOnError}) { |
42 void onDone(), | |
43 bool cancelOnError}) { | |
44 hasSubscriber = true; | 42 hasSubscriber = true; |
45 return _stream | 43 return _stream.handleError((error) { |
46 .handleError((error) { | 44 throw new HttpException(error.message, uri: uri); |
47 throw new HttpException(error.message, uri: uri); | 45 }).listen(onData, |
48 }) | 46 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
49 .listen(onData, | |
50 onError: onError, | |
51 onDone: onDone, | |
52 cancelOnError: cancelOnError); | |
53 } | 47 } |
54 | 48 |
55 // Is completed once all data have been received. | 49 // Is completed once all data have been received. |
56 Future get dataDone => _dataCompleter.future; | 50 Future get dataDone => _dataCompleter.future; |
57 | 51 |
58 void close(bool closing) { | 52 void close(bool closing) { |
59 fullBodyRead = true; | 53 fullBodyRead = true; |
60 hasSubscriber = true; | 54 hasSubscriber = true; |
61 _dataCompleter.complete(closing); | 55 _dataCompleter.complete(closing); |
62 } | 56 } |
63 } | 57 } |
64 | 58 |
65 abstract class _HttpInboundMessage extends Stream<List<int>> { | 59 abstract class _HttpInboundMessage extends Stream<List<int>> { |
66 final _HttpIncoming _incoming; | 60 final _HttpIncoming _incoming; |
67 List<Cookie> _cookies; | 61 List<Cookie> _cookies; |
68 | 62 |
69 _HttpInboundMessage(this._incoming); | 63 _HttpInboundMessage(this._incoming); |
70 | 64 |
71 List<Cookie> get cookies { | 65 List<Cookie> get cookies { |
72 if (_cookies != null) return _cookies; | 66 if (_cookies != null) return _cookies; |
73 return _cookies = headers._parseCookies(); | 67 return _cookies = headers._parseCookies(); |
74 } | 68 } |
75 | 69 |
76 _HttpHeaders get headers => _incoming.headers; | 70 _HttpHeaders get headers => _incoming.headers; |
77 String get protocolVersion => headers.protocolVersion; | 71 String get protocolVersion => headers.protocolVersion; |
78 int get contentLength => headers.contentLength; | 72 int get contentLength => headers.contentLength; |
79 bool get persistentConnection => headers.persistentConnection; | 73 bool get persistentConnection => headers.persistentConnection; |
80 } | 74 } |
81 | 75 |
82 | |
83 class _HttpRequest extends _HttpInboundMessage implements HttpRequest { | 76 class _HttpRequest extends _HttpInboundMessage implements HttpRequest { |
84 final HttpResponse response; | 77 final HttpResponse response; |
85 | 78 |
86 final _HttpServer _httpServer; | 79 final _HttpServer _httpServer; |
87 | 80 |
88 final _HttpConnection _httpConnection; | 81 final _HttpConnection _httpConnection; |
89 | 82 |
90 _HttpSession _session; | 83 _HttpSession _session; |
91 | 84 |
92 Uri _requestedUri; | 85 Uri _requestedUri; |
93 | 86 |
94 _HttpRequest(this.response, _HttpIncoming _incoming, this._httpServer, | 87 _HttpRequest(this.response, _HttpIncoming _incoming, this._httpServer, |
95 this._httpConnection) : super(_incoming) { | 88 this._httpConnection) |
| 89 : super(_incoming) { |
96 if (headers.protocolVersion == "1.1") { | 90 if (headers.protocolVersion == "1.1") { |
97 response.headers | 91 response.headers |
98 ..chunkedTransferEncoding = true | 92 ..chunkedTransferEncoding = true |
99 ..persistentConnection = headers.persistentConnection; | 93 ..persistentConnection = headers.persistentConnection; |
100 } | 94 } |
101 | 95 |
102 if (_httpServer._sessionManagerInstance != null) { | 96 if (_httpServer._sessionManagerInstance != null) { |
103 // Map to session if exists. | 97 // Map to session if exists. |
104 var sessionIds = cookies | 98 var sessionIds = cookies |
105 .where((cookie) => cookie.name.toUpperCase() == _DART_SESSION_ID) | 99 .where((cookie) => cookie.name.toUpperCase() == _DART_SESSION_ID) |
106 .map((cookie) => cookie.value); | 100 .map((cookie) => cookie.value); |
107 for (var sessionId in sessionIds) { | 101 for (var sessionId in sessionIds) { |
108 _session = _httpServer._sessionManager.getSession(sessionId); | 102 _session = _httpServer._sessionManager.getSession(sessionId); |
109 if (_session != null) { | 103 if (_session != null) { |
110 _session._markSeen(); | 104 _session._markSeen(); |
111 break; | 105 break; |
112 } | 106 } |
113 } | 107 } |
114 } | 108 } |
115 } | 109 } |
116 | 110 |
117 StreamSubscription<List<int>> listen(void onData(List<int> event), | 111 StreamSubscription<List<int>> listen(void onData(List<int> event), |
118 {Function onError, | 112 {Function onError, void onDone(), bool cancelOnError}) { |
119 void onDone(), | |
120 bool cancelOnError}) { | |
121 return _incoming.listen(onData, | 113 return _incoming.listen(onData, |
122 onError: onError, | 114 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
123 onDone: onDone, | |
124 cancelOnError: cancelOnError); | |
125 } | 115 } |
126 | 116 |
127 Uri get uri => _incoming.uri; | 117 Uri get uri => _incoming.uri; |
128 | 118 |
129 Uri get requestedUri { | 119 Uri get requestedUri { |
130 if (_requestedUri == null) { | 120 if (_requestedUri == null) { |
131 var proto = headers['x-forwarded-proto']; | 121 var proto = headers['x-forwarded-proto']; |
132 var scheme = proto != null ? proto.first : | 122 var scheme = proto != null |
133 _httpConnection._socket is SecureSocket ? "https" : "http"; | 123 ? proto.first |
| 124 : _httpConnection._socket is SecureSocket ? "https" : "http"; |
134 var hostList = headers['x-forwarded-host']; | 125 var hostList = headers['x-forwarded-host']; |
135 String host; | 126 String host; |
136 if (hostList != null) { | 127 if (hostList != null) { |
137 host = hostList.first; | 128 host = hostList.first; |
138 } else { | 129 } else { |
139 hostList = headers['host']; | 130 hostList = headers['host']; |
140 if (hostList != null) { | 131 if (hostList != null) { |
141 host = hostList.first; | 132 host = hostList.first; |
142 } else { | 133 } else { |
143 host = "${_httpServer.address.host}:${_httpServer.port}"; | 134 host = "${_httpServer.address.host}:${_httpServer.port}"; |
(...skipping 23 matching lines...) Expand all Loading... |
167 | 158 |
168 HttpConnectionInfo get connectionInfo => _httpConnection.connectionInfo; | 159 HttpConnectionInfo get connectionInfo => _httpConnection.connectionInfo; |
169 | 160 |
170 X509Certificate get certificate { | 161 X509Certificate get certificate { |
171 var socket = _httpConnection._socket; | 162 var socket = _httpConnection._socket; |
172 if (socket is SecureSocket) return socket.peerCertificate; | 163 if (socket is SecureSocket) return socket.peerCertificate; |
173 return null; | 164 return null; |
174 } | 165 } |
175 } | 166 } |
176 | 167 |
177 | 168 class _HttpClientResponse extends _HttpInboundMessage |
178 class _HttpClientResponse | 169 implements HttpClientResponse { |
179 extends _HttpInboundMessage implements HttpClientResponse { | |
180 List<RedirectInfo> get redirects => _httpRequest._responseRedirects; | 170 List<RedirectInfo> get redirects => _httpRequest._responseRedirects; |
181 | 171 |
182 // The HttpClient this response belongs to. | 172 // The HttpClient this response belongs to. |
183 final _HttpClient _httpClient; | 173 final _HttpClient _httpClient; |
184 | 174 |
185 // The HttpClientRequest of this response. | 175 // The HttpClientRequest of this response. |
186 final _HttpClientRequest _httpRequest; | 176 final _HttpClientRequest _httpRequest; |
187 | 177 |
188 _HttpClientResponse(_HttpIncoming _incoming, this._httpRequest, | 178 _HttpClientResponse( |
189 this._httpClient) : super(_incoming) { | 179 _HttpIncoming _incoming, this._httpRequest, this._httpClient) |
| 180 : super(_incoming) { |
190 // Set uri for potential exceptions. | 181 // Set uri for potential exceptions. |
191 _incoming.uri = _httpRequest.uri; | 182 _incoming.uri = _httpRequest.uri; |
192 } | 183 } |
193 | 184 |
194 int get statusCode => _incoming.statusCode; | 185 int get statusCode => _incoming.statusCode; |
195 String get reasonPhrase => _incoming.reasonPhrase; | 186 String get reasonPhrase => _incoming.reasonPhrase; |
196 | 187 |
197 X509Certificate get certificate { | 188 X509Certificate get certificate { |
198 var socket = _httpRequest._httpClientConnection._socket; | 189 var socket = _httpRequest._httpClientConnection._socket; |
199 if (socket is SecureSocket) return socket.peerCertificate; | 190 if (socket is SecureSocket) return socket.peerCertificate; |
200 throw new UnsupportedError("Socket is not a SecureSocket"); | 191 throw new UnsupportedError("Socket is not a SecureSocket"); |
201 } | 192 } |
202 | 193 |
203 List<Cookie> get cookies { | 194 List<Cookie> get cookies { |
204 if (_cookies != null) return _cookies; | 195 if (_cookies != null) return _cookies; |
205 _cookies = new List<Cookie>(); | 196 _cookies = new List<Cookie>(); |
206 List<String> values = headers[HttpHeaders.SET_COOKIE]; | 197 List<String> values = headers[HttpHeaders.SET_COOKIE]; |
207 if (values != null) { | 198 if (values != null) { |
208 values.forEach((value) { | 199 values.forEach((value) { |
209 _cookies.add(new Cookie.fromSetCookieValue(value)); | 200 _cookies.add(new Cookie.fromSetCookieValue(value)); |
210 }); | 201 }); |
211 } | 202 } |
212 return _cookies; | 203 return _cookies; |
213 } | 204 } |
214 | 205 |
215 bool get isRedirect { | 206 bool get isRedirect { |
216 if (_httpRequest.method == "GET" || _httpRequest.method == "HEAD") { | 207 if (_httpRequest.method == "GET" || _httpRequest.method == "HEAD") { |
217 return statusCode == HttpStatus.MOVED_PERMANENTLY || | 208 return statusCode == HttpStatus.MOVED_PERMANENTLY || |
218 statusCode == HttpStatus.FOUND || | 209 statusCode == HttpStatus.FOUND || |
219 statusCode == HttpStatus.SEE_OTHER || | 210 statusCode == HttpStatus.SEE_OTHER || |
220 statusCode == HttpStatus.TEMPORARY_REDIRECT; | 211 statusCode == HttpStatus.TEMPORARY_REDIRECT; |
221 } else if (_httpRequest.method == "POST") { | 212 } else if (_httpRequest.method == "POST") { |
222 return statusCode == HttpStatus.SEE_OTHER; | 213 return statusCode == HttpStatus.SEE_OTHER; |
223 } | 214 } |
224 return false; | 215 return false; |
225 } | 216 } |
226 | 217 |
227 Future<HttpClientResponse> redirect([String method, | 218 Future<HttpClientResponse> redirect( |
228 Uri url, | 219 [String method, Uri url, bool followLoops]) { |
229 bool followLoops]) { | |
230 if (method == null) { | 220 if (method == null) { |
231 // Set method as defined by RFC 2616 section 10.3.4. | 221 // Set method as defined by RFC 2616 section 10.3.4. |
232 if (statusCode == HttpStatus.SEE_OTHER && _httpRequest.method == "POST") { | 222 if (statusCode == HttpStatus.SEE_OTHER && _httpRequest.method == "POST") { |
233 method = "GET"; | 223 method = "GET"; |
234 } else { | 224 } else { |
235 method = _httpRequest.method; | 225 method = _httpRequest.method; |
236 } | 226 } |
237 } | 227 } |
238 if (url == null) { | 228 if (url == null) { |
239 String location = headers.value(HttpHeaders.LOCATION); | 229 String location = headers.value(HttpHeaders.LOCATION); |
240 if (location == null) { | 230 if (location == null) { |
241 throw new StateError("Response has no Location header for redirect"); | 231 throw new StateError("Response has no Location header for redirect"); |
242 } | 232 } |
243 url = Uri.parse(location); | 233 url = Uri.parse(location); |
244 } | 234 } |
245 if (followLoops != true) { | 235 if (followLoops != true) { |
246 for (var redirect in redirects) { | 236 for (var redirect in redirects) { |
247 if (redirect.location == url) { | 237 if (redirect.location == url) { |
248 return new Future.error( | 238 return new Future.error( |
249 new RedirectException("Redirect loop detected", redirects)); | 239 new RedirectException("Redirect loop detected", redirects)); |
250 } | 240 } |
251 } | 241 } |
252 } | 242 } |
253 return _httpClient._openUrlFromRequest(method, url, _httpRequest) | 243 return _httpClient |
| 244 ._openUrlFromRequest(method, url, _httpRequest) |
254 .then((request) { | 245 .then((request) { |
255 request._responseRedirects | 246 request._responseRedirects |
256 ..addAll(this.redirects) | 247 ..addAll(this.redirects) |
257 ..add(new _RedirectInfo(statusCode, method, url)); | 248 ..add(new _RedirectInfo(statusCode, method, url)); |
258 return request.close(); | 249 return request.close(); |
259 }); | 250 }); |
260 } | 251 } |
261 | 252 |
262 StreamSubscription<List<int>> listen(void onData(List<int> event), | 253 StreamSubscription<List<int>> listen(void onData(List<int> event), |
263 {Function onError, | 254 {Function onError, void onDone(), bool cancelOnError}) { |
264 void onDone(), | |
265 bool cancelOnError}) { | |
266 if (_incoming.upgraded) { | 255 if (_incoming.upgraded) { |
267 // If upgraded, the connection is already 'removed' form the client. | 256 // If upgraded, the connection is already 'removed' form the client. |
268 // Since listening to upgraded data is 'bogus', simply close and | 257 // Since listening to upgraded data is 'bogus', simply close and |
269 // return empty stream subscription. | 258 // return empty stream subscription. |
270 _httpRequest._httpClientConnection.destroy(); | 259 _httpRequest._httpClientConnection.destroy(); |
271 return new Stream<List<int>>.empty().listen(null, onDone: onDone); | 260 return new Stream<List<int>>.empty().listen(null, onDone: onDone); |
272 } | 261 } |
273 var stream = _incoming; | 262 var stream = _incoming; |
274 if (_httpClient.autoUncompress && | 263 if (_httpClient.autoUncompress && |
275 headers.value(HttpHeaders.CONTENT_ENCODING) == "gzip") { | 264 headers.value(HttpHeaders.CONTENT_ENCODING) == "gzip") { |
276 stream = stream.transform(GZIP.decoder); | 265 stream = stream.transform(GZIP.decoder); |
277 } | 266 } |
278 return stream.listen(onData, | 267 return stream.listen(onData, |
279 onError: onError, | 268 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
280 onDone: onDone, | |
281 cancelOnError: cancelOnError); | |
282 } | 269 } |
283 | 270 |
284 Future<Socket> detachSocket() { | 271 Future<Socket> detachSocket() { |
285 _httpClient._connectionClosed(_httpRequest._httpClientConnection); | 272 _httpClient._connectionClosed(_httpRequest._httpClientConnection); |
286 return _httpRequest._httpClientConnection.detachSocket(); | 273 return _httpRequest._httpClientConnection.detachSocket(); |
287 } | 274 } |
288 | 275 |
289 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; | 276 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; |
290 | 277 |
291 bool get _shouldAuthenticateProxy { | 278 bool get _shouldAuthenticateProxy { |
292 // Only try to authenticate if there is a challenge in the response. | 279 // Only try to authenticate if there is a challenge in the response. |
293 List<String> challenge = headers[HttpHeaders.PROXY_AUTHENTICATE]; | 280 List<String> challenge = headers[HttpHeaders.PROXY_AUTHENTICATE]; |
294 return statusCode == HttpStatus.PROXY_AUTHENTICATION_REQUIRED && | 281 return statusCode == HttpStatus.PROXY_AUTHENTICATION_REQUIRED && |
295 challenge != null && challenge.length == 1; | 282 challenge != null && |
| 283 challenge.length == 1; |
296 } | 284 } |
297 | 285 |
298 bool get _shouldAuthenticate { | 286 bool get _shouldAuthenticate { |
299 // Only try to authenticate if there is a challenge in the response. | 287 // Only try to authenticate if there is a challenge in the response. |
300 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; | 288 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; |
301 return statusCode == HttpStatus.UNAUTHORIZED && | 289 return statusCode == HttpStatus.UNAUTHORIZED && |
302 challenge != null && challenge.length == 1; | 290 challenge != null && |
| 291 challenge.length == 1; |
303 } | 292 } |
304 | 293 |
305 Future<HttpClientResponse> _authenticate(bool proxyAuth) { | 294 Future<HttpClientResponse> _authenticate(bool proxyAuth) { |
306 Future<HttpClientResponse> retry() { | 295 Future<HttpClientResponse> retry() { |
307 // Drain body and retry. | 296 // Drain body and retry. |
308 return drain().then((_) { | 297 return drain().then((_) { |
309 return _httpClient._openUrlFromRequest(_httpRequest.method, | 298 return _httpClient |
310 _httpRequest.uri, | 299 ._openUrlFromRequest( |
311 _httpRequest) | 300 _httpRequest.method, _httpRequest.uri, _httpRequest) |
312 .then((request) => request.close()); | 301 .then((request) => request.close()); |
313 }); | 302 }); |
314 } | 303 } |
315 | 304 |
316 List<String> authChallenge() { | 305 List<String> authChallenge() { |
317 return proxyAuth ? headers[HttpHeaders.PROXY_AUTHENTICATE] | 306 return proxyAuth |
318 : headers[HttpHeaders.WWW_AUTHENTICATE]; | 307 ? headers[HttpHeaders.PROXY_AUTHENTICATE] |
| 308 : headers[HttpHeaders.WWW_AUTHENTICATE]; |
319 } | 309 } |
320 | 310 |
321 _Credentials findCredentials(_AuthenticationScheme scheme) { | 311 _Credentials findCredentials(_AuthenticationScheme scheme) { |
322 return proxyAuth ? _httpClient._findProxyCredentials(_httpRequest._proxy, | 312 return proxyAuth |
323 scheme) | 313 ? _httpClient._findProxyCredentials(_httpRequest._proxy, scheme) |
324 : _httpClient._findCredentials(_httpRequest.uri, scheme); | 314 : _httpClient._findCredentials(_httpRequest.uri, scheme); |
325 } | 315 } |
326 | 316 |
327 void removeCredentials(_Credentials cr) { | 317 void removeCredentials(_Credentials cr) { |
328 if (proxyAuth) { | 318 if (proxyAuth) { |
329 _httpClient._removeProxyCredentials(cr); | 319 _httpClient._removeProxyCredentials(cr); |
330 } else { | 320 } else { |
331 _httpClient._removeCredentials(cr); | 321 _httpClient._removeCredentials(cr); |
332 } | 322 } |
333 } | 323 } |
334 | 324 |
335 Future requestAuthentication(_AuthenticationScheme scheme, String realm) { | 325 Future requestAuthentication(_AuthenticationScheme scheme, String realm) { |
336 if (proxyAuth) { | 326 if (proxyAuth) { |
337 if (_httpClient._authenticateProxy == null) { | 327 if (_httpClient._authenticateProxy == null) { |
338 return new Future.value(false); | 328 return new Future.value(false); |
339 } | 329 } |
340 var proxy = _httpRequest._proxy; | 330 var proxy = _httpRequest._proxy; |
341 return _httpClient._authenticateProxy(proxy.host, | 331 return _httpClient._authenticateProxy( |
342 proxy.port, | 332 proxy.host, proxy.port, scheme.toString(), realm); |
343 scheme.toString(), | |
344 realm); | |
345 } else { | 333 } else { |
346 if (_httpClient._authenticate == null) { | 334 if (_httpClient._authenticate == null) { |
347 return new Future.value(false); | 335 return new Future.value(false); |
348 } | 336 } |
349 return _httpClient._authenticate(_httpRequest.uri, | 337 return _httpClient._authenticate( |
350 scheme.toString(), | 338 _httpRequest.uri, scheme.toString(), realm); |
351 realm); | |
352 } | 339 } |
353 } | 340 } |
354 | 341 |
355 List<String> challenge = authChallenge(); | 342 List<String> challenge = authChallenge(); |
356 assert(challenge != null || challenge.length == 1); | 343 assert(challenge != null || challenge.length == 1); |
357 _HeaderValue header = | 344 _HeaderValue header = |
358 _HeaderValue.parse(challenge[0], parameterSeparator: ","); | 345 _HeaderValue.parse(challenge[0], parameterSeparator: ","); |
359 _AuthenticationScheme scheme = | 346 _AuthenticationScheme scheme = |
360 new _AuthenticationScheme.fromString(header.value); | 347 new _AuthenticationScheme.fromString(header.value); |
361 String realm = header.parameters["realm"]; | 348 String realm = header.parameters["realm"]; |
362 | 349 |
363 // See if any matching credentials are available. | 350 // See if any matching credentials are available. |
364 _Credentials cr = findCredentials(scheme); | 351 _Credentials cr = findCredentials(scheme); |
365 if (cr != null) { | 352 if (cr != null) { |
366 // For basic authentication don't retry already used credentials | 353 // For basic authentication don't retry already used credentials |
367 // as they must have already been added to the request causing | 354 // as they must have already been added to the request causing |
368 // this authenticate response. | 355 // this authenticate response. |
369 if (cr.scheme == _AuthenticationScheme.BASIC && !cr.used) { | 356 if (cr.scheme == _AuthenticationScheme.BASIC && !cr.used) { |
370 // Credentials where found, prepare for retrying the request. | 357 // Credentials where found, prepare for retrying the request. |
371 return retry(); | 358 return retry(); |
372 } | 359 } |
373 | 360 |
374 // Digest authentication only supports the MD5 algorithm. | 361 // Digest authentication only supports the MD5 algorithm. |
375 if (cr.scheme == _AuthenticationScheme.DIGEST && | 362 if (cr.scheme == _AuthenticationScheme.DIGEST && |
376 (header.parameters["algorithm"] == null || | 363 (header.parameters["algorithm"] == null || |
377 header.parameters["algorithm"].toLowerCase() == "md5")) { | 364 header.parameters["algorithm"].toLowerCase() == "md5")) { |
378 if (cr.nonce == null || cr.nonce == header.parameters["nonce"]) { | 365 if (cr.nonce == null || cr.nonce == header.parameters["nonce"]) { |
379 // If the nonce is not set then this is the first authenticate | 366 // If the nonce is not set then this is the first authenticate |
380 // response for these credentials. Set up authentication state. | 367 // response for these credentials. Set up authentication state. |
381 if (cr.nonce == null) { | 368 if (cr.nonce == null) { |
382 cr..nonce = header.parameters["nonce"] | 369 cr |
| 370 ..nonce = header.parameters["nonce"] |
383 ..algorithm = "MD5" | 371 ..algorithm = "MD5" |
384 ..qop = header.parameters["qop"] | 372 ..qop = header.parameters["qop"] |
385 ..nonceCount = 0; | 373 ..nonceCount = 0; |
386 } | 374 } |
387 // Credentials where found, prepare for retrying the request. | 375 // Credentials where found, prepare for retrying the request. |
388 return retry(); | 376 return retry(); |
389 } else if (header.parameters["stale"] != null && | 377 } else if (header.parameters["stale"] != null && |
390 header.parameters["stale"].toLowerCase() == "true") { | 378 header.parameters["stale"].toLowerCase() == "true") { |
391 // If stale is true retry with new nonce. | 379 // If stale is true retry with new nonce. |
392 cr.nonce = header.parameters["nonce"]; | 380 cr.nonce = header.parameters["nonce"]; |
393 // Credentials where found, prepare for retrying the request. | 381 // Credentials where found, prepare for retrying the request. |
394 return retry(); | 382 return retry(); |
395 } | 383 } |
396 } | 384 } |
397 } | 385 } |
398 | 386 |
399 // Ask for more credentials if none found or the one found has | 387 // Ask for more credentials if none found or the one found has |
400 // already been used. If it has already been used it must now be | 388 // already been used. If it has already been used it must now be |
401 // invalid and is removed. | 389 // invalid and is removed. |
402 if (cr != null) { | 390 if (cr != null) { |
403 removeCredentials(cr); | 391 removeCredentials(cr); |
404 cr = null; | 392 cr = null; |
405 } | 393 } |
406 return requestAuthentication(scheme, realm).then((credsAvailable) { | 394 return requestAuthentication(scheme, realm).then((credsAvailable) { |
407 if (credsAvailable) { | 395 if (credsAvailable) { |
408 cr = _httpClient._findCredentials(_httpRequest.uri, scheme); | 396 cr = _httpClient._findCredentials(_httpRequest.uri, scheme); |
409 return retry(); | 397 return retry(); |
410 } else { | 398 } else { |
411 // No credentials available, complete with original response. | 399 // No credentials available, complete with original response. |
412 return this; | 400 return this; |
413 } | 401 } |
414 }); | 402 }); |
415 } | 403 } |
416 } | 404 } |
417 | 405 |
418 | |
419 abstract class _HttpOutboundMessage<T> extends _IOSinkImpl { | 406 abstract class _HttpOutboundMessage<T> extends _IOSinkImpl { |
420 // Used to mark when the body should be written. This is used for HEAD | 407 // Used to mark when the body should be written. This is used for HEAD |
421 // requests and in error handling. | 408 // requests and in error handling. |
422 bool _encodingSet = false; | 409 bool _encodingSet = false; |
423 | 410 |
424 bool _bufferOutput = true; | 411 bool _bufferOutput = true; |
425 | 412 |
426 final Uri _uri; | 413 final Uri _uri; |
427 final _HttpOutgoing _outgoing; | 414 final _HttpOutgoing _outgoing; |
428 | 415 |
429 final _HttpHeaders headers; | 416 final _HttpHeaders headers; |
430 | 417 |
431 _HttpOutboundMessage(Uri uri, | 418 _HttpOutboundMessage(Uri uri, String protocolVersion, _HttpOutgoing outgoing, |
432 String protocolVersion, | 419 {_HttpHeaders initialHeaders}) |
433 _HttpOutgoing outgoing, | |
434 {_HttpHeaders initialHeaders}) | |
435 : _uri = uri, | 420 : _uri = uri, |
436 headers = new _HttpHeaders( | 421 headers = new _HttpHeaders(protocolVersion, |
437 protocolVersion, | 422 defaultPortForScheme: uri.scheme == 'https' |
438 defaultPortForScheme: uri.scheme == 'https' ? | 423 ? HttpClient.DEFAULT_HTTPS_PORT |
439 HttpClient.DEFAULT_HTTPS_PORT : | 424 : HttpClient.DEFAULT_HTTP_PORT, |
440 HttpClient.DEFAULT_HTTP_PORT, | |
441 initialHeaders: initialHeaders), | 425 initialHeaders: initialHeaders), |
442 _outgoing = outgoing, | 426 _outgoing = outgoing, |
443 super(outgoing, null) { | 427 super(outgoing, null) { |
444 _outgoing.outbound = this; | 428 _outgoing.outbound = this; |
445 _encodingMutable = false; | 429 _encodingMutable = false; |
446 } | 430 } |
447 | 431 |
448 int get contentLength => headers.contentLength; | 432 int get contentLength => headers.contentLength; |
449 void set contentLength(int contentLength) { | 433 void set contentLength(int contentLength) { |
450 headers.contentLength = contentLength; | 434 headers.contentLength = contentLength; |
451 } | 435 } |
452 | 436 |
453 bool get persistentConnection => headers.persistentConnection; | 437 bool get persistentConnection => headers.persistentConnection; |
454 void set persistentConnection(bool p) { | 438 void set persistentConnection(bool p) { |
455 headers.persistentConnection = p; | 439 headers.persistentConnection = p; |
456 } | 440 } |
457 | 441 |
458 bool get bufferOutput => _bufferOutput; | 442 bool get bufferOutput => _bufferOutput; |
459 void set bufferOutput(bool bufferOutput) { | 443 void set bufferOutput(bool bufferOutput) { |
460 if (_outgoing.headersWritten) throw new StateError("Header already sent"); | 444 if (_outgoing.headersWritten) throw new StateError("Header already sent"); |
461 _bufferOutput = bufferOutput; | 445 _bufferOutput = bufferOutput; |
462 } | 446 } |
463 | 447 |
464 | |
465 Encoding get encoding { | 448 Encoding get encoding { |
466 if (_encodingSet && _outgoing.headersWritten) { | 449 if (_encodingSet && _outgoing.headersWritten) { |
467 return _encoding; | 450 return _encoding; |
468 } | 451 } |
469 var charset; | 452 var charset; |
470 if (headers.contentType != null && headers.contentType.charset != null) { | 453 if (headers.contentType != null && headers.contentType.charset != null) { |
471 charset = headers.contentType.charset; | 454 charset = headers.contentType.charset; |
472 } else { | 455 } else { |
473 charset = "iso-8859-1"; | 456 charset = "iso-8859-1"; |
474 } | 457 } |
(...skipping 11 matching lines...) Expand all Loading... |
486 _encodingSet = true; | 469 _encodingSet = true; |
487 } | 470 } |
488 super.write(obj); | 471 super.write(obj); |
489 } | 472 } |
490 | 473 |
491 void _writeHeader(); | 474 void _writeHeader(); |
492 | 475 |
493 bool get _isConnectionClosed => false; | 476 bool get _isConnectionClosed => false; |
494 } | 477 } |
495 | 478 |
496 | |
497 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> | 479 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> |
498 implements HttpResponse { | 480 implements HttpResponse { |
499 int _statusCode = 200; | 481 int _statusCode = 200; |
500 String _reasonPhrase; | 482 String _reasonPhrase; |
501 List<Cookie> _cookies; | 483 List<Cookie> _cookies; |
502 _HttpRequest _httpRequest; | 484 _HttpRequest _httpRequest; |
503 Duration _deadline; | 485 Duration _deadline; |
504 Timer _deadlineTimer; | 486 Timer _deadlineTimer; |
505 | 487 |
506 _HttpResponse(Uri uri, | 488 _HttpResponse(Uri uri, String protocolVersion, _HttpOutgoing outgoing, |
507 String protocolVersion, | 489 HttpHeaders defaultHeaders, String serverHeader) |
508 _HttpOutgoing outgoing, | |
509 HttpHeaders defaultHeaders, | |
510 String serverHeader) | |
511 : super(uri, protocolVersion, outgoing, initialHeaders: defaultHeaders) { | 490 : super(uri, protocolVersion, outgoing, initialHeaders: defaultHeaders) { |
512 if (serverHeader != null) headers.set('server', serverHeader); | 491 if (serverHeader != null) headers.set('server', serverHeader); |
513 } | 492 } |
514 | 493 |
515 bool get _isConnectionClosed => _httpRequest._httpConnection._isClosing; | 494 bool get _isConnectionClosed => _httpRequest._httpConnection._isClosing; |
516 | 495 |
517 List<Cookie> get cookies { | 496 List<Cookie> get cookies { |
518 if (_cookies == null) _cookies = new List<Cookie>(); | 497 if (_cookies == null) _cookies = new List<Cookie>(); |
519 return _cookies; | 498 return _cookies; |
520 } | 499 } |
(...skipping 12 matching lines...) Expand all Loading... |
533 | 512 |
534 Future redirect(Uri location, {int status: HttpStatus.MOVED_TEMPORARILY}) { | 513 Future redirect(Uri location, {int status: HttpStatus.MOVED_TEMPORARILY}) { |
535 if (_outgoing.headersWritten) throw new StateError("Header already sent"); | 514 if (_outgoing.headersWritten) throw new StateError("Header already sent"); |
536 statusCode = status; | 515 statusCode = status; |
537 headers.set("location", location.toString()); | 516 headers.set("location", location.toString()); |
538 return close(); | 517 return close(); |
539 } | 518 } |
540 | 519 |
541 Future<Socket> detachSocket({bool writeHeaders: true}) { | 520 Future<Socket> detachSocket({bool writeHeaders: true}) { |
542 if (_outgoing.headersWritten) throw new StateError("Headers already sent"); | 521 if (_outgoing.headersWritten) throw new StateError("Headers already sent"); |
543 deadline = null; // Be sure to stop any deadline. | 522 deadline = null; // Be sure to stop any deadline. |
544 var future = _httpRequest._httpConnection.detachSocket(); | 523 var future = _httpRequest._httpConnection.detachSocket(); |
545 if (writeHeaders) { | 524 if (writeHeaders) { |
546 var headersFuture = _outgoing.writeHeaders(drainRequest: false, | 525 var headersFuture = |
547 setOutgoing: false); | 526 _outgoing.writeHeaders(drainRequest: false, setOutgoing: false); |
548 assert(headersFuture == null); | 527 assert(headersFuture == null); |
549 } else { | 528 } else { |
550 // Imitate having written the headers. | 529 // Imitate having written the headers. |
551 _outgoing.headersWritten = true; | 530 _outgoing.headersWritten = true; |
552 } | 531 } |
553 // Close connection so the socket is 'free'. | 532 // Close connection so the socket is 'free'. |
554 close(); | 533 close(); |
555 done.catchError((_) { | 534 done.catchError((_) { |
556 // Catch any error on done, as they automatically will be | 535 // Catch any error on done, as they automatically will be |
557 // propagated to the websocket. | 536 // propagated to the websocket. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
591 | 570 |
592 var session = _httpRequest._session; | 571 var session = _httpRequest._session; |
593 if (session != null && !session._destroyed) { | 572 if (session != null && !session._destroyed) { |
594 // Mark as not new. | 573 // Mark as not new. |
595 session._isNew = false; | 574 session._isNew = false; |
596 // Make sure we only send the current session id. | 575 // Make sure we only send the current session id. |
597 bool found = false; | 576 bool found = false; |
598 for (int i = 0; i < cookies.length; i++) { | 577 for (int i = 0; i < cookies.length; i++) { |
599 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { | 578 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { |
600 cookies[i] | 579 cookies[i] |
601 ..value = session.id | 580 ..value = session.id |
602 ..httpOnly = true | 581 ..httpOnly = true |
603 ..path = "/"; | 582 ..path = "/"; |
604 found = true; | 583 found = true; |
605 } | 584 } |
606 } | 585 } |
607 if (!found) { | 586 if (!found) { |
608 var cookie = new Cookie(_DART_SESSION_ID, session.id); | 587 var cookie = new Cookie(_DART_SESSION_ID, session.id); |
609 cookies.add(cookie | 588 cookies.add(cookie |
610 ..httpOnly = true | 589 ..httpOnly = true |
611 ..path = "/"); | 590 ..path = "/"); |
612 } | 591 } |
613 } | 592 } |
614 // Add all the cookies set to the headers. | 593 // Add all the cookies set to the headers. |
615 if (_cookies != null) { | 594 if (_cookies != null) { |
616 _cookies.forEach((cookie) { | 595 _cookies.forEach((cookie) { |
617 headers.add(HttpHeaders.SET_COOKIE, cookie); | 596 headers.add(HttpHeaders.SET_COOKIE, cookie); |
618 }); | 597 }); |
619 } | 598 } |
620 | 599 |
621 headers._finalize(); | 600 headers._finalize(); |
622 | 601 |
623 // Write headers. | 602 // Write headers. |
624 headers._build(buffer); | 603 headers._build(buffer); |
625 buffer.addByte(_CharCode.CR); | 604 buffer.addByte(_CharCode.CR); |
626 buffer.addByte(_CharCode.LF); | 605 buffer.addByte(_CharCode.LF); |
627 Uint8List headerBytes = buffer.takeBytes(); | 606 Uint8List headerBytes = buffer.takeBytes(); |
628 _outgoing.setHeader(headerBytes, headerBytes.length); | 607 _outgoing.setHeader(headerBytes, headerBytes.length); |
629 } | 608 } |
630 | 609 |
631 String _findReasonPhrase(int statusCode) { | 610 String _findReasonPhrase(int statusCode) { |
632 if (_reasonPhrase != null) { | 611 if (_reasonPhrase != null) { |
633 return _reasonPhrase; | 612 return _reasonPhrase; |
634 } | 613 } |
635 | 614 |
636 switch (statusCode) { | 615 switch (statusCode) { |
637 case HttpStatus.CONTINUE: return "Continue"; | 616 case HttpStatus.CONTINUE: |
638 case HttpStatus.SWITCHING_PROTOCOLS: return "Switching Protocols"; | 617 return "Continue"; |
639 case HttpStatus.OK: return "OK"; | 618 case HttpStatus.SWITCHING_PROTOCOLS: |
640 case HttpStatus.CREATED: return "Created"; | 619 return "Switching Protocols"; |
641 case HttpStatus.ACCEPTED: return "Accepted"; | 620 case HttpStatus.OK: |
| 621 return "OK"; |
| 622 case HttpStatus.CREATED: |
| 623 return "Created"; |
| 624 case HttpStatus.ACCEPTED: |
| 625 return "Accepted"; |
642 case HttpStatus.NON_AUTHORITATIVE_INFORMATION: | 626 case HttpStatus.NON_AUTHORITATIVE_INFORMATION: |
643 return "Non-Authoritative Information"; | 627 return "Non-Authoritative Information"; |
644 case HttpStatus.NO_CONTENT: return "No Content"; | 628 case HttpStatus.NO_CONTENT: |
645 case HttpStatus.RESET_CONTENT: return "Reset Content"; | 629 return "No Content"; |
646 case HttpStatus.PARTIAL_CONTENT: return "Partial Content"; | 630 case HttpStatus.RESET_CONTENT: |
647 case HttpStatus.MULTIPLE_CHOICES: return "Multiple Choices"; | 631 return "Reset Content"; |
648 case HttpStatus.MOVED_PERMANENTLY: return "Moved Permanently"; | 632 case HttpStatus.PARTIAL_CONTENT: |
649 case HttpStatus.FOUND: return "Found"; | 633 return "Partial Content"; |
650 case HttpStatus.SEE_OTHER: return "See Other"; | 634 case HttpStatus.MULTIPLE_CHOICES: |
651 case HttpStatus.NOT_MODIFIED: return "Not Modified"; | 635 return "Multiple Choices"; |
652 case HttpStatus.USE_PROXY: return "Use Proxy"; | 636 case HttpStatus.MOVED_PERMANENTLY: |
653 case HttpStatus.TEMPORARY_REDIRECT: return "Temporary Redirect"; | 637 return "Moved Permanently"; |
654 case HttpStatus.BAD_REQUEST: return "Bad Request"; | 638 case HttpStatus.FOUND: |
655 case HttpStatus.UNAUTHORIZED: return "Unauthorized"; | 639 return "Found"; |
656 case HttpStatus.PAYMENT_REQUIRED: return "Payment Required"; | 640 case HttpStatus.SEE_OTHER: |
657 case HttpStatus.FORBIDDEN: return "Forbidden"; | 641 return "See Other"; |
658 case HttpStatus.NOT_FOUND: return "Not Found"; | 642 case HttpStatus.NOT_MODIFIED: |
659 case HttpStatus.METHOD_NOT_ALLOWED: return "Method Not Allowed"; | 643 return "Not Modified"; |
660 case HttpStatus.NOT_ACCEPTABLE: return "Not Acceptable"; | 644 case HttpStatus.USE_PROXY: |
| 645 return "Use Proxy"; |
| 646 case HttpStatus.TEMPORARY_REDIRECT: |
| 647 return "Temporary Redirect"; |
| 648 case HttpStatus.BAD_REQUEST: |
| 649 return "Bad Request"; |
| 650 case HttpStatus.UNAUTHORIZED: |
| 651 return "Unauthorized"; |
| 652 case HttpStatus.PAYMENT_REQUIRED: |
| 653 return "Payment Required"; |
| 654 case HttpStatus.FORBIDDEN: |
| 655 return "Forbidden"; |
| 656 case HttpStatus.NOT_FOUND: |
| 657 return "Not Found"; |
| 658 case HttpStatus.METHOD_NOT_ALLOWED: |
| 659 return "Method Not Allowed"; |
| 660 case HttpStatus.NOT_ACCEPTABLE: |
| 661 return "Not Acceptable"; |
661 case HttpStatus.PROXY_AUTHENTICATION_REQUIRED: | 662 case HttpStatus.PROXY_AUTHENTICATION_REQUIRED: |
662 return "Proxy Authentication Required"; | 663 return "Proxy Authentication Required"; |
663 case HttpStatus.REQUEST_TIMEOUT: return "Request Time-out"; | 664 case HttpStatus.REQUEST_TIMEOUT: |
664 case HttpStatus.CONFLICT: return "Conflict"; | 665 return "Request Time-out"; |
665 case HttpStatus.GONE: return "Gone"; | 666 case HttpStatus.CONFLICT: |
666 case HttpStatus.LENGTH_REQUIRED: return "Length Required"; | 667 return "Conflict"; |
667 case HttpStatus.PRECONDITION_FAILED: return "Precondition Failed"; | 668 case HttpStatus.GONE: |
| 669 return "Gone"; |
| 670 case HttpStatus.LENGTH_REQUIRED: |
| 671 return "Length Required"; |
| 672 case HttpStatus.PRECONDITION_FAILED: |
| 673 return "Precondition Failed"; |
668 case HttpStatus.REQUEST_ENTITY_TOO_LARGE: | 674 case HttpStatus.REQUEST_ENTITY_TOO_LARGE: |
669 return "Request Entity Too Large"; | 675 return "Request Entity Too Large"; |
670 case HttpStatus.REQUEST_URI_TOO_LONG: return "Request-URI Too Large"; | 676 case HttpStatus.REQUEST_URI_TOO_LONG: |
671 case HttpStatus.UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; | 677 return "Request-URI Too Large"; |
| 678 case HttpStatus.UNSUPPORTED_MEDIA_TYPE: |
| 679 return "Unsupported Media Type"; |
672 case HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE: | 680 case HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE: |
673 return "Requested range not satisfiable"; | 681 return "Requested range not satisfiable"; |
674 case HttpStatus.EXPECTATION_FAILED: return "Expectation Failed"; | 682 case HttpStatus.EXPECTATION_FAILED: |
675 case HttpStatus.INTERNAL_SERVER_ERROR: return "Internal Server Error"; | 683 return "Expectation Failed"; |
676 case HttpStatus.NOT_IMPLEMENTED: return "Not Implemented"; | 684 case HttpStatus.INTERNAL_SERVER_ERROR: |
677 case HttpStatus.BAD_GATEWAY: return "Bad Gateway"; | 685 return "Internal Server Error"; |
678 case HttpStatus.SERVICE_UNAVAILABLE: return "Service Unavailable"; | 686 case HttpStatus.NOT_IMPLEMENTED: |
679 case HttpStatus.GATEWAY_TIMEOUT: return "Gateway Time-out"; | 687 return "Not Implemented"; |
| 688 case HttpStatus.BAD_GATEWAY: |
| 689 return "Bad Gateway"; |
| 690 case HttpStatus.SERVICE_UNAVAILABLE: |
| 691 return "Service Unavailable"; |
| 692 case HttpStatus.GATEWAY_TIMEOUT: |
| 693 return "Gateway Time-out"; |
680 case HttpStatus.HTTP_VERSION_NOT_SUPPORTED: | 694 case HttpStatus.HTTP_VERSION_NOT_SUPPORTED: |
681 return "Http Version not supported"; | 695 return "Http Version not supported"; |
682 default: return "Status $statusCode"; | 696 default: |
| 697 return "Status $statusCode"; |
683 } | 698 } |
684 } | 699 } |
685 } | 700 } |
686 | 701 |
687 | |
688 class _HttpClientRequest extends _HttpOutboundMessage<HttpClientResponse> | 702 class _HttpClientRequest extends _HttpOutboundMessage<HttpClientResponse> |
689 implements HttpClientRequest { | 703 implements HttpClientRequest { |
690 final String method; | 704 final String method; |
691 final Uri uri; | 705 final Uri uri; |
692 final List<Cookie> cookies = new List<Cookie>(); | 706 final List<Cookie> cookies = new List<Cookie>(); |
693 | 707 |
694 // The HttpClient this request belongs to. | 708 // The HttpClient this request belongs to. |
695 final _HttpClient _httpClient; | 709 final _HttpClient _httpClient; |
696 final _HttpClientConnection _httpClientConnection; | 710 final _HttpClientConnection _httpClientConnection; |
697 | 711 |
698 final Completer<HttpClientResponse> _responseCompleter | 712 final Completer<HttpClientResponse> _responseCompleter = |
699 = new Completer<HttpClientResponse>(); | 713 new Completer<HttpClientResponse>(); |
700 | 714 |
701 final _Proxy _proxy; | 715 final _Proxy _proxy; |
702 | 716 |
703 Future<HttpClientResponse> _response; | 717 Future<HttpClientResponse> _response; |
704 | 718 |
705 // TODO(ajohnsen): Get default value from client? | 719 // TODO(ajohnsen): Get default value from client? |
706 bool _followRedirects = true; | 720 bool _followRedirects = true; |
707 | 721 |
708 int _maxRedirects = 5; | 722 int _maxRedirects = 5; |
709 | 723 |
710 List<RedirectInfo> _responseRedirects = []; | 724 List<RedirectInfo> _responseRedirects = []; |
711 | 725 |
712 _HttpClientRequest(_HttpOutgoing outgoing, Uri uri, this.method, this._proxy, | 726 _HttpClientRequest(_HttpOutgoing outgoing, Uri uri, this.method, this._proxy, |
713 this._httpClient, this._httpClientConnection) | 727 this._httpClient, this._httpClientConnection) |
714 : uri = uri, | 728 : uri = uri, |
715 super(uri, "1.1", outgoing) { | 729 super(uri, "1.1", outgoing) { |
716 // GET and HEAD have 'content-length: 0' by default. | 730 // GET and HEAD have 'content-length: 0' by default. |
717 if (method == "GET" || method == "HEAD") { | 731 if (method == "GET" || method == "HEAD") { |
718 contentLength = 0; | 732 contentLength = 0; |
719 } else { | 733 } else { |
720 headers.chunkedTransferEncoding = true; | 734 headers.chunkedTransferEncoding = true; |
721 } | 735 } |
722 } | 736 } |
723 | 737 |
724 Future<HttpClientResponse> get done { | 738 Future<HttpClientResponse> get done { |
725 if (_response == null) { | 739 if (_response == null) { |
726 _response = Future.wait([_responseCompleter.future, super.done], | 740 _response = Future.wait([_responseCompleter.future, super.done], |
727 eagerError: true) | 741 eagerError: true).then((list) => list[0]); |
728 .then((list) => list[0]); | |
729 } | 742 } |
730 return _response; | 743 return _response; |
731 } | 744 } |
732 | 745 |
733 Future<HttpClientResponse> close() { | 746 Future<HttpClientResponse> close() { |
734 super.close(); | 747 super.close(); |
735 return done; | 748 return done; |
736 } | 749 } |
737 | 750 |
738 int get maxRedirects => _maxRedirects; | 751 int get maxRedirects => _maxRedirects; |
739 void set maxRedirects(int maxRedirects) { | 752 void set maxRedirects(int maxRedirects) { |
740 if (_outgoing.headersWritten) throw new StateError("Request already sent"); | 753 if (_outgoing.headersWritten) throw new StateError("Request already sent"); |
741 _maxRedirects = maxRedirects; | 754 _maxRedirects = maxRedirects; |
742 } | 755 } |
743 | 756 |
744 bool get followRedirects => _followRedirects; | 757 bool get followRedirects => _followRedirects; |
745 void set followRedirects(bool followRedirects) { | 758 void set followRedirects(bool followRedirects) { |
746 if (_outgoing.headersWritten) throw new StateError("Request already sent"); | 759 if (_outgoing.headersWritten) throw new StateError("Request already sent"); |
747 _followRedirects = followRedirects; | 760 _followRedirects = followRedirects; |
748 } | 761 } |
749 | 762 |
750 HttpConnectionInfo get connectionInfo => _httpClientConnection.connectionInfo; | 763 HttpConnectionInfo get connectionInfo => _httpClientConnection.connectionInfo; |
751 | 764 |
752 void _onIncoming(_HttpIncoming incoming) { | 765 void _onIncoming(_HttpIncoming incoming) { |
753 var response = new _HttpClientResponse(incoming, this, _httpClient); | 766 var response = new _HttpClientResponse(incoming, this, _httpClient); |
754 Future<HttpClientResponse> future; | 767 Future<HttpClientResponse> future; |
755 if (followRedirects && response.isRedirect) { | 768 if (followRedirects && response.isRedirect) { |
756 if (response.redirects.length < maxRedirects) { | 769 if (response.redirects.length < maxRedirects) { |
757 // Redirect and drain response. | 770 // Redirect and drain response. |
758 future = response.drain() | 771 future = response |
| 772 .drain() |
759 .then<HttpClientResponse>((_) => response.redirect()); | 773 .then<HttpClientResponse>((_) => response.redirect()); |
760 } else { | 774 } else { |
761 // End with exception, too many redirects. | 775 // End with exception, too many redirects. |
762 future = response.drain() | 776 future = response.drain().then<HttpClientResponse>((_) { |
763 .then<HttpClientResponse>((_) { | 777 return new Future<HttpClientResponse>.error(new RedirectException( |
764 return new Future<HttpClientResponse>.error( | 778 "Redirect limit exceeded", response.redirects)); |
765 new RedirectException("Redirect limit exceeded", | |
766 response.redirects)); | |
767 }); | 779 }); |
768 } | 780 } |
769 } else if (response._shouldAuthenticateProxy) { | 781 } else if (response._shouldAuthenticateProxy) { |
770 future = response._authenticate(true); | 782 future = response._authenticate(true); |
771 } else if (response._shouldAuthenticate) { | 783 } else if (response._shouldAuthenticate) { |
772 future = response._authenticate(false); | 784 future = response._authenticate(false); |
773 } else { | 785 } else { |
774 future = new Future<HttpClientResponse>.value(response); | 786 future = new Future<HttpClientResponse>.value(response); |
775 } | 787 } |
776 future.then( | 788 future.then((v) => _responseCompleter.complete(v), |
777 (v) => _responseCompleter.complete(v), | |
778 onError: _responseCompleter.completeError); | 789 onError: _responseCompleter.completeError); |
779 } | 790 } |
780 | 791 |
781 void _onError(error, StackTrace stackTrace) { | 792 void _onError(error, StackTrace stackTrace) { |
782 _responseCompleter.completeError(error, stackTrace); | 793 _responseCompleter.completeError(error, stackTrace); |
783 } | 794 } |
784 | 795 |
785 // Generate the request URI based on the method and proxy. | 796 // Generate the request URI based on the method and proxy. |
786 String _requestUri() { | 797 String _requestUri() { |
787 // Generate the request URI starting from the path component. | 798 // Generate the request URI starting from the path component. |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
861 if (chunk is Uint8List) { | 872 if (chunk is Uint8List) { |
862 _consume(new Uint8List.view(chunk.buffer, start, end - start)); | 873 _consume(new Uint8List.view(chunk.buffer, start, end - start)); |
863 } else { | 874 } else { |
864 _consume(chunk.sublist(start, end - start)); | 875 _consume(chunk.sublist(start, end - start)); |
865 } | 876 } |
866 } | 877 } |
867 | 878 |
868 void close() {} | 879 void close() {} |
869 } | 880 } |
870 | 881 |
871 | |
872 // The _HttpOutgoing handles all of the following: | 882 // The _HttpOutgoing handles all of the following: |
873 // - Buffering | 883 // - Buffering |
874 // - GZip compressionm | 884 // - GZip compressionm |
875 // - Content-Length validation. | 885 // - Content-Length validation. |
876 // - Errors. | 886 // - Errors. |
877 // | 887 // |
878 // Most notable is the GZip compression, that uses a double-buffering system, | 888 // Most notable is the GZip compression, that uses a double-buffering system, |
879 // one before gzip (_gzipBuffer) and one after (_buffer). | 889 // one before gzip (_gzipBuffer) and one after (_buffer). |
880 class _HttpOutgoing implements StreamConsumer<List<int>> { | 890 class _HttpOutgoing implements StreamConsumer<List<int>> { |
881 static const List<int> _footerAndChunk0Length = | 891 static const List<int> _footerAndChunk0Length = const [ |
882 const [_CharCode.CR, _CharCode.LF, 0x30, _CharCode.CR, _CharCode.LF, | 892 _CharCode.CR, |
883 _CharCode.CR, _CharCode.LF]; | 893 _CharCode.LF, |
| 894 0x30, |
| 895 _CharCode.CR, |
| 896 _CharCode.LF, |
| 897 _CharCode.CR, |
| 898 _CharCode.LF |
| 899 ]; |
884 | 900 |
885 static const List<int> _chunk0Length = | 901 static const List<int> _chunk0Length = const [ |
886 const [0x30, _CharCode.CR, _CharCode.LF, _CharCode.CR, _CharCode.LF]; | 902 0x30, |
| 903 _CharCode.CR, |
| 904 _CharCode.LF, |
| 905 _CharCode.CR, |
| 906 _CharCode.LF |
| 907 ]; |
887 | 908 |
888 final Completer<Socket> _doneCompleter = new Completer<Socket>(); | 909 final Completer<Socket> _doneCompleter = new Completer<Socket>(); |
889 final Socket socket; | 910 final Socket socket; |
890 | 911 |
891 bool ignoreBody = false; | 912 bool ignoreBody = false; |
892 bool headersWritten = false; | 913 bool headersWritten = false; |
893 | 914 |
894 Uint8List _buffer; | 915 Uint8List _buffer; |
895 int _length = 0; | 916 int _length = 0; |
896 | 917 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
958 } | 979 } |
959 } | 980 } |
960 if (drainFuture != null) { | 981 if (drainFuture != null) { |
961 return drainFuture.then((_) => outbound._writeHeader()); | 982 return drainFuture.then((_) => outbound._writeHeader()); |
962 } | 983 } |
963 } | 984 } |
964 outbound._writeHeader(); | 985 outbound._writeHeader(); |
965 return null; | 986 return null; |
966 } | 987 } |
967 | 988 |
968 | |
969 Future addStream(Stream<List<int>> stream) { | 989 Future addStream(Stream<List<int>> stream) { |
970 if (_socketError) { | 990 if (_socketError) { |
971 stream.listen(null).cancel(); | 991 stream.listen(null).cancel(); |
972 return new Future.value(outbound); | 992 return new Future.value(outbound); |
973 } | 993 } |
974 if (ignoreBody) { | 994 if (ignoreBody) { |
975 stream.drain().catchError((_) {}); | 995 stream.drain().catchError((_) {}); |
976 var future = writeHeaders(); | 996 var future = writeHeaders(); |
977 if (future != null) { | 997 if (future != null) { |
978 return future.then((_) => close()); | 998 return future.then((_) => close()); |
979 } | 999 } |
980 return close(); | 1000 return close(); |
981 } | 1001 } |
982 StreamSubscription<List<int>> sub; | 1002 StreamSubscription<List<int>> sub; |
983 // Use new stream so we are able to pause (see below listen). The | 1003 // Use new stream so we are able to pause (see below listen). The |
984 // alternative is to use stream.extand, but that won't give us a way of | 1004 // alternative is to use stream.extand, but that won't give us a way of |
985 // pausing. | 1005 // pausing. |
986 var controller = new StreamController<List<int>>( | 1006 var controller = new StreamController<List<int>>( |
987 onPause: () => sub.pause(), | 1007 onPause: () => sub.pause(), onResume: () => sub.resume(), sync: true); |
988 onResume: () => sub.resume(), | |
989 sync: true); | |
990 | 1008 |
991 void onData(List<int> data) { | 1009 void onData(List<int> data) { |
992 if (_socketError) return; | 1010 if (_socketError) return; |
993 if (data.length == 0) return; | 1011 if (data.length == 0) return; |
994 if (chunked) { | 1012 if (chunked) { |
995 if (_gzip) { | 1013 if (_gzip) { |
996 _gzipAdd = controller.add; | 1014 _gzipAdd = controller.add; |
997 _addGZipChunk(data, _gzipSink.add); | 1015 _addGZipChunk(data, _gzipSink.add); |
998 _gzipAdd = null; | 1016 _gzipAdd = null; |
999 return; | 1017 return; |
1000 } | 1018 } |
1001 _addChunk(_chunkHeader(data.length), controller.add); | 1019 _addChunk(_chunkHeader(data.length), controller.add); |
1002 _pendingChunkedFooter = 2; | 1020 _pendingChunkedFooter = 2; |
1003 } else { | 1021 } else { |
1004 if (contentLength != null) { | 1022 if (contentLength != null) { |
1005 _bytesWritten += data.length; | 1023 _bytesWritten += data.length; |
1006 if (_bytesWritten > contentLength) { | 1024 if (_bytesWritten > contentLength) { |
1007 controller.addError(new HttpException( | 1025 controller.addError(new HttpException( |
1008 "Content size exceeds specified contentLength. " | 1026 "Content size exceeds specified contentLength. " |
1009 "$_bytesWritten bytes written while expected " | 1027 "$_bytesWritten bytes written while expected " |
1010 "$contentLength. " | 1028 "$contentLength. " |
1011 "[${new String.fromCharCodes(data)}]")); | 1029 "[${new String.fromCharCodes(data)}]")); |
1012 return; | 1030 return; |
1013 } | 1031 } |
1014 } | 1032 } |
1015 } | 1033 } |
1016 _addChunk(data, controller.add); | 1034 _addChunk(data, controller.add); |
1017 } | 1035 } |
1018 | 1036 |
1019 sub = stream.listen( | 1037 sub = stream.listen(onData, |
1020 onData, | |
1021 onError: controller.addError, | 1038 onError: controller.addError, |
1022 onDone: controller.close, | 1039 onDone: controller.close, |
1023 cancelOnError: true); | 1040 cancelOnError: true); |
1024 // Write headers now that we are listening to the stream. | 1041 // Write headers now that we are listening to the stream. |
1025 if (!headersWritten) { | 1042 if (!headersWritten) { |
1026 var future = writeHeaders(); | 1043 var future = writeHeaders(); |
1027 if (future != null) { | 1044 if (future != null) { |
1028 // While incoming is being drained, the pauseFuture is non-null. Pause | 1045 // While incoming is being drained, the pauseFuture is non-null. Pause |
1029 // output until it's drained. | 1046 // output until it's drained. |
1030 sub.pause(future); | 1047 sub.pause(future); |
1031 } | 1048 } |
1032 } | 1049 } |
1033 return socket.addStream(controller.stream) | 1050 return socket.addStream(controller.stream).then((_) { |
1034 .then((_) { | 1051 return outbound; |
1035 return outbound; | 1052 }, onError: (error, stackTrace) { |
1036 }, onError: (error, stackTrace) { | 1053 // Be sure to close it in case of an error. |
1037 // Be sure to close it in case of an error. | 1054 if (_gzip) _gzipSink.close(); |
1038 if (_gzip) _gzipSink.close(); | 1055 _socketError = true; |
1039 _socketError = true; | 1056 _doneCompleter.completeError(error, stackTrace); |
1040 _doneCompleter.completeError(error, stackTrace); | 1057 if (_ignoreError(error)) { |
1041 if (_ignoreError(error)) { | 1058 return outbound; |
1042 return outbound; | 1059 } else { |
1043 } else { | 1060 throw error; |
1044 throw error; | 1061 } |
1045 } | 1062 }); |
1046 }); | |
1047 } | 1063 } |
1048 | 1064 |
1049 Future close() { | 1065 Future close() { |
1050 // If we are already closed, return that future. | 1066 // If we are already closed, return that future. |
1051 if (_closeFuture != null) return _closeFuture; | 1067 if (_closeFuture != null) return _closeFuture; |
1052 // If we earlier saw an error, return immediate. The notification to | 1068 // If we earlier saw an error, return immediate. The notification to |
1053 // _Http*Connection is already done. | 1069 // _Http*Connection is already done. |
1054 if (_socketError) return new Future.value(outbound); | 1070 if (_socketError) return new Future.value(outbound); |
1055 if (outbound._isConnectionClosed) return new Future.value(outbound); | 1071 if (outbound._isConnectionClosed) return new Future.value(outbound); |
1056 if (!headersWritten && !ignoreBody) { | 1072 if (!headersWritten && !ignoreBody) { |
1057 if (outbound.headers.contentLength == -1) { | 1073 if (outbound.headers.contentLength == -1) { |
1058 // If no body was written, ignoreBody is false (it's not a HEAD | 1074 // If no body was written, ignoreBody is false (it's not a HEAD |
1059 // request) and the content-length is unspecified, set contentLength to | 1075 // request) and the content-length is unspecified, set contentLength to |
1060 // 0. | 1076 // 0. |
1061 outbound.headers.chunkedTransferEncoding = false; | 1077 outbound.headers.chunkedTransferEncoding = false; |
1062 outbound.headers.contentLength = 0; | 1078 outbound.headers.contentLength = 0; |
1063 } else if (outbound.headers.contentLength > 0) { | 1079 } else if (outbound.headers.contentLength > 0) { |
1064 var error = new HttpException( | 1080 var error = new HttpException( |
1065 "No content even though contentLength was specified to be " | 1081 "No content even though contentLength was specified to be " |
1066 "greater than 0: ${outbound.headers.contentLength}.", | 1082 "greater than 0: ${outbound.headers.contentLength}.", |
1067 uri: outbound._uri); | 1083 uri: outbound._uri); |
1068 _doneCompleter.completeError(error); | 1084 _doneCompleter.completeError(error); |
1069 return _closeFuture = new Future.error(error); | 1085 return _closeFuture = new Future.error(error); |
1070 } | 1086 } |
1071 } | 1087 } |
1072 // If contentLength was specified, validate it. | 1088 // If contentLength was specified, validate it. |
1073 if (contentLength != null) { | 1089 if (contentLength != null) { |
1074 if (_bytesWritten < contentLength) { | 1090 if (_bytesWritten < contentLength) { |
1075 var error = new HttpException( | 1091 var error = new HttpException( |
1076 "Content size below specified contentLength. " | 1092 "Content size below specified contentLength. " |
1077 " $_bytesWritten bytes written but expected " | 1093 " $_bytesWritten bytes written but expected " |
1078 "$contentLength.", | 1094 "$contentLength.", |
1079 uri: outbound._uri); | 1095 uri: outbound._uri); |
1080 _doneCompleter.completeError(error); | 1096 _doneCompleter.completeError(error); |
1081 return _closeFuture = new Future.error(error); | 1097 return _closeFuture = new Future.error(error); |
1082 } | 1098 } |
1083 } | 1099 } |
1084 | 1100 |
1085 Future finalize() { | 1101 Future finalize() { |
1086 // In case of chunked encoding (and gzip), handle remaining gzip data and | 1102 // In case of chunked encoding (and gzip), handle remaining gzip data and |
1087 // append the 'footer' for chunked encoding. | 1103 // append the 'footer' for chunked encoding. |
1088 if (chunked) { | 1104 if (chunked) { |
1089 if (_gzip) { | 1105 if (_gzip) { |
1090 _gzipAdd = socket.add; | 1106 _gzipAdd = socket.add; |
1091 if (_gzipBufferLength > 0) { | 1107 if (_gzipBufferLength > 0) { |
1092 _gzipSink.add(new Uint8List.view( | 1108 _gzipSink.add( |
1093 _gzipBuffer.buffer, 0, _gzipBufferLength)); | 1109 new Uint8List.view(_gzipBuffer.buffer, 0, _gzipBufferLength)); |
1094 } | 1110 } |
1095 _gzipBuffer = null; | 1111 _gzipBuffer = null; |
1096 _gzipSink.close(); | 1112 _gzipSink.close(); |
1097 _gzipAdd = null; | 1113 _gzipAdd = null; |
1098 } | 1114 } |
1099 _addChunk(_chunkHeader(0), socket.add); | 1115 _addChunk(_chunkHeader(0), socket.add); |
1100 } | 1116 } |
1101 // Add any remaining data in the buffer. | 1117 // Add any remaining data in the buffer. |
1102 if (_length > 0) { | 1118 if (_length > 0) { |
1103 socket.add(new Uint8List.view(_buffer.buffer, 0, _length)); | 1119 socket.add(new Uint8List.view(_buffer.buffer, 0, _length)); |
1104 } | 1120 } |
1105 // Clear references, for better GC. | 1121 // Clear references, for better GC. |
1106 _buffer = null; | 1122 _buffer = null; |
1107 // And finally flush it. As we support keep-alive, never close it from | 1123 // And finally flush it. As we support keep-alive, never close it from |
1108 // here. Once the socket is flushed, we'll be able to reuse it (signaled | 1124 // here. Once the socket is flushed, we'll be able to reuse it (signaled |
1109 // by the 'done' future). | 1125 // by the 'done' future). |
1110 return socket.flush() | 1126 return socket.flush().then((_) { |
1111 .then((_) { | 1127 _doneCompleter.complete(socket); |
1112 _doneCompleter.complete(socket); | 1128 return outbound; |
| 1129 }, onError: (error, stackTrace) { |
| 1130 _doneCompleter.completeError(error, stackTrace); |
| 1131 if (_ignoreError(error)) { |
1113 return outbound; | 1132 return outbound; |
1114 }, onError: (error, stackTrace) { | 1133 } else { |
1115 _doneCompleter.completeError(error, stackTrace); | 1134 throw error; |
1116 if (_ignoreError(error)) { | 1135 } |
1117 return outbound; | 1136 }); |
1118 } else { | |
1119 throw error; | |
1120 } | |
1121 }); | |
1122 } | 1137 } |
1123 | 1138 |
1124 var future = writeHeaders(); | 1139 var future = writeHeaders(); |
1125 if (future != null) { | 1140 if (future != null) { |
1126 return _closeFuture = future.whenComplete(finalize); | 1141 return _closeFuture = future.whenComplete(finalize); |
1127 } | 1142 } |
1128 return _closeFuture = finalize(); | 1143 return _closeFuture = finalize(); |
1129 } | 1144 } |
1130 | 1145 |
1131 Future<Socket> get done => _doneCompleter.future; | 1146 Future<Socket> get done => _doneCompleter.future; |
1132 | 1147 |
1133 void setHeader(List<int> data, int length) { | 1148 void setHeader(List<int> data, int length) { |
1134 assert(_length == 0); | 1149 assert(_length == 0); |
1135 _buffer = data; | 1150 _buffer = data; |
1136 _length = length; | 1151 _length = length; |
1137 } | 1152 } |
1138 | 1153 |
1139 void set gzip(bool value) { | 1154 void set gzip(bool value) { |
1140 _gzip = value; | 1155 _gzip = value; |
1141 if (_gzip) { | 1156 if (_gzip) { |
1142 _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE); | 1157 _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE); |
1143 assert(_gzipSink == null); | 1158 assert(_gzipSink == null); |
1144 _gzipSink = new ZLibEncoder(gzip: true) | 1159 _gzipSink = new ZLibEncoder(gzip: true) |
1145 .startChunkedConversion( | 1160 .startChunkedConversion(new _HttpGZipSink((data) { |
1146 new _HttpGZipSink((data) { | 1161 // We are closing down prematurely, due to an error. Discard. |
1147 // We are closing down prematurely, due to an error. Discard. | 1162 if (_gzipAdd == null) return; |
1148 if (_gzipAdd == null) return; | 1163 _addChunk(_chunkHeader(data.length), _gzipAdd); |
1149 _addChunk(_chunkHeader(data.length), _gzipAdd); | 1164 _pendingChunkedFooter = 2; |
1150 _pendingChunkedFooter = 2; | 1165 _addChunk(data, _gzipAdd); |
1151 _addChunk(data, _gzipAdd); | 1166 })); |
1152 })); | |
1153 } | 1167 } |
1154 } | 1168 } |
1155 | 1169 |
1156 bool _ignoreError(error) | 1170 bool _ignoreError(error) => |
1157 => (error is SocketException || error is TlsException) && | 1171 (error is SocketException || error is TlsException) && |
1158 outbound is HttpResponse; | 1172 outbound is HttpResponse; |
1159 | 1173 |
1160 void _addGZipChunk(List<int> chunk, void add(List<int> data)) { | 1174 void _addGZipChunk(List<int> chunk, void add(List<int> data)) { |
1161 if (!outbound.bufferOutput) { | 1175 if (!outbound.bufferOutput) { |
1162 add(chunk); | 1176 add(chunk); |
1163 return; | 1177 return; |
1164 } | 1178 } |
1165 if (chunk.length > _gzipBuffer.length - _gzipBufferLength) { | 1179 if (chunk.length > _gzipBuffer.length - _gzipBufferLength) { |
1166 add(new Uint8List.view( | 1180 add(new Uint8List.view(_gzipBuffer.buffer, 0, _gzipBufferLength)); |
1167 _gzipBuffer.buffer, 0, _gzipBufferLength)); | |
1168 _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE); | 1181 _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE); |
1169 _gzipBufferLength = 0; | 1182 _gzipBufferLength = 0; |
1170 } | 1183 } |
1171 if (chunk.length > _OUTGOING_BUFFER_SIZE) { | 1184 if (chunk.length > _OUTGOING_BUFFER_SIZE) { |
1172 add(chunk); | 1185 add(chunk); |
1173 } else { | 1186 } else { |
1174 _gzipBuffer.setRange(_gzipBufferLength, | 1187 _gzipBuffer.setRange( |
1175 _gzipBufferLength + chunk.length, | 1188 _gzipBufferLength, _gzipBufferLength + chunk.length, chunk); |
1176 chunk); | |
1177 _gzipBufferLength += chunk.length; | 1189 _gzipBufferLength += chunk.length; |
1178 } | 1190 } |
1179 } | 1191 } |
1180 | 1192 |
1181 void _addChunk(List<int> chunk, void add(List<int> data)) { | 1193 void _addChunk(List<int> chunk, void add(List<int> data)) { |
1182 if (!outbound.bufferOutput) { | 1194 if (!outbound.bufferOutput) { |
1183 if (_buffer != null) { | 1195 if (_buffer != null) { |
1184 // If _buffer is not null, we have not written the header yet. Write | 1196 // If _buffer is not null, we have not written the header yet. Write |
1185 // it now. | 1197 // it now. |
1186 add(new Uint8List.view(_buffer.buffer, 0, _length)); | 1198 add(new Uint8List.view(_buffer.buffer, 0, _length)); |
(...skipping 10 matching lines...) Expand all Loading... |
1197 } | 1209 } |
1198 if (chunk.length > _OUTGOING_BUFFER_SIZE) { | 1210 if (chunk.length > _OUTGOING_BUFFER_SIZE) { |
1199 add(chunk); | 1211 add(chunk); |
1200 } else { | 1212 } else { |
1201 _buffer.setRange(_length, _length + chunk.length, chunk); | 1213 _buffer.setRange(_length, _length + chunk.length, chunk); |
1202 _length += chunk.length; | 1214 _length += chunk.length; |
1203 } | 1215 } |
1204 } | 1216 } |
1205 | 1217 |
1206 List<int> _chunkHeader(int length) { | 1218 List<int> _chunkHeader(int length) { |
1207 const hexDigits = const [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, | 1219 const hexDigits = const [ |
1208 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46]; | 1220 0x30, |
| 1221 0x31, |
| 1222 0x32, |
| 1223 0x33, |
| 1224 0x34, |
| 1225 0x35, |
| 1226 0x36, |
| 1227 0x37, |
| 1228 0x38, |
| 1229 0x39, |
| 1230 0x41, |
| 1231 0x42, |
| 1232 0x43, |
| 1233 0x44, |
| 1234 0x45, |
| 1235 0x46 |
| 1236 ]; |
1209 if (length == 0) { | 1237 if (length == 0) { |
1210 if (_pendingChunkedFooter == 2) return _footerAndChunk0Length; | 1238 if (_pendingChunkedFooter == 2) return _footerAndChunk0Length; |
1211 return _chunk0Length; | 1239 return _chunk0Length; |
1212 } | 1240 } |
1213 int size = _pendingChunkedFooter; | 1241 int size = _pendingChunkedFooter; |
1214 int len = length; | 1242 int len = length; |
1215 // Compute a fast integer version of (log(length + 1) / log(16)).ceil(). | 1243 // Compute a fast integer version of (log(length + 1) / log(16)).ceil(). |
1216 while (len > 0) { | 1244 while (len > 0) { |
1217 size++; | 1245 size++; |
1218 len >>= 4; | 1246 len >>= 4; |
(...skipping 24 matching lines...) Expand all Loading... |
1243 final _HttpClient _httpClient; | 1271 final _HttpClient _httpClient; |
1244 bool _dispose = false; | 1272 bool _dispose = false; |
1245 Timer _idleTimer; | 1273 Timer _idleTimer; |
1246 bool closed = false; | 1274 bool closed = false; |
1247 Uri _currentUri; | 1275 Uri _currentUri; |
1248 | 1276 |
1249 Completer<_HttpIncoming> _nextResponseCompleter; | 1277 Completer<_HttpIncoming> _nextResponseCompleter; |
1250 Future<Socket> _streamFuture; | 1278 Future<Socket> _streamFuture; |
1251 | 1279 |
1252 _HttpClientConnection(this.key, this._socket, this._httpClient, | 1280 _HttpClientConnection(this.key, this._socket, this._httpClient, |
1253 [this._proxyTunnel = false, this._context]) | 1281 [this._proxyTunnel = false, this._context]) |
1254 : _httpParser = new _HttpParser.responseParser() { | 1282 : _httpParser = new _HttpParser.responseParser() { |
1255 _httpParser.listenToStream(_socket); | 1283 _httpParser.listenToStream(_socket); |
1256 | 1284 |
1257 // Set up handlers on the parser here, so we are sure to get 'onDone' from | 1285 // Set up handlers on the parser here, so we are sure to get 'onDone' from |
1258 // the parser. | 1286 // the parser. |
1259 _subscription = _httpParser.listen( | 1287 _subscription = _httpParser.listen((incoming) { |
1260 (incoming) { | 1288 // Only handle one incoming response at the time. Keep the |
1261 // Only handle one incoming response at the time. Keep the | 1289 // stream paused until the response have been processed. |
1262 // stream paused until the response have been processed. | 1290 _subscription.pause(); |
1263 _subscription.pause(); | 1291 // We assume the response is not here, until we have send the request. |
1264 // We assume the response is not here, until we have send the request. | 1292 if (_nextResponseCompleter == null) { |
1265 if (_nextResponseCompleter == null) { | 1293 throw new HttpException( |
1266 throw new HttpException( | 1294 "Unexpected response (unsolicited response without request).", |
1267 "Unexpected response (unsolicited response without request).", | 1295 uri: _currentUri); |
1268 uri: _currentUri); | 1296 } |
1269 } | |
1270 | 1297 |
1271 // Check for status code '100 Continue'. In that case just | 1298 // Check for status code '100 Continue'. In that case just |
1272 // consume that response as the final response will follow | 1299 // consume that response as the final response will follow |
1273 // it. There is currently no API for the client to wait for | 1300 // it. There is currently no API for the client to wait for |
1274 // the '100 Continue' response. | 1301 // the '100 Continue' response. |
1275 if (incoming.statusCode == 100) { | 1302 if (incoming.statusCode == 100) { |
1276 incoming.drain().then((_) { | 1303 incoming.drain().then((_) { |
1277 _subscription.resume(); | 1304 _subscription.resume(); |
1278 }).catchError((error, [StackTrace stackTrace]) { | 1305 }).catchError((error, [StackTrace stackTrace]) { |
1279 _nextResponseCompleter.completeError( | 1306 _nextResponseCompleter.completeError( |
1280 new HttpException(error.message, uri: _currentUri), | 1307 new HttpException(error.message, uri: _currentUri), stackTrace); |
1281 stackTrace); | 1308 _nextResponseCompleter = null; |
1282 _nextResponseCompleter = null; | |
1283 }); | |
1284 } else { | |
1285 _nextResponseCompleter.complete(incoming); | |
1286 _nextResponseCompleter = null; | |
1287 } | |
1288 }, | |
1289 onError: (error, [StackTrace stackTrace]) { | |
1290 if (_nextResponseCompleter != null) { | |
1291 _nextResponseCompleter.completeError( | |
1292 new HttpException(error.message, uri: _currentUri), | |
1293 stackTrace); | |
1294 _nextResponseCompleter = null; | |
1295 } | |
1296 }, | |
1297 onDone: () { | |
1298 if (_nextResponseCompleter != null) { | |
1299 _nextResponseCompleter.completeError(new HttpException( | |
1300 "Connection closed before response was received", | |
1301 uri: _currentUri)); | |
1302 _nextResponseCompleter = null; | |
1303 } | |
1304 close(); | |
1305 }); | 1309 }); |
| 1310 } else { |
| 1311 _nextResponseCompleter.complete(incoming); |
| 1312 _nextResponseCompleter = null; |
| 1313 } |
| 1314 }, onError: (error, [StackTrace stackTrace]) { |
| 1315 if (_nextResponseCompleter != null) { |
| 1316 _nextResponseCompleter.completeError( |
| 1317 new HttpException(error.message, uri: _currentUri), stackTrace); |
| 1318 _nextResponseCompleter = null; |
| 1319 } |
| 1320 }, onDone: () { |
| 1321 if (_nextResponseCompleter != null) { |
| 1322 _nextResponseCompleter.completeError(new HttpException( |
| 1323 "Connection closed before response was received", |
| 1324 uri: _currentUri)); |
| 1325 _nextResponseCompleter = null; |
| 1326 } |
| 1327 close(); |
| 1328 }); |
1306 } | 1329 } |
1307 | 1330 |
1308 _HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy) { | 1331 _HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy) { |
1309 if (closed) { | 1332 if (closed) { |
1310 throw new HttpException( | 1333 throw new HttpException("Socket closed before request was sent", |
1311 "Socket closed before request was sent", uri: uri); | 1334 uri: uri); |
1312 } | 1335 } |
1313 _currentUri = uri; | 1336 _currentUri = uri; |
1314 // Start with pausing the parser. | 1337 // Start with pausing the parser. |
1315 _subscription.pause(); | 1338 _subscription.pause(); |
1316 _ProxyCredentials proxyCreds; // Credentials used to authorize proxy. | 1339 _ProxyCredentials proxyCreds; // Credentials used to authorize proxy. |
1317 _SiteCredentials creds; // Credentials used to authorize this request. | 1340 _SiteCredentials creds; // Credentials used to authorize this request. |
1318 var outgoing = new _HttpOutgoing(_socket); | 1341 var outgoing = new _HttpOutgoing(_socket); |
1319 // Create new request object, wrapping the outgoing connection. | 1342 // Create new request object, wrapping the outgoing connection. |
1320 var request = new _HttpClientRequest(outgoing, | 1343 var request = |
1321 uri, | 1344 new _HttpClientRequest(outgoing, uri, method, proxy, _httpClient, this); |
1322 method, | |
1323 proxy, | |
1324 _httpClient, | |
1325 this); | |
1326 // For the Host header an IPv6 address must be enclosed in []'s. | 1345 // For the Host header an IPv6 address must be enclosed in []'s. |
1327 var host = uri.host; | 1346 var host = uri.host; |
1328 if (host.contains(':')) host = "[$host]"; | 1347 if (host.contains(':')) host = "[$host]"; |
1329 request.headers | 1348 request.headers |
1330 ..host = host | 1349 ..host = host |
1331 ..port = port | 1350 ..port = port |
1332 .._add(HttpHeaders.ACCEPT_ENCODING, "gzip"); | 1351 .._add(HttpHeaders.ACCEPT_ENCODING, "gzip"); |
1333 if (_httpClient.userAgent != null) { | 1352 if (_httpClient.userAgent != null) { |
1334 request.headers._add('user-agent', _httpClient.userAgent); | 1353 request.headers._add('user-agent', _httpClient.userAgent); |
1335 } | 1354 } |
1336 if (proxy.isAuthenticated) { | 1355 if (proxy.isAuthenticated) { |
1337 // If the proxy configuration contains user information use that | 1356 // If the proxy configuration contains user information use that |
1338 // for proxy basic authorization. | 1357 // for proxy basic authorization. |
1339 String auth = _CryptoUtils.bytesToBase64( | 1358 String auth = _CryptoUtils |
1340 UTF8.encode("${proxy.username}:${proxy.password}")); | 1359 .bytesToBase64(UTF8.encode("${proxy.username}:${proxy.password}")); |
1341 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth"); | 1360 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth"); |
1342 } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) { | 1361 } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) { |
1343 proxyCreds = _httpClient._findProxyCredentials(proxy); | 1362 proxyCreds = _httpClient._findProxyCredentials(proxy); |
1344 if (proxyCreds != null) { | 1363 if (proxyCreds != null) { |
1345 proxyCreds.authorize(request); | 1364 proxyCreds.authorize(request); |
1346 } | 1365 } |
1347 } | 1366 } |
1348 if (uri.userInfo != null && !uri.userInfo.isEmpty) { | 1367 if (uri.userInfo != null && !uri.userInfo.isEmpty) { |
1349 // If the URL contains user information use that for basic | 1368 // If the URL contains user information use that for basic |
1350 // authorization. | 1369 // authorization. |
1351 String auth = | 1370 String auth = _CryptoUtils.bytesToBase64(UTF8.encode(uri.userInfo)); |
1352 _CryptoUtils.bytesToBase64(UTF8.encode(uri.userInfo)); | |
1353 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); | 1371 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); |
1354 } else { | 1372 } else { |
1355 // Look for credentials. | 1373 // Look for credentials. |
1356 creds = _httpClient._findCredentials(uri); | 1374 creds = _httpClient._findCredentials(uri); |
1357 if (creds != null) { | 1375 if (creds != null) { |
1358 creds.authorize(request); | 1376 creds.authorize(request); |
1359 } | 1377 } |
1360 } | 1378 } |
1361 // Start sending the request (lazy, delayed until the user provides | 1379 // Start sending the request (lazy, delayed until the user provides |
1362 // data). | 1380 // data). |
1363 _httpParser.isHead = method == "HEAD"; | 1381 _httpParser.isHead = method == "HEAD"; |
1364 _streamFuture = outgoing.done | 1382 _streamFuture = outgoing.done.then<Socket>((Socket s) { |
1365 .then<Socket>((Socket s) { | 1383 // Request sent, set up response completer. |
1366 // Request sent, set up response completer. | 1384 _nextResponseCompleter = new Completer(); |
1367 _nextResponseCompleter = new Completer(); | |
1368 | 1385 |
1369 // Listen for response. | 1386 // Listen for response. |
1370 _nextResponseCompleter.future | 1387 _nextResponseCompleter.future.then((incoming) { |
1371 .then((incoming) { | 1388 _currentUri = null; |
1372 _currentUri = null; | 1389 incoming.dataDone.then((closing) { |
1373 incoming.dataDone.then((closing) { | 1390 if (incoming.upgraded) { |
1374 if (incoming.upgraded) { | 1391 _httpClient._connectionClosed(this); |
1375 _httpClient._connectionClosed(this); | 1392 startTimer(); |
1376 startTimer(); | 1393 return; |
1377 return; | 1394 } |
1378 } | 1395 if (closed) return; |
1379 if (closed) return; | 1396 if (!closing && |
1380 if (!closing && | 1397 !_dispose && |
1381 !_dispose && | 1398 incoming.headers.persistentConnection && |
1382 incoming.headers.persistentConnection && | 1399 request.persistentConnection) { |
1383 request.persistentConnection) { | 1400 // Return connection, now we are done. |
1384 // Return connection, now we are done. | 1401 _httpClient._returnConnection(this); |
1385 _httpClient._returnConnection(this); | 1402 _subscription.resume(); |
1386 _subscription.resume(); | 1403 } else { |
1387 } else { | 1404 destroy(); |
1388 destroy(); | 1405 } |
1389 } | 1406 }); |
1390 }); | 1407 // For digest authentication if proxy check if the proxy |
1391 // For digest authentication if proxy check if the proxy | 1408 // requests the client to start using a new nonce for proxy |
1392 // requests the client to start using a new nonce for proxy | 1409 // authentication. |
1393 // authentication. | 1410 if (proxyCreds != null && |
1394 if (proxyCreds != null && | 1411 proxyCreds.scheme == _AuthenticationScheme.DIGEST) { |
1395 proxyCreds.scheme == _AuthenticationScheme.DIGEST) { | 1412 var authInfo = incoming.headers["proxy-authentication-info"]; |
1396 var authInfo = incoming.headers["proxy-authentication-info"]; | 1413 if (authInfo != null && authInfo.length == 1) { |
1397 if (authInfo != null && authInfo.length == 1) { | 1414 var header = |
1398 var header = | 1415 _HeaderValue.parse(authInfo[0], parameterSeparator: ','); |
1399 _HeaderValue.parse( | 1416 var nextnonce = header.parameters["nextnonce"]; |
1400 authInfo[0], parameterSeparator: ','); | 1417 if (nextnonce != null) proxyCreds.nonce = nextnonce; |
1401 var nextnonce = header.parameters["nextnonce"]; | 1418 } |
1402 if (nextnonce != null) proxyCreds.nonce = nextnonce; | 1419 } |
1403 } | 1420 // For digest authentication check if the server requests the |
1404 } | 1421 // client to start using a new nonce. |
1405 // For digest authentication check if the server requests the | 1422 if (creds != null && creds.scheme == _AuthenticationScheme.DIGEST) { |
1406 // client to start using a new nonce. | 1423 var authInfo = incoming.headers["authentication-info"]; |
1407 if (creds != null && | 1424 if (authInfo != null && authInfo.length == 1) { |
1408 creds.scheme == _AuthenticationScheme.DIGEST) { | 1425 var header = |
1409 var authInfo = incoming.headers["authentication-info"]; | 1426 _HeaderValue.parse(authInfo[0], parameterSeparator: ','); |
1410 if (authInfo != null && authInfo.length == 1) { | 1427 var nextnonce = header.parameters["nextnonce"]; |
1411 var header = | 1428 if (nextnonce != null) creds.nonce = nextnonce; |
1412 _HeaderValue.parse( | 1429 } |
1413 authInfo[0], parameterSeparator: ','); | 1430 } |
1414 var nextnonce = header.parameters["nextnonce"]; | 1431 request._onIncoming(incoming); |
1415 if (nextnonce != null) creds.nonce = nextnonce; | 1432 }) |
1416 } | 1433 // If we see a state error, we failed to get the 'first' |
1417 } | 1434 // element. |
1418 request._onIncoming(incoming); | 1435 .catchError((error) { |
1419 }) | 1436 throw new HttpException("Connection closed before data was received", |
1420 // If we see a state error, we failed to get the 'first' | 1437 uri: uri); |
1421 // element. | 1438 }, test: (error) => error is StateError).catchError((error, stackTrace) { |
1422 .catchError((error) { | 1439 // We are done with the socket. |
1423 throw new HttpException( | 1440 destroy(); |
1424 "Connection closed before data was received", uri: uri); | 1441 request._onError(error, stackTrace); |
1425 }, test: (error) => error is StateError) | 1442 }); |
1426 .catchError((error, stackTrace) { | |
1427 // We are done with the socket. | |
1428 destroy(); | |
1429 request._onError(error, stackTrace); | |
1430 }); | |
1431 | 1443 |
1432 // Resume the parser now we have a handler. | 1444 // Resume the parser now we have a handler. |
1433 _subscription.resume(); | 1445 _subscription.resume(); |
1434 return s; | 1446 return s; |
1435 }, onError: (e) { | 1447 }, onError: (e) { |
1436 destroy(); | 1448 destroy(); |
1437 }); | 1449 }); |
1438 return request; | 1450 return request; |
1439 } | 1451 } |
1440 | 1452 |
1441 Future<Socket> detachSocket() { | 1453 Future<Socket> detachSocket() { |
1442 return _streamFuture.then( | 1454 return _streamFuture.then( |
1443 (_) => new _DetachedSocket(_socket, _httpParser.detachIncoming())); | 1455 (_) => new _DetachedSocket(_socket, _httpParser.detachIncoming())); |
1444 } | 1456 } |
1445 | 1457 |
1446 void destroy() { | 1458 void destroy() { |
1447 closed = true; | 1459 closed = true; |
1448 _httpClient._connectionClosed(this); | 1460 _httpClient._connectionClosed(this); |
1449 _socket.destroy(); | 1461 _socket.destroy(); |
1450 } | 1462 } |
1451 | 1463 |
1452 void close() { | 1464 void close() { |
1453 closed = true; | 1465 closed = true; |
1454 _httpClient._connectionClosed(this); | 1466 _httpClient._connectionClosed(this); |
1455 _streamFuture | 1467 _streamFuture |
1456 // TODO(ajohnsen): Add timeout. | 1468 // TODO(ajohnsen): Add timeout. |
1457 .then((_) => _socket.destroy()); | 1469 .then((_) => _socket.destroy()); |
1458 } | 1470 } |
1459 | 1471 |
1460 Future<_HttpClientConnection> createProxyTunnel(String host, int port, | 1472 Future<_HttpClientConnection> createProxyTunnel(String host, int port, |
1461 _Proxy proxy, bool callback(X509Certificate certificate)) { | 1473 _Proxy proxy, bool callback(X509Certificate certificate)) { |
1462 _HttpClientRequest request = | 1474 _HttpClientRequest request = |
1463 send(new Uri(host: host, port: port), | 1475 send(new Uri(host: host, port: port), port, "CONNECT", proxy); |
1464 port, | |
1465 "CONNECT", | |
1466 proxy); | |
1467 if (proxy.isAuthenticated) { | 1476 if (proxy.isAuthenticated) { |
1468 // If the proxy configuration contains user information use that | 1477 // If the proxy configuration contains user information use that |
1469 // for proxy basic authorization. | 1478 // for proxy basic authorization. |
1470 String auth = _CryptoUtils.bytesToBase64( | 1479 String auth = _CryptoUtils |
1471 UTF8.encode("${proxy.username}:${proxy.password}")); | 1480 .bytesToBase64(UTF8.encode("${proxy.username}:${proxy.password}")); |
1472 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth"); | 1481 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth"); |
1473 } | 1482 } |
1474 return request.close() | 1483 return request.close().then((response) { |
1475 .then((response) { | 1484 if (response.statusCode != HttpStatus.OK) { |
1476 if (response.statusCode != HttpStatus.OK) { | 1485 throw "Proxy failed to establish tunnel " |
1477 throw "Proxy failed to establish tunnel " | 1486 "(${response.statusCode} ${response.reasonPhrase})"; |
1478 "(${response.statusCode} ${response.reasonPhrase})"; | 1487 } |
1479 } | 1488 var socket = (response as _HttpClientResponse) |
1480 var socket = (response as _HttpClientResponse)._httpRequest | 1489 ._httpRequest |
1481 ._httpClientConnection._socket; | 1490 ._httpClientConnection |
1482 return SecureSocket.secure( | 1491 ._socket; |
1483 socket, | 1492 return SecureSocket.secure(socket, |
1484 host: host, | 1493 host: host, context: _context, onBadCertificate: callback); |
1485 context: _context, | 1494 }).then((secureSocket) { |
1486 onBadCertificate: callback); | 1495 String key = _HttpClientConnection.makeKey(true, host, port); |
1487 }) | 1496 return new _HttpClientConnection( |
1488 .then((secureSocket) { | 1497 key, secureSocket, request._httpClient, true); |
1489 String key = _HttpClientConnection.makeKey(true, host, port); | 1498 }); |
1490 return new _HttpClientConnection( | |
1491 key, secureSocket, request._httpClient, true); | |
1492 }); | |
1493 } | 1499 } |
1494 | 1500 |
1495 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket); | 1501 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket); |
1496 | 1502 |
1497 static makeKey(bool isSecure, String host, int port) { | 1503 static makeKey(bool isSecure, String host, int port) { |
1498 return isSecure ? "ssh:$host:$port" : "$host:$port"; | 1504 return isSecure ? "ssh:$host:$port" : "$host:$port"; |
1499 } | 1505 } |
1500 | 1506 |
1501 void stopTimer() { | 1507 void stopTimer() { |
1502 if (_idleTimer != null) { | 1508 if (_idleTimer != null) { |
1503 _idleTimer.cancel(); | 1509 _idleTimer.cancel(); |
1504 _idleTimer = null; | 1510 _idleTimer = null; |
1505 } | 1511 } |
1506 } | 1512 } |
1507 | 1513 |
1508 void startTimer() { | 1514 void startTimer() { |
1509 assert(_idleTimer == null); | 1515 assert(_idleTimer == null); |
1510 _idleTimer = new Timer( | 1516 _idleTimer = new Timer(_httpClient.idleTimeout, () { |
1511 _httpClient.idleTimeout, | 1517 _idleTimer = null; |
1512 () { | 1518 close(); |
1513 _idleTimer = null; | 1519 }); |
1514 close(); | |
1515 }); | |
1516 } | 1520 } |
1517 } | 1521 } |
1518 | 1522 |
1519 class _ConnectionInfo { | 1523 class _ConnectionInfo { |
1520 final _HttpClientConnection connection; | 1524 final _HttpClientConnection connection; |
1521 final _Proxy proxy; | 1525 final _Proxy proxy; |
1522 | 1526 |
1523 _ConnectionInfo(this.connection, this.proxy); | 1527 _ConnectionInfo(this.connection, this.proxy); |
1524 } | 1528 } |
1525 | 1529 |
1526 | |
1527 class _ConnectionTarget { | 1530 class _ConnectionTarget { |
1528 // Unique key for this connection target. | 1531 // Unique key for this connection target. |
1529 final String key; | 1532 final String key; |
1530 final String host; | 1533 final String host; |
1531 final int port; | 1534 final int port; |
1532 final bool isSecure; | 1535 final bool isSecure; |
1533 final SecurityContext context; | 1536 final SecurityContext context; |
1534 final Set<_HttpClientConnection> _idle = new HashSet(); | 1537 final Set<_HttpClientConnection> _idle = new HashSet(); |
1535 final Set<_HttpClientConnection> _active = new HashSet(); | 1538 final Set<_HttpClientConnection> _active = new HashSet(); |
1536 final Queue _pending = new ListQueue(); | 1539 final Queue _pending = new ListQueue(); |
1537 int _connecting = 0; | 1540 int _connecting = 0; |
1538 | 1541 |
1539 _ConnectionTarget(this.key, | 1542 _ConnectionTarget( |
1540 this.host, | 1543 this.key, this.host, this.port, this.isSecure, this.context); |
1541 this.port, | |
1542 this.isSecure, | |
1543 this.context); | |
1544 | 1544 |
1545 bool get isEmpty => _idle.isEmpty && _active.isEmpty && _connecting == 0; | 1545 bool get isEmpty => _idle.isEmpty && _active.isEmpty && _connecting == 0; |
1546 | 1546 |
1547 bool get hasIdle => _idle.isNotEmpty; | 1547 bool get hasIdle => _idle.isNotEmpty; |
1548 | 1548 |
1549 bool get hasActive => _active.isNotEmpty || _connecting > 0; | 1549 bool get hasActive => _active.isNotEmpty || _connecting > 0; |
1550 | 1550 |
1551 _HttpClientConnection takeIdle() { | 1551 _HttpClientConnection takeIdle() { |
1552 assert(hasIdle); | 1552 assert(hasIdle); |
1553 _HttpClientConnection connection = _idle.first; | 1553 _HttpClientConnection connection = _idle.first; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1586 for (var c in _idle.toList()) { | 1586 for (var c in _idle.toList()) { |
1587 c.close(); | 1587 c.close(); |
1588 } | 1588 } |
1589 if (force) { | 1589 if (force) { |
1590 for (var c in _active.toList()) { | 1590 for (var c in _active.toList()) { |
1591 c.destroy(); | 1591 c.destroy(); |
1592 } | 1592 } |
1593 } | 1593 } |
1594 } | 1594 } |
1595 | 1595 |
1596 Future<_ConnectionInfo> connect(String uriHost, | 1596 Future<_ConnectionInfo> connect( |
1597 int uriPort, | 1597 String uriHost, int uriPort, _Proxy proxy, _HttpClient client) { |
1598 _Proxy proxy, | |
1599 _HttpClient client) { | |
1600 if (hasIdle) { | 1598 if (hasIdle) { |
1601 var connection = takeIdle(); | 1599 var connection = takeIdle(); |
1602 client._connectionsChanged(); | 1600 client._connectionsChanged(); |
1603 return new Future.value(new _ConnectionInfo(connection, proxy)); | 1601 return new Future.value(new _ConnectionInfo(connection, proxy)); |
1604 } | 1602 } |
1605 if (client.maxConnectionsPerHost != null && | 1603 if (client.maxConnectionsPerHost != null && |
1606 _active.length + _connecting >= client.maxConnectionsPerHost) { | 1604 _active.length + _connecting >= client.maxConnectionsPerHost) { |
1607 var completer = new Completer<_ConnectionInfo>(); | 1605 var completer = new Completer<_ConnectionInfo>(); |
1608 _pending.add(() { | 1606 _pending.add(() { |
1609 completer.complete(connect(uriHost, uriPort, proxy, client)); | 1607 completer.complete(connect(uriHost, uriPort, proxy, client)); |
1610 }); | 1608 }); |
1611 return completer.future; | 1609 return completer.future; |
1612 } | 1610 } |
1613 var currentBadCertificateCallback = client._badCertificateCallback; | 1611 var currentBadCertificateCallback = client._badCertificateCallback; |
1614 | 1612 |
1615 bool callback(X509Certificate certificate) { | 1613 bool callback(X509Certificate certificate) { |
1616 if (currentBadCertificateCallback == null) return false; | 1614 if (currentBadCertificateCallback == null) return false; |
1617 return currentBadCertificateCallback(certificate, uriHost, uriPort); | 1615 return currentBadCertificateCallback(certificate, uriHost, uriPort); |
1618 } | 1616 } |
1619 | 1617 |
1620 Future socketFuture = (isSecure && proxy.isDirect | 1618 Future socketFuture = (isSecure && proxy.isDirect |
1621 ? SecureSocket.connect(host, | 1619 ? SecureSocket.connect(host, port, |
1622 port, | 1620 context: context, onBadCertificate: callback) |
1623 context: context, | |
1624 onBadCertificate: callback) | |
1625 : Socket.connect(host, port)); | 1621 : Socket.connect(host, port)); |
1626 _connecting++; | 1622 _connecting++; |
1627 return socketFuture.then((socket) { | 1623 return socketFuture.then((socket) { |
1628 _connecting--; | 1624 _connecting--; |
1629 socket.setOption(SocketOption.TCP_NODELAY, true); | 1625 socket.setOption(SocketOption.TCP_NODELAY, true); |
1630 var connection = | 1626 var connection = |
1631 new _HttpClientConnection(key, socket, client, false, context); | 1627 new _HttpClientConnection(key, socket, client, false, context); |
1632 if (isSecure && !proxy.isDirect) { | 1628 if (isSecure && !proxy.isDirect) { |
1633 connection._dispose = true; | 1629 connection._dispose = true; |
1634 return connection.createProxyTunnel(uriHost, uriPort, proxy, callback) | 1630 return connection |
1635 .then((tunnel) { | 1631 .createProxyTunnel(uriHost, uriPort, proxy, callback) |
1636 client._getConnectionTarget(uriHost, uriPort, true) | 1632 .then((tunnel) { |
1637 .addNewActive(tunnel); | 1633 client |
1638 return new _ConnectionInfo(tunnel, proxy); | 1634 ._getConnectionTarget(uriHost, uriPort, true) |
1639 }); | 1635 .addNewActive(tunnel); |
1640 } else { | 1636 return new _ConnectionInfo(tunnel, proxy); |
1641 addNewActive(connection); | 1637 }); |
1642 return new _ConnectionInfo(connection, proxy); | 1638 } else { |
1643 } | 1639 addNewActive(connection); |
1644 }, onError: (error) { | 1640 return new _ConnectionInfo(connection, proxy); |
1645 _connecting--; | 1641 } |
1646 _checkPending(); | 1642 }, onError: (error) { |
1647 throw error; | 1643 _connecting--; |
1648 }); | 1644 _checkPending(); |
| 1645 throw error; |
| 1646 }); |
1649 } | 1647 } |
1650 } | 1648 } |
1651 | 1649 |
1652 typedef bool BadCertificateCallback(X509Certificate cr, String host, int port); | 1650 typedef bool BadCertificateCallback(X509Certificate cr, String host, int port); |
1653 | 1651 |
1654 class _HttpClient implements HttpClient { | 1652 class _HttpClient implements HttpClient { |
1655 bool _closing = false; | 1653 bool _closing = false; |
1656 bool _closingForcefully = false; | 1654 bool _closingForcefully = false; |
1657 final Map<String, _ConnectionTarget> _connectionTargets | 1655 final Map<String, _ConnectionTarget> _connectionTargets = |
1658 = new HashMap<String, _ConnectionTarget>(); | 1656 new HashMap<String, _ConnectionTarget>(); |
1659 final List<_Credentials> _credentials = []; | 1657 final List<_Credentials> _credentials = []; |
1660 final List<_ProxyCredentials> _proxyCredentials = []; | 1658 final List<_ProxyCredentials> _proxyCredentials = []; |
1661 final SecurityContext _context; | 1659 final SecurityContext _context; |
1662 Function _authenticate; | 1660 Function _authenticate; |
1663 Function _authenticateProxy; | 1661 Function _authenticateProxy; |
1664 Function _findProxy = HttpClient.findProxyFromEnvironment; | 1662 Function _findProxy = HttpClient.findProxyFromEnvironment; |
1665 Duration _idleTimeout = const Duration(seconds: 15); | 1663 Duration _idleTimeout = const Duration(seconds: 15); |
1666 BadCertificateCallback _badCertificateCallback; | 1664 BadCertificateCallback _badCertificateCallback; |
1667 | 1665 |
1668 Duration get idleTimeout => _idleTimeout; | 1666 Duration get idleTimeout => _idleTimeout; |
(...skipping 10 matching lines...) Expand all Loading... |
1679 _idleTimeout = timeout; | 1677 _idleTimeout = timeout; |
1680 for (var c in _connectionTargets.values) { | 1678 for (var c in _connectionTargets.values) { |
1681 for (var idle in c._idle) { | 1679 for (var idle in c._idle) { |
1682 // Reset timer. This is fine, as it's not happening often. | 1680 // Reset timer. This is fine, as it's not happening often. |
1683 idle.stopTimer(); | 1681 idle.stopTimer(); |
1684 idle.startTimer(); | 1682 idle.startTimer(); |
1685 } | 1683 } |
1686 } | 1684 } |
1687 } | 1685 } |
1688 | 1686 |
1689 set badCertificateCallback(bool callback(X509Certificate cert, | 1687 set badCertificateCallback( |
1690 String host, | 1688 bool callback(X509Certificate cert, String host, int port)) { |
1691 int port)) { | |
1692 _badCertificateCallback = callback; | 1689 _badCertificateCallback = callback; |
1693 } | 1690 } |
1694 | 1691 |
1695 | 1692 Future<HttpClientRequest> open( |
1696 Future<HttpClientRequest> open(String method, | 1693 String method, String host, int port, String path) { |
1697 String host, | |
1698 int port, | |
1699 String path) { | |
1700 const int hashMark = 0x23; | 1694 const int hashMark = 0x23; |
1701 const int questionMark = 0x3f; | 1695 const int questionMark = 0x3f; |
1702 int fragmentStart = path.length; | 1696 int fragmentStart = path.length; |
1703 int queryStart = path.length; | 1697 int queryStart = path.length; |
1704 for (int i = path.length - 1; i >= 0; i--) { | 1698 for (int i = path.length - 1; i >= 0; i--) { |
1705 var char = path.codeUnitAt(i); | 1699 var char = path.codeUnitAt(i); |
1706 if (char == hashMark) { | 1700 if (char == hashMark) { |
1707 fragmentStart = i; | 1701 fragmentStart = i; |
1708 queryStart = i; | 1702 queryStart = i; |
1709 } else if (char == questionMark) { | 1703 } else if (char == questionMark) { |
1710 queryStart = i; | 1704 queryStart = i; |
1711 } | 1705 } |
1712 } | 1706 } |
1713 String query = null; | 1707 String query = null; |
1714 if (queryStart < fragmentStart) { | 1708 if (queryStart < fragmentStart) { |
1715 query = path.substring(queryStart + 1, fragmentStart); | 1709 query = path.substring(queryStart + 1, fragmentStart); |
1716 path = path.substring(0, queryStart); | 1710 path = path.substring(0, queryStart); |
1717 } | 1711 } |
1718 Uri uri = new Uri(scheme: "http", host: host, port: port, | 1712 Uri uri = new Uri( |
1719 path: path, query: query); | 1713 scheme: "http", host: host, port: port, path: path, query: query); |
1720 return _openUrl(method, uri); | 1714 return _openUrl(method, uri); |
1721 } | 1715 } |
1722 | 1716 |
1723 Future<HttpClientRequest> openUrl(String method, Uri url) | 1717 Future<HttpClientRequest> openUrl(String method, Uri url) => |
1724 => _openUrl(method, url); | 1718 _openUrl(method, url); |
1725 | 1719 |
1726 Future<HttpClientRequest> get(String host, int port, String path) | 1720 Future<HttpClientRequest> get(String host, int port, String path) => |
1727 => open("get", host, port, path); | 1721 open("get", host, port, path); |
1728 | 1722 |
1729 Future<HttpClientRequest> getUrl(Uri url) => _openUrl("get", url); | 1723 Future<HttpClientRequest> getUrl(Uri url) => _openUrl("get", url); |
1730 | 1724 |
1731 Future<HttpClientRequest> post(String host, int port, String path) | 1725 Future<HttpClientRequest> post(String host, int port, String path) => |
1732 => open("post", host, port, path); | 1726 open("post", host, port, path); |
1733 | 1727 |
1734 Future<HttpClientRequest> postUrl(Uri url) => _openUrl("post", url); | 1728 Future<HttpClientRequest> postUrl(Uri url) => _openUrl("post", url); |
1735 | 1729 |
1736 Future<HttpClientRequest> put(String host, int port, String path) | 1730 Future<HttpClientRequest> put(String host, int port, String path) => |
1737 => open("put", host, port, path); | 1731 open("put", host, port, path); |
1738 | 1732 |
1739 Future<HttpClientRequest> putUrl(Uri url) => _openUrl("put", url); | 1733 Future<HttpClientRequest> putUrl(Uri url) => _openUrl("put", url); |
1740 | 1734 |
1741 Future<HttpClientRequest> delete(String host, int port, String path) | 1735 Future<HttpClientRequest> delete(String host, int port, String path) => |
1742 => open("delete", host, port, path); | 1736 open("delete", host, port, path); |
1743 | 1737 |
1744 Future<HttpClientRequest> deleteUrl(Uri url) => _openUrl("delete", url); | 1738 Future<HttpClientRequest> deleteUrl(Uri url) => _openUrl("delete", url); |
1745 | 1739 |
1746 Future<HttpClientRequest> head(String host, int port, String path) | 1740 Future<HttpClientRequest> head(String host, int port, String path) => |
1747 => open("head", host, port, path); | 1741 open("head", host, port, path); |
1748 | 1742 |
1749 Future<HttpClientRequest> headUrl(Uri url) => _openUrl("head", url); | 1743 Future<HttpClientRequest> headUrl(Uri url) => _openUrl("head", url); |
1750 | 1744 |
1751 Future<HttpClientRequest> patch(String host, int port, String path) | 1745 Future<HttpClientRequest> patch(String host, int port, String path) => |
1752 => open("patch", host, port, path); | 1746 open("patch", host, port, path); |
1753 | 1747 |
1754 Future<HttpClientRequest> patchUrl(Uri url) => _openUrl("patch", url); | 1748 Future<HttpClientRequest> patchUrl(Uri url) => _openUrl("patch", url); |
1755 | 1749 |
1756 void close({bool force: false}) { | 1750 void close({bool force: false}) { |
1757 _closing = true; | 1751 _closing = true; |
1758 _closingForcefully = force; | 1752 _closingForcefully = force; |
1759 _closeConnections(_closingForcefully); | 1753 _closeConnections(_closingForcefully); |
1760 assert(!_connectionTargets.values.any((s) => s.hasIdle)); | 1754 assert(!_connectionTargets.values.any((s) => s.hasIdle)); |
1761 assert(!force || | 1755 assert( |
1762 !_connectionTargets.values.any((s) => s._active.isNotEmpty)); | 1756 !force || !_connectionTargets.values.any((s) => s._active.isNotEmpty)); |
1763 } | 1757 } |
1764 | 1758 |
1765 set authenticate(Future<bool> f(Uri url, String scheme, String realm)) { | 1759 set authenticate(Future<bool> f(Uri url, String scheme, String realm)) { |
1766 _authenticate = f; | 1760 _authenticate = f; |
1767 } | 1761 } |
1768 | 1762 |
1769 void addCredentials(Uri url, String realm, HttpClientCredentials cr) { | 1763 void addCredentials(Uri url, String realm, HttpClientCredentials cr) { |
1770 _credentials.add(new _SiteCredentials(url, realm, cr)); | 1764 _credentials.add(new _SiteCredentials(url, realm, cr)); |
1771 } | 1765 } |
1772 | 1766 |
1773 set authenticateProxy( | 1767 set authenticateProxy( |
1774 Future<bool> f(String host, int port, String scheme, String realm)) { | 1768 Future<bool> f(String host, int port, String scheme, String realm)) { |
1775 _authenticateProxy = f; | 1769 _authenticateProxy = f; |
1776 } | 1770 } |
1777 | 1771 |
1778 void addProxyCredentials(String host, | 1772 void addProxyCredentials( |
1779 int port, | 1773 String host, int port, String realm, HttpClientCredentials cr) { |
1780 String realm, | |
1781 HttpClientCredentials cr) { | |
1782 _proxyCredentials.add(new _ProxyCredentials(host, port, realm, cr)); | 1774 _proxyCredentials.add(new _ProxyCredentials(host, port, realm, cr)); |
1783 } | 1775 } |
1784 | 1776 |
1785 set findProxy(String f(Uri uri)) => _findProxy = f; | 1777 set findProxy(String f(Uri uri)) => _findProxy = f; |
1786 | 1778 |
1787 Future<_HttpClientRequest> _openUrl(String method, Uri uri) { | 1779 Future<_HttpClientRequest> _openUrl(String method, Uri uri) { |
1788 // Ignore any fragments on the request URI. | 1780 // Ignore any fragments on the request URI. |
1789 uri = uri.removeFragment(); | 1781 uri = uri.removeFragment(); |
1790 | 1782 |
1791 if (method == null) { | 1783 if (method == null) { |
1792 throw new ArgumentError(method); | 1784 throw new ArgumentError(method); |
1793 } | 1785 } |
1794 if (method != "CONNECT") { | 1786 if (method != "CONNECT") { |
1795 if (uri.host.isEmpty) { | 1787 if (uri.host.isEmpty) { |
1796 throw new ArgumentError("No host specified in URI $uri"); | 1788 throw new ArgumentError("No host specified in URI $uri"); |
1797 } else if (uri.scheme != "http" && uri.scheme != "https") { | 1789 } else if (uri.scheme != "http" && uri.scheme != "https") { |
1798 throw new ArgumentError( | 1790 throw new ArgumentError( |
1799 "Unsupported scheme '${uri.scheme}' in URI $uri"); | 1791 "Unsupported scheme '${uri.scheme}' in URI $uri"); |
1800 } | 1792 } |
1801 } | 1793 } |
1802 | 1794 |
1803 bool isSecure = (uri.scheme == "https"); | 1795 bool isSecure = (uri.scheme == "https"); |
1804 int port = uri.port; | 1796 int port = uri.port; |
1805 if (port == 0) { | 1797 if (port == 0) { |
1806 port = isSecure ? | 1798 port = isSecure |
1807 HttpClient.DEFAULT_HTTPS_PORT : | 1799 ? HttpClient.DEFAULT_HTTPS_PORT |
1808 HttpClient.DEFAULT_HTTP_PORT; | 1800 : HttpClient.DEFAULT_HTTP_PORT; |
1809 } | 1801 } |
1810 // Check to see if a proxy server should be used for this connection. | 1802 // Check to see if a proxy server should be used for this connection. |
1811 var proxyConf = const _ProxyConfiguration.direct(); | 1803 var proxyConf = const _ProxyConfiguration.direct(); |
1812 if (_findProxy != null) { | 1804 if (_findProxy != null) { |
1813 // TODO(sgjesse): Keep a map of these as normally only a few | 1805 // TODO(sgjesse): Keep a map of these as normally only a few |
1814 // configuration strings will be used. | 1806 // configuration strings will be used. |
1815 try { | 1807 try { |
1816 proxyConf = new _ProxyConfiguration(_findProxy(uri)); | 1808 proxyConf = new _ProxyConfiguration(_findProxy(uri)); |
1817 } catch (error, stackTrace) { | 1809 } catch (error, stackTrace) { |
1818 return new Future.error(error, stackTrace); | 1810 return new Future.error(error, stackTrace); |
1819 } | 1811 } |
1820 } | 1812 } |
1821 return _getConnection(uri.host, port, proxyConf, isSecure) | 1813 return _getConnection(uri.host, port, proxyConf, isSecure) |
1822 .then((_ConnectionInfo info) { | 1814 .then((_ConnectionInfo info) { |
| 1815 _HttpClientRequest send(_ConnectionInfo info) { |
| 1816 return info.connection |
| 1817 .send(uri, port, method.toUpperCase(), info.proxy); |
| 1818 } |
1823 | 1819 |
1824 _HttpClientRequest send(_ConnectionInfo info) { | 1820 // If the connection was closed before the request was sent, create |
1825 return info.connection.send(uri, | 1821 // and use another connection. |
1826 port, | 1822 if (info.connection.closed) { |
1827 method.toUpperCase(), | 1823 return _getConnection(uri.host, port, proxyConf, isSecure).then(send); |
1828 info.proxy); | 1824 } |
1829 } | 1825 return send(info); |
1830 | 1826 }); |
1831 // If the connection was closed before the request was sent, create | |
1832 // and use another connection. | |
1833 if (info.connection.closed) { | |
1834 return _getConnection(uri.host, port, proxyConf, isSecure) | |
1835 .then(send); | |
1836 } | |
1837 return send(info); | |
1838 }); | |
1839 } | 1827 } |
1840 | 1828 |
1841 Future<_HttpClientRequest> _openUrlFromRequest(String method, | 1829 Future<_HttpClientRequest> _openUrlFromRequest( |
1842 Uri uri, | 1830 String method, Uri uri, _HttpClientRequest previous) { |
1843 _HttpClientRequest previous) { | |
1844 // If the new URI is relative (to either '/' or some sub-path), | 1831 // If the new URI is relative (to either '/' or some sub-path), |
1845 // construct a full URI from the previous one. | 1832 // construct a full URI from the previous one. |
1846 Uri resolved = previous.uri.resolveUri(uri); | 1833 Uri resolved = previous.uri.resolveUri(uri); |
1847 return _openUrl(method, resolved).then((_HttpClientRequest request) { | 1834 return _openUrl(method, resolved).then((_HttpClientRequest request) { |
1848 | 1835 request |
1849 request | 1836 // Only follow redirects if initial request did. |
1850 // Only follow redirects if initial request did. | 1837 ..followRedirects = previous.followRedirects |
1851 ..followRedirects = previous.followRedirects | 1838 // Allow same number of redirects. |
1852 // Allow same number of redirects. | 1839 ..maxRedirects = previous.maxRedirects; |
1853 ..maxRedirects = previous.maxRedirects; | 1840 // Copy headers. |
1854 // Copy headers. | 1841 for (var header in previous.headers._headers.keys) { |
1855 for (var header in previous.headers._headers.keys) { | 1842 if (request.headers[header] == null) { |
1856 if (request.headers[header] == null) { | 1843 request.headers.set(header, previous.headers[header]); |
1857 request.headers.set(header, previous.headers[header]); | 1844 } |
1858 } | 1845 } |
1859 } | 1846 return request |
1860 return request | 1847 ..headers.chunkedTransferEncoding = false |
1861 ..headers.chunkedTransferEncoding = false | 1848 ..contentLength = 0; |
1862 ..contentLength = 0; | 1849 }); |
1863 }); | |
1864 } | 1850 } |
1865 | 1851 |
1866 // Return a live connection to the idle pool. | 1852 // Return a live connection to the idle pool. |
1867 void _returnConnection(_HttpClientConnection connection) { | 1853 void _returnConnection(_HttpClientConnection connection) { |
1868 _connectionTargets[connection.key].returnConnection(connection); | 1854 _connectionTargets[connection.key].returnConnection(connection); |
1869 _connectionsChanged(); | 1855 _connectionsChanged(); |
1870 } | 1856 } |
1871 | 1857 |
1872 // Remove a closed connnection from the active set. | 1858 // Remove a closed connnection from the active set. |
1873 void _connectionClosed(_HttpClientConnection connection) { | 1859 void _connectionClosed(_HttpClientConnection connection) { |
(...skipping 21 matching lines...) Expand all Loading... |
1895 } | 1881 } |
1896 | 1882 |
1897 _ConnectionTarget _getConnectionTarget(String host, int port, bool isSecure) { | 1883 _ConnectionTarget _getConnectionTarget(String host, int port, bool isSecure) { |
1898 String key = _HttpClientConnection.makeKey(isSecure, host, port); | 1884 String key = _HttpClientConnection.makeKey(isSecure, host, port); |
1899 return _connectionTargets.putIfAbsent(key, () { | 1885 return _connectionTargets.putIfAbsent(key, () { |
1900 return new _ConnectionTarget(key, host, port, isSecure, _context); | 1886 return new _ConnectionTarget(key, host, port, isSecure, _context); |
1901 }); | 1887 }); |
1902 } | 1888 } |
1903 | 1889 |
1904 // Get a new _HttpClientConnection, from the matching _ConnectionTarget. | 1890 // Get a new _HttpClientConnection, from the matching _ConnectionTarget. |
1905 Future<_ConnectionInfo> _getConnection(String uriHost, | 1891 Future<_ConnectionInfo> _getConnection(String uriHost, int uriPort, |
1906 int uriPort, | 1892 _ProxyConfiguration proxyConf, bool isSecure) { |
1907 _ProxyConfiguration proxyConf, | |
1908 bool isSecure) { | |
1909 Iterator<_Proxy> proxies = proxyConf.proxies.iterator; | 1893 Iterator<_Proxy> proxies = proxyConf.proxies.iterator; |
1910 | 1894 |
1911 Future<_ConnectionInfo> connect(error) { | 1895 Future<_ConnectionInfo> connect(error) { |
1912 if (!proxies.moveNext()) return new Future.error(error); | 1896 if (!proxies.moveNext()) return new Future.error(error); |
1913 _Proxy proxy = proxies.current; | 1897 _Proxy proxy = proxies.current; |
1914 String host = proxy.isDirect ? uriHost: proxy.host; | 1898 String host = proxy.isDirect ? uriHost : proxy.host; |
1915 int port = proxy.isDirect ? uriPort: proxy.port; | 1899 int port = proxy.isDirect ? uriPort : proxy.port; |
1916 return _getConnectionTarget(host, port, isSecure) | 1900 return _getConnectionTarget(host, port, isSecure) |
1917 .connect(uriHost, uriPort, proxy, this) | 1901 .connect(uriHost, uriPort, proxy, this) |
1918 // On error, continue with next proxy. | 1902 // On error, continue with next proxy. |
1919 .catchError(connect); | 1903 .catchError(connect); |
1920 } | 1904 } |
| 1905 |
1921 // Make sure we go through the event loop before taking a | 1906 // Make sure we go through the event loop before taking a |
1922 // connection from the pool. For long-running synchronous code the | 1907 // connection from the pool. For long-running synchronous code the |
1923 // server might have closed the connection, so this lowers the | 1908 // server might have closed the connection, so this lowers the |
1924 // probability of getting a connection that was already closed. | 1909 // probability of getting a connection that was already closed. |
1925 return new Future<_ConnectionInfo>( | 1910 return new Future<_ConnectionInfo>( |
1926 () => connect(new HttpException("No proxies given"))); | 1911 () => connect(new HttpException("No proxies given"))); |
1927 } | 1912 } |
1928 | 1913 |
1929 _SiteCredentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) { | 1914 _SiteCredentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) { |
1930 // Look for credentials. | 1915 // Look for credentials. |
1931 _SiteCredentials cr = | 1916 _SiteCredentials cr = |
1932 _credentials.fold(null, (_SiteCredentials prev, value) { | 1917 _credentials.fold(null, (_SiteCredentials prev, value) { |
1933 var siteCredentials = value as _SiteCredentials; | 1918 var siteCredentials = value as _SiteCredentials; |
1934 if (siteCredentials.applies(url, scheme)) { | 1919 if (siteCredentials.applies(url, scheme)) { |
1935 if (prev == null) return value; | 1920 if (prev == null) return value; |
1936 return siteCredentials.uri.path.length > prev.uri.path.length | 1921 return siteCredentials.uri.path.length > prev.uri.path.length |
1937 ? siteCredentials | 1922 ? siteCredentials |
1938 : prev; | 1923 : prev; |
1939 } else { | 1924 } else { |
1940 return prev; | 1925 return prev; |
1941 } | 1926 } |
1942 }); | 1927 }); |
1943 return cr; | 1928 return cr; |
1944 } | 1929 } |
1945 | 1930 |
1946 _ProxyCredentials _findProxyCredentials(_Proxy proxy, | 1931 _ProxyCredentials _findProxyCredentials(_Proxy proxy, |
1947 [_AuthenticationScheme scheme]) { | 1932 [_AuthenticationScheme scheme]) { |
1948 // Look for credentials. | 1933 // Look for credentials. |
1949 var it = _proxyCredentials.iterator; | 1934 var it = _proxyCredentials.iterator; |
1950 while (it.moveNext()) { | 1935 while (it.moveNext()) { |
1951 if (it.current.applies(proxy, scheme)) { | 1936 if (it.current.applies(proxy, scheme)) { |
1952 return it.current; | 1937 return it.current; |
1953 } | 1938 } |
1954 } | 1939 } |
1955 return null; | 1940 return null; |
1956 } | 1941 } |
1957 | 1942 |
1958 void _removeCredentials(_Credentials cr) { | 1943 void _removeCredentials(_Credentials cr) { |
1959 int index = _credentials.indexOf(cr); | 1944 int index = _credentials.indexOf(cr); |
1960 if (index != -1) { | 1945 if (index != -1) { |
1961 _credentials.removeAt(index); | 1946 _credentials.removeAt(index); |
1962 } | 1947 } |
1963 } | 1948 } |
1964 | 1949 |
1965 void _removeProxyCredentials(_Credentials cr) { | 1950 void _removeProxyCredentials(_Credentials cr) { |
1966 int index = _proxyCredentials.indexOf(cr); | 1951 int index = _proxyCredentials.indexOf(cr); |
1967 if (index != -1) { | 1952 if (index != -1) { |
1968 _proxyCredentials.removeAt(index); | 1953 _proxyCredentials.removeAt(index); |
1969 } | 1954 } |
1970 } | 1955 } |
1971 | 1956 |
1972 static String _findProxyFromEnvironment(Uri url, | 1957 static String _findProxyFromEnvironment( |
1973 Map<String, String> environment) { | 1958 Uri url, Map<String, String> environment) { |
1974 checkNoProxy(String option) { | 1959 checkNoProxy(String option) { |
1975 if (option == null) return null; | 1960 if (option == null) return null; |
1976 Iterator<String> names = option.split(",").map((s) => s.trim()).iterator; | 1961 Iterator<String> names = option.split(",").map((s) => s.trim()).iterator; |
1977 while (names.moveNext()) { | 1962 while (names.moveNext()) { |
1978 var name = names.current; | 1963 var name = names.current; |
1979 if ((name.startsWith("[") && | 1964 if ((name.startsWith("[") && |
1980 name.endsWith("]") && | 1965 name.endsWith("]") && |
1981 "[${url.host}]" == name) || | 1966 "[${url.host}]" == name) || |
1982 (name.isNotEmpty && | 1967 (name.isNotEmpty && url.host.endsWith(name))) { |
1983 url.host.endsWith(name))) { | |
1984 return "DIRECT"; | 1968 return "DIRECT"; |
1985 } | 1969 } |
1986 } | 1970 } |
1987 return null; | 1971 return null; |
1988 } | 1972 } |
1989 | 1973 |
1990 checkProxy(String option) { | 1974 checkProxy(String option) { |
1991 if (option == null) return null; | 1975 if (option == null) return null; |
1992 option = option.trim(); | 1976 option = option.trim(); |
1993 if (option.isEmpty) return null; | 1977 if (option.isEmpty) return null; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2032 if ((proxyCfg = checkProxy(proxy)) != null) { | 2016 if ((proxyCfg = checkProxy(proxy)) != null) { |
2033 return proxyCfg; | 2017 return proxyCfg; |
2034 } | 2018 } |
2035 } | 2019 } |
2036 return "DIRECT"; | 2020 return "DIRECT"; |
2037 } | 2021 } |
2038 | 2022 |
2039 static Map<String, String> _platformEnvironmentCache = Platform.environment; | 2023 static Map<String, String> _platformEnvironmentCache = Platform.environment; |
2040 } | 2024 } |
2041 | 2025 |
2042 | 2026 class _HttpConnection extends LinkedListEntry<_HttpConnection> |
2043 class _HttpConnection | 2027 with _ServiceObject { |
2044 extends LinkedListEntry<_HttpConnection> with _ServiceObject { | |
2045 static const _ACTIVE = 0; | 2028 static const _ACTIVE = 0; |
2046 static const _IDLE = 1; | 2029 static const _IDLE = 1; |
2047 static const _CLOSING = 2; | 2030 static const _CLOSING = 2; |
2048 static const _DETACHED = 3; | 2031 static const _DETACHED = 3; |
2049 | 2032 |
2050 // Use HashMap, as we don't need to keep order. | 2033 // Use HashMap, as we don't need to keep order. |
2051 static Map<int, _HttpConnection> _connections = | 2034 static Map<int, _HttpConnection> _connections = |
2052 new HashMap<int, _HttpConnection>(); | 2035 new HashMap<int, _HttpConnection>(); |
2053 | 2036 |
2054 final /*_ServerSocket*/ _socket; | 2037 final /*_ServerSocket*/ _socket; |
2055 final _HttpServer _httpServer; | 2038 final _HttpServer _httpServer; |
2056 final _HttpParser _httpParser; | 2039 final _HttpParser _httpParser; |
2057 int _state = _IDLE; | 2040 int _state = _IDLE; |
2058 StreamSubscription _subscription; | 2041 StreamSubscription _subscription; |
2059 bool _idleMark = false; | 2042 bool _idleMark = false; |
2060 Future _streamFuture; | 2043 Future _streamFuture; |
2061 | 2044 |
2062 _HttpConnection(this._socket, this._httpServer) | 2045 _HttpConnection(this._socket, this._httpServer) |
2063 : _httpParser = new _HttpParser.requestParser() { | 2046 : _httpParser = new _HttpParser.requestParser() { |
2064 try { _socket._owner = this; } catch (_) { print(_); } | 2047 try { |
| 2048 _socket._owner = this; |
| 2049 } catch (_) { |
| 2050 print(_); |
| 2051 } |
2065 _connections[_serviceId] = this; | 2052 _connections[_serviceId] = this; |
2066 _httpParser.listenToStream(_socket as Object/*=Socket*/); | 2053 _httpParser.listenToStream(_socket as Object/*=Socket*/); |
2067 _subscription = _httpParser.listen( | 2054 _subscription = _httpParser.listen((incoming) { |
2068 (incoming) { | 2055 _httpServer._markActive(this); |
2069 _httpServer._markActive(this); | 2056 // If the incoming was closed, close the connection. |
2070 // If the incoming was closed, close the connection. | 2057 incoming.dataDone.then((closing) { |
2071 incoming.dataDone.then((closing) { | 2058 if (closing) destroy(); |
2072 if (closing) destroy(); | 2059 }); |
2073 }); | 2060 // Only handle one incoming request at the time. Keep the |
2074 // Only handle one incoming request at the time. Keep the | 2061 // stream paused until the request has been send. |
2075 // stream paused until the request has been send. | 2062 _subscription.pause(); |
2076 _subscription.pause(); | 2063 _state = _ACTIVE; |
2077 _state = _ACTIVE; | 2064 var outgoing = new _HttpOutgoing(_socket); |
2078 var outgoing = new _HttpOutgoing(_socket); | 2065 var response = new _HttpResponse( |
2079 var response = new _HttpResponse(incoming.uri, | 2066 incoming.uri, |
2080 incoming.headers.protocolVersion, | 2067 incoming.headers.protocolVersion, |
2081 outgoing, | 2068 outgoing, |
2082 _httpServer.defaultResponseHeaders, | 2069 _httpServer.defaultResponseHeaders, |
2083 _httpServer.serverHeader); | 2070 _httpServer.serverHeader); |
2084 var request = new _HttpRequest(response, incoming, _httpServer, this); | 2071 var request = new _HttpRequest(response, incoming, _httpServer, this); |
2085 _streamFuture = outgoing.done | 2072 _streamFuture = outgoing.done.then((_) { |
2086 .then((_) { | 2073 response.deadline = null; |
2087 response.deadline = null; | 2074 if (_state == _DETACHED) return; |
2088 if (_state == _DETACHED) return; | 2075 if (response.persistentConnection && |
2089 if (response.persistentConnection && | 2076 request.persistentConnection && |
2090 request.persistentConnection && | 2077 incoming.fullBodyRead && |
2091 incoming.fullBodyRead && | 2078 !_httpParser.upgrade && |
2092 !_httpParser.upgrade && | 2079 !_httpServer.closed) { |
2093 !_httpServer.closed) { | 2080 _state = _IDLE; |
2094 _state = _IDLE; | 2081 _idleMark = false; |
2095 _idleMark = false; | 2082 _httpServer._markIdle(this); |
2096 _httpServer._markIdle(this); | 2083 // Resume the subscription for incoming requests as the |
2097 // Resume the subscription for incoming requests as the | 2084 // request is now processed. |
2098 // request is now processed. | 2085 _subscription.resume(); |
2099 _subscription.resume(); | 2086 } else { |
2100 } else { | 2087 // Close socket, keep-alive not used or body sent before |
2101 // Close socket, keep-alive not used or body sent before | 2088 // received data was handled. |
2102 // received data was handled. | |
2103 destroy(); | |
2104 } | |
2105 }, onError: (_) { | |
2106 destroy(); | |
2107 }); | |
2108 outgoing.ignoreBody = request.method == "HEAD"; | |
2109 response._httpRequest = request; | |
2110 _httpServer._handleRequest(request); | |
2111 }, | |
2112 onDone: () { | |
2113 destroy(); | 2089 destroy(); |
2114 }, | 2090 } |
2115 onError: (error) { | 2091 }, onError: (_) { |
2116 // Ignore failed requests that was closed before headers was received. | 2092 destroy(); |
2117 destroy(); | 2093 }); |
2118 }); | 2094 outgoing.ignoreBody = request.method == "HEAD"; |
| 2095 response._httpRequest = request; |
| 2096 _httpServer._handleRequest(request); |
| 2097 }, onDone: () { |
| 2098 destroy(); |
| 2099 }, onError: (error) { |
| 2100 // Ignore failed requests that was closed before headers was received. |
| 2101 destroy(); |
| 2102 }); |
2119 } | 2103 } |
2120 | 2104 |
2121 void markIdle() { | 2105 void markIdle() { |
2122 _idleMark = true; | 2106 _idleMark = true; |
2123 } | 2107 } |
2124 | 2108 |
2125 bool get isMarkedIdle => _idleMark; | 2109 bool get isMarkedIdle => _idleMark; |
2126 | 2110 |
2127 void destroy() { | 2111 void destroy() { |
2128 if (_state == _CLOSING || _state == _DETACHED) return; | 2112 if (_state == _CLOSING || _state == _DETACHED) return; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2172 r['socket'] = _socket._toJSON(true); | 2156 r['socket'] = _socket._toJSON(true); |
2173 } catch (_) { | 2157 } catch (_) { |
2174 r['socket'] = { | 2158 r['socket'] = { |
2175 'id': _servicePath, | 2159 'id': _servicePath, |
2176 'type': '@Socket', | 2160 'type': '@Socket', |
2177 'name': 'UserSocket', | 2161 'name': 'UserSocket', |
2178 'user_name': 'UserSocket', | 2162 'user_name': 'UserSocket', |
2179 }; | 2163 }; |
2180 } | 2164 } |
2181 switch (_state) { | 2165 switch (_state) { |
2182 case _ACTIVE: r['state'] = "Active"; break; | 2166 case _ACTIVE: |
2183 case _IDLE: r['state'] = "Idle"; break; | 2167 r['state'] = "Active"; |
2184 case _CLOSING: r['state'] = "Closing"; break; | 2168 break; |
2185 case _DETACHED: r['state'] = "Detached"; break; | 2169 case _IDLE: |
2186 default: r['state'] = 'Unknown'; break; | 2170 r['state'] = "Idle"; |
| 2171 break; |
| 2172 case _CLOSING: |
| 2173 r['state'] = "Closing"; |
| 2174 break; |
| 2175 case _DETACHED: |
| 2176 r['state'] = "Detached"; |
| 2177 break; |
| 2178 default: |
| 2179 r['state'] = 'Unknown'; |
| 2180 break; |
2187 } | 2181 } |
2188 return r; | 2182 return r; |
2189 } | 2183 } |
2190 } | 2184 } |
2191 | 2185 |
2192 | |
2193 // HTTP server waiting for socket connections. | 2186 // HTTP server waiting for socket connections. |
2194 class _HttpServer | 2187 class _HttpServer extends Stream<HttpRequest> |
2195 extends Stream<HttpRequest> with _ServiceObject | 2188 with _ServiceObject |
2196 implements HttpServer { | 2189 implements HttpServer { |
2197 // Use default Map so we keep order. | 2190 // Use default Map so we keep order. |
2198 static Map<int, _HttpServer> _servers = new Map<int, _HttpServer>(); | 2191 static Map<int, _HttpServer> _servers = new Map<int, _HttpServer>(); |
2199 | 2192 |
2200 String serverHeader; | 2193 String serverHeader; |
2201 final HttpHeaders defaultResponseHeaders = _initDefaultResponseHeaders(); | 2194 final HttpHeaders defaultResponseHeaders = _initDefaultResponseHeaders(); |
2202 bool autoCompress = false; | 2195 bool autoCompress = false; |
2203 | 2196 |
2204 Duration _idleTimeout; | 2197 Duration _idleTimeout; |
2205 Timer _idleTimer; | 2198 Timer _idleTimer; |
2206 | 2199 |
2207 static Future<HttpServer> bind( | 2200 static Future<HttpServer> bind( |
2208 address, int port, int backlog, bool v6Only, bool shared) { | 2201 address, int port, int backlog, bool v6Only, bool shared) { |
2209 return ServerSocket.bind( | 2202 return ServerSocket |
2210 address, port, backlog: backlog, v6Only: v6Only, shared: shared) | 2203 .bind(address, port, backlog: backlog, v6Only: v6Only, shared: shared) |
2211 .then((socket) { | 2204 .then((socket) { |
2212 return new _HttpServer._(socket, true); | 2205 return new _HttpServer._(socket, true); |
2213 }); | 2206 }); |
2214 } | 2207 } |
2215 | 2208 |
2216 static Future<HttpServer> bindSecure(address, | 2209 static Future<HttpServer> bindSecure( |
2217 int port, | 2210 address, |
2218 SecurityContext context, | 2211 int port, |
2219 int backlog, | 2212 SecurityContext context, |
2220 bool v6Only, | 2213 int backlog, |
2221 bool requestClientCertificate, | 2214 bool v6Only, |
2222 bool shared) { | 2215 bool requestClientCertificate, |
2223 return SecureServerSocket.bind( | 2216 bool shared) { |
2224 address, | 2217 return SecureServerSocket |
2225 port, | 2218 .bind(address, port, context, |
2226 context, | 2219 backlog: backlog, |
2227 backlog: backlog, | 2220 v6Only: v6Only, |
2228 v6Only: v6Only, | 2221 requestClientCertificate: requestClientCertificate, |
2229 requestClientCertificate: requestClientCertificate, | 2222 shared: shared) |
2230 shared: shared) | 2223 .then((socket) { |
2231 .then((socket) { | 2224 return new _HttpServer._(socket, true); |
2232 return new _HttpServer._(socket, true); | 2225 }); |
2233 }); | |
2234 } | 2226 } |
2235 | 2227 |
2236 _HttpServer._(this._serverSocket, this._closeServer) { | 2228 _HttpServer._(this._serverSocket, this._closeServer) { |
2237 _controller = new StreamController<HttpRequest>(sync: true, | 2229 _controller = |
2238 onCancel: close); | 2230 new StreamController<HttpRequest>(sync: true, onCancel: close); |
2239 idleTimeout = const Duration(seconds: 120); | 2231 idleTimeout = const Duration(seconds: 120); |
2240 _servers[_serviceId] = this; | 2232 _servers[_serviceId] = this; |
2241 _serverSocket._owner = this; | 2233 _serverSocket._owner = this; |
2242 } | 2234 } |
2243 | 2235 |
2244 _HttpServer.listenOn(this._serverSocket) : _closeServer = false { | 2236 _HttpServer.listenOn(this._serverSocket) : _closeServer = false { |
2245 _controller = new StreamController<HttpRequest>(sync: true, | 2237 _controller = |
2246 onCancel: close); | 2238 new StreamController<HttpRequest>(sync: true, onCancel: close); |
2247 idleTimeout = const Duration(seconds: 120); | 2239 idleTimeout = const Duration(seconds: 120); |
2248 _servers[_serviceId] = this; | 2240 _servers[_serviceId] = this; |
2249 try { _serverSocket._owner = this; } catch (_) {} | 2241 try { |
| 2242 _serverSocket._owner = this; |
| 2243 } catch (_) {} |
2250 } | 2244 } |
2251 | 2245 |
2252 static HttpHeaders _initDefaultResponseHeaders() { | 2246 static HttpHeaders _initDefaultResponseHeaders() { |
2253 var defaultResponseHeaders = new _HttpHeaders('1.1'); | 2247 var defaultResponseHeaders = new _HttpHeaders('1.1'); |
2254 defaultResponseHeaders.contentType = ContentType.TEXT; | 2248 defaultResponseHeaders.contentType = ContentType.TEXT; |
2255 defaultResponseHeaders.set('X-Frame-Options', 'SAMEORIGIN'); | 2249 defaultResponseHeaders.set('X-Frame-Options', 'SAMEORIGIN'); |
2256 defaultResponseHeaders.set('X-Content-Type-Options', 'nosniff'); | 2250 defaultResponseHeaders.set('X-Content-Type-Options', 'nosniff'); |
2257 defaultResponseHeaders.set('X-XSS-Protection', '1; mode=block'); | 2251 defaultResponseHeaders.set('X-XSS-Protection', '1; mode=block'); |
2258 return defaultResponseHeaders; | 2252 return defaultResponseHeaders; |
2259 } | 2253 } |
(...skipping 13 matching lines...) Expand all Loading... |
2273 idle.destroy(); | 2267 idle.destroy(); |
2274 } else { | 2268 } else { |
2275 idle.markIdle(); | 2269 idle.markIdle(); |
2276 } | 2270 } |
2277 } | 2271 } |
2278 }); | 2272 }); |
2279 } | 2273 } |
2280 } | 2274 } |
2281 | 2275 |
2282 StreamSubscription<HttpRequest> listen(void onData(HttpRequest event), | 2276 StreamSubscription<HttpRequest> listen(void onData(HttpRequest event), |
2283 {Function onError, | 2277 {Function onError, void onDone(), bool cancelOnError}) { |
2284 void onDone(), | 2278 _serverSocket.listen((Socket socket) { |
2285 bool cancelOnError}) { | 2279 socket.setOption(SocketOption.TCP_NODELAY, true); |
2286 _serverSocket.listen( | 2280 // Accept the client connection. |
2287 (Socket socket) { | 2281 _HttpConnection connection = new _HttpConnection(socket, this); |
2288 socket.setOption(SocketOption.TCP_NODELAY, true); | 2282 _idleConnections.add(connection); |
2289 // Accept the client connection. | 2283 }, onError: (error, stackTrace) { |
2290 _HttpConnection connection = new _HttpConnection(socket, this); | 2284 // Ignore HandshakeExceptions as they are bound to a single request, |
2291 _idleConnections.add(connection); | 2285 // and are not fatal for the server. |
2292 }, | 2286 if (error is! HandshakeException) { |
2293 onError: (error, stackTrace) { | 2287 _controller.addError(error, stackTrace); |
2294 // Ignore HandshakeExceptions as they are bound to a single request, | 2288 } |
2295 // and are not fatal for the server. | 2289 }, onDone: _controller.close); |
2296 if (error is! HandshakeException) { | |
2297 _controller.addError(error, stackTrace); | |
2298 } | |
2299 }, | |
2300 onDone: _controller.close); | |
2301 return _controller.stream.listen(onData, | 2290 return _controller.stream.listen(onData, |
2302 onError: onError, | 2291 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
2303 onDone: onDone, | |
2304 cancelOnError: cancelOnError); | |
2305 } | 2292 } |
2306 | 2293 |
2307 Future close({bool force: false}) { | 2294 Future close({bool force: false}) { |
2308 closed = true; | 2295 closed = true; |
2309 Future result; | 2296 Future result; |
2310 if (_serverSocket != null && _closeServer) { | 2297 if (_serverSocket != null && _closeServer) { |
2311 result = _serverSocket.close(); | 2298 result = _serverSocket.close(); |
2312 } else { | 2299 } else { |
2313 result = new Future.value(); | 2300 result = new Future.value(); |
2314 } | 2301 } |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2432 return r; | 2419 return r; |
2433 } | 2420 } |
2434 | 2421 |
2435 _HttpSessionManager _sessionManagerInstance; | 2422 _HttpSessionManager _sessionManagerInstance; |
2436 | 2423 |
2437 // Indicated if the http server has been closed. | 2424 // Indicated if the http server has been closed. |
2438 bool closed = false; | 2425 bool closed = false; |
2439 | 2426 |
2440 // The server listen socket. Untyped as it can be both ServerSocket and | 2427 // The server listen socket. Untyped as it can be both ServerSocket and |
2441 // SecureServerSocket. | 2428 // SecureServerSocket. |
2442 final dynamic/*ServerSocket|SecureServerSocket*/ _serverSocket; | 2429 final dynamic /*ServerSocket|SecureServerSocket*/ _serverSocket; |
2443 final bool _closeServer; | 2430 final bool _closeServer; |
2444 | 2431 |
2445 // Set of currently connected clients. | 2432 // Set of currently connected clients. |
2446 final LinkedList<_HttpConnection> _activeConnections | 2433 final LinkedList<_HttpConnection> _activeConnections = |
2447 = new LinkedList<_HttpConnection>(); | 2434 new LinkedList<_HttpConnection>(); |
2448 final LinkedList<_HttpConnection> _idleConnections | 2435 final LinkedList<_HttpConnection> _idleConnections = |
2449 = new LinkedList<_HttpConnection>(); | 2436 new LinkedList<_HttpConnection>(); |
2450 StreamController<HttpRequest> _controller; | 2437 StreamController<HttpRequest> _controller; |
2451 } | 2438 } |
2452 | 2439 |
2453 | |
2454 class _ProxyConfiguration { | 2440 class _ProxyConfiguration { |
2455 static const String PROXY_PREFIX = "PROXY "; | 2441 static const String PROXY_PREFIX = "PROXY "; |
2456 static const String DIRECT_PREFIX = "DIRECT"; | 2442 static const String DIRECT_PREFIX = "DIRECT"; |
2457 | 2443 |
2458 _ProxyConfiguration(String configuration) : proxies = new List<_Proxy>() { | 2444 _ProxyConfiguration(String configuration) : proxies = new List<_Proxy>() { |
2459 if (configuration == null) { | 2445 if (configuration == null) { |
2460 throw new HttpException("Invalid proxy configuration $configuration"); | 2446 throw new HttpException("Invalid proxy configuration $configuration"); |
2461 } | 2447 } |
2462 List<String> list = configuration.split(";"); | 2448 List<String> list = configuration.split(";"); |
2463 list.forEach((String proxy) { | 2449 list.forEach((String proxy) { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2503 proxies.add(new _Proxy(host, port, username, password)); | 2489 proxies.add(new _Proxy(host, port, username, password)); |
2504 } else if (proxy.trim() == DIRECT_PREFIX) { | 2490 } else if (proxy.trim() == DIRECT_PREFIX) { |
2505 proxies.add(new _Proxy.direct()); | 2491 proxies.add(new _Proxy.direct()); |
2506 } else { | 2492 } else { |
2507 throw new HttpException("Invalid proxy configuration $configuration"); | 2493 throw new HttpException("Invalid proxy configuration $configuration"); |
2508 } | 2494 } |
2509 } | 2495 } |
2510 }); | 2496 }); |
2511 } | 2497 } |
2512 | 2498 |
2513 const _ProxyConfiguration.direct() | 2499 const _ProxyConfiguration.direct() : proxies = const [const _Proxy.direct()]; |
2514 : proxies = const [const _Proxy.direct()]; | |
2515 | 2500 |
2516 final List<_Proxy> proxies; | 2501 final List<_Proxy> proxies; |
2517 } | 2502 } |
2518 | 2503 |
2519 | |
2520 class _Proxy { | 2504 class _Proxy { |
2521 final String host; | 2505 final String host; |
2522 final int port; | 2506 final int port; |
2523 final String username; | 2507 final String username; |
2524 final String password; | 2508 final String password; |
2525 final bool isDirect; | 2509 final bool isDirect; |
2526 | 2510 |
2527 const _Proxy(this.host, this.port, this.username, this.password) | 2511 const _Proxy(this.host, this.port, this.username, this.password) |
2528 : isDirect = false; | 2512 : isDirect = false; |
2529 const _Proxy.direct() : host = null, port = null, | 2513 const _Proxy.direct() |
2530 username = null, password = null, isDirect = true; | 2514 : host = null, |
| 2515 port = null, |
| 2516 username = null, |
| 2517 password = null, |
| 2518 isDirect = true; |
2531 | 2519 |
2532 bool get isAuthenticated => username != null; | 2520 bool get isAuthenticated => username != null; |
2533 } | 2521 } |
2534 | 2522 |
2535 | |
2536 class _HttpConnectionInfo implements HttpConnectionInfo { | 2523 class _HttpConnectionInfo implements HttpConnectionInfo { |
2537 InternetAddress remoteAddress; | 2524 InternetAddress remoteAddress; |
2538 int remotePort; | 2525 int remotePort; |
2539 int localPort; | 2526 int localPort; |
2540 | 2527 |
2541 static _HttpConnectionInfo create(Socket socket) { | 2528 static _HttpConnectionInfo create(Socket socket) { |
2542 if (socket == null) return null; | 2529 if (socket == null) return null; |
2543 try { | 2530 try { |
2544 _HttpConnectionInfo info = new _HttpConnectionInfo(); | 2531 _HttpConnectionInfo info = new _HttpConnectionInfo(); |
2545 return info | 2532 return info |
2546 ..remoteAddress = socket.remoteAddress | 2533 ..remoteAddress = socket.remoteAddress |
2547 ..remotePort = socket.remotePort | 2534 ..remotePort = socket.remotePort |
2548 ..localPort = socket.port; | 2535 ..localPort = socket.port; |
2549 } catch (e) { } | 2536 } catch (e) {} |
2550 return null; | 2537 return null; |
2551 } | 2538 } |
2552 } | 2539 } |
2553 | 2540 |
2554 | |
2555 class _DetachedSocket extends Stream<List<int>> implements Socket { | 2541 class _DetachedSocket extends Stream<List<int>> implements Socket { |
2556 final Stream<List<int>> _incoming; | 2542 final Stream<List<int>> _incoming; |
2557 final Socket _socket; | 2543 final Socket _socket; |
2558 | 2544 |
2559 _DetachedSocket(this._socket, this._incoming); | 2545 _DetachedSocket(this._socket, this._incoming); |
2560 | 2546 |
2561 StreamSubscription<List<int>> listen(void onData(List<int> event), | 2547 StreamSubscription<List<int>> listen(void onData(List<int> event), |
2562 {Function onError, | 2548 {Function onError, void onDone(), bool cancelOnError}) { |
2563 void onDone(), | |
2564 bool cancelOnError}) { | |
2565 return _incoming.listen(onData, | 2549 return _incoming.listen(onData, |
2566 onError: onError, | 2550 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
2567 onDone: onDone, | |
2568 cancelOnError: cancelOnError); | |
2569 } | 2551 } |
2570 | 2552 |
2571 Encoding get encoding => _socket.encoding; | 2553 Encoding get encoding => _socket.encoding; |
2572 | 2554 |
2573 void set encoding(Encoding value) { | 2555 void set encoding(Encoding value) { |
2574 _socket.encoding = value; | 2556 _socket.encoding = value; |
2575 } | 2557 } |
2576 | 2558 |
2577 void write(Object obj) { _socket.write(obj); } | 2559 void write(Object obj) { |
| 2560 _socket.write(obj); |
| 2561 } |
2578 | 2562 |
2579 void writeln([Object obj = ""]) { _socket.writeln(obj); } | 2563 void writeln([Object obj = ""]) { |
| 2564 _socket.writeln(obj); |
| 2565 } |
2580 | 2566 |
2581 void writeCharCode(int charCode) { _socket.writeCharCode(charCode); } | 2567 void writeCharCode(int charCode) { |
| 2568 _socket.writeCharCode(charCode); |
| 2569 } |
2582 | 2570 |
2583 void writeAll(Iterable objects, [String separator = ""]) { | 2571 void writeAll(Iterable objects, [String separator = ""]) { |
2584 _socket.writeAll(objects, separator); | 2572 _socket.writeAll(objects, separator); |
2585 } | 2573 } |
2586 | 2574 |
2587 void add(List<int> bytes) { _socket.add(bytes); } | 2575 void add(List<int> bytes) { |
| 2576 _socket.add(bytes); |
| 2577 } |
2588 | 2578 |
2589 void addError(error, [StackTrace stackTrace]) => | 2579 void addError(error, [StackTrace stackTrace]) => |
2590 _socket.addError(error, stackTrace); | 2580 _socket.addError(error, stackTrace); |
2591 | 2581 |
2592 Future addStream(Stream<List<int>> stream) { | 2582 Future addStream(Stream<List<int>> stream) { |
2593 return _socket.addStream(stream); | 2583 return _socket.addStream(stream); |
2594 } | 2584 } |
2595 | 2585 |
2596 void destroy() { _socket.destroy(); } | 2586 void destroy() { |
| 2587 _socket.destroy(); |
| 2588 } |
2597 | 2589 |
2598 Future flush() => _socket.flush(); | 2590 Future flush() => _socket.flush(); |
2599 | 2591 |
2600 Future<Socket> close() => _socket.close(); | 2592 Future<Socket> close() => _socket.close(); |
2601 | 2593 |
2602 Future<Socket> get done => _socket.done; | 2594 Future<Socket> get done => _socket.done; |
2603 | 2595 |
2604 int get port => _socket.port; | 2596 int get port => _socket.port; |
2605 | 2597 |
2606 InternetAddress get address => _socket.address; | 2598 InternetAddress get address => _socket.address; |
2607 | 2599 |
2608 InternetAddress get remoteAddress => _socket.remoteAddress; | 2600 InternetAddress get remoteAddress => _socket.remoteAddress; |
2609 | 2601 |
2610 int get remotePort => _socket.remotePort; | 2602 int get remotePort => _socket.remotePort; |
2611 | 2603 |
2612 bool setOption(SocketOption option, bool enabled) { | 2604 bool setOption(SocketOption option, bool enabled) { |
2613 return _socket.setOption(option, enabled); | 2605 return _socket.setOption(option, enabled); |
2614 } | 2606 } |
2615 | 2607 |
2616 Map _toJSON(bool ref) { | 2608 Map _toJSON(bool ref) { |
2617 return (_socket as dynamic)._toJSON(ref); | 2609 return (_socket as dynamic)._toJSON(ref); |
2618 } | 2610 } |
| 2611 |
2619 void set _owner(owner) { | 2612 void set _owner(owner) { |
2620 (_socket as dynamic)._owner = owner; | 2613 (_socket as dynamic)._owner = owner; |
2621 } | 2614 } |
2622 } | 2615 } |
2623 | 2616 |
2624 | |
2625 class _AuthenticationScheme { | 2617 class _AuthenticationScheme { |
2626 final int _scheme; | 2618 final int _scheme; |
2627 | 2619 |
2628 static const UNKNOWN = const _AuthenticationScheme(-1); | 2620 static const UNKNOWN = const _AuthenticationScheme(-1); |
2629 static const BASIC = const _AuthenticationScheme(0); | 2621 static const BASIC = const _AuthenticationScheme(0); |
2630 static const DIGEST = const _AuthenticationScheme(1); | 2622 static const DIGEST = const _AuthenticationScheme(1); |
2631 | 2623 |
2632 const _AuthenticationScheme(this._scheme); | 2624 const _AuthenticationScheme(this._scheme); |
2633 | 2625 |
2634 factory _AuthenticationScheme.fromString(String scheme) { | 2626 factory _AuthenticationScheme.fromString(String scheme) { |
2635 if (scheme.toLowerCase() == "basic") return BASIC; | 2627 if (scheme.toLowerCase() == "basic") return BASIC; |
2636 if (scheme.toLowerCase() == "digest") return DIGEST; | 2628 if (scheme.toLowerCase() == "digest") return DIGEST; |
2637 return UNKNOWN; | 2629 return UNKNOWN; |
2638 } | 2630 } |
2639 | 2631 |
2640 String toString() { | 2632 String toString() { |
2641 if (this == BASIC) return "Basic"; | 2633 if (this == BASIC) return "Basic"; |
2642 if (this == DIGEST) return "Digest"; | 2634 if (this == DIGEST) return "Digest"; |
2643 return "Unknown"; | 2635 return "Unknown"; |
2644 } | 2636 } |
2645 } | 2637 } |
2646 | 2638 |
2647 | |
2648 abstract class _Credentials { | 2639 abstract class _Credentials { |
2649 _HttpClientCredentials credentials; | 2640 _HttpClientCredentials credentials; |
2650 String realm; | 2641 String realm; |
2651 bool used = false; | 2642 bool used = false; |
2652 | 2643 |
2653 // Digest specific fields. | 2644 // Digest specific fields. |
2654 String ha1; | 2645 String ha1; |
2655 String nonce; | 2646 String nonce; |
2656 String algorithm; | 2647 String algorithm; |
2657 String qop; | 2648 String qop; |
2658 int nonceCount; | 2649 int nonceCount; |
2659 | 2650 |
2660 _Credentials(this.credentials, this.realm) { | 2651 _Credentials(this.credentials, this.realm) { |
2661 if (credentials.scheme == _AuthenticationScheme.DIGEST) { | 2652 if (credentials.scheme == _AuthenticationScheme.DIGEST) { |
2662 // Calculate the H(A1) value once. There is no mentioning of | 2653 // Calculate the H(A1) value once. There is no mentioning of |
2663 // username/password encoding in RFC 2617. However there is an | 2654 // username/password encoding in RFC 2617. However there is an |
2664 // open draft for adding an additional accept-charset parameter to | 2655 // open draft for adding an additional accept-charset parameter to |
2665 // the WWW-Authenticate and Proxy-Authenticate headers, see | 2656 // the WWW-Authenticate and Proxy-Authenticate headers, see |
2666 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For | 2657 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For |
2667 // now always use UTF-8 encoding. | 2658 // now always use UTF-8 encoding. |
2668 _HttpClientDigestCredentials creds = credentials; | 2659 _HttpClientDigestCredentials creds = credentials; |
2669 var hasher = new _MD5() | 2660 var hasher = new _MD5() |
2670 ..add(UTF8.encode(creds.username)) | 2661 ..add(UTF8.encode(creds.username)) |
2671 ..add([_CharCode.COLON]) | 2662 ..add([_CharCode.COLON]) |
2672 ..add(realm.codeUnits) | 2663 ..add(realm.codeUnits) |
2673 ..add([_CharCode.COLON]) | 2664 ..add([_CharCode.COLON]) |
2674 ..add(UTF8.encode(creds.password)); | 2665 ..add(UTF8.encode(creds.password)); |
2675 ha1 = _CryptoUtils.bytesToHex(hasher.close()); | 2666 ha1 = _CryptoUtils.bytesToHex(hasher.close()); |
2676 } | 2667 } |
2677 } | 2668 } |
2678 | 2669 |
2679 _AuthenticationScheme get scheme => credentials.scheme; | 2670 _AuthenticationScheme get scheme => credentials.scheme; |
2680 | 2671 |
2681 void authorize(HttpClientRequest request); | 2672 void authorize(HttpClientRequest request); |
2682 } | 2673 } |
2683 | 2674 |
2684 class _SiteCredentials extends _Credentials { | 2675 class _SiteCredentials extends _Credentials { |
2685 Uri uri; | 2676 Uri uri; |
2686 | 2677 |
2687 _SiteCredentials(this.uri, realm, _HttpClientCredentials creds) | 2678 _SiteCredentials(this.uri, realm, _HttpClientCredentials creds) |
2688 : super(creds, realm); | 2679 : super(creds, realm); |
2689 | 2680 |
2690 bool applies(Uri uri, _AuthenticationScheme scheme) { | 2681 bool applies(Uri uri, _AuthenticationScheme scheme) { |
2691 if (scheme != null && credentials.scheme != scheme) return false; | 2682 if (scheme != null && credentials.scheme != scheme) return false; |
2692 if (uri.host != this.uri.host) return false; | 2683 if (uri.host != this.uri.host) return false; |
2693 int thisPort = | 2684 int thisPort = |
2694 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; | 2685 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; |
2695 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; | 2686 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; |
2696 if (otherPort != thisPort) return false; | 2687 if (otherPort != thisPort) return false; |
2697 return uri.path.startsWith(this.uri.path); | 2688 return uri.path.startsWith(this.uri.path); |
2698 } | 2689 } |
2699 | 2690 |
2700 void authorize(HttpClientRequest request) { | 2691 void authorize(HttpClientRequest request) { |
2701 // Digest credentials cannot be used without a nonce from the | 2692 // Digest credentials cannot be used without a nonce from the |
2702 // server. | 2693 // server. |
2703 if (credentials.scheme == _AuthenticationScheme.DIGEST && | 2694 if (credentials.scheme == _AuthenticationScheme.DIGEST && nonce == null) { |
2704 nonce == null) { | |
2705 return; | 2695 return; |
2706 } | 2696 } |
2707 credentials.authorize(this, request); | 2697 credentials.authorize(this, request); |
2708 used = true; | 2698 used = true; |
2709 } | 2699 } |
2710 } | 2700 } |
2711 | 2701 |
2712 | |
2713 class _ProxyCredentials extends _Credentials { | 2702 class _ProxyCredentials extends _Credentials { |
2714 String host; | 2703 String host; |
2715 int port; | 2704 int port; |
2716 | 2705 |
2717 _ProxyCredentials(this.host, | 2706 _ProxyCredentials(this.host, this.port, realm, _HttpClientCredentials creds) |
2718 this.port, | |
2719 realm, | |
2720 _HttpClientCredentials creds) | |
2721 : super(creds, realm); | 2707 : super(creds, realm); |
2722 | 2708 |
2723 bool applies(_Proxy proxy, _AuthenticationScheme scheme) { | 2709 bool applies(_Proxy proxy, _AuthenticationScheme scheme) { |
2724 if (scheme != null && credentials.scheme != scheme) return false; | 2710 if (scheme != null && credentials.scheme != scheme) return false; |
2725 return proxy.host == host && proxy.port == port; | 2711 return proxy.host == host && proxy.port == port; |
2726 } | 2712 } |
2727 | 2713 |
2728 void authorize(HttpClientRequest request) { | 2714 void authorize(HttpClientRequest request) { |
2729 // Digest credentials cannot be used without a nonce from the | 2715 // Digest credentials cannot be used without a nonce from the |
2730 // server. | 2716 // server. |
2731 if (credentials.scheme == _AuthenticationScheme.DIGEST && | 2717 if (credentials.scheme == _AuthenticationScheme.DIGEST && nonce == null) { |
2732 nonce == null) { | |
2733 return; | 2718 return; |
2734 } | 2719 } |
2735 credentials.authorizeProxy(this, request); | 2720 credentials.authorizeProxy(this, request); |
2736 } | 2721 } |
2737 } | 2722 } |
2738 | 2723 |
2739 | |
2740 abstract class _HttpClientCredentials implements HttpClientCredentials { | 2724 abstract class _HttpClientCredentials implements HttpClientCredentials { |
2741 _AuthenticationScheme get scheme; | 2725 _AuthenticationScheme get scheme; |
2742 void authorize(_Credentials credentials, HttpClientRequest request); | 2726 void authorize(_Credentials credentials, HttpClientRequest request); |
2743 void authorizeProxy(_ProxyCredentials credentials, HttpClientRequest request); | 2727 void authorizeProxy(_ProxyCredentials credentials, HttpClientRequest request); |
2744 } | 2728 } |
2745 | 2729 |
2746 | 2730 class _HttpClientBasicCredentials extends _HttpClientCredentials |
2747 class _HttpClientBasicCredentials | |
2748 extends _HttpClientCredentials | |
2749 implements HttpClientBasicCredentials { | 2731 implements HttpClientBasicCredentials { |
2750 String username; | 2732 String username; |
2751 String password; | 2733 String password; |
2752 | 2734 |
2753 _HttpClientBasicCredentials(this.username, this.password); | 2735 _HttpClientBasicCredentials(this.username, this.password); |
2754 | 2736 |
2755 _AuthenticationScheme get scheme => _AuthenticationScheme.BASIC; | 2737 _AuthenticationScheme get scheme => _AuthenticationScheme.BASIC; |
2756 | 2738 |
2757 String authorization() { | 2739 String authorization() { |
2758 // There is no mentioning of username/password encoding in RFC | 2740 // There is no mentioning of username/password encoding in RFC |
2759 // 2617. However there is an open draft for adding an additional | 2741 // 2617. However there is an open draft for adding an additional |
2760 // accept-charset parameter to the WWW-Authenticate and | 2742 // accept-charset parameter to the WWW-Authenticate and |
2761 // Proxy-Authenticate headers, see | 2743 // Proxy-Authenticate headers, see |
2762 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For | 2744 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For |
2763 // now always use UTF-8 encoding. | 2745 // now always use UTF-8 encoding. |
2764 String auth = | 2746 String auth = |
2765 _CryptoUtils.bytesToBase64(UTF8.encode("$username:$password")); | 2747 _CryptoUtils.bytesToBase64(UTF8.encode("$username:$password")); |
2766 return "Basic $auth"; | 2748 return "Basic $auth"; |
2767 } | 2749 } |
2768 | 2750 |
2769 void authorize(_Credentials _, HttpClientRequest request) { | 2751 void authorize(_Credentials _, HttpClientRequest request) { |
2770 request.headers.set(HttpHeaders.AUTHORIZATION, authorization()); | 2752 request.headers.set(HttpHeaders.AUTHORIZATION, authorization()); |
2771 } | 2753 } |
2772 | 2754 |
2773 void authorizeProxy(_ProxyCredentials _, HttpClientRequest request) { | 2755 void authorizeProxy(_ProxyCredentials _, HttpClientRequest request) { |
2774 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, authorization()); | 2756 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, authorization()); |
2775 } | 2757 } |
2776 } | 2758 } |
2777 | 2759 |
2778 | 2760 class _HttpClientDigestCredentials extends _HttpClientCredentials |
2779 class _HttpClientDigestCredentials | |
2780 extends _HttpClientCredentials | |
2781 implements HttpClientDigestCredentials { | 2761 implements HttpClientDigestCredentials { |
2782 String username; | 2762 String username; |
2783 String password; | 2763 String password; |
2784 | 2764 |
2785 _HttpClientDigestCredentials(this.username, this.password); | 2765 _HttpClientDigestCredentials(this.username, this.password); |
2786 | 2766 |
2787 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; | 2767 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; |
2788 | 2768 |
2789 String authorization(_Credentials credentials, _HttpClientRequest request) { | 2769 String authorization(_Credentials credentials, _HttpClientRequest request) { |
2790 String requestUri = request._requestUri(); | 2770 String requestUri = request._requestUri(); |
2791 _MD5 hasher = new _MD5() | 2771 _MD5 hasher = new _MD5() |
2792 ..add(request.method.codeUnits) | 2772 ..add(request.method.codeUnits) |
2793 ..add([_CharCode.COLON]) | 2773 ..add([_CharCode.COLON]) |
2794 ..add(requestUri.codeUnits); | 2774 ..add(requestUri.codeUnits); |
2795 var ha2 = _CryptoUtils.bytesToHex(hasher.close()); | 2775 var ha2 = _CryptoUtils.bytesToHex(hasher.close()); |
2796 | 2776 |
2797 String qop; | 2777 String qop; |
2798 String cnonce; | 2778 String cnonce; |
2799 String nc; | 2779 String nc; |
2800 var x; | 2780 var x; |
2801 hasher = new _MD5() | 2781 hasher = new _MD5()..add(credentials.ha1.codeUnits)..add([_CharCode.COLON]); |
2802 ..add(credentials.ha1.codeUnits) | |
2803 ..add([_CharCode.COLON]); | |
2804 if (credentials.qop == "auth") { | 2782 if (credentials.qop == "auth") { |
2805 qop = credentials.qop; | 2783 qop = credentials.qop; |
2806 cnonce = _CryptoUtils.bytesToHex(_IOCrypto.getRandomBytes(4)); | 2784 cnonce = _CryptoUtils.bytesToHex(_IOCrypto.getRandomBytes(4)); |
2807 ++credentials.nonceCount; | 2785 ++credentials.nonceCount; |
2808 nc = credentials.nonceCount.toRadixString(16); | 2786 nc = credentials.nonceCount.toRadixString(16); |
2809 nc = "00000000".substring(0, 8 - nc.length + 1) + nc; | 2787 nc = "00000000".substring(0, 8 - nc.length + 1) + nc; |
2810 hasher | 2788 hasher |
2811 ..add(credentials.nonce.codeUnits) | 2789 ..add(credentials.nonce.codeUnits) |
2812 ..add([_CharCode.COLON]) | 2790 ..add([_CharCode.COLON]) |
2813 ..add(nc.codeUnits) | 2791 ..add(nc.codeUnits) |
2814 ..add([_CharCode.COLON]) | 2792 ..add([_CharCode.COLON]) |
2815 ..add(cnonce.codeUnits) | 2793 ..add(cnonce.codeUnits) |
2816 ..add([_CharCode.COLON]) | 2794 ..add([_CharCode.COLON]) |
2817 ..add(credentials.qop.codeUnits) | 2795 ..add(credentials.qop.codeUnits) |
2818 ..add([_CharCode.COLON]) | 2796 ..add([_CharCode.COLON]) |
2819 ..add(ha2.codeUnits); | 2797 ..add(ha2.codeUnits); |
2820 } else { | 2798 } else { |
2821 hasher | 2799 hasher |
2822 ..add(credentials.nonce.codeUnits) | 2800 ..add(credentials.nonce.codeUnits) |
2823 ..add([_CharCode.COLON]) | 2801 ..add([_CharCode.COLON]) |
2824 ..add(ha2.codeUnits); | 2802 ..add(ha2.codeUnits); |
2825 } | 2803 } |
2826 var response = _CryptoUtils.bytesToHex(hasher.close()); | 2804 var response = _CryptoUtils.bytesToHex(hasher.close()); |
2827 | 2805 |
2828 StringBuffer buffer = new StringBuffer() | 2806 StringBuffer buffer = new StringBuffer() |
2829 ..write('Digest ') | 2807 ..write('Digest ') |
2830 ..write('username="$username"') | 2808 ..write('username="$username"') |
2831 ..write(', realm="${credentials.realm}"') | 2809 ..write(', realm="${credentials.realm}"') |
2832 ..write(', nonce="${credentials.nonce}"') | 2810 ..write(', nonce="${credentials.nonce}"') |
2833 ..write(', uri="$requestUri"') | 2811 ..write(', uri="$requestUri"') |
2834 ..write(', algorithm="${credentials.algorithm}"'); | 2812 ..write(', algorithm="${credentials.algorithm}"'); |
2835 if (qop == "auth") { | 2813 if (qop == "auth") { |
2836 buffer | 2814 buffer |
2837 ..write(', qop="$qop"') | 2815 ..write(', qop="$qop"') |
2838 ..write(', cnonce="$cnonce"') | 2816 ..write(', cnonce="$cnonce"') |
2839 ..write(', nc="$nc"'); | 2817 ..write(', nc="$nc"'); |
2840 } | 2818 } |
2841 buffer.write(', response="$response"'); | 2819 buffer.write(', response="$response"'); |
2842 return buffer.toString(); | 2820 return buffer.toString(); |
2843 } | 2821 } |
2844 | 2822 |
2845 void authorize(_Credentials credentials, HttpClientRequest request) { | 2823 void authorize(_Credentials credentials, HttpClientRequest request) { |
2846 request.headers.set(HttpHeaders.AUTHORIZATION, | 2824 request.headers |
2847 authorization(credentials, request)); | 2825 .set(HttpHeaders.AUTHORIZATION, authorization(credentials, request)); |
2848 } | 2826 } |
2849 | 2827 |
2850 void authorizeProxy(_ProxyCredentials credentials, | 2828 void authorizeProxy( |
2851 HttpClientRequest request) { | 2829 _ProxyCredentials credentials, HttpClientRequest request) { |
2852 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, | 2830 request.headers.set( |
2853 authorization(credentials, request)); | 2831 HttpHeaders.PROXY_AUTHORIZATION, authorization(credentials, request)); |
2854 } | 2832 } |
2855 } | 2833 } |
2856 | 2834 |
2857 | |
2858 class _RedirectInfo implements RedirectInfo { | 2835 class _RedirectInfo implements RedirectInfo { |
2859 final int statusCode; | 2836 final int statusCode; |
2860 final String method; | 2837 final String method; |
2861 final Uri location; | 2838 final Uri location; |
2862 const _RedirectInfo(this.statusCode, this.method, this.location); | 2839 const _RedirectInfo(this.statusCode, this.method, this.location); |
2863 } | 2840 } |
2864 | 2841 |
2865 String _getHttpVersion() { | 2842 String _getHttpVersion() { |
2866 var version = Platform.version; | 2843 var version = Platform.version; |
2867 // Only include major and minor version numbers. | 2844 // Only include major and minor version numbers. |
2868 int index = version.indexOf('.', version.indexOf('.') + 1); | 2845 int index = version.indexOf('.', version.indexOf('.') + 1); |
2869 version = version.substring(0, index); | 2846 version = version.substring(0, index); |
2870 return 'Dart/$version (dart:io)'; | 2847 return 'Dart/$version (dart:io)'; |
2871 } | 2848 } |
OLD | NEW |