1use std::future::Future;
24use std::pin::Pin;
25
26use crate::primitives::canonical::Response;
27use crate::primitives::request::Request;
28use crate::primitives::request_body_error::RequestBodyError;
29use crate::primitives::request_body_policy::RequestBodyPolicy;
30
31#[derive(Debug)]
38pub struct ServiceError {
39 kind: ServiceErrorKind,
40 message: String,
41}
42
43#[derive(Debug)]
44enum ServiceErrorKind {
45 Internal,
47 Rejected(u16),
49 #[allow(dead_code)]
51 Panic,
52 Timeout,
54}
55
56impl ServiceError {
57 pub fn internal(message: impl Into<String>) -> Self {
59 Self {
60 kind: ServiceErrorKind::Internal,
61 message: message.into(),
62 }
63 }
64
65 pub fn rejected(status: u16, message: impl Into<String>) -> Self {
67 Self {
68 kind: ServiceErrorKind::Rejected(status),
69 message: message.into(),
70 }
71 }
72
73 #[allow(dead_code)]
74 pub(crate) fn panic(message: impl Into<String>) -> Self {
75 Self {
76 kind: ServiceErrorKind::Panic,
77 message: message.into(),
78 }
79 }
80
81 pub(crate) fn timeout(message: impl Into<String>) -> Self {
82 Self {
83 kind: ServiceErrorKind::Timeout,
84 message: message.into(),
85 }
86 }
87
88 pub fn message(&self) -> &str {
90 &self.message
91 }
92
93 pub fn is_panic(&self) -> bool {
95 matches!(self.kind, ServiceErrorKind::Panic)
96 }
97
98 pub fn is_timeout(&self) -> bool {
100 matches!(self.kind, ServiceErrorKind::Timeout)
101 }
102
103 pub(crate) fn to_response(&self) -> hyper::Response<crate::response::BoxBodyInner> {
109 let status = match self.kind {
110 ServiceErrorKind::Internal | ServiceErrorKind::Panic => {
111 hyper::StatusCode::INTERNAL_SERVER_ERROR
112 }
113 ServiceErrorKind::Rejected(code) => hyper::StatusCode::from_u16(code)
114 .unwrap_or(hyper::StatusCode::INTERNAL_SERVER_ERROR),
115 ServiceErrorKind::Timeout => hyper::StatusCode::GATEWAY_TIMEOUT,
116 };
117 let body = match self.kind {
118 ServiceErrorKind::Panic => "500 Internal Server Error\n",
119 ServiceErrorKind::Timeout => "504 Gateway Timeout\n",
120 ServiceErrorKind::Rejected(code) => match code {
121 400 => "400 Bad Request\n",
122 403 => "403 Forbidden\n",
123 404 => "404 Not Found\n",
124 405 => "405 Method Not Allowed\n",
125 503 => "503 Service Unavailable\n",
126 _ => "500 Internal Server Error\n",
127 },
128 ServiceErrorKind::Internal => "500 Internal Server Error\n",
129 };
130 crate::response::canonical_error(status, body, false)
131 }
132}
133
134impl std::fmt::Display for ServiceError {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 match self.kind {
137 ServiceErrorKind::Internal => write!(f, "internal error: {}", self.message),
138 ServiceErrorKind::Rejected(code) => {
139 write!(f, "rejected ({}): {}", code, self.message)
140 }
141 ServiceErrorKind::Panic => write!(f, "handler panicked: {}", self.message),
142 ServiceErrorKind::Timeout => write!(f, "handler timeout: {}", self.message),
143 }
144 }
145}
146
147impl std::error::Error for ServiceError {}
148
149impl From<RequestBodyError> for ServiceError {
150 fn from(err: RequestBodyError) -> Self {
151 let status = err.to_status_code();
152 let message = match err {
153 RequestBodyError::RejectedByPolicy => "request body rejected by policy",
154 RequestBodyError::DeclaredLengthTooLarge { .. }
155 | RequestBodyError::LimitExceeded { .. } => "request body exceeds configured limit",
156 RequestBodyError::ReadTimeout => "request body read timed out",
157 RequestBodyError::PrematureEof { .. } => {
158 "request body ended before its declared length"
159 }
160 RequestBodyError::LengthMismatch { .. } => "request body length mismatch",
161 RequestBodyError::InvalidChunkFraming(_) => "invalid request body framing",
162 RequestBodyError::Cancelled => "request body consumption cancelled",
163 RequestBodyError::Disconnected => "client disconnected while sending request body",
164 RequestBodyError::AlreadyConsumed | RequestBodyError::MixedConsumptionMode => {
165 "request body was already consumed"
166 }
167 RequestBodyError::Transport(_) => "request body transport failure",
168 };
169 ServiceError::rejected(status, message)
170 }
171}
172
173pub trait Service: Send + Sync + 'static {
192 fn request_body_policy(
202 &self,
203 _head: &crate::primitives::request_head::RequestHead,
204 ) -> RequestBodyPolicy {
205 RequestBodyPolicy::Reject
206 }
207
208 fn call(
212 &self,
213 request: Request,
214 ) -> Pin<Box<dyn Future<Output = Result<Response, ServiceError>> + Send + '_>>;
215}
216
217pub fn service_fn<F, Fut>(f: F) -> ServiceFn<F>
235where
236 F: Fn(Request) -> Fut + Send + Sync + 'static,
237 Fut: Future<Output = Result<Response, ServiceError>> + Send + 'static,
238{
239 ServiceFn {
240 f,
241 body_policy: None,
242 }
243}
244
245pub fn service_fn_head<F, Fut>(f: F) -> ServiceFn<impl Fn(Request) -> Fut + Send + Sync + 'static>
248where
249 F: Fn(crate::primitives::request_head::RequestHead) -> Fut + Send + Sync + 'static,
250 Fut: Future<Output = Result<Response, ServiceError>> + Send + 'static,
251{
252 ServiceFn {
253 f: move |req: Request| {
254 let (head, _body) = req.into_head_and_body();
255 f(head)
256 },
257 body_policy: Some(RequestBodyPolicy::Reject),
258 }
259}
260
261pub fn service_fn_with_policy<F, Fut>(f: F, policy: RequestBodyPolicy) -> ServiceFn<F>
263where
264 F: Fn(Request) -> Fut + Send + Sync + 'static,
265 Fut: Future<Output = Result<Response, ServiceError>> + Send + 'static,
266{
267 ServiceFn {
268 f,
269 body_policy: Some(policy),
270 }
271}
272
273pub struct ServiceFn<F> {
275 f: F,
276 body_policy: Option<RequestBodyPolicy>,
277}
278
279impl<F, Fut> Service for ServiceFn<F>
280where
281 F: Fn(Request) -> Fut + Send + Sync + 'static,
282 Fut: Future<Output = Result<Response, ServiceError>> + Send + 'static,
283{
284 fn request_body_policy(
285 &self,
286 _head: &crate::primitives::request_head::RequestHead,
287 ) -> RequestBodyPolicy {
288 self.body_policy.unwrap_or(RequestBodyPolicy::Reject)
289 }
290
291 fn call(
292 &self,
293 request: Request,
294 ) -> Pin<Box<dyn Future<Output = Result<Response, ServiceError>> + Send + '_>> {
295 Box::pin((self.f)(request))
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302 use crate::primitives::canonical::ResponseBody as CanonicalResponseBody;
303 use crate::primitives::canonical::StatusCode;
304 use crate::primitives::connection_info::{ConnectionInfo, Scheme};
305 use crate::primitives::header_block::HeaderBlock;
306 use crate::primitives::request_body::RequestBody;
307 use std::net::SocketAddr;
308
309 fn make_test_request(path: &str) -> Request {
310 Request::new(
311 crate::primitives::request_head::RequestHead::new(
312 crate::primitives::method::Method::get(),
313 crate::primitives::request_target::RequestTarget::parse(path).unwrap(),
314 crate::primitives::version::HttpVersion::Http11,
315 HeaderBlock::new(),
316 ),
317 RequestBody::empty(),
318 ConnectionInfo {
319 local_addr: "127.0.0.1:8000".parse::<SocketAddr>().unwrap(),
320 remote_addr: "127.0.0.1:12345".parse::<SocketAddr>().unwrap(),
321 scheme: Scheme::Http,
322 tls: None,
323 },
324 )
325 }
326
327 #[tokio::test]
328 async fn service_fn_calls_handler() {
329 let svc = service_fn(|_req: Request| async {
330 Ok(Response::builder()
331 .status(StatusCode::OK)
332 .body(CanonicalResponseBody::Bytes(b"ok".to_vec()))
333 .unwrap())
334 });
335 let req = make_test_request("/test");
336 let resp = svc.call(req).await.unwrap();
337 assert_eq!(resp.status(), StatusCode::OK);
338 }
339
340 #[tokio::test]
341 async fn custom_service_returns_bytes() {
342 struct ByteService;
343 impl Service for ByteService {
344 fn call(
345 &self,
346 _req: Request,
347 ) -> Pin<Box<dyn Future<Output = Result<Response, ServiceError>> + Send + '_>>
348 {
349 Box::pin(async {
350 Ok(Response::builder()
351 .status(StatusCode::OK)
352 .body(CanonicalResponseBody::Bytes(b"custom bytes".to_vec()))
353 .unwrap())
354 })
355 }
356 }
357 let svc = ByteService;
358 let req = make_test_request("/test");
359 let resp = svc.call(req).await.unwrap();
360 assert_eq!(resp.status(), StatusCode::OK);
361 }
362
363 #[test]
364 fn service_error_response_codes() {
365 let err = ServiceError::internal("oops");
367 assert_eq!(
368 err.to_response().status(),
369 hyper::StatusCode::INTERNAL_SERVER_ERROR
370 );
371
372 let err = ServiceError::panic("crashed");
374 assert_eq!(
375 err.to_response().status(),
376 hyper::StatusCode::INTERNAL_SERVER_ERROR
377 );
378
379 let err = ServiceError::timeout("slow");
381 assert_eq!(
382 err.to_response().status(),
383 hyper::StatusCode::GATEWAY_TIMEOUT
384 );
385
386 let err = ServiceError::rejected(400, "bad");
388 assert_eq!(err.to_response().status(), hyper::StatusCode::BAD_REQUEST);
389
390 let err = ServiceError::rejected(403, "no");
392 assert_eq!(err.to_response().status(), hyper::StatusCode::FORBIDDEN);
393
394 let err = ServiceError::rejected(404, "miss");
396 assert_eq!(err.to_response().status(), hyper::StatusCode::NOT_FOUND);
397
398 let err = ServiceError::rejected(405, "nope");
400 assert_eq!(
401 err.to_response().status(),
402 hyper::StatusCode::METHOD_NOT_ALLOWED
403 );
404
405 let err = ServiceError::rejected(503, "busy");
407 assert_eq!(
408 err.to_response().status(),
409 hyper::StatusCode::SERVICE_UNAVAILABLE
410 );
411
412 let err = ServiceError::rejected(999, "weird");
414 assert_eq!(
415 err.to_response().status(),
416 hyper::StatusCode::from_u16(999).unwrap()
417 );
418 }
419
420 #[tokio::test]
421 async fn service_fn_with_captured_state() {
422 let greeting = "hello";
423 let svc = service_fn(move |_req: Request| {
424 let greeting = greeting.to_string();
425 async move {
426 Ok(Response::builder()
427 .status(StatusCode::OK)
428 .body(CanonicalResponseBody::Bytes(greeting.into_bytes()))
429 .unwrap())
430 }
431 });
432 let req = make_test_request("/test");
433 let resp = svc.call(req).await.unwrap();
434 assert_eq!(resp.status(), StatusCode::OK);
435 }
436
437 #[test]
438 fn service_fn_implements_service() {
439 let svc = service_fn(|_req: Request| async {
440 Ok(Response::builder()
441 .status(StatusCode::OK)
442 .body(CanonicalResponseBody::Empty)
443 .unwrap())
444 });
445 fn assert_service<S: Service>(_svc: &S) {}
446 assert_service(&svc);
447 }
448
449 #[test]
450 fn service_error_display() {
451 let err = ServiceError::internal("something broke");
452 assert!(err.to_string().contains("something broke"));
453 assert!(!err.is_panic());
454 assert!(!err.is_timeout());
455
456 let err = ServiceError::rejected(404, "not found");
457 assert!(err.to_string().contains("404"));
458
459 let err = ServiceError::panic("oops");
460 assert!(err.is_panic());
461
462 let err = ServiceError::timeout("too slow");
463 assert!(err.is_timeout());
464 }
465
466 #[test]
467 fn service_error_to_response() {
468 let err = ServiceError::panic("oops");
469 let resp = err.to_response();
470 assert_eq!(resp.status(), hyper::StatusCode::INTERNAL_SERVER_ERROR);
471
472 let err = ServiceError::timeout("slow");
473 let resp = err.to_response();
474 assert_eq!(resp.status(), hyper::StatusCode::GATEWAY_TIMEOUT);
475
476 let err = ServiceError::rejected(404, "nope");
477 let resp = err.to_response();
478 assert_eq!(resp.status(), hyper::StatusCode::NOT_FOUND);
479 }
480}