1pub use wasip3::http_compat::{IncomingMessage, Request, Response};
2
3use hyperium as http;
4pub use hyperium::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, Uri};
5use std::any::Any;
6use wasip3::{
7 http::types,
8 http_compat::{
9 http_from_wasi_request, http_from_wasi_response, http_into_wasi_request,
10 http_into_wasi_response,
11 },
12};
13
14pub mod body;
15#[cfg(feature = "grpc")]
17#[cfg_attr(docsrs, doc(cfg(feature = "grpc")))]
18pub mod grpc;
19
20pub type Result<T, E = Error> = ::std::result::Result<T, E>;
25
26type HttpResult<T> = Result<T, types::ErrorCode>;
27
28#[derive(Debug)]
40pub enum Error {
41 ErrorCode(wasip3::http::types::ErrorCode),
46 HttpError(http::Error),
52 Other(Box<dyn std::error::Error + Send + Sync>),
57 Response(wasip3::http::types::Response),
63}
64
65impl std::fmt::Display for Error {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 match self {
68 Error::ErrorCode(e) => write!(f, "{e}"),
69 Error::HttpError(e) => write!(f, "{e}"),
70 Error::Other(e) => write!(f, "{e}"),
71 Error::Response(resp) => match http::StatusCode::from_u16(resp.get_status_code()) {
72 Ok(status) => write!(f, "{status}"),
73 Err(_) => write!(f, "invalid status code {}", resp.get_status_code()),
74 },
75 }
76 }
77}
78
79impl std::error::Error for Error {}
80
81impl From<http::Error> for Error {
82 fn from(err: http::Error) -> Error {
83 Error::HttpError(err)
84 }
85}
86
87impl From<anyhow::Error> for Error {
88 fn from(err: anyhow::Error) -> Error {
89 match err.downcast::<types::ErrorCode>() {
90 Ok(code) => Error::ErrorCode(code),
91 Err(other) => match other.downcast::<Error>() {
92 Ok(err) => err,
93 Err(other) => Error::Other(other.into_boxed_dyn_error()),
94 },
95 }
96 }
97}
98
99impl From<std::convert::Infallible> for Error {
100 fn from(v: std::convert::Infallible) -> Self {
101 match v {}
102 }
103}
104
105impl From<types::ErrorCode> for Error {
106 fn from(code: types::ErrorCode) -> Self {
107 Error::ErrorCode(code)
108 }
109}
110
111impl From<types::Response> for Error {
112 fn from(resp: types::Response) -> Self {
113 Error::Response(resp)
114 }
115}
116
117impl From<String> for Error {
118 fn from(s: String) -> Self {
119 Error::other(s)
120 }
121}
122
123impl From<&'static str> for Error {
124 fn from(s: &'static str) -> Self {
125 Error::other(s)
126 }
127}
128
129impl Error {
130 pub fn other(msg: impl Into<String>) -> Self {
135 anyhow::Error::msg(msg.into()).into()
136 }
137}
138
139impl<Ok: IntoResponse, Err: Into<Error>> IntoResponse for Result<Ok, Err> {
140 fn into_response(self) -> HttpResult<types::Response> {
141 match self {
142 Ok(ok) => ok.into_response(),
143 Err(err) => match err.into() {
144 Error::ErrorCode(code) => Err(code),
145 Error::Response(resp) => Ok(resp),
146 Error::HttpError(err) => match err {
147 err if err.is::<http::method::InvalidMethod>() => {
148 Err(types::ErrorCode::HttpRequestMethodInvalid)
149 }
150 err if err.is::<http::uri::InvalidUri>() => {
151 Err(types::ErrorCode::HttpRequestUriInvalid)
152 }
153 err => Err(types::ErrorCode::InternalError(Some(err.to_string()))),
154 },
155 Error::Other(other) => {
156 Err(types::ErrorCode::InternalError(Some(other.to_string())))
157 }
158 },
159 }
160 }
161}
162
163pub async fn send(request: impl IntoRequest) -> HttpResult<Response> {
170 let request = request.into_request()?;
171 let response = wasip3::http::client::send(request).await?;
172 Response::from_response(response)
173}
174
175pub async fn get(url: impl AsRef<str>) -> HttpResult<Response> {
186 let request = http::Request::get(url.as_ref())
187 .body(EmptyBody::new())
188 .map_err(|_| types::ErrorCode::HttpRequestUriInvalid)?;
189 send(request).await
190}
191
192pub async fn post(url: impl AsRef<str>, body: impl Into<bytes::Bytes>) -> HttpResult<Response> {
203 let request = http::Request::post(url.as_ref())
204 .body(FullBody::new(body.into()))
205 .map_err(|_| types::ErrorCode::HttpRequestUriInvalid)?;
206 send(request).await
207}
208
209pub async fn put(url: impl AsRef<str>, body: impl Into<bytes::Bytes>) -> HttpResult<Response> {
211 let request = http::Request::put(url.as_ref())
212 .body(FullBody::new(body.into()))
213 .map_err(|_| types::ErrorCode::HttpRequestUriInvalid)?;
214 send(request).await
215}
216
217pub async fn patch(url: impl AsRef<str>, body: impl Into<bytes::Bytes>) -> HttpResult<Response> {
219 let request = http::Request::patch(url.as_ref())
220 .body(FullBody::new(body.into()))
221 .map_err(|_| types::ErrorCode::HttpRequestUriInvalid)?;
222 send(request).await
223}
224
225pub async fn delete(url: impl AsRef<str>) -> HttpResult<Response> {
227 let request = http::Request::delete(url.as_ref())
228 .body(EmptyBody::new())
229 .map_err(|_| types::ErrorCode::HttpRequestUriInvalid)?;
230 send(request).await
231}
232
233pub type EmptyBody = http_body_util::Empty<bytes::Bytes>;
250
251pub type FullBody<T> = http_body_util::Full<T>;
273
274pub type OptionalBody<T> = http_body_util::Either<FullBody<T>, EmptyBody>;
297
298pub trait FromRequest {
308 fn from_request(req: wasip3::http::types::Request) -> HttpResult<Self>
310 where
311 Self: Sized;
312}
313
314impl FromRequest for types::Request {
315 fn from_request(req: types::Request) -> HttpResult<Self> {
316 Ok(req)
317 }
318}
319
320impl FromRequest for Request {
321 fn from_request(req: types::Request) -> HttpResult<Self> {
322 http_from_wasi_request(req)
323 }
324}
325
326pub trait IntoRequest {
339 fn into_request(self) -> HttpResult<wasip3::http::types::Request>;
341}
342
343impl<T> IntoRequest for http::Request<T>
344where
345 T: http_body::Body + Any,
346 T::Data: Into<Vec<u8>>,
347 T::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
348{
349 fn into_request(self) -> HttpResult<types::Request> {
350 http_into_wasi_request(self)
351 }
352}
353
354pub trait FromResponse {
363 fn from_response(response: wasip3::http::types::Response) -> HttpResult<Self>
365 where
366 Self: Sized;
367}
368
369impl FromResponse for Response {
370 fn from_response(resp: types::Response) -> HttpResult<Self> {
371 http_from_wasi_response(resp)
372 }
373}
374
375pub trait IntoResponse {
387 fn into_response(self) -> HttpResult<wasip3::http::types::Response>;
389}
390
391impl IntoResponse for types::Response {
392 fn into_response(self) -> HttpResult<types::Response> {
393 Ok(self)
394 }
395}
396
397impl<T> IntoResponse for (http::StatusCode, T)
398where
399 T: http_body::Body + Any,
400 T::Data: Into<Vec<u8>>,
401 T::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
402{
403 fn into_response(self) -> HttpResult<types::Response> {
404 http_into_wasi_response(
405 http::Response::builder()
406 .status(self.0)
407 .body(self.1)
408 .unwrap(),
409 )
410 }
411}
412
413impl IntoResponse for http::StatusCode {
414 fn into_response(self) -> HttpResult<types::Response> {
415 (self, EmptyBody::new()).into_response()
416 }
417}
418
419impl IntoResponse for &'static str {
420 fn into_response(self) -> HttpResult<types::Response> {
421 http::Response::new(http_body_util::Full::new(self.as_bytes())).into_response()
422 }
423}
424
425impl IntoResponse for String {
426 fn into_response(self) -> HttpResult<types::Response> {
427 http::Response::new(self).into_response()
428 }
429}
430
431impl<T> IntoResponse for http::Response<T>
432where
433 T: http_body::Body + Any,
434 T::Data: Into<Vec<u8>>,
435 T::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
436{
437 fn into_response(self) -> HttpResult<types::Response> {
438 http_into_wasi_response(self)
439 }
440}
441
442impl IntoResponse for () {
443 fn into_response(self) -> HttpResult<types::Response> {
444 http::StatusCode::OK.into_response()
445 }
446}
447
448impl IntoResponse for &[u8] {
449 fn into_response(self) -> HttpResult<types::Response> {
450 self.to_vec().into_response()
451 }
452}
453
454impl IntoResponse for Vec<u8> {
455 fn into_response(self) -> HttpResult<types::Response> {
456 http::Response::new(FullBody::new(bytes::Bytes::from(self))).into_response()
457 }
458}
459
460impl IntoResponse for bytes::Bytes {
461 fn into_response(self) -> HttpResult<types::Response> {
462 http::Response::new(FullBody::new(self)).into_response()
463 }
464}
465
466impl<T> IntoResponse for (http::StatusCode, http::HeaderMap, T)
467where
468 T: http_body::Body + Any,
469 T::Data: Into<Vec<u8>>,
470 T::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
471{
472 fn into_response(self) -> HttpResult<types::Response> {
473 let (status, headers, body) = self;
474 let mut resp = http::Response::builder().status(status).body(body).unwrap();
475 *resp.headers_mut() = headers;
476 resp.into_response()
477 }
478}
479
480#[cfg(feature = "json")]
501#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
502pub struct Json<T>(pub T);
503
504#[cfg(feature = "json")]
505impl<T: serde::Serialize> IntoResponse for Json<T> {
506 fn into_response(self) -> HttpResult<types::Response> {
507 let body = serde_json::to_vec(&self.0)
508 .map_err(|e| types::ErrorCode::InternalError(Some(e.to_string())))?;
509 let mut resp = http::Response::builder()
510 .status(http::StatusCode::OK)
511 .body(FullBody::new(bytes::Bytes::from(body)))
512 .unwrap();
513 resp.headers_mut().insert(
514 http::header::CONTENT_TYPE,
515 http::HeaderValue::from_static("application/json"),
516 );
517 resp.into_response()
518 }
519}
520
521#[cfg(feature = "json")]
522impl<T: serde::Serialize> IntoResponse for (http::StatusCode, Json<T>) {
523 fn into_response(self) -> HttpResult<types::Response> {
524 let body = serde_json::to_vec(&self.1 .0)
525 .map_err(|e| types::ErrorCode::InternalError(Some(e.to_string())))?;
526 let mut resp = http::Response::builder()
527 .status(self.0)
528 .body(FullBody::new(bytes::Bytes::from(body)))
529 .unwrap();
530 resp.headers_mut().insert(
531 http::header::CONTENT_TYPE,
532 http::HeaderValue::from_static("application/json"),
533 );
534 resp.into_response()
535 }
536}