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}