http: add ureq-based sync client
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
parent
f429fcb592
commit
9c444d7a94
@ -21,6 +21,7 @@ openssl = { version = "0.10", optional = true }
|
|||||||
serde_json = { version = "1.0", optional = true }
|
serde_json = { version = "1.0", optional = true }
|
||||||
tokio = { version = "1.0", features = [], optional = true }
|
tokio = { version = "1.0", features = [], optional = true }
|
||||||
tokio-openssl = { version = "0.6.1", optional = true }
|
tokio-openssl = { version = "0.6.1", optional = true }
|
||||||
|
ureq = { version = "2.4", features = ["native-certs"], optional = true }
|
||||||
url = { version = "2", optional = true }
|
url = { version = "2", optional = true }
|
||||||
|
|
||||||
proxmox-async = { path = "../proxmox-async", optional = true, version = "0.4.1" }
|
proxmox-async = { path = "../proxmox-async", optional = true, version = "0.4.1" }
|
||||||
@ -32,6 +33,7 @@ proxmox-lang = { path = "../proxmox-lang", optional = true, version = "1.1" }
|
|||||||
default = []
|
default = []
|
||||||
|
|
||||||
client = [ "dep:futures", "http-helpers", "dep:hyper", "hyper?/full", "dep:openssl", "dep:tokio", "tokio?/io-util", "dep:tokio-openssl" ]
|
client = [ "dep:futures", "http-helpers", "dep:hyper", "hyper?/full", "dep:openssl", "dep:tokio", "tokio?/io-util", "dep:tokio-openssl" ]
|
||||||
|
client-sync = [ "client-trait", "http-helpers", "dep:ureq" ]
|
||||||
client-trait = [ "dep:http" ]
|
client-trait = [ "dep:http" ]
|
||||||
http-helpers = [ "dep:base64", "dep:http", "dep:proxmox-sys", "dep:serde_json", "dep:url" ]
|
http-helpers = [ "dep:base64", "dep:http", "dep:proxmox-sys", "dep:serde_json", "dep:url" ]
|
||||||
websocket = [
|
websocket = [
|
||||||
|
@ -1,17 +1,36 @@
|
|||||||
//! Simple TLS capable HTTP client implementation.
|
//! Simple TLS capable HTTP client implementations.
|
||||||
//!
|
//!
|
||||||
//! Contains a lightweight wrapper around `hyper` with support for TLS connections.
|
//! Feature `client` contains a lightweight wrapper around `hyper` with support for TLS connections
|
||||||
|
//! in [`SimpleHttp`](crate::client::SimpleHttp).
|
||||||
|
//!
|
||||||
|
//! Feature `client-sync` contains a lightweight wrapper around `ureq` in
|
||||||
|
//! [`sync::Client`](crate::client::sync::Client).
|
||||||
|
//!
|
||||||
|
//! Both clients implement [`HttpClient`](crate::HttpClient) if the feature `client-trait` is enabled.
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
mod rate_limiter;
|
mod rate_limiter;
|
||||||
|
#[cfg(feature = "client")]
|
||||||
pub use rate_limiter::{RateLimit, RateLimiter, RateLimiterVec, ShareableRateLimit};
|
pub use rate_limiter::{RateLimit, RateLimiter, RateLimiterVec, ShareableRateLimit};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
mod rate_limited_stream;
|
mod rate_limited_stream;
|
||||||
|
#[cfg(feature = "client")]
|
||||||
pub use rate_limited_stream::RateLimitedStream;
|
pub use rate_limited_stream::RateLimitedStream;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
mod connector;
|
mod connector;
|
||||||
|
#[cfg(feature = "client")]
|
||||||
pub use connector::HttpsConnector;
|
pub use connector::HttpsConnector;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
mod simple;
|
mod simple;
|
||||||
|
#[cfg(feature = "client")]
|
||||||
pub use simple::SimpleHttp;
|
pub use simple::SimpleHttp;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
pub mod tls;
|
pub mod tls;
|
||||||
|
|
||||||
|
#[cfg(feature = "client-sync")]
|
||||||
|
/// Blocking HTTP client
|
||||||
|
pub mod sync;
|
||||||
|
108
proxmox-http/src/client/sync.rs
Normal file
108
proxmox-http/src/client/sync.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use anyhow::{format_err, Error};
|
||||||
|
use http::Response;
|
||||||
|
|
||||||
|
use crate::HttpClient;
|
||||||
|
use crate::HttpOptions;
|
||||||
|
|
||||||
|
pub const DEFAULT_USER_AGENT_STRING: &str = "proxmox-sync-http-client/0.1";
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
/// Blocking HTTP client for usage with [`HttpClient`].
|
||||||
|
pub struct Client {
|
||||||
|
options: HttpOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new(options: HttpOptions) -> Self {
|
||||||
|
Self { options }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn agent(&self) -> Result<ureq::Agent, Error> {
|
||||||
|
let mut builder = ureq::AgentBuilder::new();
|
||||||
|
if let Some(proxy_config) = &self.options.proxy_config {
|
||||||
|
builder = builder.proxy(ureq::Proxy::new(proxy_config.to_proxy_string()?)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(builder.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec_request(
|
||||||
|
&self,
|
||||||
|
req: ureq::Request,
|
||||||
|
body: Option<&str>,
|
||||||
|
) -> Result<Response<String>, Error> {
|
||||||
|
let req = req.set(
|
||||||
|
"User-Agent",
|
||||||
|
self.options
|
||||||
|
.user_agent
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or(DEFAULT_USER_AGENT_STRING),
|
||||||
|
);
|
||||||
|
|
||||||
|
let res = match body {
|
||||||
|
Some(body) => req.send_string(body),
|
||||||
|
None => req.call(),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let mut builder = http::response::Builder::new()
|
||||||
|
.status(http::status::StatusCode::from_u16(res.status())?);
|
||||||
|
|
||||||
|
for header in res.headers_names() {
|
||||||
|
if let Some(value) = res.header(&header) {
|
||||||
|
builder = builder.header(header, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder
|
||||||
|
.body(res.into_string()?)
|
||||||
|
.map_err(|err| format_err!("Failed to convert HTTP response - {err}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpClient<String> for Client {
|
||||||
|
fn get(
|
||||||
|
&self,
|
||||||
|
uri: &str,
|
||||||
|
extra_headers: Option<&HashMap<String, String>>,
|
||||||
|
) -> Result<Response<String>, Error> {
|
||||||
|
let mut req = self.agent()?.get(uri);
|
||||||
|
|
||||||
|
if let Some(extra_headers) = extra_headers {
|
||||||
|
for (header, value) in extra_headers {
|
||||||
|
req = req.set(header, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.exec_request(req, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post(
|
||||||
|
&self,
|
||||||
|
uri: &str,
|
||||||
|
body: Option<&str>,
|
||||||
|
content_type: Option<&str>,
|
||||||
|
) -> Result<Response<String>, Error> {
|
||||||
|
let mut req = self.agent()?.post(uri);
|
||||||
|
if let Some(content_type) = content_type {
|
||||||
|
req = req.set("Content-Type", content_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.exec_request(req, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request(&self, request: http::Request<String>) -> Result<Response<String>, Error> {
|
||||||
|
let mut req = self
|
||||||
|
.agent()?
|
||||||
|
.request(request.method().as_str(), &request.uri().to_string());
|
||||||
|
let orig_headers = request.headers();
|
||||||
|
|
||||||
|
for header in orig_headers.keys() {
|
||||||
|
for value in orig_headers.get_all(header) {
|
||||||
|
req = req.set(header.as_str(), value.to_str()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.exec_request(req, Some(request.body().as_str()))
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@ mod http_options;
|
|||||||
#[cfg(feature = "http-helpers")]
|
#[cfg(feature = "http-helpers")]
|
||||||
pub use http_options::HttpOptions;
|
pub use http_options::HttpOptions;
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(any(feature = "client", feature = "client-sync"))]
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
|
||||||
#[cfg(feature = "client-trait")]
|
#[cfg(feature = "client-trait")]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! HTTP proxy configuration.
|
//! HTTP proxy configuration.
|
||||||
//!
|
//!
|
||||||
//! This can be used with the [`SimpleHttp`](crate::client::SimpleHttp).
|
//! This can be used with the async [`SimpleHttp`](crate::client::SimpleHttp) or sync [`Client`](crate::client::sync::Client).
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user