Index: utils/pub/curl_client.dart |
diff --git a/utils/pub/curl_client.dart b/utils/pub/curl_client.dart |
deleted file mode 100644 |
index 7001a17c1865c6f44c155883dd268cfb598fa78e..0000000000000000000000000000000000000000 |
--- a/utils/pub/curl_client.dart |
+++ /dev/null |
@@ -1,198 +0,0 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-library curl_client; |
- |
-import 'dart:async'; |
-import 'dart:io'; |
- |
-import '../../pkg/http/lib/http.dart' as http; |
-import 'io.dart'; |
-import 'log.dart' as log; |
-import 'utils.dart'; |
- |
-/// A drop-in replacement for [http.Client] that uses the `curl` command-line |
-/// utility rather than [dart:io] to make requests. This class will only exist |
-/// temporarily until [dart:io] natively supports requests over HTTPS. |
-class CurlClient extends http.BaseClient { |
- /// The path to the `curl` executable to run. |
- /// |
- /// By default on Unix-like operating systems, this will look up `curl` on the |
- /// system path. On Windows, it will use the bundled `curl.exe`. |
- final String executable; |
- |
- /// Creates a new [CurlClient] with [executable] as the path to the `curl` |
- /// executable. |
- /// |
- /// By default on Unix-like operating systems, this will look up `curl` on the |
- /// system path. On Windows, it will use the bundled `curl.exe`. |
- CurlClient([String executable]) |
- : executable = executable == null ? _defaultExecutable : executable; |
- |
- /// Sends a request via `curl` and returns the response. |
- Future<http.StreamedResponse> send(http.BaseRequest request) { |
- log.fine("Sending Curl request $request"); |
- |
- var requestStream = request.finalize(); |
- return withTempDir((tempDir) { |
- var headerFile = join(tempDir, "curl-headers"); |
- var arguments = _argumentsForRequest(request, headerFile); |
- var process; |
- return startProcess(executable, arguments).then((process_) { |
- process = process_; |
- return requestStream.pipe(wrapOutputStream(process.stdin)); |
- }).then((_) { |
- return _waitForHeaders(process, expectBody: request.method != "HEAD"); |
- }).then((_) => new File(headerFile).readAsLines()) |
- .then((lines) => _buildResponse(request, process, lines)); |
- }); |
- } |
- |
- /// Returns the list of arguments to `curl` necessary for performing |
- /// [request]. [headerFile] is the path to the file where the response headers |
- /// should be stored. |
- List<String> _argumentsForRequest( |
- http.BaseRequest request, String headerFile) { |
- // Note: This line of code gets munged by create_sdk.py to be the correct |
- // relative path to the certificate file in the SDK. |
- var pathToCertificates = "../../third_party/curl/ca-certificates.crt"; |
- |
- var arguments = [ |
- "--dump-header", headerFile, |
- "--cacert", relativeToPub(pathToCertificates) |
- ]; |
- if (request.method == 'HEAD') { |
- arguments.add("--head"); |
- } else { |
- arguments.add("--request"); |
- arguments.add(request.method); |
- } |
- if (request.followRedirects) { |
- arguments.add("--location"); |
- arguments.add("--max-redirs"); |
- arguments.add(request.maxRedirects.toString()); |
- } |
- if (request.contentLength != 0) { |
- arguments.add("--data-binary"); |
- arguments.add("@-"); |
- } |
- |
- // Override the headers automatically added by curl. We want to make it |
- // behave as much like the dart:io client as possible. |
- var headers = { |
- 'accept': '', |
- 'user-agent': '' |
- }; |
- request.headers.forEach((name, value) => headers[name] = value); |
- if (request.contentLength < 0) { |
- headers['content-length'] = ''; |
- headers['transfer-encoding'] = 'chunked'; |
- } else if (request.contentLength > 0) { |
- headers['content-length'] = request.contentLength.toString(); |
- } |
- |
- headers.forEach((name, value) { |
- arguments.add("--header"); |
- arguments.add("$name: $value"); |
- }); |
- arguments.add(request.url.toString()); |
- |
- return arguments; |
- } |
- |
- /// Returns a [Future] that completes once the `curl` [process] has finished |
- /// receiving the response headers. [expectBody] indicates that the server is |
- /// expected to send a response body (which is not the case for HEAD |
- /// requests). |
- /// |
- /// Curl prints the headers to a file and then prints the body to stdout. So, |
- /// in theory, we could read the headers as soon as we see anything appear |
- /// in stdout. However, that seems to be too early to successfully read the |
- /// file (at least on Mac). Instead, this just waits until the entire process |
- /// has completed. |
- Future _waitForHeaders(Process process, {bool expectBody}) { |
- var completer = new Completer(); |
- process.onExit = (exitCode) { |
- log.io("Curl process exited with code $exitCode."); |
- |
- if (exitCode == 0) { |
- completer.complete(null); |
- return; |
- } |
- |
- chainToCompleter(consumeInputStream(process.stderr).then((stderrBytes) { |
- var message = new String.fromCharCodes(stderrBytes); |
- log.fine('Got error reading headers from curl: $message'); |
- if (exitCode == 47) { |
- throw new RedirectLimitExceededException([]); |
- } else { |
- throw new HttpException(message); |
- } |
- }), completer); |
- }; |
- |
- // If there's not going to be a response body (e.g. for HEAD requests), curl |
- // prints the headers to stdout instead of the body. We want to wait until |
- // all the headers are received to read them from the header file. |
- if (!expectBody) { |
- return Future.wait([ |
- consumeInputStream(process.stdout), |
- completer.future |
- ]); |
- } |
- |
- return completer.future; |
- } |
- |
- /// Returns a [http.StreamedResponse] from the response data printed by the |
- /// `curl` [process]. [lines] are the headers that `curl` wrote to a file. |
- http.StreamedResponse _buildResponse( |
- http.BaseRequest request, Process process, List<String> lines) { |
- // When curl follows redirects, it prints the redirect headers as well as |
- // the headers of the final request. Each block is separated by a blank |
- // line. We just care about the last block. There is one trailing empty |
- // line, though, which we don't want to consider a separator. |
- var lastBlank = lines.lastIndexOf("", lines.length - 2); |
- if (lastBlank != -1) lines.removeRange(0, lastBlank + 1); |
- |
- var statusParts = lines.removeAt(0).split(" "); |
- var status = int.parse(statusParts[1]); |
- var isRedirect = status >= 300 && status < 400; |
- var reasonPhrase = |
- Strings.join(statusParts.getRange(2, statusParts.length - 2), " "); |
- var headers = {}; |
- for (var line in lines) { |
- if (line.isEmpty) continue; |
- var split = split1(line, ":"); |
- headers[split[0].toLowerCase()] = split[1].trim(); |
- } |
- var responseStream = process.stdout; |
- if (responseStream.closed) { |
- responseStream = new ListInputStream(); |
- responseStream.markEndOfStream(); |
- } |
- var contentLength = -1; |
- if (headers.containsKey('content-length')) { |
- contentLength = int.parse(headers['content-length']); |
- } |
- |
- return new http.StreamedResponse( |
- wrapInputStream(responseStream), status, contentLength, |
- request: request, |
- headers: headers, |
- isRedirect: isRedirect, |
- reasonPhrase: reasonPhrase); |
- } |
- |
- /// The default executable to use for running curl. On Windows, this is the |
- /// path to the bundled `curl.exe`; elsewhere, this is just "curl", and we |
- /// assume it to be installed and on the user's PATH. |
- static String get _defaultExecutable { |
- if (Platform.operatingSystem != 'windows') return 'curl'; |
- // Note: This line of code gets munged by create_sdk.py to be the correct |
- // relative path to curl in the SDK. |
- var pathToCurl = "../../third_party/curl/curl.exe"; |
- return relativeToPub(pathToCurl); |
- } |
-} |