Skip to content

Commit

Permalink
reshape middleware configuration and loading
Browse files Browse the repository at this point in the history
  • Loading branch information
jondot committed Oct 10, 2024
1 parent fbfc333 commit 9bda05b
Show file tree
Hide file tree
Showing 28 changed files with 547 additions and 510 deletions.
162 changes: 154 additions & 8 deletions docs-site/content/docs/the-app/controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,148 @@ impl Hooks for App {

Loco comes with a set of built-in middleware out of the box. Some are enabled by default, while others need to be configured. Middleware registration is flexible and can be managed either through the `*.yaml` environment configuration or directly in the code.

## The default stack

You get all the enabled middlewares run the following command
<!-- <snip id="cli-middleware-list" inject_from="yaml" template="sh"> -->
```sh
cargo loco middleware --config
```
<!-- </snip> -->

This is the stack in `development` mode:

```sh
$ cargo loco middleware --config

limit_payload {"enable":true,"body_limit":2000000}
cors {"enable":true,"allow_origins":["any"],"allow_headers":["*"],"allow_methods":["*"],"max_age":null,"vary":["origin","access-control-request-method","access-control-request-headers"]}
catch_panic {"enable":true}
etag {"enable":true}
logger {"config":{"enable":true},"environment":"development"}
request_id {"enable":true}
fallback {"enable":true,"code":200,"file":null,"not_found":null}
powered_by {"ident":"loco.rs"}


remote_ip (disabled)
compression (disabled)
timeout (disabled)
static_assets (disabled)
secure_headers (disabled)
```

### Example: disable all middleware

Take what ever is enabled, and use `enable: false` with the relevant field. If `middlewares:` section in `server` is missing, add it.

```yaml
server:
middlewares:
limit_payload:
enable: false
cors:
enable: false
catch_panic:
enable: false
etag:
enable: false
logger:
enable: false
request_id:
enable: false
fallback:
enable: false
```
The result:
```sh
$ cargo loco middleware --config
powered_by {"ident":"loco.rs"}


limit_payload (disabled)
cors (disabled)
catch_panic (disabled)
etag (disabled)
remote_ip (disabled)
compression (disabled)
timeout_request (disabled)
static (disabled)
secure_headers (disabled)
logger (disabled)
request_id (disabled)
fallback (disabled)
```

You can control the `powered_by` middleware by changing the value for `server.ident`:

```yaml
server:
ident: my-server #(or empty string to disable)
```
### Example: add a non-default middleware
Lets add the _Remote IP_ middleware to the stack. This is done just by configuration:
```yaml
server:
middlewares:
remote_ip:
enable: true
```
The result:
```sh
$ cargo loco middleware --config

limit_payload {"enable":true,"body_limit":2000000}
cors {"enable":true,"allow_origins":["any"],"allow_headers":["*"],"allow_methods":["*"],"max_age":null,"vary":["origin","access-control-request-method","access-control-request-headers"]}
catch_panic {"enable":true}
etag {"enable":true}
remote_ip {"enable":true,"trusted_proxies":null}
logger {"config":{"enable":true},"environment":"development"}
request_id {"enable":true}
fallback {"enable":true,"code":200,"file":null,"not_found":null}
powered_by {"ident":"loco.rs"}
```

### Example: change a configuration for an enabled middleware

Let's change the request body limit to `5mb`. When overriding a middleware configuration, rememeber to keep an `enable: true`:

```yaml
middlewares:
limit_payload:
enable: true
body_limit: 5mb
```
The result:
```sh
$ cargo loco middleware --config

limit_payload {"enable":true,"body_limit":5000000}
cors {"enable":true,"allow_origins":["any"],"allow_headers":["*"],"allow_methods":["*"],"max_age":null,"vary":["origin","access-control-request-method","access-control-request-headers"]}
catch_panic {"enable":true}
etag {"enable":true}
logger {"config":{"enable":true},"environment":"development"}
request_id {"enable":true}
fallback {"enable":true,"code":200,"file":null,"not_found":null}
powered_by {"ident":"loco.rs"}


remote_ip (disabled)
compression (disabled)
timeout_request (disabled)
static (disabled)
secure_headers (disabled)
```

### Authentication
In the `Loco` framework, middleware plays a crucial role in authentication. `Loco` supports various authentication methods, including JSON Web Token (JWT) and API Key authentication. This section outlines how to configure and use authentication middleware in your application.

Expand Down Expand Up @@ -523,6 +658,16 @@ server:
foo: bar
```
To support `htmx`, You can add the following override, to allow some inline running of scripts:

```yaml
secure_headers:
preset: github
overrides:
# this allows you to use HTMX, and has unsafe-inline. Remove or consider in production
"Content-Security-Policy": "default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src 'unsafe-inline' 'self' https:; style-src 'self' https: 'unsafe-inline'"
```

## Compression

`Loco` leverages [CompressionLayer](https://docs.rs/tower-http/0.5.0/tower_http/compression/index.html) to enable a `one click` solution.
Expand Down Expand Up @@ -554,14 +699,7 @@ middlewares:
precompressed: true
```

## Handler and Route based middleware

`Loco` also allow us to apply [layers](https://docs.rs/tower/latest/tower/trait.Layer.html) to specific handlers or
routes.
For more information on handler and route based middleware, refer to the [middleware](/docs/the-app/middlewares)
documentation.

## Cors
## CORS
This middleware enables Cross-Origin Resource Sharing (CORS) by allowing configurable origins, methods, and headers in HTTP requests.
It can be tailored to fit various application requirements, supporting permissive CORS or specific rules as defined in the middleware configuration.

Expand All @@ -585,6 +723,14 @@ middlewares:

```

## Handler and Route based middleware

`Loco` also allow us to apply [layers](https://docs.rs/tower/latest/tower/trait.Layer.html) to specific handlers or
routes.
For more information on handler and route based middleware, refer to the [middleware](/docs/the-app/middlewares)
documentation.


### Handler based middleware:

Apply a layer to a specific handler using `layer` method.
Expand Down
2 changes: 1 addition & 1 deletion examples/demo/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 1 addition & 52 deletions examples/demo/config/development.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,58 +30,7 @@ server:
port: {{ get_env(name="NODE_PORT", default=5150) }}
# The UI hostname or IP address that mailers will point to.
host: http://localhost
# Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block
# </snip>
middlewares:
# Allows to limit the payload size request. payload that bigger than this file will blocked the request.
limit_payload:
# Enable/Disable the middleware.
enable: true
# the limit size. can be b,kb,kib,mb,mib,gb,gib
body_limit: 5mb
# set secure headers
secure_headers:
preset: github
overrides:
# this allows you to use HTMX, and has unsafe-inline. Remove or consider in production
"Content-Security-Policy": "default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src 'unsafe-inline' 'self' https:; style-src 'self' https: 'unsafe-inline'"
# Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details.
logger:
# Enable/Disable the middleware.
enable: true
# when your code is panicked, the request still returns 500 status code.
catch_panic:
# Enable/Disable the middleware.
enable: true
# Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned.
timeout_request:
# Enable/Disable the middleware.
enable: true
# Duration time in milliseconds.
timeout: 5000
compression:
# Enable/Disable the middleware.
enable: true
static_assets:
enable: true
must_exist: true
precompressed: true
folder:
path: assets
fallback: index.html
cors:
enable: true
# Set the value of the [`Access-Control-Allow-Origin`][mdn] header
# allow_origins:
# - https://loco.rs
# Set the value of the [`Access-Control-Allow-Headers`][mdn] header
# allow_headers:
# - Content-Type
# Set the value of the [`Access-Control-Allow-Methods`][mdn] header
# allow_methods:
# - POST
# Set the value of the [`Access-Control-Max-Age`][mdn] header in seconds
# max_age: 3600
# </snip>

# Worker Configuration
workers:
Expand Down
28 changes: 13 additions & 15 deletions src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ use crate::{
task::{self, Tasks},
Result,
};
use colored::Colorize;

/// Represents the application startup mode.
pub enum StartMode {
Expand Down Expand Up @@ -381,23 +380,22 @@ pub fn list_endpoints<H: Hooks>(ctx: &AppContext) -> Vec<ListRoutes> {
H::routes(ctx).collect()
}

pub struct MiddlewareInfo {
pub id: String,
pub enabled: bool,
pub detail: String,
}

#[must_use]
pub fn list_middlewares<H: Hooks>(ctx: &AppContext, with_config: bool) -> Vec<String> {
H::routes(ctx)
.middlewares::<H>(ctx)
pub fn list_middlewares<H: Hooks>(ctx: &AppContext) -> Vec<MiddlewareInfo> {
H::middlewares(ctx)
.iter()
.map(|m| {
let text = heck::AsSnakeCase(m.name()).to_string().bold();
if with_config {
format!(
"{text:<22} {}",
serde_json::to_string(&m.config().unwrap_or_default()).unwrap_or_default()
)
} else {
format!("{text}")
}
.map(|m| MiddlewareInfo {
id: m.name().to_string(),
enabled: m.is_enabled(),
detail: m.config().unwrap_or_default().to_string(),
})
.collect::<Vec<String>>()
.collect::<Vec<_>>()
}

/// Initializes an [`EmailSender`] based on the mailer configuration settings
Expand Down
20 changes: 17 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,8 @@ pub async fn playground<H: Hooks>() -> crate::Result<AppContext> {
#[allow(clippy::too_many_lines)]
#[allow(clippy::cognitive_complexity)]
pub async fn main<H: Hooks, M: MigratorTrait>() -> crate::Result<()> {
use colored::Colorize;

let cli: Cli = Cli::parse();
let environment: Environment = cli.environment.unwrap_or_else(resolve_from_env).into();

Expand Down Expand Up @@ -473,9 +475,21 @@ pub async fn main<H: Hooks, M: MigratorTrait>() -> crate::Result<()> {
}
Commands::Middleware { config } => {
let app_context = create_context::<H>(&environment).await?;
let middlewares = list_middlewares::<H>(&app_context, config);
for middleware in middlewares {
println!("{middleware}");
let middlewares = list_middlewares::<H>(&app_context);
for middleware in middlewares.iter().filter(|m| m.enabled) {
println!(
"{:<22} {}",
middleware.id.bold(),
if config {
middleware.detail.as_str()
} else {
""
}
);
}
println!("\n");
for middleware in middlewares.iter().filter(|m| !m.enabled) {
println!("{:<22} (disabled)", middleware.id.bold().dimmed(),);
}
}
Commands::Task { name, params } => {
Expand Down
1 change: 1 addition & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ pub struct Server {
pub ident: Option<String>,
/// Middleware configurations for the server, including payload limits,
/// logging, and error handling.
#[serde(default)]
pub middlewares: middleware::Config,
}

Expand Down
7 changes: 1 addition & 6 deletions src/controller/middleware/catch_panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,10 @@ use crate::{

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CatchPanic {
#[serde(default)]
pub enable: bool,
}

impl Default for CatchPanic {
fn default() -> Self {
Self { enable: true }
}
}

/// Handler function for the [`CatchPanicLayer`] middleware.
///
/// This function processes panics by extracting error messages, logging them,
Expand Down
12 changes: 4 additions & 8 deletions src/controller/middleware/compression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,16 @@
//! times and reducing bandwidth usage. The middleware configuration allows for
//! enabling or disabling compression based on the application settings.
use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result};
use axum::Router as AXRouter;
use serde::{Deserialize, Serialize};
use tower_http::compression::CompressionLayer;

use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Compression {
enable: bool,
}

impl Default for Compression {
fn default() -> Self {
Self { enable: true }
}
#[serde(default)]
pub enable: bool,
}

impl MiddlewareLayer for Compression {
Expand Down
Loading

0 comments on commit 9bda05b

Please sign in to comment.