-
Notifications
You must be signed in to change notification settings - Fork 86
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
Allow responses to omit a body and Content-Length header #288
Conversation
Hm, I don't really like the idea of fixing up responses in the C++ code -- in the long run, this could lead to complicated logic and maintenance difficulties, and users might be confused about why the response they create isn't the one that's being sent. I think it makes more sense for the application author to send a correct response, but it looks like this may require some modifications, namely, allowing a response to not to have a One other thing: I see some places in the existing code where Also, it will help if you can include an example app and some tests. |
I think that the need to fix these specific responses basically follows from the decision to automatically add Nor do I think it will make sense to trigger this depending on whether a body has been added. The current code already accepts
The classic use for a HTTP 204 response is for I'm also happy to add tests, that was an oversight for sure.
Yeah, I noticed that outdated check and piggybacked on it to simplify the implementation -- uninitialized |
Also, if you want examples of how other frameworks/languages handle this -- from my cursory look, they do -- I can find a few. |
Unless I'm mistaken, the current code does not allow Here's an example app with library(httpuv)
handle_req <- function(req) {
list(
status = 200,
headers = list(
'Content-Type' = 'text/plain'
),
body = NULL
)
}
s <- startServer("0.0.0.0", 5000,
list(
call = function(req) {
# Call out to external function so we can modify it dynamically for
# experimentation.
handle_req(req)
}
)
) Here's what happens when you try to access it:
Same for So I'm thinking that to indicate that there's no body, the returned object could have
A |
5cbf760
to
180263b
Compare
I must be going crazy, I was also sure that
Oh, that makes sense.
My objection to this would be that it passes the buck to downstream frameworks to handle this correctly, and I'm not sure that's the right thing to do. Currently Plumber, ps. I added a small unit test to the commit. |
180263b
to
37f1270
Compare
However, I am willing to implement the |
If someone says to give this response: list(
status = 304,
headers = list(
'Content-Type' = 'text/plain'
),
body = "this is some body content"
) I think that it's reasonable for httpuv to send the whole thing (with the body) instead of "fixing" the response by removing the body. If they want to create a response that conforms to the RFC, they could do this: list(
status = 304,
body = NULL
) Or even: list(
status = 304,
) (BTW, httpuv had some issues with empty headers -- they had to be an empty named list -- but I fixed that in #289.) I don't really understand the issue with plumber, breakr, fiery, and others: they could be modified to provide a The logic in httpuv is pretty straightforward with what I'm proposing. In general, it will send what you tell it to send, and add a |
Hmm, hit a snag here. I just tried implementing this (an optional body signals no Given the following server:
That is, it'll just hang, waiting for additional data to know that the body is empty. Curl-based R clients seem to do the same. So we can add support for omitting the body and header by passing |
I think makes sense to be able to send a message without a body, for any response code. For example, if it's a HEAD request, the spec says the response shouldn't have a body. From Section 4.3 of https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html:
Also relevant:
Here's how I understand the spec:
The tricky thing here is that by the time httpuv gets the the response, it doesn't know if the request was a HEAD or GET. But for a HEAD request, it is valid to send a 200 response with no body (and no I think it makes sense to do the following:
It will also need some documentation so that users will know about this behavior. One other quick thing: I realized that |
I'm not sure I understand this -- do you mean that a user can literally pass a Also, I'm a fan of docs for this, but where would you like it to be documented? I'll add a test for |
Currently, you can set a
What I was proposing is that this would suppress the header, even if a body was actually included:
But now that you mention it, maybe that's not necessary... I can't think any cases when it would be helpful. I think that it should be sufficient to have As for docs, how about putting it in the help for
|
RFC 7231 (part of the HTTP 1.1 specification) states[0]: > A 204 response is terminated by the first empty line after the header > fields because it cannot contain a message body. while RFC 7232 says[1]: > A 304 response cannot contain a message-body; it is always terminated > by the first empty line after the header fields. In addition, RFC 7230 says the following about the Content-Length header[2]: > A server MAY send a Content-Length header field in a 304 (Not > Modified) response to a conditional GET request (Section 4.1 of > [RFC7232]); a server MUST NOT send Content-Length in such a response > unless its field-value equals the decimal number of octets that would > have been sent in the payload body of a 200 (OK) response to the same > request. > > A server MUST NOT send a Content-Length header field in any response > with a status code of 1xx (Informational) or 204 (No Content). That is, we need to provide a way to suppress the inclusion of a body and the generated Content-Length header so that downstream users can emit correct HTTP 204 and 304 responses. This commit makes that possible by allowing the Rook response to have a missing (or NULL) body, in which case the usual body handling will be skipped. It's still possible for users to add a Content-Length header directly, which they might want to do, e.g. to fulfill the HTTP 304 behaviour quoted above. As per Winston Chang's request, we don't explicitly handle these status codes -- users must implement this behaviour themselves. Unit tests and updated documentation on the reponse object are included. [0]: https://tools.ietf.org/html/rfc7231#section-6.3.5 [1]: https://tools.ietf.org/html/rfc7232#section-4.1 [2]: https://tools.ietf.org/html/rfc7230#section-3.3.2
37f1270
to
874ab6d
Compare
Done ready for review. I took the liberty of fixing a small typo in the existing docs as I was updating them, as well as mentioning the new behaviour for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
The current behaviour of httpuv is to automatically set a
Content-Length
header computed from the length of the body. This results in incorrect HTTP 204, 304, and 1xx responses.RFC 7230 (part of the HTTP 1.1 specification) says the following about the Content-Length header:
Moreover, HTTP 204 and 304 responses should actually send no body at all. RFC 7231 states:
and RFC 7232 says:
This PR proposes changes to the handling of HTTP 204 and 304 responses to bring them in line with the spec. I haven't touched HTTP 1xx responses because they're mostly irrelevant and some old Websocket implementations actually do, weirdly, use aContent-Length
.The implementation is actually really simple: we now ignores any body if a Rook response includes 204 or 304 as thestatus, leaving the
DataSource
pointer asNULL
. This ensures (1) that we don't send a body, and as a side effect (2) that we don't set aContent-Length
header. I added a comment to the Content-Length header code explaining this edge case, as well, even though there are no code changes there.Edit: As per discussion, this behaviour is now optional, triggered by excluding (or setting as
NULL
) thebody
in a Rook response.It's still possible for users to add a
Content-Length
header directly, which they might want to do, e.g. to fulfill the HTTP 304 behaviour quoted above.