ferron/common/mod.rs
1#![allow(dead_code)]
2
3use std::{error::Error, future::Future, net::SocketAddr, pin::Pin};
4
5use async_channel::Sender;
6use async_trait::async_trait;
7use http_body_util::combinators::BoxBody;
8use hyper::{body::Bytes, upgrade::Upgraded, HeaderMap, Request, Response, StatusCode, Uri};
9use hyper_tungstenite::HyperWebsocket;
10use tokio::runtime::Handle;
11use yaml_rust2::Yaml;
12
13#[path = "log.rs"]
14mod log;
15#[path = "with_runtime.rs"]
16mod with_runtime;
17
18/// Contains information about a network socket, including remote and local addresses,
19/// and whether the connection is encrypted.
20pub struct SocketData {
21 /// The remote address of the socket.
22 pub remote_addr: SocketAddr,
23 /// The local address of the socket.
24 pub local_addr: SocketAddr,
25 /// Indicates if the connection is encrypted.
26 pub encrypted: bool,
27}
28
29impl SocketData {
30 /// Creates a new `SocketData` instance.
31 ///
32 /// # Parameters
33 ///
34 /// - `remote_addr`: The remote address of the socket.
35 /// - `local_addr`: The local address of the socket.
36 /// - `encrypted`: A boolean indicating if the connection is encrypted.
37 ///
38 /// # Returns
39 ///
40 /// A new `SocketData` instance with the provided parameters.
41 pub fn new(remote_addr: SocketAddr, local_addr: SocketAddr, encrypted: bool) -> Self {
42 Self {
43 remote_addr,
44 local_addr,
45 encrypted,
46 }
47 }
48}
49
50/// Represents a log message. This is a type alias for `crate::log::LogMessage`.
51pub type LogMessage = log::LogMessage;
52
53/// Represents the server configuration object. This is a type alias for `Yaml` from the `yaml_rust2` crate.
54pub type ServerConfig = Yaml;
55
56/// Represents the HTTP request from Hyper.
57pub type HyperRequest = Request<BoxBody<Bytes, std::io::Error>>;
58
59/// Represents the HTTP response from Hyper.
60pub type HyperResponse = Response<BoxBody<Bytes, std::io::Error>>;
61
62/// Represents the upgraded HTTP connection from Hyper.
63pub type HyperUpgraded = Upgraded;
64
65/// A wrapper that ensures a function is executed within a specific runtime context.
66/// This is a type alias for `crate::with_runtime::WithRuntime<F>`.
67pub type WithRuntime<F> = with_runtime::WithRuntime<F>;
68
69/// Contains data related to an HTTP request, including the original Hyper request
70/// and optional authentication user information.
71pub struct RequestData {
72 hyper_request: HyperRequest,
73 auth_user: Option<String>,
74 original_url: Option<Uri>,
75 error_status_code: Option<StatusCode>,
76}
77
78impl RequestData {
79 /// Creates a new `RequestData` instance.
80 ///
81 /// # Parameters
82 ///
83 /// - `hyper_request`: The original Hyper `Request` object.
84 /// - `auth_user`: An optional string representing the authenticated user.
85 /// - `original_url`: An optional string representing the original request URL before rewriting.
86 /// - `status_code`: An optional object representing the error status code.
87 ///
88 /// # Returns
89 ///
90 /// A new `RequestData` instance with the provided parameters.
91 pub fn new(
92 hyper_request: HyperRequest,
93 auth_user: Option<String>,
94 original_url: Option<Uri>,
95 error_status_code: Option<StatusCode>,
96 ) -> Self {
97 Self {
98 hyper_request,
99 auth_user,
100 original_url,
101 error_status_code,
102 }
103 }
104
105 /// Sets the authenticated user for the request.
106 ///
107 /// # Parameters
108 ///
109 /// - `auth_user`: A string representing the authenticated user.
110 pub fn set_auth_user(&mut self, auth_user: String) {
111 self.auth_user = Some(auth_user);
112 }
113
114 /// Retrieves the authenticated user associated with the request, if any.
115 ///
116 /// # Returns
117 ///
118 /// An `Option` containing a reference to the authenticated user's string, or `None` if not set.
119 pub fn get_auth_user(&self) -> Option<&str> {
120 match &self.auth_user {
121 Some(auth_user) => Some(auth_user),
122 None => None,
123 }
124 }
125
126 /// Sets the original URL (before URL rewriting) for the request.
127 ///
128 /// # Parameters
129 ///
130 /// - `original_url`: An `Uri` object representing the original request URL before rewriting.
131 pub fn set_original_url(&mut self, original_url: Uri) {
132 self.original_url = Some(original_url);
133 }
134
135 /// Retrieves the original URL (before URL rewriting) associated with the request, if any.
136 ///
137 /// # Returns
138 ///
139 /// An `Option` containing a reference to the `Uri` object representing the original request URL before rewriting, or `None` if not set.
140 pub fn get_original_url(&self) -> Option<&Uri> {
141 match &self.original_url {
142 Some(original_url) => Some(original_url),
143 None => None,
144 }
145 }
146
147 /// Sets the original response status code for the request.
148 ///
149 /// # Parameters
150 ///
151 /// - `original_url`: An `Uri` object representing the original request URL before rewriting.
152 pub fn set_error_status_code(&mut self, error_status_code: StatusCode) {
153 self.error_status_code = Some(error_status_code);
154 }
155
156 /// Retrieves the original response status code associated with the request, if any.
157 ///
158 /// # Returns
159 ///
160 /// An `Option` containing an original response status code, or `None` if not set.
161 pub fn get_error_status_code(&self) -> Option<&StatusCode> {
162 match &self.error_status_code {
163 Some(error_status_code) => Some(error_status_code),
164 None => None,
165 }
166 }
167
168 /// Provides a reference to the underlying Hyper `Request` object.
169 ///
170 /// # Returns
171 ///
172 /// A reference to the `HyperRequest` object.
173 pub fn get_hyper_request(&self) -> &HyperRequest {
174 &self.hyper_request
175 }
176
177 /// Provides a mutable reference to the underlying Hyper `Request` object.
178 ///
179 /// # Returns
180 ///
181 /// A mutable reference to the `HyperRequest` object.
182 pub fn get_mut_hyper_request(&mut self) -> &mut HyperRequest {
183 &mut self.hyper_request
184 }
185
186 /// Consumes the `RequestData` instance and returns its components.
187 ///
188 /// # Returns
189 ///
190 /// A tuple containing the `HyperRequest` object, an optional authenticated user string, an optional `Uri` object representing the original request URL before rewriting, and an optional `StatusCode` object representing the error status code.
191 pub fn into_parts(
192 self,
193 ) -> (
194 HyperRequest,
195 Option<String>,
196 Option<Uri>,
197 Option<StatusCode>,
198 ) {
199 (
200 self.hyper_request,
201 self.auth_user,
202 self.original_url,
203 self.error_status_code,
204 )
205 }
206}
207
208/// Facilitates logging of error messages through a provided logger sender.
209pub struct ErrorLogger {
210 logger: Option<Sender<LogMessage>>,
211}
212
213impl ErrorLogger {
214 /// Creates a new `ErrorLogger` instance.
215 ///
216 /// # Parameters
217 ///
218 /// - `logger`: A `Sender<LogMessage>` used for sending log messages.
219 ///
220 /// # Returns
221 ///
222 /// A new `ErrorLogger` instance associated with the provided logger.
223 pub fn new(logger: Sender<LogMessage>) -> Self {
224 Self {
225 logger: Some(logger),
226 }
227 }
228
229 /// Creates a new `ErrorLogger` instance without any underlying logger.
230 ///
231 /// # Returns
232 ///
233 /// A new `ErrorLogger` instance not associated with any logger.
234 pub fn without_logger() -> Self {
235 Self { logger: None }
236 }
237
238 /// Logs an error message asynchronously.
239 ///
240 /// # Parameters
241 ///
242 /// - `message`: A string slice containing the error message to be logged.
243 ///
244 /// # Examples
245 ///
246 /// ```
247 /// # use crate::ferron_common::ErrorLogger;
248 /// # #[tokio::main]
249 /// # async fn main() {
250 /// let (tx, mut rx) = async_channel::bounded(100);
251 /// let logger = ErrorLogger::new(tx);
252 /// logger.log("An error occurred").await;
253 /// # }
254 /// ```
255 pub async fn log(&self, message: &str) {
256 if let Some(logger) = &self.logger {
257 logger
258 .send(LogMessage::new(String::from(message), true))
259 .await
260 .unwrap_or_default();
261 }
262 }
263}
264
265impl Clone for ErrorLogger {
266 /// Clone a `ErrorLogger`.
267 ///
268 /// # Returns
269 ///
270 /// A cloned `ErrorLogger` instance
271 fn clone(&self) -> Self {
272 Self {
273 logger: self.logger.clone(),
274 }
275 }
276}
277
278/// Holds data related to an HTTP response, including the original request,
279/// optional authentication user information, and the response details.
280pub struct ResponseData {
281 request: Option<HyperRequest>,
282 auth_user: Option<String>,
283 original_url: Option<Uri>,
284 response: Option<Response<BoxBody<Bytes, std::io::Error>>>,
285 response_status: Option<StatusCode>,
286 response_headers: Option<HeaderMap>,
287 new_remote_address: Option<SocketAddr>,
288 parallel_fn: Option<Pin<Box<dyn Future<Output = ()> + Send>>>,
289}
290
291impl ResponseData {
292 /// Initiates the building process for a `ResponseData` instance using a `RequestData` object.
293 ///
294 /// # Parameters
295 ///
296 /// - `request`: A `RequestData` instance containing the original request and authentication information.
297 ///
298 /// # Returns
299 ///
300 /// A `ResponseDataBuilder` initialized with the provided request data.
301 pub fn builder(request: RequestData) -> ResponseDataBuilder {
302 let (request, auth_user, original_url, _) = request.into_parts();
303
304 ResponseDataBuilder {
305 request: Some(request),
306 auth_user,
307 original_url,
308 response: None,
309 response_status: None,
310 response_headers: None,
311 new_remote_address: None,
312 parallel_fn: None,
313 }
314 }
315
316 /// Initiates the building process for a `ResponseData` instance without a `RequestData` object.
317 ///
318 /// # Returns
319 ///
320 /// A `ResponseDataBuilder` initialized without any request data.
321 pub fn builder_without_request() -> ResponseDataBuilder {
322 ResponseDataBuilder {
323 request: None,
324 auth_user: None,
325 original_url: None,
326 response: None,
327 response_status: None,
328 response_headers: None,
329 new_remote_address: None,
330 parallel_fn: None,
331 }
332 }
333
334 /// Consumes the `ResponseData` instance and returns its components.
335 ///
336 /// # Returns
337 ///
338 /// A tuple containing:
339 /// - The optional original `HyperRequest` object.
340 /// - An optional authenticated user string.
341 /// - An optional `Uri` object representing the original request URL (before rewriting)
342 /// - An optional `Response` object encapsulated in a `BoxBody` with `Bytes` and `std::io::Error`.
343 /// - An optional HTTP `StatusCode`.
344 /// - An optional `HeaderMap` containing the HTTP headers.
345 /// - An optional `SocketAddr` containing the client's new IP address and port.
346 /// - An optional `Future` with `()` output that would be executed in parallel.
347 #[allow(clippy::type_complexity)]
348 pub fn into_parts(
349 self,
350 ) -> (
351 Option<HyperRequest>,
352 Option<String>,
353 Option<Uri>,
354 Option<Response<BoxBody<Bytes, std::io::Error>>>,
355 Option<StatusCode>,
356 Option<HeaderMap>,
357 Option<SocketAddr>,
358 Option<Pin<Box<dyn Future<Output = ()> + Send>>>,
359 ) {
360 (
361 self.request,
362 self.auth_user,
363 self.original_url,
364 self.response,
365 self.response_status,
366 self.response_headers,
367 self.new_remote_address,
368 self.parallel_fn,
369 )
370 }
371}
372
373pub struct ResponseDataBuilder {
374 request: Option<HyperRequest>,
375 auth_user: Option<String>,
376 original_url: Option<Uri>,
377 response: Option<Response<BoxBody<Bytes, std::io::Error>>>,
378 response_status: Option<StatusCode>,
379 response_headers: Option<HeaderMap>,
380 new_remote_address: Option<SocketAddr>,
381 parallel_fn: Option<Pin<Box<dyn Future<Output = ()> + Send>>>,
382}
383
384impl ResponseDataBuilder {
385 /// Sets the response for the `ResponseData`.
386 ///
387 /// # Parameters
388 ///
389 /// - `response`: A `Response` object encapsulated in a `BoxBody` with `Bytes` and `std::io::Error`.
390 ///
391 /// # Returns
392 ///
393 /// The updated `ResponseDataBuilder` instance with the specified response.
394 pub fn response(mut self, response: Response<BoxBody<Bytes, std::io::Error>>) -> Self {
395 self.response = Some(response);
396 self
397 }
398
399 /// Sets the status code for the `ResponseData`.
400 ///
401 /// # Parameters
402 ///
403 /// - `status`: A `StatusCode` representing the HTTP status code.
404 ///
405 /// # Returns
406 ///
407 /// The updated `ResponseDataBuilder` instance with the specified status code.
408 pub fn status(mut self, status: StatusCode) -> Self {
409 self.response_status = Some(status);
410 self
411 }
412
413 /// Sets the headers for the `ResponseData`.
414 ///
415 /// # Parameters
416 ///
417 /// - `headers`: A `HeaderMap` containing the HTTP headers.
418 ///
419 /// # Returns
420 ///
421 /// The updated `ResponseDataBuilder` instance with the specified headers.
422 pub fn headers(mut self, headers: HeaderMap) -> Self {
423 self.response_headers = Some(headers);
424 self
425 }
426
427 /// Sets the new client address for the `ResponseData`.
428 ///
429 /// # Parameters
430 ///
431 /// - `new_remote_address`: A `SocketAddr` containing the new client's IP address and port.
432 ///
433 /// # Returns
434 ///
435 /// The updated `ResponseDataBuilder` instance with the specified headers.
436 pub fn new_remote_address(mut self, new_remote_address: SocketAddr) -> Self {
437 self.new_remote_address = Some(new_remote_address);
438 self
439 }
440
441 /// Sets the function to be executed in parallel.
442 ///
443 /// # Parameters
444 ///
445 /// - `parallel_fn`: A `Future` with `()` output.
446 ///
447 /// # Returns
448 ///
449 /// The updated `ResponseDataBuilder` instance with the specified function to be executed in parallel.
450 pub fn parallel_fn(mut self, parallel_fn: impl Future<Output = ()> + Send + 'static) -> Self {
451 self.parallel_fn = Some(Box::pin(parallel_fn));
452 self
453 }
454
455 /// Builds the `ResponseData` instance.
456 ///
457 /// # Returns
458 ///
459 /// A `ResponseData` object containing the accumulated data from the builder.
460 pub fn build(self) -> ResponseData {
461 ResponseData {
462 request: self.request,
463 auth_user: self.auth_user,
464 original_url: self.original_url,
465 response: self.response,
466 response_status: self.response_status,
467 response_headers: self.response_headers,
468 new_remote_address: self.new_remote_address,
469 parallel_fn: self.parallel_fn,
470 }
471 }
472}
473
474/// Defines the interface for server module handlers, specifying how requests should be processed.
475#[async_trait]
476pub trait ServerModuleHandlers {
477 /// Handles an incoming request.
478 ///
479 /// # Parameters
480 ///
481 /// - `request`: A `RequestData` object containing the incoming request and associated data.
482 /// - `config`: A reference to the combined server configuration (`ServerConfig`). The combined configuration has properties in its root.
483 /// - `socket_data`: A reference to the `SocketData` containing socket-related information.
484 /// - `error_logger`: A reference to an `ErrorLogger` for logging errors.
485 ///
486 /// # Returns
487 ///
488 /// A `Result` containing a `ResponseData` object upon success, or a boxed `dyn Error` if an error occurs.
489 async fn request_handler(
490 &mut self,
491 request: RequestData,
492 config: &ServerConfig,
493 socket_data: &SocketData,
494 error_logger: &ErrorLogger,
495 ) -> Result<ResponseData, Box<dyn Error + Send + Sync>>;
496
497 /// Handles an incoming forward proxy request (not using CONNECT method).
498 ///
499 /// # Parameters
500 ///
501 /// - `request`: A `RequestData` object containing the incoming request and associated data.
502 /// - `config`: A reference to the combined server configuration (`ServerConfig`). The combined configuration has properties in its root.
503 /// - `socket_data`: A reference to the `SocketData` containing socket-related information.
504 /// - `error_logger`: A reference to an `ErrorLogger` for logging errors.
505 ///
506 /// # Returns
507 ///
508 /// A `Result` containing a `ResponseData` object upon success, or a boxed `dyn Error` if an error occurs.
509 async fn proxy_request_handler(
510 &mut self,
511 request: RequestData,
512 config: &ServerConfig,
513 socket_data: &SocketData,
514 error_logger: &ErrorLogger,
515 ) -> Result<ResponseData, Box<dyn Error + Send + Sync>>;
516
517 /// Modifies an outgoing response before it is sent to the client.
518 ///
519 /// This function allows for inspection and modification of the response generated by the server
520 /// or other handlers. Implementers can use this to add, remove, or alter headers, change the
521 /// status code, or modify the body of the response as needed.
522 ///
523 /// # Parameters
524 ///
525 /// - `response`: A `HyperResponse` object representing the outgoing HTTP response.
526 ///
527 /// # Returns
528 ///
529 /// A `Result` containing the potentially modified `HyperResponse` object upon success, or a boxed
530 /// `dyn Error` if an error occurs during processing.
531 async fn response_modifying_handler(
532 &mut self,
533 response: HyperResponse,
534 ) -> Result<HyperResponse, Box<dyn Error + Send + Sync>>;
535
536 /// Modifies an outgoing response for forward proxy requests (not using CONNECT method) before it is sent to the client.
537 ///
538 /// This function allows for inspection and modification of the response generated by the server
539 /// or other handlers. Implementers can use this to add, remove, or alter headers, change the
540 /// status code, or modify the body of the response as needed.
541 ///
542 /// # Parameters
543 ///
544 /// - `response`: A `HyperResponse` object representing the outgoing HTTP response.
545 ///
546 /// # Returns
547 ///
548 /// A `Result` containing the potentially modified `HyperResponse` object upon success, or a boxed
549 /// `dyn Error` if an error occurs during processing.
550 async fn proxy_response_modifying_handler(
551 &mut self,
552 response: HyperResponse,
553 ) -> Result<HyperResponse, Box<dyn Error + Send + Sync>>;
554
555 /// Handles an incoming forward proxy request (using CONNECT method).
556 ///
557 /// # Parameters
558 ///
559 /// - `upgraded_request`: A `HyperUpgraded` object containing the upgraded HTTP connection.
560 /// - `connect_address`: A reference to a string containing the address and port number of the destination server (for example "example.com:443").
561 /// - `config`: A reference to the combined server configuration (`ServerConfig`). The combined configuration has properties in its root.
562 /// - `socket_data`: A reference to the `SocketData` containing socket-related information.
563 /// - `error_logger`: A reference to an `ErrorLogger` for logging errors.
564 ///
565 /// # Returns
566 ///
567 /// A `Result` containing an empty value upon success, or a boxed `dyn Error` if an error occurs.
568 async fn connect_proxy_request_handler(
569 &mut self,
570 upgraded_request: HyperUpgraded,
571 connect_address: &str,
572 config: &ServerConfig,
573 socket_data: &SocketData,
574 error_logger: &ErrorLogger,
575 ) -> Result<(), Box<dyn Error + Send + Sync>>;
576
577 /// Checks if the module is a forward proxy module utilizing CONNECT method.
578 ///
579 /// # Returns
580 ///
581 /// `true` if the module is a forward proxy module utlilzing CONNECT method, or `false` otherwise.
582 fn does_connect_proxy_requests(&mut self) -> bool;
583
584 /// Handles an incoming WebSocket request.
585 ///
586 /// # Parameters
587 ///
588 /// - `websocket`: A `HyperWebsocket` object containing a future that resolves to a WebSocket stream.
589 /// - `uri`: A `hyper::Uri` object containig the HTTP request URI.
590 /// - `config`: A reference to the combined server configuration (`ServerConfig`). The combined configuration has properties in its root.
591 /// - `socket_data`: A reference to the `SocketData` containing socket-related information.
592 /// - `error_logger`: A reference to an `ErrorLogger` for logging errors.
593 ///
594 /// # Returns
595 ///
596 /// A `Result` containing an empty value upon success, or a boxed `dyn Error` if an error occurs.
597 async fn websocket_request_handler(
598 &mut self,
599 websocket: HyperWebsocket,
600 uri: &hyper::Uri,
601 headers: &hyper::HeaderMap,
602 config: &ServerConfig,
603 socket_data: &SocketData,
604 error_logger: &ErrorLogger,
605 ) -> Result<(), Box<dyn Error + Send + Sync>>;
606
607 /// Checks if the module is a module supporting WebSocket requests.
608 ///
609 /// # Parameters
610 ///
611 /// - `config`: A reference to the combined server configuration (`ServerConfig`). The combined configuration has properties in its root.
612 /// - `socket_data`: A reference to the `SocketData` containing socket-related information.
613 ///
614 /// # Returns
615 ///
616 /// `true` if the module is a module supporting WebSocket requests, or `false` otherwise.
617 fn does_websocket_requests(&mut self, config: &ServerConfig, socket_data: &SocketData) -> bool;
618}
619
620/// Represents a server module that can provide handlers for processing requests.
621pub trait ServerModule {
622 /// Retrieves the handlers associated with the server module.
623 ///
624 /// # Parameters
625 ///
626 /// - `handle`: A `Handle` to the Tokio runtime.
627 ///
628 /// # Returns
629 ///
630 /// A boxed object implementing `ServerModuleHandlers` that can be sent across threads.
631 fn get_handlers(&self, handle: Handle) -> Box<dyn ServerModuleHandlers + Send>;
632}