reactor / reactor-netty

TCP/HTTP/UDP/QUIC client/server with Reactor over Netty

Home Page:https://projectreactor.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Netty Http Client adds zero Content-Length header for GET and HEAD

yurybubnov opened this issue · comments

Netty Http Client adds content-length: 0 header for GET and HEAD with empty body.
This is contradicts RFC9110

A user agent SHOULD NOT send a Content-Length header field when the request message does not contain content and the method semantics do not anticipate such data.

This is also causing errors in AWS ALB if Desync mitigation mode is set to strictest. See GetHeadZeroContentLength in Classification reasons

Expected Behavior

GET and HEAD requests with no body should not have Content-Length header.

Actual Behavior

Requests have header set with 0 value

content-length: 0

Steps to Reproduce

Make any GET call with the Client

Possible Solution

The actual call is happening here. The header is added here.
Possible solution could be either to remove Content-Length header if no body or add it only if there is body or HTTP method no GET or HEAD.
Like:

if(body.readableBytes() > 0 || (!Objects.equals(method(), HttpMethod.GET) && !Objects.equals(method(), HttpMethod.HEAD))) {
    requestHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, body.readableBytes());
}

@yurybubnov

A user agent SHOULD NOT

This is not a strict requirement.

@violetagg you're correct, this is not a strict requirement but on the other side Netty Client is not fully compliant with HTTP RFC. AWS ALB considers this behavior suspicious and might block requests if configured in defensive mode.

Netty Client is not fully compliant with HTTP RFC

Well that's not correct - you are compliant if you implement all requirements. All the rest is recommendations.

According to RFC 2119

SHOULD NOT   This phrase, or the phrase "NOT RECOMMENDED" mean that
   there may exist valid reasons in particular circumstances when the
   particular behavior is acceptable or even useful, but the full
   implications should be understood and the case carefully weighed
   before implementing any behavior described with this label.

I cannot think of any "valid reason" to send content-length: 0 with GET or HEAD but this is causing problems is you're building service on AWS.

@yurybubnov You can always extend Reactor Netty HTTPClient and manipulate the headers as you like before sending them to the network.

I'm not using it directly. I'm using Spring Cloud Gateway which uses the Netty Client, so I have no control of HTTPClient creation.

Also, as I understood from the code, the Content-Length header is added right before the request is sent over the wire so it's not possible to remove it.

You have control. Spring Cloud Gateway gives you a way to customise the HTTPClient. I think it was something like this

@Bean
public HttpClientCustomizer httpClientCustomizer() {
    return httpClient -> ...;
}

Also, as I understood from the code, the Content-Length header is added right before the request is sent over the wire so it's not possible to remove it.

HttpClient client = HttpClient.create()
		.doOnChannelInit((obs, ch, add) ->
				ch.pipeline().addAfter(NettyPipeline.HttpCodec, "test",
						new ChannelOutboundHandlerAdapter() {
							@Override
							public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
								if (msg instanceof HttpRequest) {
									// manipulate headers
									((HttpRequest) msg).headers().remove(HttpHeaderNames.CONTENT_LENGTH);
								}
								ctx.write(msg, promise);
							}
						}));

@violetagg thank you for the snippet. For some reason, when I use the Http client customizer, metrics have disappeared.

@yurybubnov Please contact Spring Cloud Gateway maintainers.

@violetagg spring metrics are fine, it's the Netty client metrics that have disappeared.

@violetagg spring metrics are fine, it's the Netty client metrics that have disappeared.

We would like to handle one problem per GitHub issue. Please report this new problem in a new issue and provide reproducible example.
Thanks