Issue #25441: asyncio: Raise error from drain() when socket is closed.

This commit is contained in:
Guido van Rossum 2015-10-19 11:49:30 -07:00
parent 2bf91bf46c
commit c44ecdf687
3 changed files with 54 additions and 0 deletions

View File

@ -301,6 +301,15 @@ class StreamWriter:
exc = self._reader.exception()
if exc is not None:
raise exc
if self._transport is not None:
if self._transport._closing:
# Yield to the event loop so connection_lost() may be
# called. Without this, _drain_helper() would return
# immediately, and code that calls
# write(...); yield from drain()
# in a loop would never call connection_lost(), so it
# would not see an error when the socket is closed.
yield
yield from self._protocol._drain_helper()

View File

@ -2,8 +2,10 @@
import gc
import os
import queue
import socket
import sys
import threading
import unittest
from unittest import mock
try:
@ -632,6 +634,47 @@ os.close(fd)
protocol = asyncio.StreamReaderProtocol(reader)
self.assertIs(protocol._loop, self.loop)
def test_drain_raises(self):
# See http://bugs.python.org/issue25441
# This test should not use asyncio for the mock server; the
# whole point of the test is to test for a bug in drain()
# where it never gives up the event loop but the socket is
# closed on the server side.
q = queue.Queue()
def server():
# Runs in a separate thread.
sock = socket.socket()
sock.bind(('localhost', 0))
sock.listen(1)
addr = sock.getsockname()
q.put(addr)
clt, _ = sock.accept()
clt.close()
@asyncio.coroutine
def client(host, port):
reader, writer = yield from asyncio.open_connection(host, port, loop=self.loop)
while True:
writer.write(b"foo\n")
yield from writer.drain()
# Start the server thread and wait for it to be listening.
thread = threading.Thread(target=server)
thread.setDaemon(True)
thread.start()
addr = q.get()
# Should not be stuck in an infinite loop.
with self.assertRaises((ConnectionResetError, BrokenPipeError)):
self.loop.run_until_complete(client(*addr))
# Clean up the thread. (Only on success; on failure, it may
# be stuck in accept().)
thread.join()
def test___repr__(self):
stream = asyncio.StreamReader(loop=self.loop)
self.assertEqual("<StreamReader>", repr(stream))

View File

@ -96,6 +96,8 @@ Core and Builtins
Library
-------
- Issue #25441: asyncio: Raise error from drain() when socket is closed.
- Issue #25411: Improved Unicode support in SMTPHandler through better use of
the email package. Thanks to user simon04 for the patch.