runtime: SocketsHttpHandler does not preserve request header format

Description

https://github.com/microsoft/reverse-proxy/pull/1498/files#diff-4ffa10100339146ae9048f52f94656c0aff1b984dabdbb5200d87284069b1379R474

The following HTTP headers are in theory equivalent, but not always in practice:

Multi-Line:

HeaderName: value1
HeaderName: value2
HeaderName: value3

Comma Separated:

HeaderName: value1, value2, value3

Mixed:

HeaderName: value1
HeaderName: value2, value3

Some clients, servers, and/or application logic requires the headers to come through in a specific format, and this is often encountered by a 3rd party that needs to accommodate them by sending that format.

Now add YARP into the mix. YARP should be as transparent as possible about headers and formats so that it doesn’t interfere with how the clients/servers/apps are trying to communicate. The linked PR verifies that YARP is able to preserve the header format for responses, but not for requests. All multi-line requests headers are consolidated to a single comma separated header.

We have specific examples where this has been a problem in responses, such as Set-Cookie and Www-Authenticate. We don’t have specific examples for requests yet. Kestrel is able to preserve the requested header format in both directions, the limitation here is in HttpClient.

If a customer were to encounter this is there any workaround possible?

What kinds of changes would it take to make this possible in SocketsHttpHandler/HttpHeaders?

Reproduction Steps

Unit tests available in https://github.com/microsoft/reverse-proxy/pull/1498/files#diff-4ffa10100339146ae9048f52f94656c0aff1b984dabdbb5200d87284069b1379R474

Expected behavior

The header format can be preserved.

Actual behavior

Multi-line headers are consolidated to a single comma separated header.

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 2
  • Comments: 17 (14 by maintainers)

Most upvoted comments

an you please clarify if it broke your gRPC usage, or if you found a workaorund. If there is a workaround, or not high impact on customers, we would like

It did broke our protocol, but luckily since we own both client and server, we modified the server to accept comma separated metadata.

Yes, that’s what I meant.

keeping TryAdd(IEnumerable values) on a single line.

I’m not sure we want to do this anyway. Seems like this:

TryAddWithoutValidation("Foo", new[] { "c", "d" });

Should be equivalent to this:

TryAddWithoutValidation("Foo", "c");
TryAddWithoutValidation("Foo", "d");