springtomcatheaderwebclientcontent-length

How to prevent Spring Webclient changing 'Content-Length' header to lowercase 'content-length'?


I am sending a request to a third-party API, and their server (PHP 5.5) requires that the content-length header be uppercased 'Content-Length'.

However, I'm using Spring webclient, and it is sending out the 'Content-Length' in lowercased 'content-length', however I tried to send it out as uppercased.

webClient
                    .method(method)
                    .uri(uri)
                    .headers(headersConsumer)
                    .header("Content-Length-Test", Integer.toString(objectMapper.writeValueAsBytes(reqBody).length))
                    .header("Content-Length", Integer.toString(objectMapper.writeValueAsBytes(reqBody).length))
                    .bodyValue(Optional.ofNullable(reqBody).orElse(""))
                    .retrieve()
                    .bodyToMono(returnType)

result

POST /test/test HTTP/1.1
user-agent: ReactorNetty/1.0.12
Content-Type: application/json
Authorization: Basic 
Accept: */*
Host: 127.0.0.1:9999
Content-Length-Test: 202
content-length: 202

You can see that "Content-Length-Test" that I put in prints out as expected, but not for the actual "Content-Length" header.

I've read the following and related posts about how the HTTP standard specifies that all headers are case insensitive.

The problem is the API I need for my service is case-sensitive when it comes to processing headers. How to prevent of converting header name into lower case - Spring boot?

How can I prevent the Content-Length header from being lower cased?

Need help.

Thanks.


Solution

  • WebClient by default is using Reactor Netty underneath, and that inserts the Content-Length header itself; you don't need to provide it at the WebClient level. As can be seen this is indeed in lower-case.

    To fix this, you should configure a Netty HttpClient yourself and then pass it to the WebClient (via ReactorClientHttpConnector).

    The HttpClient setup should include (note that it uses a fluent interface so you need to take the HttpClient that is returned from doOnConnected):

    httpClient.doOnConnected(connection -> connection.addHandlerFirst(new MessageToMessageEncoder<HttpRequest>() {
    
                @Override
                protected void encode(ChannelHandlerContext ctx, HttpRequest msg, List<Object> out) {
    
                    HttpHeaders requestHeaders = msg.headers();
                    String value = requestHeaders.get("content-length");
                    if (value != null) {
                        requestHeaders.remove("content-length")
                                      .set("Content-Length", value);
                    } 
    
                    if (msg instanceof ReferenceCounted)
                        ((ReferenceCounted)msg).retain();
    
                    out.add(msg);
                }
    ))