use tokio::sync::oneshot;
use crate::client::Client;
use crate::error::Error;
use crate::http::{self, Method};
use crate::response::Response;
pub struct RequestBuilder {
client: Client,
method: Method,
path: String,
headers: Vec<(String, String)>,
body: Option<Vec<u8>>,
}
impl RequestBuilder {
pub(crate) fn new(client: Client, method: Method, path: String) -> Self {
Self {
client,
method,
path,
headers: Vec::new(),
body: None,
}
}
pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.push((name.into(), value.into()));
self
}
pub fn body(mut self, body: impl Into<Vec<u8>>) -> Self {
self.body = Some(body.into());
self
}
#[cfg(feature = "json")]
pub fn json<T: serde::Serialize>(mut self, value: &T) -> Result<Self, Error> {
let bytes = serde_json::to_vec(value)
.map_err(|e| Error::HttpParse(format!("JSON serialize error: {e}")))?;
self.body = Some(bytes);
self.headers
.push(("Content-Type".into(), "application/json".into()));
Ok(self)
}
pub async fn send(mut self) -> Result<Response, Error> {
if !self.headers.iter().any(|(k, _)| k.eq_ignore_ascii_case("host")) {
self.headers
.insert(0, ("Host".into(), self.client.host().to_string()));
}
if !self.headers.iter().any(|(k, _)| k.eq_ignore_ascii_case("connection")) {
self.headers.push(("Connection".into(), "close".into()));
}
let raw_request =
http::serialize_request(self.method, &self.path, &self.headers, self.body.as_deref());
let (response_tx, response_rx) = oneshot::channel();
self.client.send_request(raw_request, response_tx).await?;
let raw_response = response_rx
.await
.map_err(|_| Error::ConnectionClosed)??;
http::parse_response(&raw_response)
}
}