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}