1pub mod convert;
4
5use std::io::{BufReader, Cursor, Read};
6
7pub use http::{header, HeaderMap, Method, StatusCode, Uri};
8
9pub type Request = http::Request<Body>;
10pub type Response = http::Response<Body>;
11pub type ResponseBuilder = http::response::Builder;
12
13pub fn response_builder() -> ResponseBuilder {
14 http::Response::builder()
15}
16
17pub struct Body(BodyInner);
18
19impl Body {
20 pub fn read_to_vec(self) -> Result<Vec<u8>, WcgiError> {
21 match self.0 {
22 BodyInner::Empty => Ok(Vec::new()),
23 BodyInner::Data(data) => Ok(data.into_inner()),
24 BodyInner::Stream(mut stream) => {
25 let mut buffer = Vec::new();
26 stream
27 .read_to_end(&mut buffer)
28 .map_err(|err| WcgiError::wrap(err, "could not read request body"))?;
29 Ok(buffer)
30 }
31 }
32 }
33}
34
35impl Read for Body {
36 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
37 match &mut self.0 {
38 BodyInner::Empty => Ok(0),
39 BodyInner::Data(data) => data.read(buf),
40 BodyInner::Stream(stream) => stream.read(buf),
41 }
42 }
43}
44
45impl Body {
46 pub fn new_reader<I: std::io::Read + 'static>(read: I) -> Self {
47 Self(BodyInner::Stream(BufReader::new(Box::new(read))))
48 }
49
50 pub fn new_bytes(data: impl Into<Vec<u8>>) -> Self {
51 Self(BodyInner::Data(Cursor::new(data.into())))
52 }
53
54 pub fn new_text<V: Into<String>>(value: V) -> Self {
55 Self(BodyInner::Data(Cursor::new(value.into().into_bytes())))
56 }
57
58 pub fn empty() -> Self {
59 Self(BodyInner::Empty)
60 }
61}
62
63enum BodyInner {
64 Empty,
65 Data(Cursor<Vec<u8>>),
66 Stream(BufReader<Box<dyn Read>>),
67}
68
69impl From<()> for Body {
70 fn from(_value: ()) -> Self {
71 Body(BodyInner::Empty)
72 }
73}
74
75impl From<Vec<u8>> for Body {
76 fn from(value: Vec<u8>) -> Self {
77 Body(BodyInner::Data(Cursor::new(value)))
78 }
79}
80
81impl From<String> for Body {
82 fn from(value: String) -> Self {
83 Body(BodyInner::Data(Cursor::new(value.into_bytes())))
84 }
85}
86
87pub trait RequestExt {
88 fn url(&self) -> url::Url;
89}
90
91impl RequestExt for Request {
92 fn url(&self) -> url::Url {
93 let host = self
94 .headers()
95 .get(header::HOST)
96 .and_then(|v| v.to_str().ok())
97 .map(|v| v.trim())
98 .filter(|v| !v.is_empty())
99 .unwrap_or("nohost");
100
101 let path = self.uri();
102 let raw = format!("http://{host}{path}");
103 raw.parse().unwrap()
104 }
105}
106
107#[derive(Debug)]
108pub struct WcgiError {
109 message: String,
110 source: Option<Box<dyn std::error::Error + Send + Sync>>,
111}
112
113impl WcgiError {
114 #[track_caller]
116 pub fn wrap<E: std::error::Error + Send + Sync + 'static, M: Into<String>>(
117 error: E,
118 message: M,
119 ) -> Self {
120 Self {
121 message: message.into(),
122 source: Some(Box::new(error)),
123 }
124 }
125
126 #[track_caller]
128 pub fn msg<M: Into<String>>(message: M) -> Self {
129 Self {
130 message: message.into(),
131 source: None,
132 }
133 }
134
135 pub fn as_std(self) -> StdWcgiError {
136 StdWcgiError(self)
137 }
138
139 pub fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
140 match &self.source {
141 Some(s) => Some(&**s),
142 None => None,
143 }
144 }
145}
146
147impl std::fmt::Display for WcgiError {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 write!(f, "{}", &self.message)?;
150 if let Some(source) = &self.source {
151 write!(f, "{source}")?;
152 }
153 Ok(())
154 }
155}
156
157impl<E: std::error::Error + Send + Sync + 'static> From<E> for WcgiError {
158 #[track_caller]
159 fn from(value: E) -> Self {
160 Self {
161 message: String::new(),
162 source: Some(Box::new(value)),
163 }
164 }
165}
166
167pub struct StdWcgiError(WcgiError);
169
170impl std::fmt::Debug for StdWcgiError {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 self.0.fmt(f)
173 }
174}
175
176impl std::fmt::Display for StdWcgiError {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 self.0.fmt(f)
179 }
180}
181
182impl std::error::Error for StdWcgiError {}
183
184impl From<WcgiError> for StdWcgiError {
185 fn from(value: WcgiError) -> Self {
186 Self(value)
187 }
188}
189
190pub trait WcgiHandler {
191 fn handle_request(&self, request: Request) -> Result<Response, WcgiError>;
192}
193
194impl<F: Fn(Request) -> Result<Response, WcgiError>> WcgiHandler for F {
195 fn handle_request(&self, request: Request) -> Result<Response, WcgiError> {
196 (self)(request)
197 }
198}
199
200pub fn serve_once<H: WcgiHandler>(handler: H) {
202 let request = convert::extract_request().unwrap();
203 let response = handler.handle_request(request).unwrap();
204 convert::send_response_and_terminate(response);
205}