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'; |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
156 | 156 |
157 var completer = new Completer<File>(); | 157 var completer = new Completer<File>(); |
158 var file = new File(path); | 158 var file = new File(path); |
159 var outputStream = file.openOutputStream(); | 159 var outputStream = file.openOutputStream(); |
160 stream.pipe(outputStream); | 160 stream.pipe(outputStream); |
161 | 161 |
162 outputStream.onClosed = () { | 162 outputStream.onClosed = () { |
163 completer.complete(file); | 163 completer.complete(file); |
164 }; | 164 }; |
165 | 165 |
| 166 // TODO(nweiz): remove this when issue 4061 is fixed. |
| 167 var stackTrace; |
| 168 try { |
| 169 throw null; |
| 170 } catch (_, localStackTrace) { |
| 171 stackTrace = localStackTrace; |
| 172 } |
| 173 |
166 completeError(error) { | 174 completeError(error) { |
167 if (!completer.isComplete) completer.completeException(error); | 175 if (!completer.isComplete) completer.completeException(error, stackTrace); |
168 } | 176 } |
169 | 177 |
170 stream.onError = completeError; | 178 stream.onError = completeError; |
171 outputStream.onError = completeError; | 179 outputStream.onError = completeError; |
172 | 180 |
173 return completer.future; | 181 return completer.future; |
174 } | 182 } |
175 | 183 |
176 /** | 184 /** |
177 * Creates a directory [dir]. Returns a [Future] that completes when the | 185 * Creates a directory [dir]. Returns a [Future] that completes when the |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 | 252 |
245 dir = _getDirectory(dir); | 253 dir = _getDirectory(dir); |
246 var lister = dir.list(recursive: recursive); | 254 var lister = dir.list(recursive: recursive); |
247 | 255 |
248 lister.onDone = (done) { | 256 lister.onDone = (done) { |
249 // TODO(rnystrom): May need to sort here if it turns out onDir and onFile | 257 // TODO(rnystrom): May need to sort here if it turns out onDir and onFile |
250 // aren't guaranteed to be called in a certain order. So far, they seem to. | 258 // aren't guaranteed to be called in a certain order. So far, they seem to. |
251 if (done) completer.complete(contents); | 259 if (done) completer.complete(contents); |
252 }; | 260 }; |
253 | 261 |
254 lister.onError = (error) => completer.completeException(error); | 262 // TODO(nweiz): remove this when issue 4061 is fixed. |
| 263 var stackTrace; |
| 264 try { |
| 265 throw null; |
| 266 } catch (_, localStackTrace) { |
| 267 stackTrace = localStackTrace; |
| 268 } |
| 269 |
| 270 lister.onError = (error) => completer.completeException(error, stackTrace); |
255 lister.onDir = (file) => contents.add(file); | 271 lister.onDir = (file) => contents.add(file); |
256 lister.onFile = (file) { | 272 lister.onFile = (file) { |
257 if (!includeHiddenFiles && basename(file).startsWith('.')) return; | 273 if (!includeHiddenFiles && basename(file).startsWith('.')) return; |
258 contents.add(file); | 274 contents.add(file); |
259 }; | 275 }; |
260 | 276 |
261 return completer.future; | 277 return completer.future; |
262 } | 278 } |
263 | 279 |
264 /** | 280 /** |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
392 /// A [StringInputStream] passed to this should have no callbacks registered. | 408 /// A [StringInputStream] passed to this should have no callbacks registered. |
393 Future<String> readLine([StringInputStream stream]) { | 409 Future<String> readLine([StringInputStream stream]) { |
394 if (stream == null) stream = _stringStdin; | 410 if (stream == null) stream = _stringStdin; |
395 if (stream.closed) return new Future.immediate(''); | 411 if (stream.closed) return new Future.immediate(''); |
396 void removeCallbacks() { | 412 void removeCallbacks() { |
397 stream.onClosed = null; | 413 stream.onClosed = null; |
398 stream.onLine = null; | 414 stream.onLine = null; |
399 stream.onError = null; | 415 stream.onError = null; |
400 } | 416 } |
401 | 417 |
| 418 // TODO(nweiz): remove this when issue 4061 is fixed. |
| 419 var stackTrace; |
| 420 try { |
| 421 throw null; |
| 422 } catch (_, localStackTrace) { |
| 423 stackTrace = localStackTrace; |
| 424 } |
| 425 |
402 var completer = new Completer(); | 426 var completer = new Completer(); |
403 stream.onClosed = () { | 427 stream.onClosed = () { |
404 removeCallbacks(); | 428 removeCallbacks(); |
405 completer.complete(''); | 429 completer.complete(''); |
406 }; | 430 }; |
407 | 431 |
408 stream.onLine = () { | 432 stream.onLine = () { |
409 removeCallbacks(); | 433 removeCallbacks(); |
410 completer.complete(stream.readLine()); | 434 completer.complete(stream.readLine()); |
411 }; | 435 }; |
412 | 436 |
413 stream.onError = (e) { | 437 stream.onError = (e) { |
414 removeCallbacks(); | 438 removeCallbacks(); |
415 completer.completeException(e); | 439 completer.completeException(e, stackTrace); |
416 }; | 440 }; |
417 | 441 |
418 return completer.future; | 442 return completer.future; |
419 } | 443 } |
420 | 444 |
421 // TODO(nweiz): make this configurable | 445 // TODO(nweiz): make this configurable |
422 /** | 446 /** |
423 * The amount of time in milliseconds to allow HTTP requests before assuming | 447 * The amount of time in milliseconds to allow HTTP requests before assuming |
424 * they've failed. | 448 * they've failed. |
425 */ | 449 */ |
426 final HTTP_TIMEOUT = 30 * 1000; | 450 final HTTP_TIMEOUT = 30 * 1000; |
427 | 451 |
428 /** | 452 /** |
429 * Opens an input stream for a HTTP GET request to [uri], which may be a | 453 * Opens an input stream for a HTTP GET request to [uri], which may be a |
430 * [String] or [Uri]. | 454 * [String] or [Uri]. |
431 * | 455 * |
432 * Callers should be sure to use [timeout] to make sure that the HTTP request | 456 * Callers should be sure to use [timeout] to make sure that the HTTP request |
433 * doesn't last indefinitely | 457 * doesn't last indefinitely |
434 */ | 458 */ |
435 Future<InputStream> httpGet(uri) { | 459 Future<InputStream> httpGet(uri) { |
436 // TODO(nweiz): This could return an InputStream synchronously if issue 3657 | 460 // TODO(nweiz): This could return an InputStream synchronously if issue 3657 |
437 // were fixed and errors could be propagated through it. Then we could also | 461 // were fixed and errors could be propagated through it. Then we could also |
438 // automatically attach a timeout to that stream. | 462 // automatically attach a timeout to that stream. |
439 uri = _getUri(uri); | 463 uri = _getUri(uri); |
440 | 464 |
441 var completer = new Completer<InputStream>(); | 465 var completer = new Completer<InputStream>(); |
442 var client = new HttpClient(); | 466 var client = new HttpClient(); |
443 var connection = client.getUrl(uri); | 467 var connection = client.getUrl(uri); |
444 | 468 |
| 469 // TODO(nweiz): remove this when issue 4061 is fixed. |
| 470 var stackTrace; |
| 471 try { |
| 472 throw null; |
| 473 } catch (_, localStackTrace) { |
| 474 stackTrace = localStackTrace; |
| 475 } |
| 476 |
445 connection.onError = (e) { | 477 connection.onError = (e) { |
446 // Show a friendly error if the URL couldn't be resolved. | 478 // Show a friendly error if the URL couldn't be resolved. |
447 if (e is SocketIOException && | 479 if (e is SocketIOException && |
448 e.osError != null && | 480 e.osError != null && |
449 (e.osError.errorCode == 8 || | 481 (e.osError.errorCode == 8 || |
450 e.osError.errorCode == -2 || | 482 e.osError.errorCode == -2 || |
451 e.osError.errorCode == -5 || | 483 e.osError.errorCode == -5 || |
452 e.osError.errorCode == 11004)) { | 484 e.osError.errorCode == 11004)) { |
453 e = 'Could not resolve URL "${uri.origin}".'; | 485 e = 'Could not resolve URL "${uri.origin}".'; |
454 } | 486 } |
455 | 487 |
456 client.shutdown(); | 488 client.shutdown(); |
457 completer.completeException(e); | 489 completer.completeException(e, stackTrace); |
458 }; | 490 }; |
459 | 491 |
460 connection.onResponse = (response) { | 492 connection.onResponse = (response) { |
461 if (response.statusCode >= 400) { | 493 if (response.statusCode >= 400) { |
462 client.shutdown(); | 494 client.shutdown(); |
463 completer.completeException( | 495 completer.completeException( |
464 new PubHttpException(response.statusCode, response.reasonPhrase)); | 496 new PubHttpException(response.statusCode, response.reasonPhrase), |
| 497 stackTrace); |
465 return; | 498 return; |
466 } | 499 } |
467 | 500 |
468 completer.complete(response.inputStream); | 501 completer.complete(response.inputStream); |
469 }; | 502 }; |
470 | 503 |
471 return completer.future; | 504 return completer.future; |
472 } | 505 } |
473 | 506 |
474 /** | 507 /** |
(...skipping 22 matching lines...) Expand all Loading... |
497 source.onError = (e) { throw e; }; | 530 source.onError = (e) { throw e; }; |
498 return completer.future; | 531 return completer.future; |
499 } | 532 } |
500 | 533 |
501 /** | 534 /** |
502 * Buffers all input from an InputStream and returns it as a future. | 535 * Buffers all input from an InputStream and returns it as a future. |
503 */ | 536 */ |
504 Future<List<int>> consumeInputStream(InputStream stream) { | 537 Future<List<int>> consumeInputStream(InputStream stream) { |
505 if (stream.closed) return new Future.immediate(<int>[]); | 538 if (stream.closed) return new Future.immediate(<int>[]); |
506 | 539 |
| 540 // TODO(nweiz): remove this when issue 4061 is fixed. |
| 541 var stackTrace; |
| 542 try { |
| 543 throw null; |
| 544 } catch (_, localStackTrace) { |
| 545 stackTrace = localStackTrace; |
| 546 } |
| 547 |
507 var completer = new Completer<List<int>>(); | 548 var completer = new Completer<List<int>>(); |
508 var buffer = <int>[]; | 549 var buffer = <int>[]; |
509 stream.onClosed = () => completer.complete(buffer); | 550 stream.onClosed = () => completer.complete(buffer); |
510 stream.onData = () => buffer.addAll(stream.read()); | 551 stream.onData = () => buffer.addAll(stream.read()); |
511 stream.onError = (e) => completer.completeException(e); | 552 stream.onError = (e) => completer.completeException(e, stackTrace); |
512 return completer.future; | 553 return completer.future; |
513 } | 554 } |
514 | 555 |
515 /// Buffers all input from a StringInputStream and returns it as a future. | 556 /// Buffers all input from a StringInputStream and returns it as a future. |
516 Future<String> consumeStringInputStream(StringInputStream stream) { | 557 Future<String> consumeStringInputStream(StringInputStream stream) { |
517 if (stream.closed) return new Future.immediate(''); | 558 if (stream.closed) return new Future.immediate(''); |
518 | 559 |
| 560 // TODO(nweiz): remove this when issue 4061 is fixed. |
| 561 var stackTrace; |
| 562 try { |
| 563 throw null; |
| 564 } catch (_, localStackTrace) { |
| 565 stackTrace = localStackTrace; |
| 566 } |
| 567 |
519 var completer = new Completer<String>(); | 568 var completer = new Completer<String>(); |
520 var buffer = new StringBuffer(); | 569 var buffer = new StringBuffer(); |
521 stream.onClosed = () => completer.complete(buffer.toString()); | 570 stream.onClosed = () => completer.complete(buffer.toString()); |
522 stream.onData = () => buffer.add(stream.read()); | 571 stream.onData = () => buffer.add(stream.read()); |
523 stream.onError = (e) => completer.completeException(e); | 572 stream.onError = (e) => completer.completeException(e, stackTrace); |
524 return completer.future; | 573 return completer.future; |
525 } | 574 } |
526 | 575 |
527 /// Spawns and runs the process located at [executable], passing in [args]. | 576 /// Spawns and runs the process located at [executable], passing in [args]. |
528 /// Returns a [Future] that will complete with the results of the process after | 577 /// Returns a [Future] that will complete with the results of the process after |
529 /// it has ended. | 578 /// it has ended. |
530 /// | 579 /// |
531 /// The spawned process will inherit its parent's environment variables. If | 580 /// The spawned process will inherit its parent's environment variables. If |
532 /// [environment] is provided, that will be used to augment (not replace) the | 581 /// [environment] is provided, that will be used to augment (not replace) the |
533 /// the inherited variables. | 582 /// the inherited variables. |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
584 | 633 |
585 return fn(executable, args, options); | 634 return fn(executable, args, options); |
586 } | 635 } |
587 | 636 |
588 /// Closes [response] while ignoring the body of [request]. Returns a Future | 637 /// Closes [response] while ignoring the body of [request]. Returns a Future |
589 /// that completes once the response is closed. | 638 /// that completes once the response is closed. |
590 /// | 639 /// |
591 /// Due to issue 6984, it's necessary to drain the request body before closing | 640 /// Due to issue 6984, it's necessary to drain the request body before closing |
592 /// the response. | 641 /// the response. |
593 Future closeHttpResponse(HttpRequest request, HttpResponse response) { | 642 Future closeHttpResponse(HttpRequest request, HttpResponse response) { |
| 643 // TODO(nweiz): remove this when issue 4061 is fixed. |
| 644 var stackTrace; |
| 645 try { |
| 646 throw null; |
| 647 } catch (_, localStackTrace) { |
| 648 stackTrace = localStackTrace; |
| 649 } |
| 650 |
594 var completer = new Completer(); | 651 var completer = new Completer(); |
595 request.inputStream.onError = completer.completeException; | 652 request.inputStream.onError = (e) => |
| 653 completer.completeException(e, stackTrace); |
596 request.inputStream.onData = request.inputStream.read; | 654 request.inputStream.onData = request.inputStream.read; |
597 request.inputStream.onClosed = () { | 655 request.inputStream.onClosed = () { |
598 response.outputStream.close(); | 656 response.outputStream.close(); |
599 completer.complete(null); | 657 completer.complete(null); |
600 }; | 658 }; |
601 return completer.future; | 659 return completer.future; |
602 } | 660 } |
603 | 661 |
604 /** | 662 /** |
605 * Wraps [input] to provide a timeout. If [input] completes before | 663 * Wraps [input] to provide a timeout. If [input] completes before |
606 * [milliseconds] have passed, then the return value completes in the same way. | 664 * [milliseconds] have passed, then the return value completes in the same way. |
607 * However, if [milliseconds] pass before [input] has completed, it completes | 665 * However, if [milliseconds] pass before [input] has completed, it completes |
608 * with a [TimeoutException] with [description] (which should be a fragment | 666 * with a [TimeoutException] with [description] (which should be a fragment |
609 * describing the action that timed out). | 667 * describing the action that timed out). |
610 * | 668 * |
611 * Note that timing out will not cancel the asynchronous operation behind | 669 * Note that timing out will not cancel the asynchronous operation behind |
612 * [input]. | 670 * [input]. |
613 */ | 671 */ |
614 Future timeout(Future input, int milliseconds, String description) { | 672 Future timeout(Future input, int milliseconds, String description) { |
615 var completer = new Completer(); | 673 var completer = new Completer(); |
616 var timer = new Timer(milliseconds, (_) { | 674 var timer = new Timer(milliseconds, (_) { |
617 if (completer.future.isComplete) return; | 675 if (completer.future.isComplete) return; |
618 completer.completeException(new TimeoutException( | 676 completer.completeException(new TimeoutException( |
619 'Timed out while $description.')); | 677 'Timed out while $description.')); |
620 }); | 678 }); |
621 input.handleException((e) { | 679 input.handleException((e) { |
622 if (completer.future.isComplete) return false; | 680 if (completer.future.isComplete) return false; |
623 timer.cancel(); | 681 timer.cancel(); |
624 completer.completeException(e); | 682 completer.completeException(e, input.stackTrace); |
625 return true; | 683 return true; |
626 }); | 684 }); |
627 input.then((value) { | 685 input.then((value) { |
628 if (completer.future.isComplete) return; | 686 if (completer.future.isComplete) return; |
629 timer.cancel(); | 687 timer.cancel(); |
630 completer.complete(value); | 688 completer.complete(value); |
631 }); | 689 }); |
632 return completer.future; | 690 return completer.future; |
633 } | 691 } |
634 | 692 |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
721 var completer = new Completer<int>(); | 779 var completer = new Completer<int>(); |
722 var processFuture = Process.start("tar", | 780 var processFuture = Process.start("tar", |
723 ["--extract", "--gunzip", "--directory", destination]); | 781 ["--extract", "--gunzip", "--directory", destination]); |
724 processFuture.then((process) { | 782 processFuture.then((process) { |
725 process.onExit = completer.complete; | 783 process.onExit = completer.complete; |
726 stream.pipe(process.stdin); | 784 stream.pipe(process.stdin); |
727 process.stdout.pipe(stdout, close: false); | 785 process.stdout.pipe(stdout, close: false); |
728 process.stderr.pipe(stderr, close: false); | 786 process.stderr.pipe(stderr, close: false); |
729 }); | 787 }); |
730 processFuture.handleException((error) { | 788 processFuture.handleException((error) { |
731 completer.completeException(error); | 789 completer.completeException(error, processFuture.stackTrace); |
732 return true; | 790 return true; |
733 }); | 791 }); |
734 | 792 |
735 return completer.future.transform((exitCode) => exitCode == 0); | 793 return completer.future.transform((exitCode) => exitCode == 0); |
736 } | 794 } |
737 | 795 |
738 Future<bool> _extractTarGzWindows(InputStream stream, String destination) { | 796 Future<bool> _extractTarGzWindows(InputStream stream, String destination) { |
739 // TODO(rnystrom): In the repo's history, there is an older implementation of | 797 // TODO(rnystrom): In the repo's history, there is an older implementation of |
740 // this that does everything in memory by piping streams directly together | 798 // this that does everything in memory by piping streams directly together |
741 // instead of writing out temp files. The code is simpler, but unfortunately, | 799 // instead of writing out temp files. The code is simpler, but unfortunately, |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
923 return new Directory(entry); | 981 return new Directory(entry); |
924 } | 982 } |
925 | 983 |
926 /** | 984 /** |
927 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 985 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
928 */ | 986 */ |
929 Uri _getUri(uri) { | 987 Uri _getUri(uri) { |
930 if (uri is Uri) return uri; | 988 if (uri is Uri) return uri; |
931 return new Uri.fromString(uri); | 989 return new Uri.fromString(uri); |
932 } | 990 } |
OLD | NEW |