Skip to content

Commit

Permalink
Switch to attohttpc and away from reqwest
Browse files Browse the repository at this point in the history
  • Loading branch information
charlespierce committed May 6, 2020
1 parent 6e22207 commit 5af6691
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 759 deletions.
766 changes: 67 additions & 699 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ winreg = "0.6.0"
hamcrest2 = "0.3.0"
envoy = "0.1.3"
ci_info = "0.10.0"
hyperx = "0.15.2"
hyperx = "1.0.0"

[workspace]
4 changes: 2 additions & 2 deletions crates/archive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ edition = "2018"
flate2 = "1.0"
tar = "0.4.13"
zip_rs = { version = "0.2.6", package = "zip" }
reqwest = "0.9.9"
tee = "0.1.0"
failure = "0.1.1"
failure_derive = "0.1.1"
fs-utils = { path = "../fs-utils" }
progress-read = { path = "../progress-read" }
verbatim = "0.1"
cfg-if = "0.1"
hyperx = "0.15.2"
hyperx = "1.0.0"
attohttpc = { version = "0.13.0", features = ["json"] }
2 changes: 1 addition & 1 deletion crates/archive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use failure::Fail;
#[derive(Fail, Debug)]
#[fail(display = "HTTP failure ({})", code)]
pub struct HttpError {
pub code: ::reqwest::StatusCode,
pub code: ::attohttpc::StatusCode,
}

pub use crate::tarball::Tarball;
Expand Down
41 changes: 16 additions & 25 deletions crates/archive/src/tarball.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

use attohttpc::header::HeaderMap;
use failure::{self, Fail};
use flate2::read::GzDecoder;
use fs_utils::ensure_containing_dir_exists;
use hyperx::header::{
AcceptRanges, ByteRangeSpec, ContentLength, Header, Range, RangeUnit, TypedHeaders,
};
use progress_read::ProgressRead;
use reqwest::Response;
use tee::TeeReader;

