use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use core::str::FromStr;
use bytes::Bytes;
use crate::client::ProductOSClient;
use crate::error::ProductOSRequestError;
use crate::method::Method;
use crate::policy::RedirectPolicy;
use crate::protocol::Protocol;
use crate::request::ProductOSRequest;
use crate::requester::ProductOSRequester;
use crate::response::ProductOSResponse;
use product_os_http::{Request, Response};
use product_os_http_body::{BodyBytes, BodyDataStream, BodyExt};
#[derive(Clone)]
pub struct ProductOSReqwestClient {
client: reqwest::Client,
}
impl ProductOSReqwestClient {
pub fn new() -> Self {
Self::default()
}
fn method_to_reqwest(method: &Method) -> reqwest::Method {
match method {
Method::GET => reqwest::Method::GET,
Method::POST => reqwest::Method::POST,
Method::PATCH => reqwest::Method::PATCH,
Method::PUT => reqwest::Method::PUT,
Method::DELETE => reqwest::Method::DELETE,
Method::TRACE => reqwest::Method::TRACE,
Method::HEAD => reqwest::Method::HEAD,
Method::OPTIONS => reqwest::Method::OPTIONS,
Method::CONNECT => reqwest::Method::CONNECT,
Method::ANY => reqwest::Method::GET,
}
}
fn build_request_with_body(
&self,
request: ProductOSRequest<BodyBytes>,
) -> Result<reqwest::Request, ProductOSRequestError> {
let method = Self::method_to_reqwest(&request.method);
let mut r = self.client.request(method, request.url.to_string());
let mut query = vec![];
for (key, value) in &request.query {
query.push((key.clone(), value.clone()));
}
r = r.query(query.as_slice());
let mut headers = reqwest::header::HeaderMap::new();
for (key, value) in &request.headers {
if let Ok(k) = reqwest::header::HeaderName::from_str(key.as_str()) {
if let Ok(v) = reqwest::header::HeaderValue::from_str(value.as_str()) {
headers.insert(k, v);
}
}
}
r = r.headers(headers);
if let Some(ref auth) = request.bearer_auth {
r = r.bearer_auth(auth);
}
if let Some(b) = request.body {
r = r.body(reqwest::Body::wrap(b));
}
match r.build() {
Ok(req) => Ok(req),
Err(e) => {
tracing::error!("Failed to create request: {:?}", e);
Err(ProductOSRequestError::Error(e.to_string()))
}
}
}
async fn convert_response(
response: reqwest::Response,
) -> Result<ProductOSResponse<BodyBytes>, ProductOSRequestError> {
let url = response.url().to_string();
let status = response.status();
let header_map = response.headers().clone();
let body_bytes = match response.bytes().await {
Ok(b) => b,
Err(_e) => Bytes::new(),
};
let body = BodyBytes::new(body_bytes);
let mut builder = Response::builder().status(status.as_u16());
for (name, value) in header_map.iter() {
if let Ok(n) = product_os_http::HeaderName::from_str(name.as_str()) {
if let Ok(val) = value.to_str() {
if let Ok(v) = product_os_http::HeaderValue::from_str(val) {
builder = builder.header(n, v);
}
}
}
}
match builder.body(body) {
Ok(http_response) => Ok(ProductOSResponse::from_response(http_response, url)),
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
}
}
#[cfg(feature = "stream_reqwest")]
fn convert_stream_response(
response: reqwest::Response,
) -> Result<ProductOSResponse<BodyBytes>, ProductOSRequestError> {
let url = response.url().to_string();
let status = response.status();
let header_map = response.headers().clone();
let stream = response.bytes_stream();
let body = BodyBytes::new_stream(stream);
let mut builder = Response::builder().status(status.as_u16());
for (name, value) in header_map.iter() {
if let Ok(n) = product_os_http::HeaderName::from_str(name.as_str()) {
if let Ok(val) = value.to_str() {
if let Ok(v) = product_os_http::HeaderValue::from_str(val) {
builder = builder.header(n, v);
}
}
}
}
match builder.body(body) {
Ok(http_response) => Ok(ProductOSResponse::from_response(http_response, url)),
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
}
}
}
impl ProductOSClient<product_os_http_body::BodyBytes, product_os_http_body::BodyBytes>
for ProductOSReqwestClient
{
fn build(&mut self, requester: &ProductOSRequester) {
let mut header_map = reqwest::header::HeaderMap::new();
for (name, value) in requester.headers.iter() {
let name = name.to_string();
if let Ok(val) = value.to_str() {
if let Ok(name) = reqwest::header::HeaderName::from_bytes(name.as_bytes()) {
if let Ok(value) = reqwest::header::HeaderValue::from_str(val) {
header_map.insert(name, value);
}
}
}
}
let mut builder = reqwest::ClientBuilder::new()
.default_headers(header_map)
.https_only(requester.secure)
.timeout(requester.timeout)
.connect_timeout(requester.connect_timeout)
.danger_accept_invalid_certs(requester.trust_all_certificates)
.cookie_store(true)
.gzip(true)
.brotli(true);
for cert in &requester.certificates {
match reqwest::Certificate::from_der(cert.as_slice()) {
Ok(certificate) => {
builder = builder.add_root_certificate(certificate);
}
Err(e) => {
tracing::error!("Failed to load certificate: {:?}", e);
}
}
}
if let Some(proxy) = &requester.proxy {
let address_string = match proxy.protocol {
Protocol::SOCKS5 => format!("socks5://{}", proxy.address),
Protocol::HTTP => format!("http://{}", proxy.address),
Protocol::HTTPS => format!("https://{}", proxy.address),
Protocol::ALL => format!("http://{}", proxy.address),
};
let proxy_result = match proxy.protocol {
Protocol::HTTPS => reqwest::Proxy::https(&address_string),
Protocol::ALL => reqwest::Proxy::all(&address_string),
_ => reqwest::Proxy::http(&address_string),
};
match proxy_result {
Ok(proxy) => {
tracing::info!("Async proxy set successfully: {:?}", address_string);
builder = builder.proxy(proxy);
}
Err(e) => {
tracing::error!("Failed to setup proxy: {:?}", e);
}
}
}
let redirect_policy = match requester.redirect_policy.clone() {
RedirectPolicy::None => reqwest::redirect::Policy::none(),
RedirectPolicy::Limit(hops) => reqwest::redirect::Policy::limited(hops),
RedirectPolicy::Default => reqwest::redirect::Policy::default(),
};
builder = builder.redirect(redirect_policy);
tracing::trace!("Updated async client with configuration: {:?}", builder);
match builder.build() {
Ok(client) => {
self.client = client;
}
Err(e) => {
tracing::error!("Failed to build reqwest client: {:?}", e);
}
}
}
fn new_request(
&self,
method: Method,
url: &str,
) -> ProductOSRequest<product_os_http_body::BodyBytes> {
ProductOSRequest::new(method, url)
}
async fn request(
&self,
r: ProductOSRequest<product_os_http_body::BodyBytes>,
) -> Result<ProductOSResponse<product_os_http_body::BodyBytes>, ProductOSRequestError> {
match self.build_request_with_body(r) {
Ok(request) => match self.client.execute(request).await {
Ok(response) => Self::convert_response(response).await,
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
},
Err(e) => Err(e),
}
}
#[cfg(feature = "stream_reqwest")]
async fn request_stream(
&self,
r: ProductOSRequest<product_os_http_body::BodyBytes>,
) -> Result<ProductOSResponse<product_os_http_body::BodyBytes>, ProductOSRequestError> {
match self.build_request_with_body(r) {
Ok(request) => match self.client.execute(request).await {
Ok(response) => Self::convert_stream_response(response),
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
},
Err(e) => Err(e),
}
}
async fn request_simple(
&self,
method: Method,
url: &str,
) -> Result<ProductOSResponse<product_os_http_body::BodyBytes>, ProductOSRequestError> {
let r = ProductOSRequest::<product_os_http_body::BodyBytes>::new(method, url);
match self.build_request_with_body(r) {
Ok(request) => match self.client.execute(request).await {
Ok(response) => Self::convert_response(response).await,
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
},
Err(e) => Err(e),
}
}
async fn request_raw(
&self,
r: Request<product_os_http_body::BodyBytes>,
) -> Result<ProductOSResponse<product_os_http_body::BodyBytes>, ProductOSRequestError> {
let req = ProductOSRequest::from_request(r);
match self.build_request_with_body(req) {
Ok(request) => match self.client.execute(request).await {
Ok(response) => Self::convert_response(response).await,
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
},
Err(e) => Err(e),
}
}
#[cfg(feature = "stream_reqwest")]
async fn request_stream_raw(
&self,
r: Request<product_os_http_body::BodyBytes>,
) -> Result<ProductOSResponse<product_os_http_body::BodyBytes>, ProductOSRequestError> {
let req = ProductOSRequest::from_request(r);
match self.build_request_with_body(req) {
Ok(request) => match self.client.execute(request).await {
Ok(response) => Self::convert_stream_response(response),
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
},
Err(e) => Err(e),
}
}
#[cfg(feature = "json")]
async fn set_body_json(
&self,
r: &mut ProductOSRequest<product_os_http_body::BodyBytes>,
json: serde_json::Value,
) {
let json_string = json.to_string();
let body = product_os_http_body::BodyBytes::new(bytes::Bytes::from(json_string));
r.body = Some(body);
r.add_header("content-type", "application/json", false);
}
#[cfg(feature = "form")]
async fn set_body_form(
&self,
r: &mut ProductOSRequest<product_os_http_body::BodyBytes>,
form: &str,
) {
match serde_urlencoded::to_string(form) {
Ok(form_string) => {
let body = product_os_http_body::BodyBytes::new(bytes::Bytes::from(form_string));
r.body = Some(body);
r.add_header("content-type", "application/x-www-form-urlencoded", false);
}
Err(e) => {
tracing::error!("Failed to serialize form: {:?}", e);
}
}
}
async fn text(
&self,
r: ProductOSResponse<product_os_http_body::BodyBytes>,
) -> Result<String, ProductOSRequestError> {
match r.response_async {
Some(res) => {
match <product_os_http_body::BodyBytes as Clone>::clone(res.body())
.collect()
.await
{
Ok(body) => match String::from_utf8(body.to_bytes().as_ref().to_vec()) {
Ok(res) => Ok(res),
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
},
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
}
}
None => Err(ProductOSRequestError::Error(String::from(
"No response found",
))),
}
}
#[cfg(feature = "json")]
async fn json(
&self,
r: ProductOSResponse<product_os_http_body::BodyBytes>,
) -> Result<serde_json::Value, ProductOSRequestError> {
match r.response_async {
Some(res) => {
match <product_os_http_body::BodyBytes as Clone>::clone(res.body())
.collect()
.await
{
Ok(body) => match String::from_utf8(body.to_bytes().as_ref().to_vec()) {
Ok(res) => match serde_json::from_str(&res) {
Ok(res) => Ok(res),
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
},
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
},
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
}
}
None => Err(ProductOSRequestError::Error(String::from(
"No response found",
))),
}
}
async fn bytes(
&self,
r: ProductOSResponse<product_os_http_body::BodyBytes>,
) -> Result<bytes::Bytes, ProductOSRequestError> {
match r.response_async {
Some(res) => {
match <product_os_http_body::BodyBytes as Clone>::clone(res.body())
.collect()
.await
{
Ok(body) => Ok(body.to_bytes()),
Err(e) => Err(ProductOSRequestError::Error(e.to_string())),
}
}
None => Err(ProductOSRequestError::Error(String::from(
"No response found",
))),
}
}
async fn next_bytes(
&self,
r: &mut ProductOSResponse<product_os_http_body::BodyBytes>,
) -> Result<Option<bytes::Bytes>, ProductOSRequestError> {
match r.response_async {
Some(ref mut res) => {
loop {
if let Some(res) = res.body_mut().frame().await {
let frame = match res {
Ok(frame) => frame,
Err(e) => return Err(ProductOSRequestError::Error(e.to_string())),
};
if let Ok(buf) = frame.into_data() {
return Ok(Some(buf));
}
} else {
return Ok(None);
}
}
}
None => Err(ProductOSRequestError::Error(String::from(
"No response found",
))),
}
}
fn to_stream(
&self,
r: ProductOSResponse<product_os_http_body::BodyBytes>,
) -> Result<BodyDataStream<BodyBytes>, ProductOSRequestError> {
match r.response_async {
Some(res) => Ok(BodyDataStream::new(res.into_body())),
None => Err(ProductOSRequestError::Error(String::from(
"No response found",
))),
}
}
}
impl Default for ProductOSReqwestClient {
fn default() -> Self {
Self {
client: reqwest::Client::new(),
}
}
}