Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: utils/pub/curl_client.dart

Issue 12021008: Use the dart:async Stream API thoroughly in Pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « utils/pub/command_lish.dart ('k') | utils/pub/error_group.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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:async'; 7 import 'dart:async';
8 import 'dart:io'; 8 import 'dart:io';
9 9
10 import '../../pkg/http/lib/http.dart' as http; 10 import '../../pkg/http/lib/http.dart' as http;
(...skipping 23 matching lines...) Expand all
34 Future<http.StreamedResponse> send(http.BaseRequest request) { 34 Future<http.StreamedResponse> send(http.BaseRequest request) {
35 log.fine("Sending Curl request $request"); 35 log.fine("Sending Curl request $request");
36 36
37 var requestStream = request.finalize(); 37 var requestStream = request.finalize();
38 return withTempDir((tempDir) { 38 return withTempDir((tempDir) {
39 var headerFile = join(tempDir, "curl-headers"); 39 var headerFile = join(tempDir, "curl-headers");
40 var arguments = _argumentsForRequest(request, headerFile); 40 var arguments = _argumentsForRequest(request, headerFile);
41 var process; 41 var process;
42 return startProcess(executable, arguments).then((process_) { 42 return startProcess(executable, arguments).then((process_) {
43 process = process_; 43 process = process_;
44 return requestStream.pipe(wrapOutputStream(process.stdin)); 44 return Future.wait([
45 }).then((_) { 45 store(requestStream, process.stdin),
46 return _waitForHeaders(process, expectBody: request.method != "HEAD"); 46 _waitForHeaders(process, expectBody: request.method != "HEAD")
47 ]);
47 }).then((_) => new File(headerFile).readAsLines()) 48 }).then((_) => new File(headerFile).readAsLines())
48 .then((lines) => _buildResponse(request, process, lines)); 49 .then((lines) => _buildResponse(request, process, lines));
49 }); 50 });
50 } 51 }
51 52
52 /// Returns the list of arguments to `curl` necessary for performing 53 /// Returns the list of arguments to `curl` necessary for performing
53 /// [request]. [headerFile] is the path to the file where the response headers 54 /// [request]. [headerFile] is the path to the file where the response headers
54 /// should be stored. 55 /// should be stored.
55 List<String> _argumentsForRequest( 56 List<String> _argumentsForRequest(
56 http.BaseRequest request, String headerFile) { 57 http.BaseRequest request, String headerFile) {
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 /// Returns a [Future] that completes once the `curl` [process] has finished 105 /// Returns a [Future] that completes once the `curl` [process] has finished
105 /// receiving the response headers. [expectBody] indicates that the server is 106 /// receiving the response headers. [expectBody] indicates that the server is
106 /// expected to send a response body (which is not the case for HEAD 107 /// expected to send a response body (which is not the case for HEAD
107 /// requests). 108 /// requests).
108 /// 109 ///
109 /// Curl prints the headers to a file and then prints the body to stdout. So, 110 /// 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 111 /// 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 112 /// 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 113 /// file (at least on Mac). Instead, this just waits until the entire process
113 /// has completed. 114 /// has completed.
114 Future _waitForHeaders(Process process, {bool expectBody}) { 115 Future _waitForHeaders(PubProcess process, {bool expectBody}) {
115 var completer = new Completer(); 116 var future = process.exitCode.then((exitCode) {
116 process.onExit = (exitCode) {
117 log.io("Curl process exited with code $exitCode."); 117 log.io("Curl process exited with code $exitCode.");
118 if (exitCode == 0) return;
118 119
119 if (exitCode == 0) { 120 process.stderr.bytesToString().then((message) {
120 completer.complete(null);
121 return;
122 }
123
124 chainToCompleter(consumeInputStream(process.stderr).then((stderrBytes) {
125 var message = new String.fromCharCodes(stderrBytes);
126 log.fine('Got error reading headers from curl: $message'); 121 log.fine('Got error reading headers from curl: $message');
127 if (exitCode == 47) { 122 if (exitCode == 47) {
128 throw new RedirectLimitExceededException([]); 123 throw new RedirectLimitExceededException([]);
129 } else { 124 } else {
130 throw new HttpException(message); 125 throw new HttpException(message);
131 } 126 }
132 }), completer); 127 });
133 }; 128 });
129
130 if (expectBody) return future;
134 131
135 // If there's not going to be a response body (e.g. for HEAD requests), curl 132 // If there's not going to be a response body (e.g. for HEAD requests), curl
136 // prints the headers to stdout instead of the body. We want to wait until 133 // prints the headers to stdout instead of the body. We want to wait until
137 // all the headers are received to read them from the header file. 134 // all the headers are received to read them from the header file.
138 if (!expectBody) { 135 return Future.wait([process.stdout.toBytes(), future]);
139 return Future.wait([
140 consumeInputStream(process.stdout),
141 completer.future
142 ]);
143 }
144
145 return completer.future;
146 } 136 }
147 137
148 /// Returns a [http.StreamedResponse] from the response data printed by the 138 /// Returns a [http.StreamedResponse] from the response data printed by the
149 /// `curl` [process]. [lines] are the headers that `curl` wrote to a file. 139 /// `curl` [process]. [lines] are the headers that `curl` wrote to a file.
150 http.StreamedResponse _buildResponse( 140 http.StreamedResponse _buildResponse(
151 http.BaseRequest request, Process process, List<String> lines) { 141 http.BaseRequest request, PubProcess process, List<String> lines) {
152 // When curl follows redirects, it prints the redirect headers as well as 142 // When curl follows redirects, it prints the redirect headers as well as
153 // the headers of the final request. Each block is separated by a blank 143 // the headers of the final request. Each block is separated by a blank
154 // line. We just care about the last block. There is one trailing empty 144 // line. We just care about the last block. There is one trailing empty
155 // line, though, which we don't want to consider a separator. 145 // line, though, which we don't want to consider a separator.
156 var lastBlank = lines.lastIndexOf("", lines.length - 2); 146 var lastBlank = lines.lastIndexOf("", lines.length - 2);
157 if (lastBlank != -1) lines.removeRange(0, lastBlank + 1); 147 if (lastBlank != -1) lines.removeRange(0, lastBlank + 1);
158 148
159 var statusParts = lines.removeAt(0).split(" "); 149 var statusParts = lines.removeAt(0).split(" ");
160 var status = int.parse(statusParts[1]); 150 var status = int.parse(statusParts[1]);
161 var isRedirect = status >= 300 && status < 400; 151 var isRedirect = status >= 300 && status < 400;
162 var reasonPhrase = 152 var reasonPhrase =
163 Strings.join(statusParts.getRange(2, statusParts.length - 2), " "); 153 Strings.join(statusParts.getRange(2, statusParts.length - 2), " ");
164 var headers = <String>{}; 154 var headers = <String>{};
165 for (var line in lines) { 155 for (var line in lines) {
166 if (line.isEmpty) continue; 156 if (line.isEmpty) continue;
167 var split = split1(line, ":"); 157 var split = split1(line, ":");
168 headers[split[0].toLowerCase()] = split[1].trim(); 158 headers[split[0].toLowerCase()] = split[1].trim();
169 } 159 }
170 var responseStream = process.stdout;
171 if (responseStream.closed) {
172 responseStream = new ListInputStream();
173 responseStream.markEndOfStream();
174 }
175 var contentLength = -1; 160 var contentLength = -1;
176 if (headers.containsKey('content-length')) { 161 if (headers.containsKey('content-length')) {
177 contentLength = int.parse(headers['content-length']); 162 contentLength = int.parse(headers['content-length']);
178 } 163 }
179 164
180 return new http.StreamedResponse( 165 return new http.StreamedResponse(
181 wrapInputStream(responseStream), status, contentLength, 166 process.stdout, status, contentLength,
182 request: request, 167 request: request,
183 headers: headers, 168 headers: headers,
184 isRedirect: isRedirect, 169 isRedirect: isRedirect,
185 reasonPhrase: reasonPhrase); 170 reasonPhrase: reasonPhrase);
186 } 171 }
187 172
188 /// The default executable to use for running curl. On Windows, this is the 173 /// The default executable to use for running curl. On Windows, this is the
189 /// path to the bundled `curl.exe`; elsewhere, this is just "curl", and we 174 /// path to the bundled `curl.exe`; elsewhere, this is just "curl", and we
190 /// assume it to be installed and on the user's PATH. 175 /// assume it to be installed and on the user's PATH.
191 static String get _defaultExecutable { 176 static String get _defaultExecutable {
192 if (Platform.operatingSystem != 'windows') return 'curl'; 177 if (Platform.operatingSystem != 'windows') return 'curl';
193 // Note: This line of code gets munged by create_sdk.py to be the correct 178 // Note: This line of code gets munged by create_sdk.py to be the correct
194 // relative path to curl in the SDK. 179 // relative path to curl in the SDK.
195 var pathToCurl = "../../third_party/curl/curl.exe"; 180 var pathToCurl = "../../third_party/curl/curl.exe";
196 return relativeToPub(pathToCurl); 181 return relativeToPub(pathToCurl);
197 } 182 }
198 } 183 }
OLDNEW
« no previous file with comments | « utils/pub/command_lish.dart ('k') | utils/pub/error_group.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698