http: make post() take Read, not &str

for more flexibility.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
Fabian Grünbichler 2022-08-04 13:15:47 +02:00
parent 7863eff2a5
commit 00f5eca155
4 changed files with 76 additions and 47 deletions

View File

@ -1,6 +1,7 @@
use anyhow::{bail, format_err, Error};
use std::collections::HashMap;
use std::io::Read;
#[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
use std::str::FromStr;
@ -81,14 +82,19 @@ impl Client {
self.client.request(request).map_err(Error::from).await
}
pub async fn post(
pub async fn post<R>(
&self,
uri: &str,
body: Option<String>,
body: Option<R>,
content_type: Option<&str>,
) -> Result<Response<Body>, Error> {
let body = if let Some(body) = body {
Body::from(body)
) -> Result<Response<Body>, Error>
where
R: Read,
{
let body = if let Some(mut body) = body {
let mut body_vec = Vec::new();
body.read_to_end(&mut body_vec)?;
Body::from(body_vec)
} else {
Body::empty()
};
@ -180,13 +186,16 @@ impl crate::HttpClient<Body> for Client {
proxmox_async::runtime::block_on(self.request(req))
}
fn post(
fn post<R>(
&self,
uri: &str,
body: Option<&str>,
body: Option<R>,
content_type: Option<&str>,
) -> Result<Response<Body>, Error> {
proxmox_async::runtime::block_on(self.post(uri, body.map(|s| s.to_owned()), content_type))
) -> Result<Response<Body>, Error>
where
R: Read,
{
proxmox_async::runtime::block_on(self.post(uri, body, content_type))
}
fn request(&self, request: Request<Body>) -> Result<Response<Body>, Error> {
@ -218,18 +227,17 @@ impl crate::HttpClient<String> for Client {
})
}
fn post(
fn post<R>(
&self,
uri: &str,
body: Option<&str>,
body: Option<R>,
content_type: Option<&str>,
) -> Result<Response<String>, Error> {
) -> Result<Response<String>, Error>
where
R: Read,
{
proxmox_async::runtime::block_on(async move {
Self::convert_body_to_string(
self.post(uri, body.map(|s| s.to_owned()), content_type)
.await,
)
.await
Self::convert_body_to_string(self.post(uri, body, content_type).await).await
})
}

View File

@ -29,24 +29,32 @@ impl Client {
Ok(builder.build())
}
fn exec_request(
&self,
req: ureq::Request,
body: Option<&[u8]>,
) -> Result<Response<Vec<u8>>, Error> {
let req = req.set(
fn add_user_agent(&self, req: ureq::Request) -> ureq::Request {
req.set(
"User-Agent",
self.options
.user_agent
.as_deref()
.unwrap_or(DEFAULT_USER_AGENT_STRING),
);
)
}
let res = match body {
Some(body) => req.send_bytes(body),
None => req.call(),
}?;
fn call(&self, req: ureq::Request) -> Result<Response<Vec<u8>>, Error> {
let req = self.add_user_agent(req);
Self::convert_response(req.call()?)
}
fn send<R>(&self, req: ureq::Request, body: R) -> Result<Response<Vec<u8>>, Error>
where
R: Read,
{
let req = self.add_user_agent(req);
Self::convert_response(req.send(body)?)
}
fn convert_response(res: ureq::Response) -> Result<Response<Vec<u8>>, Error> {
let mut builder = http::response::Builder::new()
.status(http::status::StatusCode::from_u16(res.status())?);
@ -83,23 +91,28 @@ impl HttpClient<String> for Client {
}
}
self.exec_request(req, None)
.and_then(Self::convert_body_to_string)
self.call(req).and_then(Self::convert_body_to_string)
}
fn post(
fn post<R>(
&self,
uri: &str,
body: Option<&str>,
body: Option<R>,
content_type: Option<&str>,
) -> Result<Response<String>, Error> {
) -> Result<Response<String>, Error>
where
R: Read,
{
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.map(|b| b.as_bytes()))
.and_then(Self::convert_body_to_string)
match body {
Some(body) => self.send(req, body),
None => self.call(req),
}
.and_then(Self::convert_body_to_string)
}
fn request(&self, request: http::Request<String>) -> Result<Response<String>, Error> {
@ -114,7 +127,7 @@ impl HttpClient<String> for Client {
}
}
self.exec_request(req, Some(request.body().as_bytes()))
self.send(req, request.body().as_bytes())
.and_then(Self::convert_body_to_string)
}
}
@ -133,21 +146,27 @@ impl HttpClient<Vec<u8>> for Client {
}
}
self.exec_request(req, None)
self.call(req)
}
fn post(
fn post<R>(
&self,
uri: &str,
body: Option<&str>,
body: Option<R>,
content_type: Option<&str>,
) -> Result<Response<Vec<u8>>, Error> {
) -> Result<Response<Vec<u8>>, Error>
where
R: Read,
{
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.map(|b| b.as_bytes()))
match body {
Some(body) => self.send(req, body),
None => self.call(req),
}
}
fn request(&self, request: http::Request<Vec<u8>>) -> Result<Response<Vec<u8>>, Error> {
@ -162,6 +181,6 @@ impl HttpClient<Vec<u8>> for Client {
}
}
self.exec_request(req, Some(request.body()))
self.send(req, request.body().as_slice())
}
}

View File

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::{collections::HashMap, io::Read};
use anyhow::Error;
use http::{Request, Response};
@ -10,12 +10,14 @@ pub trait HttpClient<T> {
extra_headers: Option<&HashMap<String, String>>,
) -> Result<Response<T>, Error>;
fn post(
fn post<R>(
&self,
uri: &str,
body: Option<&str>,
body: Option<R>,
content_type: Option<&str>,
) -> Result<Response<T>, Error>;
) -> Result<Response<T>, Error>
where
R: Read;
fn request(&self, request: Request<T>) -> Result<Response<T>, Error>;
}

View File

@ -39,7 +39,7 @@ fn register_subscription<C: HttpClient<String>>(
let query = json_object_to_query(params)?;
let response = client.post(
SHOP_URI,
Some(&query),
Some(&mut query.as_bytes()),
Some("application/x-www-form-urlencoded"),
)?;
let body = response.into_body();