| 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 |