Skip to content

Commit

Permalink
Allow checking HTTP version support at runtime (#368)
Browse files Browse the repository at this point in the history
Add `is_http_version_supported` which allows you to check which HTTP versions are supported by a particular build of Isahc at runtime. This could be used to decide whether an application would choose to enforce HTTP/2 for example, or just to verify that build configuration is as expected.
  • Loading branch information
sagebind authored Jan 10, 2022
1 parent f3b5c3c commit 37cdccd
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 23 deletions.
6 changes: 3 additions & 3 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::{env, error::Error, fs, path::PathBuf};
use std::{env, error::Error};

fn main() -> Result<(), Box<dyn Error>> {
let out_dir = PathBuf::from(env::var("OUT_DIR")?);
fs::write(out_dir.join("features.txt"), get_feature_string())?;
println!("cargo:rustc-env=ISAHC_FEATURES={}", get_feature_string());

Ok(())
}
Expand All @@ -13,6 +12,7 @@ fn get_feature_string() -> String {
.filter(|(name, _)| name.starts_with("CARGO_FEATURE_"))
.filter(|(_, value)| value == "1")
.map(|(name, _)| name.trim_start_matches("CARGO_FEATURE_").to_lowercase())
.map(|name| name.replace('_', "-"))
.collect::<Vec<String>>()
.join(",")
}
89 changes: 89 additions & 0 deletions src/info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//! Runtime support for checking versions and feature availability.
use once_cell::sync::Lazy;

// Query for curl version info just once since it is immutable.
static CURL_VERSION: Lazy<curl::Version> = Lazy::new(curl::Version::get);

/// Gets a human-readable string with the version number of Isahc and its
/// dependencies.
///
/// This function can be helpful when troubleshooting issues in Isahc or one of
/// its dependencies.
pub fn version() -> &'static str {
static VERSION_STRING: Lazy<String> = Lazy::new(|| {
format!(
"isahc/{} (features:{}) {}",
env!("CARGO_PKG_VERSION"),
env!("ISAHC_FEATURES"),
curl::Version::num(),
)
});

&VERSION_STRING
}

/// Check if runtime support is available for the given HTTP version.
///
/// This only indicates whether support for communicating with this HTTP version
/// is available, which is usually determined by which features were enabled
/// during compilation, but can also be affected by what is available in system
/// libraries when using dynamic linking.
///
/// This does not indicate which versions Isahc will attempt to use by default.
/// To customize which versions to use within a particular client or request
/// instance, see [`VersionNegotiation`][crate::config::VersionNegotiation].
pub fn is_http_version_supported(version: http::Version) -> bool {
match version {
// HTTP/0.9 was disabled by default as of 7.66.0. See also
// https://github.com/sagebind/isahc/issues/310 if we ever decide to
// allow enabling it again.
http::Version::HTTP_09 => match curl_version() {
(7, minor, _) if minor < 66 => true,
(major, _, _) if major < 7 => true,
_ => false,
},
http::Version::HTTP_10 => true,
http::Version::HTTP_11 => true,
http::Version::HTTP_2 => CURL_VERSION.feature_http2(),
http::Version::HTTP_3 => CURL_VERSION.feature_http3(),
_ => false,
}
}

fn curl_version() -> (u8, u8, u8) {
let bits = CURL_VERSION.version_num();

((bits >> 16) as u8, (bits >> 8) as u8, bits as u8)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn version_expected() {
let version = version();

assert!(version.starts_with("isahc/1."));
assert!(version.contains("curl/7."));
}

#[test]
fn curl_version_expected() {
let (major, minor, _patch) = curl_version();

assert_eq!(major, 7);
assert!(minor > 0);
}

#[test]
fn http1_always_supported() {
assert!(is_http_version_supported(http::Version::HTTP_10));
assert!(is_http_version_supported(http::Version::HTTP_11));

if cfg!(feature = "http2") {
assert!(is_http_version_supported(http::Version::HTTP_2));
}
}
}
25 changes: 5 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@
//! makes it available. To configure which HTTP versions to use in a request,
//! see [`VersionNegotiation`](config::VersionNegotiation).
//!
//! To check which HTTP versions are supported at runtime, you can use
//! [`is_http_version_supported`].
//!
//! Enabled by default.
//!
//! ## `json`
Expand Down Expand Up @@ -239,7 +242,6 @@
// These lints suggest to use features not available in our MSRV.
#![allow(clippy::manual_strip, clippy::match_like_matches_macro)]

use once_cell::sync::Lazy;
use std::convert::TryFrom;

#[macro_use]
Expand All @@ -254,6 +256,7 @@ mod client;
mod default_headers;
mod handler;
mod headers;
mod info;
mod metrics;
mod parsing;
mod redirect;
Expand All @@ -278,6 +281,7 @@ pub use crate::{
client::{HttpClient, HttpClientBuilder, ResponseFuture},
error::Error,
http::{request::Request, response::Response},
info::*,
metrics::Metrics,
request::RequestExt,
response::{AsyncReadResponseExt, ReadResponseExt, ResponseExt},
Expand Down Expand Up @@ -469,22 +473,3 @@ pub fn send<B: Into<Body>>(request: Request<B>) -> Result<Response<Body>, Error>
pub fn send_async<B: Into<AsyncBody>>(request: Request<B>) -> ResponseFuture<'static> {
HttpClient::shared().send_async(request)
}

/// Gets a human-readable string with the version number of Isahc and its
/// dependencies.
///
/// This function can be helpful when troubleshooting issues in Isahc or one of
/// its dependencies.
pub fn version() -> &'static str {
static FEATURES_STRING: &str = include_str!(concat!(env!("OUT_DIR"), "/features.txt"));
static VERSION_STRING: Lazy<String> = Lazy::new(|| {
format!(
"isahc/{} (features:{}) {}",
env!("CARGO_PKG_VERSION"),
FEATURES_STRING,
curl::Version::num(),
)
});

&VERSION_STRING
}

0 comments on commit 37cdccd

Please sign in to comment.