-
Notifications
You must be signed in to change notification settings - Fork 38.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BufferingClientHttpRequestFactory should not set content length for empty request body #32650
Comments
Hmm....
So I'm not sure if it's right to change the preferences like this. Would it be too complicated to eventually give you an interceptor to tamper with a request, or a way to customize a policy? |
Hi @onjik , thank you for your prompt answer. I do understand your point of view that I have to say that I'm far from an expert on the matter, but I don't get why Spring is enforcing that default when clearly no Last - yes I can absolutely add an interceptor as a workaround. In fact this is what I'm actually targeting as a temporary fix: @Bean
public RestClientCustomizer restClientCustomizer() {
return restClientBuilder -> restClientBuilder.requestInterceptor((request, body, execution) -> {
if (body.length == 0 && request.getHeaders().getContentType() == null) {
request.getHeaders().remove(CONTENT_LENGTH);
}
return execution.execute(request, body);
});
} That said, it feels wrong to me to do this. I was hoping that the fix I was proposing (or following similar idea) could be done at the framework level (assuming we reach a conclusion that it is ok and makes sense). Let me know your thoughts. Thank you 🙂 |
Thank you for your kind and detailed response! @nhmarujo I think you're right. There's no provision that you must attach a content-length header when sending a POST request. I don't think it's the right direction to enforce it at the framework level. |
Hi @onjik . Thank you! If we followed something like my suggestion I think it would be the best option, because:
I'm not sure if a variation of this would make more sense in regards of use case 3. to actually check if if (headers.getContentType() != null && headers.getContentLength() < 0) {
headers.setContentLength(bytes.length);
} What you think? |
As you mentioned in the first comment, I think following code is already checking if there is explicit /**
* Return the length of the body in bytes, as specified by the
* {@code Content-Length} header.
* <p>Returns -1 when the content-length is unknown.
*/
public long getContentLength() {
String value = getFirst(CONTENT_LENGTH);
return (value != null ? Long.parseLong(value) : -1);
} If there is no explicit if (headers.getContentLength() < 0) {
headers.setContentLength(bytes.length);
} How about this code if (headers.getContentLength() == -1 && bytes.length > 0) {
headers.setContentLength(bytes.length);
} It's similar to the code you provided for the first time, but if threre is explicit Not sure if What you think? |
Hi @onjik. Yes, it is basically the same as my first proposal. The reason why I suggested I think both proposals I made are valid, but the latter one might be more accurate (not sure), as the assumption is that if a Any concerns about this last proposal or any reason why you would prefer the first one? Thanks |
@nhmarujo I've been thinking about counter examples a lot, and I think this is the case.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding If you use such a chunked way, Content-Length is not used even if there is a Content-Type. So I think it's bad to guess based on the content-type header. Let me know your thoughts. Thanks!! |
Hi @onjik . Thank you, I think that is a very good point! I think the possible options are:
With this in mind, right now I don't even know what the right approach is anymore. I mean, it does seem that what the framework is doing right now is not the best approach, but I also think what we discussed so far doesn't cover all cases. I was thinking about something like this: f (headers.getContentType() != null && headers.getTransferEncoding() == null && headers.getContentLength() < 0) {
headers.setContentLength(bytes.length);
} But honestly I'm not sure if the Any thoughts? 😅 |
The "Content-Length" header is not set by Spring, but actually by the JDK HTTP client itself. Executing the following code RestClient restClient = RestClient.create();
ResponseEntity<String> response = restClient.post().uri("http://localhost:8080/test").retrieve().toEntity(String.class); Is equivalent to selecting the RestClient restClient = RestClient.builder().requestFactory(new JdkClientHttpRequestFactory()).build(); Using another client, like the httpcomponents one, does not send a RestClient restClient = RestClient.builder().requestFactory(new HttpComponentsClientHttpRequestFactory()).build(); I guess here the difference is that the Thanks! |
Hi @bclozel . Thank you for your reply. I do understand that different client implementations underneath makes a difference and in fact on our case we are using But on the other hand I think you are missing something - if you check in this line (just like I shared on the issue description), that is actually being set there by the framework and I did actually debug and validated it was being added there. Furthermore I can remove it with an interceptor later, which I think I wouldn't be able to if it was the underlying http client (whichever it was) doing it. Please let me know your thougths 🙂 Thanks |
You're welcome @bclozel and thank you for reopening the issue. Do you have any view on the matter? 🙂 P.S.: Thank you for also editing the title to something much clearer! |
@nhmarujo it's fixed now in 6.1.7-SNAPSHOTs and it will be released with next month's maintenance release. Thanks for your report! |
Thank you so much @bclozel ! |
This broke our whole system and we will not be able to upgrade spring from now on. Because our central NoSql database system expects a content-length set, even when there is no body present. Thats a critical issue for us right now. |
After discussing this more in-depth, the team decided to reconsider this issue and maybe revert this change. @nhmarujo Can you elaborate on your use case? Is the server a well-known web server? Is this a custom implementation that is setting this header? @Mario-Eis Thanks for your feedback. This means you have tested this change in production or in a testing environment? Can you share the name of the database product and the error that's raised? |
Hi @bclozel. Hard for me to say exactly what the provider is using, but looking at our access logs I don't see the header Hard to say much more. And yes, this error was not being raised when |
While the discussion around "no message body / empty message body" is relevant to the RFC, it seems that most clients send this "Content-Length: 0" request header for POST requests and that we're not in a position to consistently make the difference between those cases at the Framework level. On top of that, the use case reported here is contradictory: the server complains about a missing "Content-Length" header when it's actually present. After discussing with the team, we're going to revert this change. |
Reverted in 64b0283. |
We were recently moving from
WebClient
toRestClient
and one of the 3rd parties we deal with started returning us:HTTP Error 411. The request must be chunked or have a content length
The given request is a
POST
without any body and looking at the outgoing requests the only difference between before and after is that now the request we generate has aContent-Length: 0
while before that header was absent.I do concede that I'm not sure that the 3rd party is handling this properly, but I also have to say that I don't see why the framework would be setting
Content-Length: 0
when there is no body.I was looking deeper into the code and I can see that the decision to add the
Content-Length
header is being taken here and that happens becauseheaders.getContentLength()
returns-1
. Looking further at the logic onHttpHeaders
I can see here that when that header is not set it just returns-1
, which is happening in my case.In my view I don't think it makes sense to send this header in this case, so I was wondering if that could be fixed, perhaps by just doing instead:
This should still ensure that the header is added if it is missing and there is a body, but will not add it if there is no body. It will also not override if explicitly set.
The text was updated successfully, but these errors were encountered: