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 library curl_client; | 5 library curl_client; |
6 | 6 |
7 import 'dart:io'; | 7 import 'dart:io'; |
8 | 8 |
9 import '../../pkg/http/lib/http.dart' as http; | 9 import '../../pkg/http/lib/http.dart' as http; |
10 import 'io.dart'; | 10 import 'io.dart'; |
11 import 'log.dart' as log; | |
11 import 'utils.dart'; | 12 import 'utils.dart'; |
12 | 13 |
13 /// A drop-in replacement for [http.Client] that uses the `curl` command-line | 14 /// A drop-in replacement for [http.Client] that uses the `curl` command-line |
14 /// utility rather than [dart:io] to make requests. This class will only exist | 15 /// utility rather than [dart:io] to make requests. This class will only exist |
15 /// temporarily until [dart:io] natively supports requests over HTTPS. | 16 /// temporarily until [dart:io] natively supports requests over HTTPS. |
16 class CurlClient extends http.BaseClient { | 17 class CurlClient extends http.BaseClient { |
17 /// The path to the `curl` executable to run. | 18 /// The path to the `curl` executable to run. |
18 /// | 19 /// |
19 /// By default on Unix-like operating systems, this will look up `curl` on the | 20 /// By default on Unix-like operating systems, this will look up `curl` on the |
20 /// system path. On Windows, it will use the bundled `curl.exe`. | 21 /// system path. On Windows, it will use the bundled `curl.exe`. |
21 final String executable; | 22 final String executable; |
22 | 23 |
23 /// Creates a new [CurlClient] with [executable] as the path to the `curl` | 24 /// Creates a new [CurlClient] with [executable] as the path to the `curl` |
24 /// executable. | 25 /// executable. |
25 /// | 26 /// |
26 /// By default on Unix-like operating systems, this will look up `curl` on the | 27 /// By default on Unix-like operating systems, this will look up `curl` on the |
27 /// system path. On Windows, it will use the bundled `curl.exe`. | 28 /// system path. On Windows, it will use the bundled `curl.exe`. |
28 CurlClient([String executable]) | 29 CurlClient([String executable]) |
29 : executable = executable == null ? _defaultExecutable : executable; | 30 : executable = executable == null ? _defaultExecutable : executable; |
30 | 31 |
31 /// Sends a request via `curl` and returns the response. | 32 /// Sends a request via `curl` and returns the response. |
32 Future<http.StreamedResponse> send(http.BaseRequest request) { | 33 Future<http.StreamedResponse> send(http.BaseRequest request) { |
34 log.fine("Sending Curl request $request"); | |
35 | |
33 var requestStream = request.finalize(); | 36 var requestStream = request.finalize(); |
34 return withTempDir((tempDir) { | 37 return withTempDir((tempDir) { |
35 var headerFile = new Path(tempDir).append("curl-headers").toNativePath(); | 38 var headerFile = new Path(tempDir).append("curl-headers").toNativePath(); |
36 var arguments = _argumentsForRequest(request, headerFile); | 39 var arguments = _argumentsForRequest(request, headerFile); |
37 var process; | 40 var process; |
41 log.process(executable, arguments); | |
nweiz
2012/12/05 23:56:54
Style nit: swap this with the line above it.
Bob Nystrom
2012/12/06 01:33:26
Done.
| |
38 return Process.start(executable, arguments).chain((process_) { | 42 return Process.start(executable, arguments).chain((process_) { |
39 process = process_; | 43 process = process_; |
40 if (requestStream.closed) { | 44 if (requestStream.closed) { |
41 process.stdin.close(); | 45 process.stdin.close(); |
42 } else { | 46 } else { |
43 requestStream.pipe(process.stdin); | 47 requestStream.pipe(process.stdin); |
44 } | 48 } |
45 | 49 |
46 return _waitForHeaders(process, expectBody: request.method != "HEAD"); | 50 return _waitForHeaders(process, expectBody: request.method != "HEAD"); |
47 }).chain((_) => new File(headerFile).readAsLines()) | 51 }).chain((_) => new File(headerFile).readAsLines()) |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
91 } else if (request.contentLength > 0) { | 95 } else if (request.contentLength > 0) { |
92 headers['content-length'] = request.contentLength.toString(); | 96 headers['content-length'] = request.contentLength.toString(); |
93 } | 97 } |
94 | 98 |
95 headers.forEach((name, value) { | 99 headers.forEach((name, value) { |
96 arguments.add("--header"); | 100 arguments.add("--header"); |
97 arguments.add("$name: $value"); | 101 arguments.add("$name: $value"); |
98 }); | 102 }); |
99 arguments.add(request.url.toString()); | 103 arguments.add(request.url.toString()); |
100 | 104 |
105 log.fine("Curl request arguments: $arguments"); | |
nweiz
2012/12/05 23:56:54
This seems redundant with log.process above.
Bob Nystrom
2012/12/06 01:33:26
Removed. I think for some reason when I skimmed th
| |
106 | |
101 return arguments; | 107 return arguments; |
102 } | 108 } |
103 | 109 |
104 /// Returns a [Future] that completes once the `curl` [process] has finished | 110 /// Returns a [Future] that completes once the `curl` [process] has finished |
105 /// receiving the response headers. [expectBody] indicates that the server is | 111 /// receiving the response headers. [expectBody] indicates that the server is |
106 /// expected to send a response body (which is not the case for HEAD | 112 /// expected to send a response body (which is not the case for HEAD |
107 /// requests). | 113 /// requests). |
108 /// | 114 /// |
109 /// Curl prints the headers to a file and then prints the body to stdout. So, | 115 /// Curl prints the headers to a file and then prints the body to stdout. So, |
110 /// in theory, we could read the headers as soon as we see anything appear | 116 /// in theory, we could read the headers as soon as we see anything appear |
111 /// in stdout. However, that seems to be too early to successfully read the | 117 /// in stdout. However, that seems to be too early to successfully read the |
112 /// file (at least on Mac). Instead, this just waits until the entire process | 118 /// file (at least on Mac). Instead, this just waits until the entire process |
113 /// has completed. | 119 /// has completed. |
114 Future _waitForHeaders(Process process, {bool expectBody}) { | 120 Future _waitForHeaders(Process process, {bool expectBody}) { |
115 var completer = new Completer(); | 121 var completer = new Completer(); |
116 process.onExit = (exitCode) { | 122 process.onExit = (exitCode) { |
123 log.io("Curl process exited with code $exitCode."); | |
124 | |
117 if (exitCode == 0) { | 125 if (exitCode == 0) { |
118 completer.complete(null); | 126 completer.complete(null); |
119 return; | 127 return; |
120 } | 128 } |
121 | 129 |
122 chainToCompleter(consumeInputStream(process.stderr) | 130 chainToCompleter(consumeInputStream(process.stderr) |
123 .transform((stderrBytes) { | 131 .transform((stderrBytes) { |
124 var message = new String.fromCharCodes(stderrBytes); | 132 var message = new String.fromCharCodes(stderrBytes); |
133 log.fine(message); | |
nweiz
2012/12/05 23:56:54
Give some context for this message.
Bob Nystrom
2012/12/06 01:33:26
Done.
| |
125 if (exitCode == 47) { | 134 if (exitCode == 47) { |
126 throw new RedirectLimitExceededException([]); | 135 throw new RedirectLimitExceededException([]); |
127 } else { | 136 } else { |
128 throw new HttpException(message); | 137 throw new HttpException(message); |
129 } | 138 } |
130 }), completer); | 139 }), completer); |
131 }; | 140 }; |
132 | 141 |
133 // If there's not going to be a response body (e.g. for HEAD requests), curl | 142 // If there's not going to be a response body (e.g. for HEAD requests), curl |
134 // prints the headers to stdout instead of the body. We want to wait until | 143 // prints the headers to stdout instead of the body. We want to wait until |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
186 /// path to the bundled `curl.exe`; elsewhere, this is just "curl", and we | 195 /// path to the bundled `curl.exe`; elsewhere, this is just "curl", and we |
187 /// assume it to be installed and on the user's PATH. | 196 /// assume it to be installed and on the user's PATH. |
188 static String get _defaultExecutable { | 197 static String get _defaultExecutable { |
189 if (Platform.operatingSystem != 'windows') return 'curl'; | 198 if (Platform.operatingSystem != 'windows') return 'curl'; |
190 // Note: This line of code gets munged by create_sdk.py to be the correct | 199 // Note: This line of code gets munged by create_sdk.py to be the correct |
191 // relative path to curl in the SDK. | 200 // relative path to curl in the SDK. |
192 var pathToCurl = "../../third_party/curl/curl.exe"; | 201 var pathToCurl = "../../third_party/curl/curl.exe"; |
193 return relativeToPub(pathToCurl); | 202 return relativeToPub(pathToCurl); |
194 } | 203 } |
195 } | 204 } |
OLD | NEW |