tii/tii_builder.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
//! Provides the core Tii app functionality.
use crate::http::response::Response;
use std::sync::Arc;
use std::time::Duration;
/// Represents the Tii app.
pub struct TiiBuilder {
  routers: Vec<Box<dyn Router>>,
  error_handler: ErrorHandler,
  not_found_handler: NotFoundHandler,
  max_head_buffer_size: usize,
  connection_timeout: Option<Duration>,
  read_timeout: Option<Duration>,
  keep_alive_timeout: Option<Duration>,
  request_body_io_timeout: Option<Duration>,
  write_timeout: Option<Duration>,
}
use crate::default_functions::{default_error_handler, default_fallback_not_found_handler};
pub use crate::functional_traits::*;
use crate::http::request_context::RequestContext;
use crate::tii_error::{TiiError, TiiResult, UserError};
use crate::tii_router::Routeable;
use crate::tii_router_builder::TiiRouterBuilder;
use crate::tii_server::TiiServer;
/// Represents a function able to handle an error.
/// The first parameter of type `Option<Request>` will be `Some` if the request could be parsed.
/// Otherwise, it will be `None` and the status code will be `StatusCode::BadRequest`.
///
/// Every app has a default error handler, which simply displays the status code.
/// The source code for this default error handler is copied below since it is a good example.
///
pub type ErrorHandler = fn(&mut RequestContext, TiiError) -> TiiResult<Response>;
/// Handler for request that couldn't route for some reason.
pub type NotRouteableHandler = fn(&mut RequestContext, &[Routeable]) -> TiiResult<Response>;
/// Fallback handler if no router handled the request.
pub type NotFoundHandler = fn(&mut RequestContext) -> TiiResult<Response>;
impl Default for TiiBuilder {
  /// Initialises a new Tii app.
  fn default() -> Self {
    Self {
      routers: Vec::new(),
      error_handler: default_error_handler,
      not_found_handler: default_fallback_not_found_handler,
      connection_timeout: None,
      max_head_buffer_size: 8192,
      keep_alive_timeout: None,
      read_timeout: None,
      request_body_io_timeout: None,
      write_timeout: None,
    }
  }
}
impl TiiBuilder {
  /// Build TiiServer using a closure or fn which receives the builder
  pub fn builder<T: FnOnce(TiiBuilder) -> TiiResult<TiiBuilder>>(
    closure: T,
  ) -> TiiResult<TiiServer> {
    closure(TiiBuilder::default()).map(|builder| builder.build())
  }
  /// Build `Arc<TiiServer>` using a closure or fn which receives the builder
  pub fn builder_arc<T: FnOnce(TiiBuilder) -> TiiResult<TiiBuilder>>(
    closure: T,
  ) -> TiiResult<Arc<TiiServer>> {
    closure(TiiBuilder::default()).map(|builder| builder.build_arc())
  }
  /// This method creates the HttpServer from the builder.
  pub fn build(self) -> TiiServer {
    TiiServer::new(
      self.routers,
      self.error_handler,
      self.not_found_handler,
      self.max_head_buffer_size,
      self.connection_timeout,
      self.read_timeout,
      self.keep_alive_timeout,
      self.request_body_io_timeout,
      self.write_timeout,
    )
  }
  /// This method is equivalent to calling `Arc::new(builder.build())`
  pub fn build_arc(self) -> Arc<TiiServer> {
    Arc::new(self.build())
  }
  /// Adds a new host sub-app to the server.
  /// The host can contain wildcards, for example `*.example.com`.
  ///
  /// ## Panics
  /// This function will panic if the host is equal to `*`, since this is the default host.
  /// If you want to add a route to every host, simply add it directly to the main app.
  pub fn add_router<T: Router + 'static>(mut self, handler: T) -> Self {
    self.routers.push(Box::new(handler));
    self
  }
  /// Adds a new router to the server and calls the closure with the new router so it can be configured.
  pub fn router<T: FnOnce(TiiRouterBuilder) -> TiiResult<TiiRouterBuilder>>(
    self,
    builder: T,
  ) -> TiiResult<Self> {
    Ok(self.add_router(builder(TiiRouterBuilder::default())?.build()))
  }
  /// Sets the error handler for the server.
  pub fn with_error_handler(mut self, handler: ErrorHandler) -> TiiResult<Self> {
    self.error_handler = handler;
    Ok(self)
  }
  /// Sets the not found handler for the server.
  pub fn with_not_found_handler(mut self, handler: NotFoundHandler) -> TiiResult<Self> {
    self.not_found_handler = handler;
    Ok(self)
  }
  /// Sets the maximum head buffer size. Default value is 8192.
  ///
  /// This affects the maximum permitted length of a header name + value pair as well
  /// as the maximum length of the status line and therefore the url.
  ///
  /// This value includes protocol overhead such as the ": " separator between header name/value pairs
  /// as well as the HTTP Method and protocol version and the CRLF trailer of each line.
  ///
  /// Setting this value to below a minimum of 0x100/256 is prevented and will cause this fn to return Err.
  ///
  pub fn with_max_head_buffer_size(mut self, size: usize) -> TiiResult<Self> {
    if size < 0x100 {
      return Err(UserError::RequestHeadBufferTooSmall(size).into());
    }
    self.max_head_buffer_size = size;
    Ok(self)
  }
  /// Sets the connection timeout,
  /// the amount of time before tii will close the connection if it sends no data to tii.
  /// If this value is not set then Tii will use the read_timeout for this purpose
  pub fn with_connection_timeout(mut self, timeout: Option<Duration>) -> TiiResult<Self> {
    self.connection_timeout = timeout;
    Ok(self)
  }
  /// Sets the read timeout
  /// the amount of time before tii will time out a connection when reading data at any point.
  /// Different timeouts might overwrite this value for certain aspects.
  /// Default is None = Infinite timeout.
  pub fn with_read_timeout(mut self, timeout: Option<Duration>) -> TiiResult<Self> {
    self.read_timeout = timeout;
    Ok(self)
  }
  /// Sets the write timeout
  /// the amount of time before tii will time out a connection when writing data to the underlying connection at any point.
  /// Default is None = Infinite timeout.
  pub fn with_write_timeout(mut self, timeout: Option<Duration>) -> TiiResult<Self> {
    self.write_timeout = timeout;
    Ok(self)
  }
  /// Sets the keep alive timeout.
  /// Setting this to None (The Default) will make tii use the read timeout instead.
  /// Setting this value to 0 will disable keep-alives and cause Tii to signal to the client
  /// that keep-alives are not supported by setting "Connection: Close" on every HTTP1/1 response.
  ///
  /// Otherwise, tii will wait this amount of time for the client to send at least 1 byte of the next request.
  pub fn with_keep_alive_timeout(mut self, timeout: Option<Duration>) -> TiiResult<Self> {
    self.keep_alive_timeout = timeout;
    Ok(self)
  }
  /// Sets the amount of time tii will wait for the client to produce at least a single byte of a request
  /// body before returning the `TimedOut` error.
  /// A value of None will cause the read timeout to be used.
  pub fn with_request_body_timeout(mut self, timeout: Option<Duration>) -> TiiResult<Self> {
    self.request_body_io_timeout = timeout;
    Ok(self)
  }
  /// Helper fn to make builder code look a bit cleaner
  pub fn ok(self) -> TiiResult<Self> {
    Ok(self)
  }
}