use super::Archive;
Expand All @@ -38,9 +38,8 @@ struct MissingHeaderError {

/// Determines the length of an HTTP response's content in bytes, using
/// the HTTP `"Content-Length"` header.
fn content_length(response: &Response) -> Result<u64, failure::Error> {
response
.headers()
fn content_length(headers: &HeaderMap) -> Result<u64, failure::Error> {
headers
.decode::<ContentLength>()
.ok()
.map(|v| v.0)
Expand Down Expand Up @@ -69,17 +68,14 @@ impl Tarball {
/// tarball that can be streamed (and that tees its data to a local
/// file as it streams).
pub fn fetch(url: &str, cache_file: &Path) -> Result<Box<dyn Archive>, failure::Error> {
let response = reqwest::get(url)?;
let (status, headers, response) = attohttpc::get(url).send()?.split();

if !response.status().is_success() {
return Err(super::HttpError {
code: response.status(),
}
.into());
if !status.is_success() {
return Err(super::HttpError { code: status }.into());
}

let compressed_size = content_length(&response)?;
let uncompressed_size = if accepts_byte_ranges(&response) {
let compressed_size = content_length(&headers)?;
let uncompressed_size = if accepts_byte_ranges(&headers) {
fetch_uncompressed_size(url, compressed_size)
} else {
None
Expand Down Expand Up @@ -151,21 +147,17 @@ struct UnexpectedContentLengthError {
/// downloading the entire gzip file. For very small files it's unlikely to be
/// more efficient than simply downloading the entire file up front.
fn fetch_isize(url: &str, len: u64) -> Result<[u8; 4], failure::Error> {
let client = reqwest::Client::new();
let range_header = Range::Bytes(vec![ByteRangeSpec::FromTo(len - 4, len - 1)]);
let mut response = client
.get(url)
let (status, headers, mut response) = attohttpc::get(url)
.header(Range::header_name(), range_header.to_string())
.send()?;
.send()?
.split();

if !response.status().is_success() {
return Err(super::HttpError {
code: response.status(),
}
.into());
if !status.is_success() {
return Err(super::HttpError { code: status }.into());
}

let actual_length = content_length(&response)?;
let actual_length = content_length(&headers)?;

if actual_length != 4 {
return Err(UnexpectedContentLengthError {
Expand All @@ -189,9 +181,8 @@ fn load_isize(file: &mut File) -> Result<[u8; 4], failure::Error> {
Ok(buf)
}

fn accepts_byte_ranges(response: &Response) -> bool {
response
.headers()
fn accepts_byte_ranges(headers: &HeaderMap) -> bool {
headers
.decode::<AcceptRanges>()
.ok()
.map(|v| v.iter().any(|unit| *unit == RangeUnit::Bytes))
Expand Down
9 changes: 3 additions & 6 deletions crates/archive/src/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,10 @@ impl Zip {
/// Initiate fetching of a Node zip archive from the given URL, returning
/// a `Remote` data source.
pub fn fetch(url: &str, cache_file: &Path) -> Result<Box<dyn Archive>, failure::Error> {
let mut response = reqwest::get(url)?;
let (status, _, mut response) = attohttpc::get(url).send()?.split();

if !response.status().is_success() {
return Err(super::HttpError {
code: response.status(),
}
.into());
if !status.is_success() {
return Err(super::HttpError { code: status }.into());
}

{
Expand Down
4 changes: 2 additions & 2 deletions crates/volta-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ lazycell = "1.2.0"
lazy_static = "1.3.0"
semver = { git = "https://github.com/mikrostew/semver", branch = "new-parser" }
cmdline_words_parser = "0.0.2"
reqwest = "0.9.9"
fs-utils = { path = "../fs-utils" }
cfg-if = "0.1"
winfolder = "0.1"
Expand All @@ -52,7 +51,8 @@ volta-layout = { path = "../volta-layout" }
double-checked-cell = "2.0.2"
dunce = "1.0.0"
ci_info = "0.10.0"
hyperx = "0.15.2"
hyperx = "1.0.0"
attohttpc = { version = "0.13.0", features = ["json"] }

[target.'cfg(windows)'.dependencies]
winreg = "0.6.0"
2 changes: 1 addition & 1 deletion crates/volta-core/src/tool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ fn download_tool_error(
fn registry_fetch_error(
tool: impl AsRef<str>,
from_url: impl AsRef<str>,
) -> impl FnOnce(&reqwest::Error) -> ErrorDetails {
) -> impl FnOnce(&attohttpc::Error) -> ErrorDetails {
let tool = tool.as_ref().to_string();
let from_url = from_url.as_ref().to_string();
|_| ErrorDetails::RegistryFetchError { tool, from_url }
Expand Down
19 changes: 12 additions & 7 deletions crates/volta-core/src/tool/node/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::session::Session;
use crate::style::progress_spinner;
use crate::tool::Node;
use crate::version::{VersionSpec, VersionTag};
use attohttpc::Response;
use cfg_if::cfg_if;
use fs_utils::ensure_containing_dir_exists;
use hyperx::header::{CacheControl, CacheDirective, Expires, HttpDate, TypedHeaders};
Expand Down Expand Up @@ -247,8 +248,8 @@ fn read_cached_opt(url: &str) -> Fallible<Option<serial::RawNodeIndex>> {
}

/// Get the cache max-age of an HTTP reponse.
fn max_age(response: &reqwest::Response) -> u32 {
if let Ok(cache_control_header) = response.headers().decode::<CacheControl>() {
fn max_age(headers: &attohttpc::header::HeaderMap) -> u32 {
if let Ok(cache_control_header) = headers.decode::<CacheControl>() {
for cache_directive in cache_control_header.iter() {
if let CacheDirective::MaxAge(max_age) = cache_directive {
return *max_age;
Expand All @@ -270,11 +271,16 @@ fn resolve_node_versions(url: &str) -> Fallible<serial::RawNodeIndex> {
debug!("Node index cache was not found or was invalid");
let spinner = progress_spinner(&format!("Fetching public registry: {}", url));

let mut response: reqwest::Response =
reqwest::get(url).with_context(registry_fetch_error("Node", url))?;
let (_, headers, response) = attohttpc::get(url)
.send()
.and_then(Response::error_for_status)
.with_context(registry_fetch_error("Node", url))?
.split();

let response_text = response
.text()
.with_context(registry_fetch_error("Node", url))?;

let index: serial::RawNodeIndex = serde_json::de::from_str(&response_text)
.with_context(|_| ErrorDetails::ParseNodeIndexError {
from_url: url.to_string(),
Expand Down Expand Up @@ -304,11 +310,10 @@ fn resolve_node_versions(url: &str) -> Fallible<serial::RawNodeIndex> {
let expiry = create_staging_file()?;
let mut expiry_file: &File = expiry.as_file();

let result = if let Ok(expires_header) = response.headers().decode::<Expires>() {
let result = if let Ok(expires_header) = headers.decode::<Expires>() {
write!(expiry_file, "{}", expires_header)
} else {
let expiry_date =
SystemTime::now() + Duration::from_secs(max_age(&response).into());
let expiry_date = SystemTime::now() + Duration::from_secs(max_age(&headers).into());

write!(expiry_file, "{}", HttpDate::from(expiry_date))
};
Expand Down
11 changes: 5 additions & 6 deletions crates/volta-core/src/tool/npm/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use crate::style::progress_spinner;
use crate::tool::package;
use crate::tool::Npm;
use crate::version::{VersionSpec, VersionTag};
use attohttpc::header::ACCEPT;
use attohttpc::Response;
use cfg_if::cfg_if;
use log::debug;
use reqwest::header::ACCEPT;
use reqwest::Client;
use semver::{Version, VersionReq};
use volta_fail::{Fallible, ResultExt};

Expand Down Expand Up @@ -60,12 +60,11 @@ fn fetch_npm_index(
};

let spinner = progress_spinner(&format!("Fetching public registry: {}", url));
let http_client = Client::new();
let metadata: package::serial::RawPackageMetadata = http_client
.get(&url)
let metadata: package::serial::RawPackageMetadata = attohttpc::get(&url)
.header(ACCEPT, NPM_ABBREVIATED_ACCEPT_HEADER)
.send()
.and_then(|mut resp| resp.json())
.and_then(Response::error_for_status)
.and_then(Response::json)
.with_context(registry_fetch_error("npm", &url))?;

spinner.finish_and_clear();
Expand Down
12 changes: 7 additions & 5 deletions crates/volta-core/src/tool/package/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::session::Session;
use crate::style::{progress_spinner, tool_version};
use crate::tool::PackageDetails;
use crate::version::{VersionSpec, VersionTag};
use attohttpc::{ErrorKind, Response, StatusCode};
use log::debug;
use semver::{Version, VersionReq};
use volta_fail::{throw, Fallible, ResultExt};
Expand Down Expand Up @@ -191,11 +192,12 @@ fn resolve_package_metadata(
package_info_url: &str,
) -> Fallible<super::serial::RawPackageMetadata> {
let spinner = progress_spinner(&format!("Fetching package metadata: {}", package_info_url));
let response_text = reqwest::get(package_info_url)
.and_then(|resp| resp.error_for_status())
.and_then(|mut resp| resp.text())
.with_context(|err| match err.status() {
Some(reqwest::StatusCode::NOT_FOUND) => ErrorDetails::PackageNotFound {
let response_text = attohttpc::get(package_info_url)
.send()
.and_then(Response::error_for_status)
.and_then(Response::text)
.with_context(|err| match err.kind() {
ErrorKind::StatusCode(StatusCode::NOT_FOUND) => ErrorDetails::PackageNotFound {
package: package_name.into(),
},
_ => ErrorDetails::PackageMetadataFetchError {
Expand Down
13 changes: 9 additions & 4 deletions crates/volta-core/src/tool/yarn/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::session::Session;
use crate::style::progress_spinner;
use crate::tool::Yarn;
use crate::version::{parse_version, VersionSpec, VersionTag};
use attohttpc::Response;
use cfg_if::cfg_if;
use log::debug;
use semver::{Version, VersionReq};
Expand Down Expand Up @@ -60,8 +61,10 @@ fn resolve_latest(hooks: Option<&ToolHooks<Yarn>>) -> Fallible<Version> {
}
_ => public_yarn_latest_version(),
};
let response_text = reqwest::get(&url)
.and_then(|mut resp| resp.text())
let response_text = attohttpc::get(&url)
.send()
.and_then(Response::error_for_status)
.and_then(Response::text)
.with_context(|_| ErrorDetails::YarnLatestFetchError {
from_url: url.clone(),
})?;
Expand All @@ -83,8 +86,10 @@ fn resolve_semver(matching: VersionReq, hooks: Option<&ToolHooks<Yarn>>) -> Fall
};

let spinner = progress_spinner(&format!("Fetching public registry: {}", url));
let releases: serial::RawYarnIndex = reqwest::get(&url)
.and_then(|mut resp| resp.json())
let releases: serial::RawYarnIndex = attohttpc::get(&url)
.send()
.and_then(Response::error_for_status)
.and_then(Response::json)
.with_context(registry_fetch_error("Yarn", &url))?;
let index = YarnIndex::from(releases);
let releases = index.entries;
Expand Down

0 comments on commit 5af6691

Please sign in to comment.