-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
StreamResponse write() does not raise an exception if client disconnects #7172
Comments
What exception are you expecting? That code only removes the task cancellation. So, if you're expecting a CancelledError, then yes, that's the expected behaviour, it won't be cancelled anymore. There was some confusion with the change being made in 3.7, but accidentally getting reverted somewhere. In 3.9 the behaviour is configurable. You can see the old and new behaviour (and how to configure them in 3.9) documented at: The behaviour is properly tested now, so mistaken changes shouldn't happen again. |
Further testing shows that the problem only exists when using SSL/TLS. Without Here is a program that has the problem: import ssl
import asyncio
from aiohttp import web
async def handle_stream(request):
print('request')
response = web.StreamResponse()
await response.prepare(request)
try:
while True:
await response.write(b"1")
await asyncio.sleep(0.1)
finally:
print('done')
app = web.Application()
app.add_routes([web.get('/', handle_stream)])
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.load_cert_chain('cert.pem', 'key.pem')
web.run_app(app, ssl_context=ssl_context)
# web.run_app(app) Run it in one terminal. In another terminal, run Create
|
OK, that's definitely a problem. Will try to find some time soon to take a look, thanks for the reproducer. |
Although we generally recommend deploying behind a proxy, so we expect most installs to not be using TLS on their application. |
This report explains why we had users run into this, but I was unable to reproduce it locally. Some of our users use SSL, others don't. Thanks for the ping @Dreamsorcerer |
Don't have time to look properly yet, but I think the issue may be in asyncio. Where the exception should be raised: The http transport is _SelectorSocketTransport and .is_closing() returns It's not obvious to me why the SSL one is not marked as closing. |
@erik-moqvist can you try #7180 ? |
I'll have to come back to this at the end of the week to try out, but I don't see how that matches up with the information I posted above. Have you tested it with the reproducer above? #7172 (comment) When I tested it, transport was not |
Yes
aiohttp/aiohttp/http_writer.py Line 172 in 4635161
aiohttp/aiohttp/http_writer.py Line 162 in 4635161
Maybe that should be changed to: diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py
index 16b28cc7..4af237a2 100644
--- a/aiohttp/http_writer.py
+++ b/aiohttp/http_writer.py
@@ -35,7 +35,6 @@ class StreamWriter(AbstractStreamWriter):
on_headers_sent: _T_OnHeadersSent = None,
) -> None:
self._protocol = protocol
- self._transport = protocol.transport
self.loop = loop
self.length = None
@@ -52,7 +51,7 @@ class StreamWriter(AbstractStreamWriter):
@property
def transport(self) -> Optional[asyncio.Transport]:
- return self._transport
+ return self._protocol.transport
@property
def protocol(self) -> BaseProtocol:
@@ -166,7 +165,6 @@ class StreamWriter(AbstractStreamWriter):
await self.drain()
self._eof = True
- self._transport = None
async def drain(self) -> None:
"""Flush the write buffer. ... but I was trying to keep this as small as possible |
Yes, this fixes the problem for me. |
#7180) <!-- Thank you for your contribution! --> ## What do these changes do? `ConnectionResetError` will always be raised when `StreamWriter.write` is called after `connection_lost` has been called on the `BaseProtocol` <!-- Please give a short brief about these changes. --> ## Are there changes in behavior for the user? Restores pre 3.8.3 behavior ## Related issue number fixes #7172 ## Checklist - [x] I think the code is well written - [x] Unit tests for the changes exist - [x] Documentation reflects the changes - [x] If you provide code modification, please add yourself to `CONTRIBUTORS.txt` * The format is <Name> <Surname>. * Please keep alphabetical order, the file is sorted by names. - [x] Add a new news fragment into the `CHANGES` folder * name it `<issue_id>.<type>` for example (588.bugfix) * if you don't have an `issue_id` change it to the pr id after creating the pr * ensure type is one of the following: * `.feature`: Signifying a new feature. * `.bugfix`: Signifying a bug fix. * `.doc`: Signifying a documentation improvement. * `.removal`: Signifying a deprecation or removal of public API. * `.misc`: A ticket has been closed, but it is not of interest to users. * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." --------- Co-authored-by: Sviatoslav Sydorenko <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sam Bull <[email protected]>
#7180) <!-- Thank you for your contribution! --> `ConnectionResetError` will always be raised when `StreamWriter.write` is called after `connection_lost` has been called on the `BaseProtocol` <!-- Please give a short brief about these changes. --> Restores pre 3.8.3 behavior fixes #7172 - [x] I think the code is well written - [x] Unit tests for the changes exist - [x] Documentation reflects the changes - [x] If you provide code modification, please add yourself to `CONTRIBUTORS.txt` * The format is <Name> <Surname>. * Please keep alphabetical order, the file is sorted by names. - [x] Add a new news fragment into the `CHANGES` folder * name it `<issue_id>.<type>` for example (588.bugfix) * if you don't have an `issue_id` change it to the pr id after creating the pr * ensure type is one of the following: * `.feature`: Signifying a new feature. * `.bugfix`: Signifying a bug fix. * `.doc`: Signifying a documentation improvement. * `.removal`: Signifying a deprecation or removal of public API. * `.misc`: A ticket has been closed, but it is not of interest to users. * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." --------- Co-authored-by: Sviatoslav Sydorenko <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sam Bull <[email protected]> (cherry picked from commit 974323f)
#7180) <!-- Thank you for your contribution! --> `ConnectionResetError` will always be raised when `StreamWriter.write` is called after `connection_lost` has been called on the `BaseProtocol` <!-- Please give a short brief about these changes. --> Restores pre 3.8.3 behavior fixes #7172 - [x] I think the code is well written - [x] Unit tests for the changes exist - [x] Documentation reflects the changes - [x] If you provide code modification, please add yourself to `CONTRIBUTORS.txt` * The format is <Name> <Surname>. * Please keep alphabetical order, the file is sorted by names. - [x] Add a new news fragment into the `CHANGES` folder * name it `<issue_id>.<type>` for example (588.bugfix) * if you don't have an `issue_id` change it to the pr id after creating the pr * ensure type is one of the following: * `.feature`: Signifying a new feature. * `.bugfix`: Signifying a bug fix. * `.doc`: Signifying a documentation improvement. * `.removal`: Signifying a deprecation or removal of public API. * `.misc`: A ticket has been closed, but it is not of interest to users. * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." --------- Co-authored-by: Sviatoslav Sydorenko <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sam Bull <[email protected]> (cherry picked from commit 974323f)
Describe the bug
Hi!
I have an application that uses a
StreamResponse
to continuously write data to the client by calling thewrite()
method. The server stops sending data when the client disconnects. In version 3.8.1 thewrite()
method raises an exception when the client disconnects, but in later version it does not. The commit that introduces the bug is 20c9365.I've tested on Python 3.7, 3.8 and 3.10.
To Reproduce
Add later if really needed.
Expected behavior
An exception is raised when the client disconnects.
Logs/tracebacks
Python Version
aiohttp Version
multidict Version
-
yarl Version
-
OS
Linux
Related component
Server
Additional context
No response
Code of Conduct
The text was updated successfully, but these errors were encountered: