http_adapter_surf/
lib.rs

1//! # HTTP adapter implementation for [`surf`](https://crates.io/crates/surf)
2//!
3//! For more details refer to [`http-adapter`](https://crates.io/crates/http-adapter)
4
5use std::error::Error as StdError;
6use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
7
8pub use surf;
9use surf::http::url::ParseError;
10use surf::http::{Method as SurfMethod, StatusCode as SurfStatusCode, Version as SurfVersion};
11
12use http_adapter::async_trait::async_trait;
13use http_adapter::http::{Method, StatusCode, Version};
14use http_adapter::{http, HttpClientAdapter};
15use http_adapter::{Request, Response};
16
17#[derive(Clone, Debug)]
18pub struct SurfAdapter {
19	client: surf::Client,
20}
21
22impl SurfAdapter {
23	pub fn new(client: surf::Client) -> Self {
24		Self { client }
25	}
26}
27
28impl Default for SurfAdapter {
29	#[inline]
30	fn default() -> Self {
31		Self {
32			client: surf::Client::new(),
33		}
34	}
35}
36
37#[derive(Debug)]
38pub enum Error {
39	Http(http::Error),
40	Surf(surf::Error),
41	InvalidMethod(String),
42	InvalidStatusCode(u16),
43	InvalidHttpVersion(String),
44	InvalidHeader(String, Vec<u8>),
45	InvalidUrl(ParseError),
46}
47
48impl Display for Error {
49	fn fmt(&self, f: &mut Formatter) -> FmtResult {
50		match self {
51			Error::Http(e) => Display::fmt(e, f),
52			Error::Surf(e) => Display::fmt(e, f),
53			Error::InvalidStatusCode(code) => {
54				write!(f, "Invalid status code: {code}")
55			}
56			Error::InvalidMethod(method) => {
57				write!(f, "Invalid method: {method}")
58			}
59			Error::InvalidHttpVersion(version) => {
60				write!(f, "Invalid HTTP version: {version}")
61			}
62			Error::InvalidHeader(name, value) => {
63				write!(f, "Invalid header: {name} value: {value:?}")
64			}
65			Error::InvalidUrl(e) => {
66				write!(f, "Invalid URL: {e}")
67			}
68		}
69	}
70}
71
72impl StdError for Error {}
73
74#[inline]
75fn from_request(request: Request<Vec<u8>>) -> Result<surf::Request, Error> {
76	let (head, body) = request.into_parts();
77	let method = match head.method {
78		Method::GET => SurfMethod::Get,
79		Method::POST => SurfMethod::Post,
80		Method::PUT => SurfMethod::Put,
81		Method::PATCH => SurfMethod::Patch,
82		Method::DELETE => SurfMethod::Delete,
83		Method::HEAD => SurfMethod::Head,
84		Method::CONNECT => SurfMethod::Connect,
85		Method::OPTIONS => SurfMethod::Options,
86		Method::TRACE => SurfMethod::Trace,
87		_ => {
88			return Err(Error::InvalidMethod(head.method.to_string()));
89		}
90	};
91	let mut out = surf::Request::new(method, surf::Url::parse(&head.uri.to_string()).map_err(Error::InvalidUrl)?);
92	for (name, value) in &head.headers {
93		out.append_header(
94			name.as_str(),
95			value
96				.to_str()
97				.map_err(|_| Error::InvalidHeader(name.to_string(), value.as_bytes().to_vec()))?,
98		);
99	}
100	out.body_bytes(body);
101	Ok(out)
102}
103
104#[inline]
105async fn to_response(mut res: surf::Response) -> Result<Response<Vec<u8>>, Error> {
106	let status = match res.status() {
107		SurfStatusCode::Continue => StatusCode::CONTINUE,
108		SurfStatusCode::SwitchingProtocols => StatusCode::SWITCHING_PROTOCOLS,
109		SurfStatusCode::Ok => StatusCode::OK,
110		SurfStatusCode::Created => StatusCode::CREATED,
111		SurfStatusCode::Accepted => StatusCode::ACCEPTED,
112		SurfStatusCode::NonAuthoritativeInformation => StatusCode::NON_AUTHORITATIVE_INFORMATION,
113		SurfStatusCode::NoContent => StatusCode::NO_CONTENT,
114		SurfStatusCode::ResetContent => StatusCode::RESET_CONTENT,
115		SurfStatusCode::PartialContent => StatusCode::PARTIAL_CONTENT,
116		SurfStatusCode::MultiStatus => StatusCode::MULTI_STATUS,
117		SurfStatusCode::ImUsed => StatusCode::IM_USED,
118		SurfStatusCode::MultipleChoice => StatusCode::MULTIPLE_CHOICES,
119		SurfStatusCode::MovedPermanently => StatusCode::MOVED_PERMANENTLY,
120		SurfStatusCode::Found => StatusCode::FOUND,
121		SurfStatusCode::SeeOther => StatusCode::SEE_OTHER,
122		SurfStatusCode::NotModified => StatusCode::NOT_MODIFIED,
123		SurfStatusCode::TemporaryRedirect => StatusCode::TEMPORARY_REDIRECT,
124		SurfStatusCode::PermanentRedirect => StatusCode::PERMANENT_REDIRECT,
125		SurfStatusCode::BadRequest => StatusCode::BAD_REQUEST,
126		SurfStatusCode::Unauthorized => StatusCode::UNAUTHORIZED,
127		SurfStatusCode::PaymentRequired => StatusCode::PAYMENT_REQUIRED,
128		SurfStatusCode::Forbidden => StatusCode::FORBIDDEN,
129		SurfStatusCode::NotFound => StatusCode::NOT_FOUND,
130		SurfStatusCode::MethodNotAllowed => StatusCode::METHOD_NOT_ALLOWED,
131		SurfStatusCode::NotAcceptable => StatusCode::NOT_ACCEPTABLE,
132		SurfStatusCode::ProxyAuthenticationRequired => StatusCode::PROXY_AUTHENTICATION_REQUIRED,
133		SurfStatusCode::RequestTimeout => StatusCode::REQUEST_TIMEOUT,
134		SurfStatusCode::Conflict => StatusCode::CONFLICT,
135		SurfStatusCode::Gone => StatusCode::GONE,
136		SurfStatusCode::LengthRequired => StatusCode::LENGTH_REQUIRED,
137		SurfStatusCode::PreconditionFailed => StatusCode::PRECONDITION_FAILED,
138		SurfStatusCode::PayloadTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
139		SurfStatusCode::UriTooLong => StatusCode::URI_TOO_LONG,
140		SurfStatusCode::UnsupportedMediaType => StatusCode::UNSUPPORTED_MEDIA_TYPE,
141		SurfStatusCode::RequestedRangeNotSatisfiable => StatusCode::RANGE_NOT_SATISFIABLE,
142		SurfStatusCode::ExpectationFailed => StatusCode::EXPECTATION_FAILED,
143		SurfStatusCode::ImATeapot => StatusCode::IM_A_TEAPOT,
144		SurfStatusCode::MisdirectedRequest => StatusCode::MISDIRECTED_REQUEST,
145		SurfStatusCode::UnprocessableEntity => StatusCode::UNPROCESSABLE_ENTITY,
146		SurfStatusCode::Locked => StatusCode::LOCKED,
147		SurfStatusCode::FailedDependency => StatusCode::FAILED_DEPENDENCY,
148		SurfStatusCode::UpgradeRequired => StatusCode::UPGRADE_REQUIRED,
149		SurfStatusCode::PreconditionRequired => StatusCode::PRECONDITION_REQUIRED,
150		SurfStatusCode::TooManyRequests => StatusCode::TOO_MANY_REQUESTS,
151		SurfStatusCode::RequestHeaderFieldsTooLarge => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
152		SurfStatusCode::UnavailableForLegalReasons => StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS,
153		SurfStatusCode::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
154		SurfStatusCode::NotImplemented => StatusCode::NOT_IMPLEMENTED,
155		SurfStatusCode::BadGateway => StatusCode::BAD_GATEWAY,
156		SurfStatusCode::ServiceUnavailable => StatusCode::SERVICE_UNAVAILABLE,
157		SurfStatusCode::GatewayTimeout => StatusCode::GATEWAY_TIMEOUT,
158		SurfStatusCode::HttpVersionNotSupported => StatusCode::HTTP_VERSION_NOT_SUPPORTED,
159		SurfStatusCode::VariantAlsoNegotiates => StatusCode::VARIANT_ALSO_NEGOTIATES,
160		SurfStatusCode::InsufficientStorage => StatusCode::INSUFFICIENT_STORAGE,
161		SurfStatusCode::LoopDetected => StatusCode::LOOP_DETECTED,
162		SurfStatusCode::NotExtended => StatusCode::NOT_EXTENDED,
163		SurfStatusCode::NetworkAuthenticationRequired => StatusCode::NETWORK_AUTHENTICATION_REQUIRED,
164		SurfStatusCode::EarlyHints | SurfStatusCode::TooEarly => return Err(Error::InvalidStatusCode(u16::from(res.status()))),
165	};
166	let mut response = Response::builder().status(status);
167
168	if let Some(version) = res.version() {
169		let version = match version {
170			SurfVersion::Http0_9 => Version::HTTP_09,
171			SurfVersion::Http1_0 => Version::HTTP_10,
172			SurfVersion::Http1_1 => Version::HTTP_11,
173			SurfVersion::Http2_0 => Version::HTTP_2,
174			SurfVersion::Http3_0 => Version::HTTP_3,
175			_ => return Err(Error::InvalidHttpVersion(version.to_string())),
176		};
177		response = response.version(version)
178	}
179
180	for header_name in res.header_names() {
181		if let Some(header_values) = res.header(header_name) {
182			for header_value in header_values {
183				response = response.header(header_name.to_string(), header_value.to_string());
184			}
185		}
186	}
187
188	let body = res.body_bytes().await.map_err(Error::Surf)?;
189
190	response.body(body).map_err(Error::Http)
191}
192
193#[async_trait]
194impl HttpClientAdapter for SurfAdapter {
195	type Error = Error;
196
197	async fn execute(&self, request: Request<Vec<u8>>) -> Result<Response<Vec<u8>>, Self::Error> {
198		let res = self.client.send(from_request(request)?).await.map_err(Error::Surf)?;
199		to_response(res).await
200	}
201}