Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(151)

Side by Side Diff: sdk/lib/io/http_impl.dart

Issue 2754013002: Format all dart: library files (Closed)
Patch Set: Format all dart: library files Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sdk/lib/io/http_headers.dart ('k') | sdk/lib/io/http_session.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http_headers.dart ('k') | sdk/lib/io/http_session.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698