-
-
Notifications
You must be signed in to change notification settings - Fork 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
Add support for custom handling of requests that throw in aiohttp.http_exceptions.*
#3287
Comments
GitMate.io thinks possibly related issues are #2632 (aiohttp.http_exceptions.BadStatusLine: invalid HTTP method), #1619 (aiohttp.Client can't parse Trailer headers and throws exception), #1792 (aiohttp.http_exceptions.BadStatusLine caused by extra CRLF after POST data), #58 (aiohttp.HttpClient), and #1390 (Build chat server with aiohttp). |
The best we can do here is dropping connection without any output (but logging the fact). |
Would you make a pull request? |
No, server should reply with Yes, different servers act differently: ➜ telnet google.com 80
Trying 2a00:1450:4014:801::200e...
telnet: connect to address 2a00:1450:4014:801::200e: No route to host
Trying 172.217.23.206...
Connected to google.com.
Escape character is '^]'.
/
HTTP/1.0 400 Bad Request
Content-Type: text/html; charset=UTF-8
Referrer-Policy: no-referrer
Content-Length: 1555
Date: Thu, 27 Sep 2018 08:27:56 GMT
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 400 (Bad Request)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
</style>
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>400.</b> <ins>That’s an error.</ins>
<p>Your client has issued a malformed or illegal request. <ins>That’s all we know.</ins>
Connection closed by foreign host.
However, let's clarify terms. The first line is called Now, if you look at RFC 7230, section 3.1.2, it clearly recommended to send a reply, not drop the connection immediately:
So if we want to follow RFCs, we have to do this and not reset a TCP connection. For some users, though, who need this connection resetting may be provided via feature flag or recommendations of how to do this manually. |
@asvetlov the topic starter wants to define some HTTP handlers probably for consistency with valid requests (I'd not want to expose things to attacker who tries to find out which web server I use with just sending a malicious request). |
Got you |
Oh, and just as an idea: in CherryPy we have a setting/hook for error codes, which specifies what to send in payload. It can be either a string or a callable. Maybe you could think of some similar concept within aiohttp architecture or add a recommendation to docs on how to process such things. |
In aiohttp, we use middlewares for error handling. |
So the problem is that currently you can't catch this with middleware, right? |
The thread starts from asking for handling the error by |
Hi. Sorry for late reply and thanks for the discussion. For my application, this bug has the effect of allowing a client to bypass any attempts to serve custom headers passed in a I'd prefer AioHttp to be RFC compliant, and not being able to handle any exception that's raised, seems weird. Perhaps catching it in system code/low-level code, and raise More seasoned coders than me should fix this. I would't know the wider effects of any change I'd make. A bit confused - is this the same exception as in Standard lib's Thanks for all the work, and a very nice product! |
No, |
Update - This behaviour disappeared when adding a ssl_context to the web.TCPSite(). |
Heh, encrypted SSL channel doesn't look like a regular plain HTTP status line, isn't it? |
Wow, that looks oddly unrelated.. |
Can aiohttp include any more details of the request in the exception? I'm encountering this issue in my tests after upgrading from aiohttp 3.3.2 to 3.5.4, and I have no idea what's causing it. |
That's certainly my problem, signals are no help either. [Problem certainly is there with SSL as well (unsurprisingly), so long as you actually establish the SSL connection (i.e. openssl s_client -connect :)] |
There is no well-formed request exists, no middleware is affected. The only thing we can do is the conversion of exception with the attached stacktrace into a logged warning. |
You're correct that there's no well-formed request, and I don't think calling middlewares makes any sense either, however something catches the error and responds to the client with a well-formed 400 response.
What do you think is the right thing to do though? Removing the stacktrace and adding the peer's IP address to the error message (logged to aiohttp.server) would be nice (and trivial, I imagine), but that doesn't help with what the OP wanted (and what brought me here): customizing headers. For this, it's tempting to reuse signals but calling them with either "None" or a minimalist request seems likely to cause problems. |
Any advice? |
@asvetlov can you add more details what exactly should happen in this case? |
you can try this to block the log: |
@rodusluo where did you find this? If anyone reading this was able to provide explanation on how to validate a HTTP request before it hits AIOHTTP that would probably go someway to solving the issue here. |
Put a reverse proxy in front? |
I think changing this behaviour so the BadRequest goes through middlewares seems reasonable and doable. Looking through the code, we'd need to change: aiohttp/aiohttp/web_protocol.py Lines 537 to 542 in 4d604ea
So that we always use self._request_handler .Then we'd need to adapt Application._handle() so that it can receive an error object or similar. The main handler, before being wrapped by middlewares, would then be a system one that throws the BadRequest, similar to the NotFound ones we'd get from the router. |
I see that the issue is closed, but I also want to catch exception and answer with a custom HTTP response (while still keeping my custom headers). How would I do that ? |
Let's leave this open since we haven't implemented being able to do that yet. I accidentally marked this one as it was similar to a group of others since there are multiple requests in this issue. I renamed the issue to better clarify what the OP wants |
aiohttp.http_exceptions.*
aiohttp.http_exceptions.*
aiohttp.http_exceptions.*
Ideally we have a way to customize the logging level as well #10076 (comment) |
Note to self (or anyone who chooses to implement this): Remove the conditional debug logging from #10055 and add an example of using middlewares to customise the logging behaviour. (To change the logging level as @bdraco mentions can probably be done by logging in the middleware and not raising the exception?) |
Long story short
Not sure if this is a user error, or a bug.
On a bad request, the server raise
aiohttp.http_exceptions.BadStatusLine: invalid HTTP method
, and custom headers are ignored. I can't find any way to catch this exception.My application works as it should, and custom headers are sent on both GET and POST, and also HEAD - using the on_prepare signal.
I want to use custom headers and hide the
Server: Python/3.6 aiohttp/3.4.4
. But a simple request containing only "/" bypass all handlers, and display the default headers. And also raise a seemingly non-handable exception.Expected behaviour
I'd expect being able to handle all requests, no matter how erranous.
Actual behaviour
In console, AioHttp drops
and in logs:
Steps to reproduce
Your environment
Windows
Python/3.6 aiohttp/3.4.4
aiohttp server
The text was updated successfully, but these errors were encountered: