1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/COPYRIGHT"))]
2#![warn(missing_docs)]
12
13use std::{convert::Infallible, io};
14
15use bytes::Bytes;
16use http_body_util::combinators::BoxBody;
17use hyper::{Response, Version};
18use prosa::core::{
19 error::{BusError, ProcError},
20 msg::{ErrorMsg, InternalMsg},
21};
22use thiserror::Error;
23
24const H2: &[u8] = b"h2";
25
26#[cfg(target_family = "unix")]
28pub const PRODUCT_VERSION_HEADER: &str = concat!(
29 env!("CARGO_PKG_NAME"),
30 "/",
31 env!("CARGO_PKG_VERSION"),
32 " (Unix)"
33);
34#[cfg(target_family = "windows")]
35pub const PRODUCT_VERSION_HEADER: &str = concat!(
36 env!("CARGO_PKG_NAME"),
37 "/",
38 env!("CARGO_PKG_VERSION"),
39 " (Windows)"
40);
41#[cfg(all(not(target_family = "unix"), not(target_family = "windows")))]
42pub const PRODUCT_VERSION_HEADER: &str =
43 concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
44
45#[derive(Debug, Error)]
47pub enum HyperProcError {
48 #[error("Hyper IO error: {0}")]
50 Io(#[from] io::Error),
51 #[error("Hyper error[{1}]: `{0}`")]
53 Hyper(hyper::Error, String),
54 #[error("Internal bus error: {0}")]
56 InternalBus(#[from] BusError),
57 #[error("Other error: {0}")]
59 Other(String),
60}
61
62impl ProcError for HyperProcError {
63 fn recoverable(&self) -> bool {
64 match self {
65 HyperProcError::Io(error) => error.recoverable(),
66 HyperProcError::Hyper(..) => true,
67 HyperProcError::InternalBus(b) => b.recoverable(),
68 HyperProcError::Other(_) => true,
69 }
70 }
71}
72
73impl<M> From<tokio::sync::mpsc::error::SendError<InternalMsg<M>>> for HyperProcError
74where
75 M: 'static
76 + std::marker::Send
77 + std::marker::Sync
78 + std::marker::Sized
79 + std::clone::Clone
80 + std::fmt::Debug
81 + prosa::core::msg::Tvf
82 + std::default::Default,
83{
84 fn from(err: tokio::sync::mpsc::error::SendError<InternalMsg<M>>) -> Self {
85 HyperProcError::InternalBus(BusError::InternalQueue(format!(
86 "Failed to send message: {}",
87 err
88 )))
89 }
90}
91
92#[derive(Debug, Error)]
94pub enum HttpError {
95 #[error("Hyper error: `{0}`")]
97 Hyper(#[from] hyper::Error),
98 #[error("HTTP error: `{0}`")]
100 Http(#[from] http::Error),
101}
102
103fn hyper_version_str(version: Version) -> &'static str {
105 match version {
106 hyper::Version::HTTP_11 => "HTTP/1.1",
107 hyper::Version::HTTP_2 => "HTTP/2",
108 hyper::Version::HTTP_3 => "HTTP/3",
109 _ => "Unknown",
110 }
111}
112
113pub type SrvRespHandler<A, M> = Box<
118 dyn FnOnce(
119 &A,
120 Result<M, ErrorMsg<M>>,
121 ) -> Result<Response<BoxBody<Bytes, Infallible>>, HttpError>
122 + Send,
123>;
124
125pub enum HyperResp<A, M>
127where
128 A: server::adaptor::HyperServerAdaptor<M>,
129 M: 'static
130 + std::marker::Send
131 + std::marker::Sync
132 + std::marker::Sized
133 + std::clone::Clone
134 + std::fmt::Debug
135 + prosa::core::msg::Tvf
136 + std::default::Default,
137{
138 SrvReq(String, M, SrvRespHandler<A, M>),
140 HttpResp(Response<BoxBody<Bytes, Infallible>>),
142 HttpErr(HttpError),
144}
145
146impl<A, M> std::fmt::Debug for HyperResp<A, M>
147where
148 A: server::adaptor::HyperServerAdaptor<M>,
149 M: 'static
150 + std::marker::Send
151 + std::marker::Sync
152 + std::marker::Sized
153 + std::clone::Clone
154 + std::fmt::Debug
155 + prosa::core::msg::Tvf
156 + std::default::Default,
157{
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 match self {
160 HyperResp::SrvReq(name, msg, _) => f
161 .debug_tuple("SrvReq")
162 .field(name)
163 .field(msg)
164 .field(&"<handler>")
165 .finish(),
166 HyperResp::HttpResp(resp) => f.debug_tuple("HttpResp").field(resp).finish(),
167 HyperResp::HttpErr(err) => f.debug_tuple("HttpErr").field(err).finish(),
168 }
169 }
170}
171
172impl<A, M> From<HttpError> for HyperResp<A, M>
173where
174 A: server::adaptor::HyperServerAdaptor<M>,
175 M: 'static
176 + std::marker::Send
177 + std::marker::Sync
178 + std::marker::Sized
179 + std::clone::Clone
180 + std::fmt::Debug
181 + prosa::core::msg::Tvf
182 + std::default::Default,
183{
184 fn from(err: HttpError) -> Self {
185 Self::HttpErr(err)
186 }
187}
188
189impl<A, M> From<hyper::Error> for HyperResp<A, M>
190where
191 A: server::adaptor::HyperServerAdaptor<M>,
192 M: 'static
193 + std::marker::Send
194 + std::marker::Sync
195 + std::marker::Sized
196 + std::clone::Clone
197 + std::fmt::Debug
198 + prosa::core::msg::Tvf
199 + std::default::Default,
200{
201 fn from(err: hyper::Error) -> Self {
202 Self::HttpErr(HttpError::Hyper(err))
203 }
204}
205
206impl<A, M> From<http::Error> for HyperResp<A, M>
207where
208 A: server::adaptor::HyperServerAdaptor<M>,
209 M: 'static
210 + std::marker::Send
211 + std::marker::Sync
212 + std::marker::Sized
213 + std::clone::Clone
214 + std::fmt::Debug
215 + prosa::core::msg::Tvf
216 + std::default::Default,
217{
218 fn from(err: http::Error) -> Self {
219 Self::HttpErr(HttpError::Http(err))
220 }
221}
222
223impl<A, M> From<Result<Response<BoxBody<Bytes, Infallible>>, http::Error>> for HyperResp<A, M>
224where
225 A: server::adaptor::HyperServerAdaptor<M>,
226 M: 'static
227 + std::marker::Send
228 + std::marker::Sync
229 + std::marker::Sized
230 + std::clone::Clone
231 + std::fmt::Debug
232 + prosa::core::msg::Tvf
233 + std::default::Default,
234{
235 fn from(res: Result<Response<BoxBody<Bytes, Infallible>>, http::Error>) -> Self {
236 match res {
237 Ok(response) => Self::HttpResp(response),
238 Err(err) => Self::HttpErr(HttpError::Http(err)),
239 }
240 }
241}
242
243#[cfg(feature = "server")]
244pub mod server;
245
246#[cfg(feature = "client")]
247pub mod client;
248
249#[cfg(any(feature = "server", feature = "client"))]
250#[cfg(test)]
251pub mod tests;