mkopylec / charon-spring-boot-starter

Reverse proxy implementation in form of a Spring Boot starter.
Apache License 2.0
240 stars 54 forks source link

Should not add Content-Type and Content-Length headers to requests with empty body #126

Closed lukasl66 closed 3 months ago

lukasl66 commented 3 years ago

Hi, first of all thanks for this great library! I'm glad for this great alternative after Zuul is no longer supported by Spring.

One minor issue we noticed: When sending an HTTP requests without body (for example a simple HTTP GET request) Charon adds two headers: Content-Type: application/octet-stream and ContentLength: 0. I guess this behaviour is not intended as these headers wouldn't make any sense if there is not any content available. Moreover in our special case it causes a third-party API to reject such a request, because content type application/octet-stream is not supported by this application.

This is what I found out:

  1. ReverseProxyFilter receives Request with emtpy body
  2. ReverseProxyFilter calls HttpRequestMapper.map to create a RequestEntity. The body parameter of this RequestEntity is initialized with an empty byte array.
  3. In ReverseProxyFilter the request is passed to the exchange method of the RestTemplate
  4. Spring's ByteArrayHttpMessageConverter is called which adds the Content-Type and Content-Lenght headers!

Suggestion:

If in step 2 the HttpRequestMapper would not initialize the body with an empty array but with null then RestTemplate would handle the request appropriately. In this case ByteArrayHttpMessageConverter won't be called and the Content-Type and Content-Length headers won't be added.

sabob commented 2 years ago

Hi, just ran into this issue myself. The server rejects the GET request because of the Content-Length=0 header. Any workaround for now?

lukasl66 commented 2 years ago

Our workaround was to implement a RequestForwardingInterceptor and remove the headers when the body is empty:

override fun forward(request: HttpRequest, execution: HttpRequestExecution): HttpResponse {
        if (request.body.isEmpty()) {
            request.headers.remove("Content-Type")
            request.headers.remove("Content-Length")
        }

        return execution.execute(request)
    }
sabob commented 2 years ago

Thanks for the snippet. Maybe it's my version of Spring/Boot but after I remove the headers Spring sets the Content-Length header back to 0 via the class org.springframework.http.client.AbstractBufferingClientHttpRequest. I'll see if I can find an alernative solution.

sabob commented 2 years ago

I tried to debug a bit, but the setting of content-length occurs up the stack, outside of Charon' scope. I tried write an immutable HttpHeader implementation but Spring makes a copy of the headers upstream. Are you sure the content-length isn['t set again? I used Fiddler to monitor the network traffic from my machine to the server and the content-length header is still there. Content-type has been removed though. Holding thumbs Charon' dev can find a solution.

zymen commented 1 year ago

My analysis: package: okhttp.internal.http class: BridgeInterceptor method: intercept

In my case the problem is that the Host header is added without my permission. It seems that with current version of okhttp we're stuck. RealCall is final and there is no simple option to remove BridgeInterceptor from the list of auto added interceptors. Maybe upgrade to higher versions of okhttp could help us solve that issue.

mkopylec commented 3 months ago

The the Content-Type and Content-Length headers aren't added for empty body any more starting from version 5.2.0.