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

`Bad_request if request is more than 4152 bytes on OSX #18

Open
sgrove opened this issue Dec 5, 2017 · 6 comments
Open

`Bad_request if request is more than 4152 bytes on OSX #18

sgrove opened this issue Dec 5, 2017 · 6 comments

Comments

@sgrove
Copy link
Contributor

sgrove commented Dec 5, 2017

Example curl to trigger the issue (assuming you have an httpaf server running):

curl 'http://localhost:8000/example?application_id=0b33e830-7cde-4b90-ad7e-2a39c57c0e11' -X OPTIONS -H 'Access-Control-Request-Method: POST' -H 'Origin: http://localhost:3001' -H 'Referer: http://localhost:3001/?query=%7B%0A%20%20awsDiscovery%20%7B%0A%20%20%20%20endpoints%20%7B%0A%20%20%20%20%20%20partitions%20%7B%0A%20%20%20%20%20%20%20%20services%20%7B%0A%20%20%20%20%20%20%20%20%20%20endpoints%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20credentialScope%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20service%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20region%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20protocols%0A%20%20%20%20%20%20%20%20%20%20%20%20hostname%0A%20%20%20%20%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20defaults%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20credentialScope%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20service%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20region%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20protocols%0A%20%20%20%20%20%20%20%20%20%20%20%20hostname%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20partitionEndpoint%0A%20%20%20%20%20%20%20%20%20%20isRegionalized%0A%20%20%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20defaults%20%7B%0A%20%20%20%20%20%20%20%20%20%20signatureVersions%0A%20%20%20%20%20%20%20%20%20%20protocols%0A%20%20%20%20%20%20%20%20%20%20hostname%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20regions%20%7B%0A%20%20%20%20%20%20%20%20%20%20description%0A%20%20%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20partitionName%0A%20%20%20%20%20%20%20%20regionRegex%0A%20%20%20%20%20%20%20%20partition%0A%20%20%20%20%20%20%20%20dnsSuffix%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20version%0A%20%20%20%20%7D%0A%20%20%20%20apis%20%7B%0A%20%20%20%20%20%20service%20%7B%0A%20%20%20%20%20%20%20%20operations%20%7B%0A%20%20%20%20%20%20%20%20%20%20output%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20resultWrapper%0A%20%20%20%20%20%20%20%20%20%20%20%20shape%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20http%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20requestUri%0A%20%20%20%20%20%20%20%20%20%20%20%20method%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20errors%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20shape%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20documentation%0A%20%20%20%20%20%20%20%20%20%20input%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20shape%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20shapes%20%7B%0A%20%20%20%20%20%20%20%20%20%20members%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20documentation%0A%20%20%20%20%20%20%20%20%20%20%20%20locationName%0A%20%20%20%20%20%20%20%20%20%20%20%20shape%0A%20%20%20%20%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20error%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20httpStatusCode%0A%20%20%20%20%20%20%20%20%20%20%20%20senderFault%0A%20%20%20%20%20%20%20%20%20%20%20%20code%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20member%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20locationName%0A%20%20%20%20%20%20%20%20%20%20%20%20shape%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20documentation%0A%20%20%20%20%20%20%20%20%20%20value%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20shape%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20key%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20shape%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20exception%0A%20%20%20%20%20%20%20%20%20%20sensitive%0A%20%20%20%20%20%20%20%20%20%20awsType%0A%20%20%20%20%20%20%20%20%20%20pattern%0A%20%20%20%20%20%20%20%20%20%20enum%0A%20%20%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20%20%20max%0A%20%20%20%20%20%20%20%20%20%20min%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20metadata%20%7B%0A%20%20%20%20%20%20%20%20%20%20serviceAbbreviation%0A%20%20%20%20%20%20%20%20%20%20signatureVersion%0A%20%20%20%20%20%20%20%20%20%20timestampFormat%0A%20%20%20%20%20%20%20%20%20%20serviceFullName%0A%20%20%20%20%20%20%20%20%20%20endpointPrefix%0A%20%20%20%20%20%20%20%20%20%20xmlNamespace%0A%20%20%20%20%20%20%20%20%20%20jsonVersion%0A%20%20%20%20%20%20%20%20%20%20apiVersion%0A%20%20%20%20%20%20%20%20%20%20protocol%0A%20%20%20%20%20%20%20%20%20%20uid%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20documentation%0A%20%20%20%20%20%20%20%20version%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20paginators%20%7B%0A%20%20%20%20%20%20%20%20outputTokens%0A%20%20%20%20%20%20%20%20outputToken%0A%20%20%20%20%20%20%20%20inputTokens%0A%20%20%20%20%20%20%20%20moreResults%0A%20%20%20%20%20%20%20%20inputToken%0A%20%20%20%20%20%20%20%20resultKeys%0A%20%20%20%20%20%20%20%20resultKey%0A%20%20%20%20%20%20%20%20limitKey%0A%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36' -H 'Access-Control-Request-Headers: auth-token,content-type,show_beta_schema' --compressed

(this came from a real-world case that we have to support, unfortunately)

