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

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

Issue 11299028: Move CurlClient to Pub so that it can access resources. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review change Created 8 years, 1 month 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 | « pkg/pkg.status ('k') | utils/pub/io.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:io'; 7 import 'dart:io';
8 8
9 import 'base_client.dart'; 9 import '../../pkg/http/lib/http.dart' as http;
10 import 'base_request.dart'; 10 import 'io.dart';
11 import 'streamed_response.dart';
12 import 'utils.dart'; 11 import 'utils.dart';
13 12
14 /// A drop-in replacement for [Client] that uses the `curl` command-line utility 13 /// A drop-in replacement for [http.Client] that uses the `curl` command-line
15 /// rather than [dart:io] to make requests. This class will only exist 14 /// utility rather than [dart:io] to make requests. This class will only exist
16 /// temporarily until [dart:io] natively supports requests over HTTPS. 15 /// temporarily until [dart:io] natively supports requests over HTTPS.
17 class CurlClient extends BaseClient { 16 class CurlClient extends http.BaseClient {
18 /// The path to the `curl` executable to run. By default, this will look up 17 /// The path to the `curl` executable to run. By default, this will look up
19 /// `curl` on the system path. 18 /// `curl` on the system path.
20 final String executable; 19 final String executable;
21 20
22 /// Creates a new [CurlClient] with [executable] as the path to the `curl` 21 /// Creates a new [CurlClient] with [executable] as the path to the `curl`
23 /// executable. By default, this will look up `curl` on the system path. 22 /// executable. By default, this will look up `curl` on the system path.
24 CurlClient([String executable]) 23 CurlClient([String executable])
25 : executable = executable == null ? "curl" : executable; 24 : executable = executable == null ? "curl" : executable;
26 25
27 /// Sends a request via `curl` and returns the response. 26 /// Sends a request via `curl` and returns the response.
28 Future<StreamedResponse> send(BaseRequest request) { 27 Future<http.StreamedResponse> send(http.BaseRequest request) {
29 var requestStream = request.finalize(); 28 var requestStream = request.finalize();
30 return withTempDir((tempDir) { 29 return withTempDir((tempDir) {
31 var headerFile = new Path(tempDir).append("curl-headers").toNativePath(); 30 var headerFile = new Path(tempDir).append("curl-headers").toNativePath();
32 var arguments = _argumentsForRequest(request, headerFile); 31 var arguments = _argumentsForRequest(request, headerFile);
33 var process; 32 var process;
34 return Process.start("curl", arguments).chain((process_) { 33 return Process.start("curl", arguments).chain((process_) {
35 process = process_; 34 process = process_;
36 if (requestStream.closed) { 35 if (requestStream.closed) {
37 process.stdin.close(); 36 process.stdin.close();
38 } else { 37 } else {
39 requestStream.pipe(process.stdin); 38 requestStream.pipe(process.stdin);
40 } 39 }
41 40
42 return _waitForHeaders(process, expectBody: request.method != "HEAD"); 41 return _waitForHeaders(process, expectBody: request.method != "HEAD");
43 }).chain((_) => new File(headerFile).readAsLines()) 42 }).chain((_) => new File(headerFile).readAsLines())
44 .transform((lines) => _buildResponse(process, lines)); 43 .transform((lines) => _buildResponse(process, lines));
45 }); 44 });
46 } 45 }
47 46
48 /// Returns the list of arguments to `curl` necessary for performing 47 /// Returns the list of arguments to `curl` necessary for performing
49 /// [request]. [headerFile] is the path to the file where the response headers 48 /// [request]. [headerFile] is the path to the file where the response headers
50 /// should be stored. 49 /// should be stored.
51 List<String> _argumentsForRequest(BaseRequest request, String headerFile) { 50 List<String> _argumentsForRequest(
51 http.BaseRequest request, String headerFile) {
52 var arguments = ["--dump-header", headerFile]; 52 var arguments = ["--dump-header", headerFile];
53 if (request.method == 'HEAD') { 53 if (request.method == 'HEAD') {
54 arguments.add("--head"); 54 arguments.add("--head");
55 } else { 55 } else {
56 arguments.add("--request"); 56 arguments.add("--request");
57 arguments.add(request.method); 57 arguments.add(request.method);
58 } 58 }
59 if (request.followRedirects) { 59 if (request.followRedirects) {
60 arguments.add("--location"); 60 arguments.add("--location");
61 arguments.add("--max-redirs"); 61 arguments.add("--max-redirs");
62 arguments.add(request.maxRedirects.toString()); 62 arguments.add(request.maxRedirects.toString());
63 } 63 }
64 if (request.contentLength != 0) { 64 if (request.contentLength != 0) {
65 arguments.add("--data-binary"); 65 arguments.add("--data-binary");
66 arguments.add("@-"); 66 arguments.add("@-");
67 } 67 }
68 68
69 // Override the headers automatically added by curl. We want to make it 69 // Override the headers automatically added by curl. We want to make it
70 // behave as much like the dart:io client as possible. 70 // behave as much like the dart:io client as possible.
71 var headers = { 71 var headers = {
72 'accept': '', 72 'accept': '',
73 'user-agent': '' 73 'user-agent': ''
74 }; 74 };
75 mapAddAll(headers, request.headers); 75 request.headers.forEach((name, value) => headers[name] = value);
76 if (request.contentLength < 0) { 76 if (request.contentLength < 0) {
77 headers['content-length'] = ''; 77 headers['content-length'] = '';
78 headers['transfer-encoding'] = 'chunked'; 78 headers['transfer-encoding'] = 'chunked';
79 } else if (request.contentLength > 0) { 79 } else if (request.contentLength > 0) {
80 headers['content-length'] = request.contentLength.toString(); 80 headers['content-length'] = request.contentLength.toString();
81 } 81 }
82 82
83 headers.forEach((name, value) { 83 headers.forEach((name, value) {
84 arguments.add("--header"); 84 arguments.add("--header");
85 arguments.add("$name: $value"); 85 arguments.add("$name: $value");
(...skipping 13 matching lines...) Expand all
99 process.onExit = (exitCode) { 99 process.onExit = (exitCode) {
100 if (exitCode == 0) { 100 if (exitCode == 0) {
101 exitCompleter.complete(0); 101 exitCompleter.complete(0);
102 return; 102 return;
103 } 103 }
104 104
105 chainToCompleter(consumeInputStream(process.stderr) 105 chainToCompleter(consumeInputStream(process.stderr)
106 .transform((stderrBytes) { 106 .transform((stderrBytes) {
107 var message = new String.fromCharCodes(stderrBytes); 107 var message = new String.fromCharCodes(stderrBytes);
108 if (exitCode == 47) { 108 if (exitCode == 47) {
109 throw new RedirectLimitExceededException(message); 109 throw new RedirectLimitExceededException([]);
110 } else { 110 } else {
111 throw new HttpException(message); 111 throw new HttpException(message);
112 } 112 }
113 }), exitCompleter); 113 }), exitCompleter);
114 }; 114 };
115 115
116 // If there's not going to be a response body (e.g. for HEAD requests), curl 116 // If there's not going to be a response body (e.g. for HEAD requests), curl
117 // prints the headers to stdout instead of the body. We want to wait until 117 // prints the headers to stdout instead of the body. We want to wait until
118 // all the headers are received to read them from the header file. 118 // all the headers are received to read them from the header file.
119 if (!expectBody) { 119 if (!expectBody) {
(...skipping 21 matching lines...) Expand all
141 resetCallbacks(); 141 resetCallbacks();
142 completer.completeException(e); 142 completer.completeException(e);
143 }; 143 };
144 process.stdout.onClosed = () { 144 process.stdout.onClosed = () {
145 resetCallbacks(); 145 resetCallbacks();
146 chainToCompleter(exitFuture, completer); 146 chainToCompleter(exitFuture, completer);
147 }; 147 };
148 return completer.future; 148 return completer.future;
149 } 149 }
150 150
151 /// Returns a [StreamedResponse] from the response data printed by the `curl` 151 /// Returns a [http.StreamedResponse] from the response data printed by the
152 /// [process]. [lines] are the headers that `curl` wrote to a file. 152 /// `curl` [process]. [lines] are the headers that `curl` wrote to a file.
153 StreamedResponse _buildResponse(Process process, List<String> lines) { 153 http.StreamedResponse _buildResponse(Process process, List<String> lines) {
154 // When curl follows redirects, it prints the redirect headers as well as 154 // When curl follows redirects, it prints the redirect headers as well as
155 // the headers of the final request. Each block is separated by a blank 155 // the headers of the final request. Each block is separated by a blank
156 // line. We just care about the last block. There is one trailing empty 156 // line. We just care about the last block. There is one trailing empty
157 // line, though, which we don't want to consider a separator. 157 // line, though, which we don't want to consider a separator.
158 var lastBlank = lines.lastIndexOf("", lines.length - 2); 158 var lastBlank = lines.lastIndexOf("", lines.length - 2);
159 if (lastBlank != -1) lines.removeRange(0, lastBlank + 1); 159 if (lastBlank != -1) lines.removeRange(0, lastBlank + 1);
160 160
161 var statusParts = lines.removeAt(0).split(" "); 161 var statusParts = lines.removeAt(0).split(" ");
162 var status = int.parse(statusParts[1]); 162 var status = int.parse(statusParts[1]);
163 var isRedirect = status >= 300 && status < 400; 163 var isRedirect = status >= 300 && status < 400;
164 var reasonPhrase = 164 var reasonPhrase =
165 Strings.join(" ", statusParts.getRange(2, statusParts.length - 2)); 165 Strings.join(statusParts.getRange(2, statusParts.length - 2), " ");
166 var headers = <String>{}; 166 var headers = <String>{};
167 for (var line in lines) { 167 for (var line in lines) {
168 if (line.isEmpty) continue; 168 if (line.isEmpty) continue;
169 var split = split1(line, ":"); 169 var split = split1(line, ":");
170 headers[split[0].toLowerCase()] = split[1].trim(); 170 headers[split[0].toLowerCase()] = split[1].trim();
171 } 171 }
172 var responseStream = process.stdout; 172 var responseStream = process.stdout;
173 if (responseStream.closed) { 173 if (responseStream.closed) {
174 responseStream = new ListInputStream(); 174 responseStream = new ListInputStream();
175 responseStream.markEndOfStream(); 175 responseStream.markEndOfStream();
176 } 176 }
177 var contentLength = -1; 177 var contentLength = -1;
178 if (headers.containsKey('content-length')) { 178 if (headers.containsKey('content-length')) {
179 contentLength = int.parse(headers['content-length']); 179 contentLength = int.parse(headers['content-length']);
180 } 180 }
181 181
182 return new StreamedResponse(responseStream, status, contentLength, 182 return new http.StreamedResponse(responseStream, status, contentLength,
183 headers: headers, 183 headers: headers,
184 isRedirect: isRedirect, 184 isRedirect: isRedirect,
185 reasonPhrase: reasonPhrase); 185 reasonPhrase: reasonPhrase);
186 } 186 }
187 } 187 }
OLDNEW
« no previous file with comments | « pkg/pkg.status ('k') | utils/pub/io.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698