| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -571,6 +571,15 @@ def _loop_reading(self, fut=None): | |||
| 571 | 571 | else: | |
| 572 | 572 | self._read_fut = self._loop._proactor.recvfrom(self._sock, | |
| 573 | 573 | self.max_size) | |
| 574 | + except ConnectionResetError as exc: | ||
| 575 | + # WSARecvFrom() reports a stale ICMP port unreachable | ||
| 576 | + # notification as a synchronous ConnectionResetError when the | ||
| 577 | + # same socket was used to send to an address that is not | ||
| 578 | + # listening. This is transient, so reschedule the read loop | ||
| 579 | + # instead of leaving it dead. | ||
| 580 | + self._protocol.error_received(exc) | ||
| 581 | + if not self._closing: | ||
| 582 | + self._loop.call_soon(self._loop_reading) | ||
| 574 | 583 | except OSError as exc: | |
| 575 | 584 | self._protocol.error_received(exc) | |
| 576 | 585 | except exceptions.CancelledError: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1454,6 +1454,60 @@ def create_socket(): | |||
| 1454 | 1454 | transport_1.close() | |
| 1455 | 1455 | transport_2.close() | |
| 1456 | 1456 | ||
| 1457 | + def test_datagram_recvfrom_connection_reset_recovers(self): | ||
| 1458 | + # gh-127057: a UDP socket that sent a datagram to an address that | ||
| 1459 | + # wasn't listening can raise ConnectionResetError on a later | ||
| 1460 | + # receive. The transport must keep working afterwards. | ||
| 1461 | + loop = self.loop | ||
| 1462 | + | ||
| 1463 | + class Protocol(asyncio.DatagramProtocol): | ||
| 1464 | + def connection_made(self, transport): | ||
| 1465 | + self.transport = transport | ||
| 1466 | + self.errors = [] | ||
| 1467 | + self.received = [] | ||
| 1468 | + self.datagram_received_event = loop.create_future() | ||
| 1469 | + | ||
| 1470 | + def error_received(self, exc): | ||
| 1471 | + self.errors.append(exc) | ||
| 1472 | + | ||
| 1473 | + def datagram_received(self, data, addr): | ||
| 1474 | + self.received.append(data) | ||
| 1475 | + if not self.datagram_received_event.done(): | ||
| 1476 | + self.datagram_received_event.set_result(None) | ||
| 1477 | + | ||
| 1478 | + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||
| 1479 | + sock.setblocking(False) | ||
| 1480 | + sock.bind(('127.0.0.1', 0)) | ||
| 1481 | + addr = sock.getsockname() | ||
| 1482 | + | ||
| 1483 | + # Bind and immediately close a second socket to get an address | ||
| 1484 | + # that is guaranteed not to be listening. | ||
| 1485 | + closed = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||
| 1486 | + closed.bind(('127.0.0.1', 0)) | ||
| 1487 | + closed_addr = closed.getsockname() | ||
| 1488 | + closed.close() | ||
| 1489 | + | ||
| 1490 | + # Trigger the error before the socket is wrapped in a transport, | ||
| 1491 | + # so that the first read raises synchronously. | ||
| 1492 | + sock.sendto(b'x', closed_addr) | ||
| 1493 | + | ||
| 1494 | + transport, protocol = loop.run_until_complete( | ||
| 1495 | + loop.create_datagram_endpoint(Protocol, sock=sock)) | ||
| 1496 | + | ||
| 1497 | + transport.sendto(b'ping', addr) | ||
| 1498 | + loop.run_until_complete(asyncio.wait_for( | ||
| 1499 | + protocol.datagram_received_event, support.SHORT_TIMEOUT)) | ||
| 1500 | + self.assertEqual(protocol.received, [b'ping']) | ||
| 1501 | + | ||
| 1502 | + if sys.platform == 'win32': | ||
| 1503 | + # Other platforms don't report ICMP errors on an | ||
| 1504 | + # unconnected UDP socket. | ||
| 1505 | + self.assertEqual(len(protocol.errors), 1) | ||
| 1506 | + self.assertIsInstance(protocol.errors[0], ConnectionResetError) | ||
| 1507 | + | ||
| 1508 | + transport.close() | ||
| 1509 | + test_utils.run_briefly(loop) | ||
| 1510 | + | ||
| 1457 | 1511 | def test_internal_fds(self): | |
| 1458 | 1512 | loop = self.create_event_loop() | |
| 1459 | 1513 | if not isinstance(loop, selector_events.BaseSelectorEventLoop): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + Fix :class:`asyncio.ProactorEventLoop` UDP transports so that a | ||
| 2 | + :exc:`ConnectionResetError` raised by ``WSARecvFrom`` no longer stops the | ||
| 3 | + read loop. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments