Actix-web an application-wide adhoc middleware to set a response header for all routes

Hi,

Please help with the following question.

I am using actix-web, some of my end-point handlers return HttpResponse, and some others return Responder.

I would like to set custom response header for all responses before they are sent back to the clients.

I think an adhoc middleware would be appropriate for this. I tried the below, which is basically example from the documentation:

Cargo.toml
...
[dependencies]
actix-web = {version = "4.4.0", features = ["openssl"]}
futures-util = "0.3"
...
src/main.rs
use actix_web::{dev::Service as _, web, App};
use actix_web::{HttpServer, http::header};

use futures_util::future::FutureExt;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap_fn(|req, srv| {
                println!("Hi from start. You requested: {}", req.path());
                srv.call(req).map(|mut res| {
                    res.as_mut().unwrap().headers_mut().append(header::HeaderName::from_static("X-Custom-Header"), header::HeaderValue::from_static("Custom Value"));
                    println!("Hi from response");
                    res
                })
            })
            .route(
                "/index.html",
                web::get().to(|| async { "Hello, middleware!" }),
            )
        })
        .bind(("0.0.0.0", 5000))?
        .run()
        .await        
}

It compiles and runs. When I access http://localhost:5000/index.html -- the only route, it results in the following panic:

Hi from start. You requested: /index.html
thread 'actix-rt|system:0|arbiter:0' panicked at C:\Users\behai\.cargo\registry\src\index.crates.io-6f17d22bba15001f\http-0.2.11\src\header\name.rs:1270:13:
index out of bounds: the len is 0 but the index is 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Hi from start. You requested: /index.html
thread 'actix-rt|system:0|arbiter:1' panicked at C:\Users\behai\.cargo\registry\src\index.crates.io-6f17d22bba15001f\http-0.2.11\src\header\name.rs:1270:13:
index out of bounds: the len is 0 but the index is 0
Hi from start. You requested: /index.html
thread 'actix-rt|system:0|arbiter:2' panicked at C:\Users\behai\.cargo\registry\src\index.crates.io-6f17d22bba15001f\http-0.2.11\src\header\name.rs:1270:13:
index out of bounds: the len is 0 but the index is 0
Hi from start. You requested: /index.html
thread 'actix-rt|system:0|arbiter:3' panicked at C:\Users\behai\.cargo\registry\src\index.crates.io-6f17d22bba15001f\http-0.2.11\src\header\name.rs:1270:13:
index out of bounds: the len is 0 but the index is 0
Hi from start. You requested: /index.html
thread 'actix-rt|system:0|arbiter:4' panicked at C:\Users\behai\.cargo\registry\src\index.crates.io-6f17d22bba15001f\http-0.2.11\src\header\name.rs:1270:13:
index out of bounds: the len is 0 but the index is 0

Changed to insert:

res.as_mut().unwrap().headers_mut().insert(header::HeaderName::from_static("X-Custom-Header"), header::HeaderValue::from_static("Custom Value"));

would still result in the same panic.

What have I done wrong, please?

Thank you and best regards,

...behai.

Tricky one indeed. The culprit is HeaderName::from_static (emphasis by me):

This function requires the static string to only contain lowercase characters, numerals and symbols, as per the HTTP/2.0 specification and header names internal representation within this library.

So if you change

header::HeaderName::from_static("X-Custom-Header")

to

header::HeaderName::from_static("x-custom-header")

it should work.


Just as a tip, HeaderName::from_static is const, so you could define something like const KEY: HeaderName = HeaderName::from_static("X-Custom-Header"). This would throw a compile time error rather than a runtime error, which is probably how the library authors intended the function to be used anyway given the terrible panic message.

3 Likes

Good evening @jofas,

Thank you for helps, yet again :slight_smile: I would never get this!

I had noticed before that response header names at the client side are in camel case, but this is tricky, indeed.

It works :sweat_smile::sweat_smile: I have the header at the browser side.

Thank you and best regards,

...behai.

1 Like