summaryrefslogtreecommitdiff
path: root/cli/http_util.rs
diff options
context:
space:
mode:
authorgeoFlux <blakejason5@gmail.com>2020-02-17 11:59:51 -0500
committerGitHub <noreply@github.com>2020-02-17 11:59:51 -0500
commit2e7d449623b7faee8feb0a2970e0d6bb7c66b409 (patch)
tree4b3cae1d91709391fcc8facc2d2dca409349f026 /cli/http_util.rs
parent98e585a284f6b8bb568cec52ca3599612b663ef3 (diff)
feat: add --cert flag for http client (#3972)
Diffstat (limited to 'cli/http_util.rs')
-rw-r--r--cli/http_util.rs164
1 files changed, 153 insertions, 11 deletions
diff --git a/cli/http_util.rs b/cli/http_util.rs
index 6bff0f8bb..0140d014a 100644
--- a/cli/http_util.rs
+++ b/cli/http_util.rs
@@ -1,6 +1,7 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::deno_error;
use crate::deno_error::DenoError;
+use crate::deno_error::ErrorKind;
use crate::version;
use brotli2::read::BrotliDecoder;
use bytes::Bytes;
@@ -21,6 +22,7 @@ use reqwest::Client;
use reqwest::Response;
use reqwest::StatusCode;
use std::cmp::min;
+use std::fs::File;
use std::future::Future;
use std::io;
use std::io::Read;
@@ -32,20 +34,31 @@ use url::Url;
/// Create new instance of async reqwest::Client. This client supports
/// proxies and doesn't follow redirects.
-pub fn create_http_client() -> Client {
+pub fn create_http_client(ca_file: Option<String>) -> Result<Client, ErrBox> {
let mut headers = HeaderMap::new();
headers.insert(
USER_AGENT,
format!("Deno/{}", version::DENO).parse().unwrap(),
);
- Client::builder()
+ let mut builder = Client::builder()
.redirect(Policy::none())
.default_headers(headers)
- .use_rustls_tls()
- .build()
- .unwrap()
-}
+ .use_rustls_tls();
+
+ if let Some(ca_file) = ca_file {
+ let mut buf = Vec::new();
+ File::open(ca_file)?.read_to_end(&mut buf)?;
+ let cert = reqwest::Certificate::from_pem(&buf)?;
+ builder = builder.add_root_certificate(cert);
+ }
+ builder.build().map_err(|_| {
+ ErrBox::from(DenoError::new(
+ ErrorKind::Other,
+ "Unable to build http client".to_string(),
+ ))
+ })
+}
/// Construct the next uri based on base uri and location header fragment
/// See <https://tools.ietf.org/html/rfc3986#section-4.2>
fn resolve_url_from_location(base_url: &Url, location: &str) -> Url {
@@ -276,7 +289,7 @@ mod tests {
// Relies on external http server. See tools/http_server.py
let url =
Url::parse("http://127.0.0.1:4545/cli/tests/fixture.json").unwrap();
- let client = create_http_client();
+ let client = create_http_client(None).unwrap();
let result = fetch_once(client, &url, None).await;
if let Ok(FetchOnceResult::Code(payload)) = result {
assert!(!payload.body.is_empty());
@@ -297,7 +310,7 @@ mod tests {
"http://127.0.0.1:4545/cli/tests/053_import_compression/gziped",
)
.unwrap();
- let client = create_http_client();
+ let client = create_http_client(None).unwrap();
let result = fetch_once(client, &url, None).await;
if let Ok(FetchOnceResult::Code(payload)) = result {
assert_eq!(
@@ -320,7 +333,7 @@ mod tests {
async fn test_fetch_with_etag() {
let http_server_guard = crate::test_util::http_server();
let url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap();
- let client = create_http_client();
+ let client = create_http_client(None).unwrap();
let result = fetch_once(client.clone(), &url, None).await;
if let Ok(FetchOnceResult::Code(ResultPayload {
body,
@@ -353,7 +366,7 @@ mod tests {
"http://127.0.0.1:4545/cli/tests/053_import_compression/brotli",
)
.unwrap();
- let client = create_http_client();
+ let client = create_http_client(None).unwrap();
let result = fetch_once(client, &url, None).await;
if let Ok(FetchOnceResult::Code(payload)) = result {
assert!(!payload.body.is_empty());
@@ -382,7 +395,7 @@ mod tests {
// Dns resolver substitutes `127.0.0.1` with `localhost`
let target_url =
Url::parse("http://localhost:4545/cli/tests/fixture.json").unwrap();
- let client = create_http_client();
+ let client = create_http_client(None).unwrap();
let result = fetch_once(client, &url, None).await;
if let Ok(FetchOnceResult::Redirect(url)) = result {
assert_eq!(url, target_url);
@@ -429,4 +442,133 @@ mod tests {
assert_eq!(new_uri.host_str().unwrap(), "deno.land");
assert_eq!(new_uri.path(), "/z");
}
+
+ #[tokio::test]
+ async fn test_fetch_with_cafile_sync_string() {
+ let http_server_guard = crate::test_util::http_server();
+ // Relies on external http server. See tools/http_server.py
+ let url =
+ Url::parse("https://localhost:5545/cli/tests/fixture.json").unwrap();
+
+ let client = create_http_client(Some(String::from(
+ crate::test_util::root_path()
+ .join("std/http/testdata/tls/RootCA.pem")
+ .to_str()
+ .unwrap(),
+ )))
+ .unwrap();
+ let result = fetch_once(client, &url, None).await;
+
+ if let Ok(FetchOnceResult::Code(payload)) = result {
+ assert!(!payload.body.is_empty());
+ assert_eq!(payload.content_type, Some("application/json".to_string()));
+ assert_eq!(payload.etag, None);
+ assert_eq!(payload.x_typescript_types, None);
+ } else {
+ panic!();
+ }
+ drop(http_server_guard);
+ }
+
+ #[tokio::test]
+ async fn test_fetch_with_cafile_gzip() {
+ let http_server_guard = crate::test_util::http_server();
+ // Relies on external http server. See tools/http_server.py
+ let url = Url::parse(
+ "https://localhost:5545/cli/tests/053_import_compression/gziped",
+ )
+ .unwrap();
+ let client = create_http_client(Some(String::from(
+ crate::test_util::root_path()
+ .join("std/http/testdata/tls/RootCA.pem")
+ .to_str()
+ .unwrap(),
+ )))
+ .unwrap();
+ let result = fetch_once(client, &url, None).await;
+ if let Ok(FetchOnceResult::Code(payload)) = result {
+ assert_eq!(
+ String::from_utf8(payload.body).unwrap(),
+ "console.log('gzip')"
+ );
+ assert_eq!(
+ payload.content_type,
+ Some("application/javascript".to_string())
+ );
+ assert_eq!(payload.etag, None);
+ assert_eq!(payload.x_typescript_types, None);
+ } else {
+ panic!();
+ }
+ drop(http_server_guard);
+ }
+
+ #[tokio::test]
+ async fn test_fetch_with_cafile_with_etag() {
+ let http_server_guard = crate::test_util::http_server();
+ let url = Url::parse("https://localhost:5545/etag_script.ts").unwrap();
+ let client = create_http_client(Some(String::from(
+ crate::test_util::root_path()
+ .join("std/http/testdata/tls/RootCA.pem")
+ .to_str()
+ .unwrap(),
+ )))
+ .unwrap();
+ let result = fetch_once(client.clone(), &url, None).await;
+ if let Ok(FetchOnceResult::Code(ResultPayload {
+ body,
+ content_type,
+ etag,
+ x_typescript_types,
+ })) = result
+ {
+ assert!(!body.is_empty());
+ assert_eq!(String::from_utf8(body).unwrap(), "console.log('etag')");
+ assert_eq!(content_type, Some("application/typescript".to_string()));
+ assert_eq!(etag, Some("33a64df551425fcc55e".to_string()));
+ assert_eq!(x_typescript_types, None);
+ } else {
+ panic!();
+ }
+
+ let res =
+ fetch_once(client, &url, Some("33a64df551425fcc55e".to_string())).await;
+ assert_eq!(res.unwrap(), FetchOnceResult::NotModified);
+
+ drop(http_server_guard);
+ }
+
+ #[tokio::test]
+ async fn test_fetch_with_cafile_brotli() {
+ let http_server_guard = crate::test_util::http_server();
+ // Relies on external http server. See tools/http_server.py
+ let url = Url::parse(
+ "https://localhost:5545/cli/tests/053_import_compression/brotli",
+ )
+ .unwrap();
+ let client = create_http_client(Some(String::from(
+ crate::test_util::root_path()
+ .join("std/http/testdata/tls/RootCA.pem")
+ .to_str()
+ .unwrap(),
+ )))
+ .unwrap();
+ let result = fetch_once(client, &url, None).await;
+ if let Ok(FetchOnceResult::Code(payload)) = result {
+ assert!(!payload.body.is_empty());
+ assert_eq!(
+ String::from_utf8(payload.body).unwrap(),
+ "console.log('brotli');"
+ );
+ assert_eq!(
+ payload.content_type,
+ Some("application/javascript".to_string())
+ );
+ assert_eq!(payload.etag, None);
+ assert_eq!(payload.x_typescript_types, None);
+ } else {
+ panic!();
+ }
+ drop(http_server_guard);
+ }
}