OLD | NEW |
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:isolate'; |
12 import 'dart:uri'; | 12 import 'dart:uri'; |
13 | 13 |
| 14 // TODO(nweiz): Make this import better. |
| 15 import '../../pkg/http/lib/http.dart' as http; |
14 import 'utils.dart'; | 16 import 'utils.dart'; |
| 17 import 'curl_client.dart'; |
15 | 18 |
16 bool _isGitInstalledCache; | 19 bool _isGitInstalledCache; |
17 | 20 |
18 /// The cached Git command. | 21 /// The cached Git command. |
19 String _gitCommandCache; | 22 String _gitCommandCache; |
20 | 23 |
21 /** Gets the current working directory. */ | 24 /** Gets the current working directory. */ |
22 String get currentWorkingDir => new File('.').fullPathSync(); | 25 String get currentWorkingDir => new File('.').fullPathSync(); |
23 | 26 |
24 final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?"); | 27 final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?"); |
(...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
443 return completer.future; | 446 return completer.future; |
444 } | 447 } |
445 | 448 |
446 // TODO(nweiz): make this configurable | 449 // TODO(nweiz): make this configurable |
447 /** | 450 /** |
448 * The amount of time in milliseconds to allow HTTP requests before assuming | 451 * The amount of time in milliseconds to allow HTTP requests before assuming |
449 * they've failed. | 452 * they've failed. |
450 */ | 453 */ |
451 final HTTP_TIMEOUT = 30 * 1000; | 454 final HTTP_TIMEOUT = 30 * 1000; |
452 | 455 |
453 /** | 456 /// An HTTP client that transforms 40* errors and socket exceptions into more |
454 * Opens an input stream for a HTTP GET request to [uri], which may be a | 457 /// user-friendly error messages. |
455 * [String] or [Uri]. | 458 class PubHttpClient extends http.BaseClient { |
456 * | 459 final http.Client _inner; |
457 * Callers should be sure to use [timeout] to make sure that the HTTP request | |
458 * doesn't last indefinitely | |
459 */ | |
460 Future<InputStream> httpGet(uri) { | |
461 // TODO(nweiz): This could return an InputStream synchronously if issue 3657 | |
462 // were fixed and errors could be propagated through it. Then we could also | |
463 // automatically attach a timeout to that stream. | |
464 uri = _getUri(uri); | |
465 | 460 |
466 var completer = new Completer<InputStream>(); | 461 PubHttpClient([http.Client inner]) |
467 var client = new HttpClient(); | 462 : _inner = inner == null ? new http.Client() : inner; |
468 var connection = client.getUrl(uri); | |
469 | 463 |
470 // TODO(nweiz): remove this when issue 4061 is fixed. | 464 Future<http.StreamedResponse> send(http.BaseRequest request) { |
471 var stackTrace; | 465 // TODO(nweiz): remove this when issue 4061 is fixed. |
472 try { | 466 var stackTrace; |
473 throw ""; | 467 try { |
474 } catch (_, localStackTrace) { | 468 throw null; |
475 stackTrace = localStackTrace; | 469 } catch (_, localStackTrace) { |
476 } | 470 stackTrace = localStackTrace; |
477 | |
478 connection.onError = (e) { | |
479 // Show a friendly error if the URL couldn't be resolved. | |
480 if (e is SocketIOException && | |
481 e.osError != null && | |
482 (e.osError.errorCode == 8 || | |
483 e.osError.errorCode == -2 || | |
484 e.osError.errorCode == -5 || | |
485 e.osError.errorCode == 11004)) { | |
486 e = 'Could not resolve URL "${uri.origin}".'; | |
487 } | 471 } |
488 | 472 |
489 client.shutdown(); | 473 // TODO(nweiz): Ideally the timeout would extend to reading from the |
490 completer.completeException(e, stackTrace); | 474 // response input stream, but until issue 3657 is fixed that's not feasible. |
491 }; | 475 return timeout(_inner.send(request).chain((streamedResponse) { |
| 476 if (streamedResponse.statusCode < 400) { |
| 477 return new Future.immediate(streamedResponse); |
| 478 } |
492 | 479 |
493 connection.onResponse = (response) { | 480 return http.Response.fromStream(streamedResponse).transform((response) { |
494 if (response.statusCode >= 400) { | 481 throw new PubHttpException(response); |
495 client.shutdown(); | 482 }); |
496 completer.completeException( | 483 }).transformException((e) { |
497 new PubHttpException(response.statusCode, response.reasonPhrase), | 484 if (e is SocketIOException && |
498 stackTrace); | 485 e.osError != null && |
499 return; | 486 (e.osError.errorCode == 8 || |
500 } | 487 e.osError.errorCode == -2 || |
501 | 488 e.osError.errorCode == -5 || |
502 completer.complete(response.inputStream); | 489 e.osError.errorCode == 11004)) { |
503 }; | 490 throw 'Could not resolve URL "${request.url.origin}".'; |
504 | 491 } |
505 return completer.future; | 492 throw e; |
| 493 }), HTTP_TIMEOUT, 'fetching URL "${request.url}"'); |
| 494 } |
506 } | 495 } |
507 | 496 |
508 /** | 497 /// The HTTP client to use for all HTTP requests. |
509 * Opens an input stream for a HTTP GET request to [uri], which may be a | 498 final httpClient = new PubHttpClient(); |
510 * [String] or [Uri]. Completes with the result of the request as a String. | 499 |
511 */ | 500 final curlClient = new PubHttpClient(new CurlClient()); |
512 Future<String> httpGetString(uri) { | |
513 var future = httpGet(uri).chain((stream) => consumeInputStream(stream)) | |
514 .transform((bytes) => new String.fromCharCodes(bytes)); | |
515 return timeout(future, HTTP_TIMEOUT, 'fetching URL "$uri"'); | |
516 } | |
517 | 501 |
518 /** | 502 /** |
519 * Takes all input from [source] and writes it to [sink]. | 503 * Takes all input from [source] and writes it to [sink]. |
520 * | 504 * |
521 * Returns a future that completes when [source] is closed. | 505 * Returns a future that completes when [source] is closed. |
522 */ | 506 */ |
523 Future pipeInputToInput(InputStream source, ListInputStream sink) { | 507 Future pipeInputToInput(InputStream source, ListInputStream sink) { |
524 var completer = new Completer(); | 508 var completer = new Completer(); |
525 source.onClosed = () { | 509 source.onClosed = () { |
526 sink.markEndOfStream(); | 510 sink.markEndOfStream(); |
(...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
912 return pipeInputToInput(process.stdout, stream); | 896 return pipeInputToInput(process.stdout, stream); |
913 }); | 897 }); |
914 }); | 898 }); |
915 return stream; | 899 return stream; |
916 } | 900 } |
917 | 901 |
918 /** | 902 /** |
919 * Exception thrown when an HTTP operation fails. | 903 * Exception thrown when an HTTP operation fails. |
920 */ | 904 */ |
921 class PubHttpException implements Exception { | 905 class PubHttpException implements Exception { |
922 final int statusCode; | 906 final http.Response response; |
923 final String reason; | |
924 | 907 |
925 const PubHttpException(this.statusCode, this.reason); | 908 const PubHttpException(this.response); |
926 | 909 |
927 String toString() => 'HTTP error $statusCode: $reason'; | 910 String toString() => 'HTTP error ${response.statusCode}: ${response.reason}'; |
928 } | 911 } |
929 | 912 |
930 /** | 913 /** |
931 * Exception thrown when an operation times out. | 914 * Exception thrown when an operation times out. |
932 */ | 915 */ |
933 class TimeoutException implements Exception { | 916 class TimeoutException implements Exception { |
934 final String message; | 917 final String message; |
935 | 918 |
936 const TimeoutException(this.message); | 919 const TimeoutException(this.message); |
937 | 920 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
982 return new Directory(entry); | 965 return new Directory(entry); |
983 } | 966 } |
984 | 967 |
985 /** | 968 /** |
986 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 969 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
987 */ | 970 */ |
988 Uri _getUri(uri) { | 971 Uri _getUri(uri) { |
989 if (uri is Uri) return uri; | 972 if (uri is Uri) return uri; |
990 return new Uri.fromString(uri); | 973 return new Uri.fromString(uri); |
991 } | 974 } |
OLD | NEW |