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)); | |
Bob Nystrom
2012/09/06 22:52:05
Indent +2
nweiz
2012/09/06 23:33:34
Done.
| |
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 [exception]. | |
449 */ | |
450 Future timeout(Future input, int milliSeconds, exception) { | |
Bob Nystrom
2012/09/06 22:52:05
Can you create a TimeoutException class and then t
nweiz
2012/09/06 23:33:34
Done.
| |
451 var completer = new Completer(); | |
452 var timer = new Timer(milliSeconds, (_) { | |
453 if (completer.future.isComplete) return; | |
454 completer.completeException(exception); | |
Bob Nystrom
2012/09/06 22:52:05
I like the idea of timeout() being a separate orth
nweiz
2012/09/06 23:33:34
Given that we do explicitly exit, I don't see anyt
Bob Nystrom
2012/09/07 00:15:34
OK. Can you update the doc comment to note that th
| |
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 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
551 return new Directory(entry); | 559 return new Directory(entry); |
552 } | 560 } |
553 | 561 |
554 /** | 562 /** |
555 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 563 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
556 */ | 564 */ |
557 Uri _getUri(uri) { | 565 Uri _getUri(uri) { |
558 if (uri is Uri) return uri; | 566 if (uri is Uri) return uri; |
559 return new Uri.fromString(uri); | 567 return new Uri.fromString(uri); |
560 } | 568 } |
OLD | NEW |