| 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: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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |