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