The above snippet should trigger the ~error_handler in create_connection_handler, with an error that matches | #Status.standard as error => print_endline (Status.default_reason_phrase error) and returns Bad request. I suspect this should be 413 Request_entity_too_large instead.

I searched a bit, but not sure if there's a knob we can tweak somewhere to allow for larger headers.

@sgrove sgrove changed the title `Bad_request if Referrer header is more than `Bad_request if Referrer header is more than 3661 characters Dec 5, 2017
@sgrove
Copy link
Contributor Author

sgrove commented Dec 9, 2017

Any guidance on where I can tweak httpaf to support this?

@sgrove sgrove changed the title `Bad_request if Referrer header is more than 3661 characters `Bad_request if request is more than 4152 bytes on OSX Dec 11, 2017
@sgrove
Copy link
Contributor Author

sgrove commented Dec 11, 2017

Updating the title as I realized while trying to debug this that this happens on any request that's > 4152 bytes (wireshark was handy to keep track of this). This may be specific to OSX, still trying to track down the cause.

@seliopou
Copy link
Member

Try setting read_buffer_size to some value higher than 4096. This may be a parser bug but changing that parameter may alleviate it.

@sgrove
Copy link
Contributor Author

sgrove commented Dec 11, 2017

Yeah, that does the trick. Bumping it fixes the problem until the request gets larger. Is that intended behavior (i.e. we should be carefully selecting for possibly-large bodies), or a bug?

@seliopou
Copy link
Member

seliopou commented Dec 11, 2017

This isn't due to a large request body, but rather because of a large header. Request bodies that can't fit into the read buffer will be delivered to the application in at most read_buffer_size chunks, regardless of the body encoding. In other words if a body's received in a chunked encoding and you get a chunk that's 5k but your read buffer's only 4k, the application will get a 4k and a 1k chunk.

The same is not true for headers. That part of the parser does as little copying and buffering as possible. As a result if it can't fit any one component into the read buffer, that will result in a "parser stall" which results in turn results in the bad request response that you're observing. This is by design, both for efficiency and to minimize the effect of DoSing... which I guess is the same thing.

A few things can be done to ameliorate: Right now there is an implicit requirement is entire request message (besides the body) can fit into the read buffer. That can be relaxed slightly so that the first line of the request has to fit into a read buffer. That'll leave a bit more room for the headers. The requirement could be further relaxed to allow each header-line to fit in the read buffer.

But regardless there's always going to be an upper-bound on the length of the components of request messages (besides the body) and that's for sure by design.

I think the best thing to do is to maybe relax the parser in the first way I suggested, and then document this.

@sgrove
Copy link
Contributor Author

sgrove commented Dec 30, 2017

I agree with the "best thing to do" part, but probably with one addition - make it easy to catch/handle this in a way that we can return a 413 rather than a 500 somehow

anmonteiro referenced this issue in anmonteiro/httpun Dec 15, 2018
This refs #18 in that users can now set the buffer size they want. It
also gets rid of a pending `TODO` in the code.
anmonteiro added a commit to anmonteiro/httpun that referenced this issue Apr 14, 2020
Upstream issue inhabitedtype/httpaf#18 details
a case where the http/af parser is unable to parse a message if the read
buffer size isn't as big as all the bytes that form the message headers.
While it is desirable, and a design goal of the library, to be efficient
in parsing and thus avoid DoS attacks, it is more sensible to allow each
message header to fit within the read buffer size up to its limit. This
diff makes that change, which better accommodates real world use cases
for the default read buffer size.
anmonteiro added a commit to anmonteiro/httpun that referenced this issue Apr 14, 2020
Upstream issue inhabitedtype/httpaf#18 details
a case where the http/af parser is unable to parse a message if the read
buffer size isn't as big as all the bytes that form the message headers.
While it is desirable, and a design goal of the library, to be efficient
in parsing and thus avoid DoS attacks, it is more sensible to allow each
message header to fit within the read buffer size up to its limit. This
diff makes that change, which better accommodates real world use cases
for the default read buffer size.
anmonteiro added a commit to anmonteiro/httpun that referenced this issue May 11, 2020
Upstream issue inhabitedtype/httpaf#18 details
a case where the http/af parser is unable to parse a message if the read
buffer size isn't as big as all the bytes that form the message headers.
While it is desirable, and a design goal of the library, to be efficient
in parsing and thus avoid DoS attacks, it is more sensible to allow each
message header to fit within the read buffer size up to its limit. This
diff makes that change, which better accommodates real world use cases
for the default read buffer size.
anmonteiro added a commit to anmonteiro/httpun that referenced this issue May 11, 2020
Upstream issue inhabitedtype/httpaf#18 details
a case where the http/af parser is unable to parse a message if the read
buffer size isn't as big as all the bytes that form the message headers.
While it is desirable, and a design goal of the library, to be efficient
in parsing and thus avoid DoS attacks, it is more sensible to allow each
message header to fit within the read buffer size up to its limit. This
diff makes that change, which better accommodates real world use cases
for the default read buffer size.
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