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