Solved invalid headers with Reqwest — now looking to preserve header casing

I’m integrating with an old SMSC server that sends non-standard HTTP headers like:


-ERROR : ERROR

This obviously violates HTTP syntax, so reqwest (via hyperhttparse) fails to parse the response. I’ve confirmed that httparse has an ignore_invalid_headers configuration flag, and newer versions of hyper expose an ignore_invalid_headers(true) option on the HTTP/1 Builder.

However:

  • reqwest doesn’t seem to expose that configuration for clients, and

  • it looks like hyper hard-codes ignore_invalid_headers = false internally for its client path.

My goal is to make reqwest (or hyper) ignore invalid headers — e.g., skip lines like -ERROR : ERRORjust for this project, without forking too much code.

So my questions are:

  1. Is there a supported way to enable ignore_invalid_headers in reqwest’s client stack?

  2. If not, where exactly in hyper should I patch to set ignore_invalid_headers = true for HTTP/1 client connections?

  3. Is there a recommended approach for handling malformed headers from legacy systems like this — perhaps via a custom connector, proxy, or a local [patch.crates-io] override of httparse?

Context

  • reqwest 0.12.x (which uses hyper 1.x)

  • The SMSC returns valid HTTP status lines and body but includes malformed header lines like -ERROR : ERROR.

Any insight into the cleanest or most maintainable way to make reqwest tolerant to such legacy responses would be appreciated.

What about reqwest::ClientBuilder::http1_ignore_invalid_headers_in_responses?

1 Like

Thanks for pointing that out, I have to admit I’m a bit embarrassed I missed http1_ignore_invalid_headers_in_responses on the ClientBuilder. It works perfectly for the invalid header issue.

However, I have a related problem I haven’t been able to solve yet:
the server I’m integrating with also seems to depend on header casing (e.g., it expects SenderID instead of senderid).

I noticed that Hyper has a setting called:

http1_preserve_header_case(true)

but it doesn’t seem to be exposed by Reqwest.

Do you happen to know if there’s any way to access this through ClientBuilder, or would I need to patch Reqwest locally to enable it?

There is http1_title_case_headers, but it wouldn't correctly format senderid as SenderID. Indeed, it looks like reqwest doesn't expose the preserve_header_case option from hyper's HTTP/1 client. Exposing it should be straight forward though, similar to how http1_title_case_headers was implemented.[1]


  1. The PR I linked was created when reqwest was still using hyper v0.12. In hyper v1 the hyper::Client struct was moved to hyper_util::client::legacy::Client, but its Builder exposes a similar API to the one from v0.12, including http1_preserve_header_case, which you'd need to call. ↩︎

Thanks for confirming that, really appreciate your help!
I’ll patch Reqwest locally and expose the http1_preserve_header_case flag as you suggested. It sounds like the right approach, similar to the earlier title case patch that was merged.

1 Like

I'm sure Sean and the community would appreciate a PR with your patch, once it's ready. :slightly_smiling_face:

2 Likes

Thanks for the suggestion, I went ahead and explored the possibility of exposing and enabling http1_preserve_header_casewithin Reqwest.

However, after digging through the code, I realized the challenge runs deeper than simply exposing Hyper’s builder option. Reqwest uses the http crate’s HeaderMap, which internally stores HeaderName in lowercase form by design. The canonicalization happens at insertion time, so by the time headers reach Hyper, all original casing information has already been lost.

Even if I enable http1_preserve_header_case(true) in the underlying Hyper builder, there’s no effect because Hyper never sees the original header names. Preserving casing would require a more invasive change, either wrapping HeaderName with a custom structure that retains the original case or bypassing HeaderMap entirely and injecting headers directly into the hyper::Request.

That means patching Reqwest would involve significant rework, not just exposing a single flag. It’s a fair amount of effort and risk of breakage for a fairly non-standard need (dealing with a non-compliant server that depends on header casing).

it’s more involved than exposing a single flag, the lowercasing originates from the http crate itself.