Skip to content

Header names with capital letters are not deserialized #1373

@JuliDi

Description

@JuliDi

Headers with (renamed) names that include capital letters fail to deserialize.

For example:

#[derive(Serialize, Deserialize, JsonSchema)]
pub struct MyHeaders {
    header_a: String,
    #[serde(rename = "X-Header-B")]
    header_b: String,
}

#[endpoint {
    method = GET,
    path = "/test",
}]
pub async fn get_test(
    rqctx: RequestContext<AppContext>,
    header_params: Header<MyHeaders>,
) -> Result<HttpResponseOk<()>, HttpError> {
    info!("rqctx headers: {:?}", rqctx.request.headers());
    let headers = header_params.into_inner();
    info!("Headers: {:?}, {:?}", headers.header_a, headers.header_b);

    Ok(HttpResponseOk(()))
}

Using the following request

Image

causes the following error:

thread 'tokio-runtime-worker' panicked at /home/user/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dropshot-0.16.2/src/extractor/header.rs:53:44:
called `Result::unwrap()` on an `Err` value: "missing field `X-Header-B`"
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Jul 18 08:31:05.526 ERRO handler panicked; propogating panic, uri: /test, method: GET, req_id: 921b5a49-668e-4a6a-9e48-284ff37f91cb, remote_addr: 127.0.0.1:60506, local_addr: 127.0.0.1:8020

Removing the #[serde(rename = "X-Header-B")] reveals in the logs:

rqctx headers: {"accept": "application/json, text/plain, */*", "user-agent": "bruno-runtime/2.7.0", "header_a": "header_a value", "x-header-b": "header_b value", "header_b": "header_b value", "request-start-time": "1752827600143", "accept-encoding": "gzip, compress, deflate, br", "host": "localhost:8020", "connection": "keep-alive"}

So the error seems to be caused by "X-Header-B" being renamed to "x-header-b" in the request context. Since HTTP headers are not case-sensitive, I think deserialization should generally not be case sensitive either.

If someone could tell me where to best introduce this change, I am happy to make a PR for this.

As a workaround, it seems like it's sufficient to specify an alias, e.g. #[serde(rename = "X-Header-B", alias = "x-header-b")]

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions