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

Side by Side Diff: utils/pub/io.dart

Issue 10928041: Add a 30s timeout for all HTTP requests in Pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes. Created 8 years, 3 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 | Annotate | Revision Log
« no previous file with comments | « utils/pub/hosted_source.dart ('k') | no next file » | 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) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** 5 /**
6 * Helper functionality to make working with IO easier. 6 * Helper functionality to make working with IO easier.
7 */ 7 */
8 #library('io'); 8 #library('io');
9 9
10 #import('dart:io'); 10 #import('dart:io');
11 #import('dart:isolate');
11 #import('dart:uri'); 12 #import('dart:uri');
12 13
13 /** Gets the current working directory. */ 14 /** Gets the current working directory. */
14 String get workingDir => new File('.').fullPathSync(); 15 String get workingDir => new File('.').fullPathSync();
15 16
16 /** 17 /**
17 * Prints the given string to `stderr` on its own line. 18 * Prints the given string to `stderr` on its own line.
18 */ 19 */
19 void printError(value) { 20 void printError(value) {
20 stderr.writeString(value.toString()); 21 stderr.writeString(value.toString());
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 }); 279 });
279 } 280 }
280 281
281 /** 282 /**
282 * Given [entry] which may be a [String], [File], or [Directory] relative to 283 * Given [entry] which may be a [String], [File], or [Directory] relative to
283 * the current working directory, returns its full canonicalized path. 284 * the current working directory, returns its full canonicalized path.
284 */ 285 */
285 // TODO(rnystrom): Should this be async? 286 // TODO(rnystrom): Should this be async?
286 String getFullPath(entry) => new File(_getPath(entry)).fullPathSync(); 287 String getFullPath(entry) => new File(_getPath(entry)).fullPathSync();
287 288
289 // TODO(nweiz): make this configurable
290 /**
291 * The amount of time in milliseconds to allow HTTP requests before assuming
292 * they've failed.
293 */
294 final HTTP_TIMEOUT = 30 * 1000;
295
288 /** 296 /**
289 * Opens an input stream for a HTTP GET request to [uri], which may be a 297 * Opens an input stream for a HTTP GET request to [uri], which may be a
290 * [String] or [Uri]. 298 * [String] or [Uri].
299 *
300 * Callers should be sure to use [timeout] to make sure that the HTTP request
301 * doesn't last indefinitely
291 */ 302 */
292 InputStream httpGet(uri) { 303 Future<InputStream> httpGet(uri) {
293 var resultStream = new ListInputStream(); 304 // TODO(nweiz): This could return an InputStream synchronously if issue 3657
294 var client = new HttpClient(); 305 // were fixed and errors could be propagated through it. Then we could also
295 var connection = client.getUrl(_getUri(uri)); 306 // automatically attach a timeout to that stream.
296
297 // TODO(nweiz): propagate this error to the return value. See issue 3657.
298 connection.onError = (e) { throw e; };
299 connection.onResponse = (response) {
300 if (response.statusCode >= 400) {
301 // TODO(nweiz): propagate this error to the return value. See issue 3657.
302 throw new Exception(
303 "HTTP request for $uri failed with status ${response.statusCode}");
304 }
305
306 pipeInputToInput(response.inputStream, resultStream, client.shutdown);
307 };
308
309 return resultStream;
310 }
311
312 /**
313 * Opens an input stream for a HTTP GET request to [uri], which may be a
314 * [String] or [Uri]. Completes with the result of the request as a String.
315 */
316 Future<String> httpGetString(uri) {
317 // TODO(rnystrom): This code is very similar to httpGet() and
318 // consumeInputStream() (but this one propagates errors). Merge them when
319 // #3657 is fixed.
320 uri = _getUri(uri); 307 uri = _getUri(uri);
321 308
322 var completer = new Completer<String>(); 309 var completer = new Completer<InputStream>();
323 var client = new HttpClient(); 310 var client = new HttpClient();
324 var connection = client.getUrl(uri); 311 var connection = client.getUrl(uri);
325 312
326 connection.onError = (e) { 313 connection.onError = (e) {
327 // Show a friendly error if the URL couldn't be resolved. 314 // Show a friendly error if the URL couldn't be resolved.
328 if (e is SocketIOException && 315 if (e is SocketIOException &&
329 (e.osError.errorCode == 8 || 316 (e.osError.errorCode == 8 ||
330 e.osError.errorCode == -2 || 317 e.osError.errorCode == -2 ||
331 e.osError.errorCode == -5)) { 318 e.osError.errorCode == -5)) {
332 e = 'Could not resolve URL "${uri.origin}".'; 319 e = 'Could not resolve URL "${uri.origin}".';
333 } 320 }
334 321
335 client.shutdown(); 322 client.shutdown();
336 completer.completeException(e); 323 completer.completeException(e);
337 }; 324 };
338 325
339 connection.onResponse = (response) { 326 connection.onResponse = (response) {
340 if (response.statusCode >= 400) { 327 if (response.statusCode >= 400) {
341 client.shutdown(); 328 client.shutdown();
342 completer.completeException( 329 completer.completeException(
343 new HttpException(response.statusCode, response.reasonPhrase)); 330 new HttpException(response.statusCode, response.reasonPhrase));
344 return; 331 return;
345 } 332 }
346 333
347 var resultStream = new ListInputStream(); 334 completer.complete(response.inputStream);
348 var buffer = <int>[];
349
350 response.inputStream.onClosed = () {
351 client.shutdown();
352 completer.complete(new String.fromCharCodes(buffer));
353 };
354
355 response.inputStream.onData = () {
356 buffer.addAll(response.inputStream.read());
357 };
358
359 response.inputStream.onError = (e) {
360 client.shutdown();
361 completer.completeException(e);
362 };
363 }; 335 };
364 336
365 return completer.future; 337 return completer.future;
366 } 338 }
367 339
368 /** 340 /**
341 * Opens an input stream for a HTTP GET request to [uri], which may be a
342 * [String] or [Uri]. Completes with the result of the request as a String.
343 */
344 Future<String> httpGetString(uri) {
345 var future = httpGet(uri).chain((stream) => consumeInputStream(stream))
346 .transform((bytes) => new String.fromCharCodes(bytes));
347 return timeout(future, HTTP_TIMEOUT, 'Timed out while fetching URL "$uri".');
348 }
349
350 /**
369 * Takes all input from [source] and writes it to [sink]. 351 * Takes all input from [source] and writes it to [sink].
370 * 352 *
371 * [onClosed] is called when [source] is closed. 353 * [onClosed] is called when [source] is closed.
372 */ 354 */
373 void pipeInputToInput(InputStream source, ListInputStream sink, 355 void pipeInputToInput(InputStream source, ListInputStream sink,
374 [void onClosed()]) { 356 [void onClosed()]) {
375 source.onClosed = () { 357 source.onClosed = () {
376 sink.markEndOfStream(); 358 sink.markEndOfStream();
377 if (onClosed != null) onClosed(); 359 if (onClosed != null) onClosed();
378 }; 360 };
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
453 exitCode = actualExitCode; 435 exitCode = actualExitCode;
454 checkComplete(); 436 checkComplete();
455 }; 437 };
456 438
457 process.onError = (error) => completer.completeException(error); 439 process.onError = (error) => completer.completeException(error);
458 440
459 return completer.future; 441 return completer.future;
460 } 442 }
461 443
462 /** 444 /**
445 * Wraps [input] to provide a timeout. If [input] completes before
446 * [milliSeconds] have passed, then the return value completes in the same way.
447 * However, if [milliSeconds] pass before [input] has completed, it completes
448 * with a [TimeoutException] with [message].
449 */
450 Future timeout(Future input, int milliSeconds, String message) {
451 var completer = new Completer();
452 var timer = new Timer(milliSeconds, (_) {
453 if (completer.future.isComplete) return;
454 completer.completeException(new TimeoutException(message));
455 });
456 input.handleException((e) {
457 if (completer.future.isComplete) return false;
458 timer.cancel();
459 completer.completeException(e);
460 return true;
461 });
462 input.then((value) {
463 if (completer.future.isComplete) return;
464 timer.cancel();
465 completer.complete(value);
466 });
467 return completer.future;
468 }
469
470 /**
463 * Tests whether or not the git command-line app is available for use. 471 * Tests whether or not the git command-line app is available for use.
464 */ 472 */
465 Future<bool> get isGitInstalled { 473 Future<bool> get isGitInstalled {
466 // TODO(rnystrom): We could cache this after the first check. We aren't right 474 // TODO(rnystrom): We could cache this after the first check. We aren't right
467 // now because Future.immediate() will invoke its callback synchronously. 475 // now because Future.immediate() will invoke its callback synchronously.
468 // That does bad things in cases where the caller expects futures to always 476 // That does bad things in cases where the caller expects futures to always
469 // be async. In particular, withGit() in the pub tests which calls 477 // be async. In particular, withGit() in the pub tests which calls
470 // expectAsync() will fail horribly if the test isn't actually async. 478 // expectAsync() will fail horribly if the test isn't actually async.
471 479
472 var completer = new Completer<bool>(); 480 var completer = new Completer<bool>();
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
511 * Exception thrown when an HTTP operation fails. 519 * Exception thrown when an HTTP operation fails.
512 */ 520 */
513 class HttpException implements Exception { 521 class HttpException implements Exception {
514 final int statusCode; 522 final int statusCode;
515 final String reason; 523 final String reason;
516 524
517 const HttpException(this.statusCode, this.reason); 525 const HttpException(this.statusCode, this.reason);
518 } 526 }
519 527
520 /** 528 /**
529 * Exception thrown when an operation times out.
530 */
531 class TimeoutException implements Exception {
532 final String message;
533
534 const TimeoutException(this.message);
535 }
536
537 /**
521 * Contains the results of invoking a [Process] and waiting for it to complete. 538 * Contains the results of invoking a [Process] and waiting for it to complete.
522 */ 539 */
523 class PubProcessResult { 540 class PubProcessResult {
524 final List<String> stdout; 541 final List<String> stdout;
525 final List<String> stderr; 542 final List<String> stderr;
526 final int exitCode; 543 final int exitCode;
527 544
528 const PubProcessResult(this.stdout, this.stderr, this.exitCode); 545 const PubProcessResult(this.stdout, this.stderr, this.exitCode);
529 546
530 bool get success => exitCode == 0; 547 bool get success => exitCode == 0;
(...skipping 20 matching lines...) Expand all
551 return new Directory(entry); 568 return new Directory(entry);
552 } 569 }
553 570
554 /** 571 /**
555 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. 572 * Gets a [Uri] for [uri], which can either already be one, or be a [String].
556 */ 573 */
557 Uri _getUri(uri) { 574 Uri _getUri(uri) {
558 if (uri is Uri) return uri; 575 if (uri is Uri) return uri;
559 return new Uri.fromString(uri); 576 return new Uri.fromString(uri);
560 } 577 }
OLDNEW
« no previous file with comments | « utils/pub/hosted_source.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698