Skip to content

Commit

Permalink
setsockopt may rais OSError exception if socket is closed already #1595
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikolay Kim committed Feb 7, 2017
1 parent 5597797 commit ec16136
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 17 deletions.
4 changes: 3 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ CHANGES
- Do not pause transport during set_parser stage #1211

- Lingering close doesn't terminate before timeout #1559


- `setsockopt` may rais `OSError` exception if socket is closed already #1595

- Lots of CancelledError when requests are interrupted #1565

- Allow users to specify what should happen to decoding errors
Expand Down
38 changes: 24 additions & 14 deletions aiohttp/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,16 +243,23 @@ def set_tcp_nodelay(self, value):
value = bool(value)
if self._tcp_nodelay == value:
return
self._tcp_nodelay = value
if self._socket is None:
return
if self._socket.family not in (socket.AF_INET, socket.AF_INET6):
return
if self._tcp_cork:
self._tcp_cork = False
if CORK is not None: # pragma: no branch
self._socket.setsockopt(socket.IPPROTO_TCP, CORK, False)
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, value)

# socket may be closed already, on windows OSError get raised
try:
if self._tcp_cork:
if CORK is not None: # pragma: no branch
self._socket.setsockopt(socket.IPPROTO_TCP, CORK, False)
self._tcp_cork = False

self._socket.setsockopt(
socket.IPPROTO_TCP, socket.TCP_NODELAY, value)
self._tcp_nodelay = value
except OSError:
pass

@property
def tcp_cork(self):
Expand All @@ -262,18 +269,21 @@ def set_tcp_cork(self, value):
value = bool(value)
if self._tcp_cork == value:
return
self._tcp_cork = value
if self._socket is None:
return
if self._socket.family not in (socket.AF_INET, socket.AF_INET6):
return
if self._tcp_nodelay:
self._socket.setsockopt(socket.IPPROTO_TCP,
socket.TCP_NODELAY,
False)
self._tcp_nodelay = False
if CORK is not None: # pragma: no branch
self._socket.setsockopt(socket.IPPROTO_TCP, CORK, value)

try:
if self._tcp_nodelay:
self._socket.setsockopt(
socket.IPPROTO_TCP, socket.TCP_NODELAY, False)
self._tcp_nodelay = False
if CORK is not None: # pragma: no branch
self._socket.setsockopt(socket.IPPROTO_TCP, CORK, value)
self._tcp_cork = value
except OSError:
pass


class StreamProtocol(asyncio.streams.FlowControlMixin, asyncio.Protocol):
Expand Down
31 changes: 29 additions & 2 deletions tests/test_stream_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ def test_set_nodelay_no_change(loop):
assert not s.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)


def test_set_nodelay_exception(loop):
transport = mock.Mock()
s = mock.Mock()
s.setsockopt = mock.Mock()
s.family = (socket.AF_INET,)
s.setsockopt.side_effect = OSError
transport.get_extra_info.return_value = s
proto = mock.Mock()
reader = mock.Mock()
writer = StreamWriter(transport, proto, reader, loop)
writer.set_tcp_nodelay(True)
assert not writer.tcp_nodelay


def test_set_nodelay_enable(loop):
transport = mock.Mock()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Expand Down Expand Up @@ -83,7 +97,7 @@ def test_set_nodelay_enable_ipv6(loop):
reason="requires unix sockets")
def test_set_nodelay_enable_unix(loop):
transport = mock.Mock()
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
transport.get_extra_info.return_value = s
proto = mock.Mock()
reader = mock.Mock()
Expand All @@ -99,7 +113,7 @@ def test_set_nodelay_enable_no_socket(loop):
reader = mock.Mock()
writer = StreamWriter(transport, proto, reader, loop)
writer.set_tcp_nodelay(True)
assert writer.tcp_nodelay
assert not writer.tcp_nodelay
assert writer._socket is None


Expand Down Expand Up @@ -197,6 +211,19 @@ def test_set_cork_enable_no_socket(loop):
assert writer._socket is None


def test_set_cork_exception(loop):
transport = mock.Mock()
s = mock.Mock()
s.setsockopt = mock.Mock()
s.family = (socket.AF_INET,)
s.setsockopt.side_effect = OSError
proto = mock.Mock()
reader = mock.Mock()
writer = StreamWriter(transport, proto, reader, loop)
writer.set_tcp_cork(True)
assert not writer.tcp_cork


# cork and nodelay interference

@pytest.mark.skipif(CORK is None, reason="TCP_CORK or TCP_NOPUSH required")
Expand Down

0 comments on commit ec16136

Please sign in to comment.