Skip to content
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

An HTTP/2 204 (No Content) response containing a Content-Length header is considered invalid #140

Open
iqltd opened this issue Aug 27, 2024 · 5 comments

Comments

@iqltd
Copy link

iqltd commented Aug 27, 2024

As the title suggests, I think the library is too strict in regards to 204 (No Content) responses that contain a Content-Length response header, in the context of HTTP/2.

While the HTTP/1.1 spec explicitly forbid this case (RFC 7230 section 3.3.2, as well as RFC 9110 section 8.6), my understanding is that the HTTP/2 spec does not. According to RFC 9113, chapter 8.1.1, a response without content may have a content-length header field:

A request or response is also malformed if the value of a content-length header field does not equal the sum of the DATA frame payload lengths that form the content, unless the message is defined as having no content. For example, 204 or 304 responses contain no content, as does the response to a HEAD request. A response that is defined to have no content, as described in Section 6.4.1 of [HTTP], MAY have a non-zero content-length header field, even though no content is included in DATA frames.

Versions used

gun 2.1.0
cowlib 2.13.0
erlang/otp 26

Steps to reproduce

Using an HTTP/2 server that responds with a 204 (No Content) response with or without a Content-Length response header (in my case https://github.com/iqltd/go-h2-yourself):

  • when the 204 (No Content) response doesn't contain a Content-Length response header, the response is handled correctly
  • when the 204 (No Content) response contains a Content-Length response header, we get a stream_error
Eshell V14.2.4 (press Ctrl+G to abort, type help(). for help)
1> application:ensure_all_started([gun]).
{ok,[cowlib,gun]}
2> ConnectionOpts = #{ transport => tls, tls_opts => [{verify, verify_none}] }.
#{transport => tls,tls_opts => [{verify,verify_none}]}
3> {ok, Pid} = gun:open("0.0.0.0", 8000, ConnectionOpts).
{ok,<0.642.0>}
4> gun:await_up(Pid, 1000).
{ok,http2}
5> Ref = gun:get(Pid, "/without-content-length", []).
#Ref<0.1964309148.1156317188.132308>
6> gun:await(Pid, Ref).
{response,fin,204,
          [{<<"content-type">>,<<"application/json">>},
           {<<"date">>,<<"Tue, 27 Aug 2024 10:01:38 GMT">>}]}
7> Ref2 = gun:get(Pid, "/with-content-length", []).
#Ref<0.1964309148.1156317188.132351>
8> gun:await(Pid, Ref2).
{error,{stream_error,{stream_error,protocol_error,
                                   'Content-length header received in a 204 response. (RFC7230 3.3.2)'}}}
@iqltd
Copy link
Author

iqltd commented Aug 27, 2024

If this issue is acknowledged, I would like to attempt to submit a fix for it.

@essen
Copy link
Member

essen commented Aug 27, 2024

That looks like an unfortunate phrasing in the spec. The section is about framing, and as far as framing is concerned, having a content-length is fine even if there is no content because in HTTP/2+ the framing doesn't rely on the content-length. But that doesn't invalidate that sending a 204 response with a content-length header is wrong at the semantic level, which is where Gun rejects the response.

Is this an issue with real world applications?

@iqltd
Copy link
Author

iqltd commented Aug 27, 2024

Thank you for the quick response!

I encountered this issue while working with an HTTP/2 API developed by another company. We have a lot of cases where the server needs to respond with 204 (No Content) responses, and their responses contain Content-Length: 0 header field.

Based on your response, I understand that this is a problem with their web framework (I think they are using Javalin), so it should be fixed on the server side, right?

@essen
Copy link
Member

essen commented Aug 27, 2024

If it can be fixed on the server side it should be, regardless.

It doesn't help that even MDN has a 204 response example that has a content-length header... https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204

If you can demonstrate that the problem exists in the wild in more than this one app then we can make an exception in Gun, behind an option, with a detailed comment explaining why. Otherwise a PR is still welcome but I'll let it sit for a while to gather feedback.

@iqltd
Copy link
Author

iqltd commented Aug 27, 2024

Understood. Thank you so much!
I will try to search for any real-life instances in popular web libraries/frameworks/apps, and get back here if I find any.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants