Skip to content
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

Nodelay #680

Merged
merged 11 commits into from
Dec 15, 2015
17 changes: 17 additions & 0 deletions aiohttp/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def set_parser(self, parser):
import asyncio
import asyncio.streams
import inspect
import socket
from . import errors
from .streams import FlowControlDataQueue, EofStream

Expand Down Expand Up @@ -224,6 +225,22 @@ def __init__(self, transport, protocol, reader, loop):
self._protocol = protocol
self._reader = reader
self._loop = loop
self._tcp_nodelay = False
self._socket = transport.get_extra_info('socket')

@property
def tcp_nodelay(self):
return self._tcp_nodelay

def set_tcp_nodelay(self, value):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, why not setter fortcp_nodelay property?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about property's setter but found that syscall worth explicit function call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, makes sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if value not in (1,0):
    raise ValueError('.....')

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
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, value)


class StreamProtocol(asyncio.streams.FlowControlMixin, asyncio.Protocol):
Expand Down
12 changes: 12 additions & 0 deletions aiohttp/web_reqrep.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ def __init__(self, *, status=200, reason=None, headers=None):
self._req = None
self._resp_impl = None
self._eof_sent = False
self._tcp_nodelay = True

if headers is not None:
self._headers.extend(headers)
Expand Down Expand Up @@ -604,6 +605,16 @@ def last_modified(self, value):
elif isinstance(value, str):
self.headers[hdrs.LAST_MODIFIED] = value

@property
def tcp_nodelay(self):
return self._tcp_nodelay

def set_tcp_nodelay(self, value):
self._tcp_nodelay = value
if self._resp_impl is None:
return
self._resp_impl.transport.set_tcp_nodelay(value)

def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):
params = '; '.join("%s=%s" % i for i in self._content_dict.items())
if params:
Expand Down Expand Up @@ -669,6 +680,7 @@ def _start(self, request):
request.version,
not keep_alive,
self._reason)
resp_impl.transport.set_tcp_nodelay(self._tcp_nodelay)

self._copy_cookies()

Expand Down
88 changes: 88 additions & 0 deletions tests/test_stream_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import pytest
import socket
from aiohttp.parsers import StreamWriter
from unittest import mock


def test_nodelay_default(loop):
transport = mock.Mock()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
transport.get_extra_info.return_value = s
proto = mock.Mock()
reader = mock.Mock()
writer = StreamWriter(transport, proto, reader, loop)
assert not writer.tcp_nodelay
assert not s.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)


def test_set_nodelay_no_change(loop):
transport = mock.Mock()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
transport.get_extra_info.return_value = s
proto = mock.Mock()
reader = mock.Mock()
writer = StreamWriter(transport, proto, reader, loop)
writer.set_tcp_nodelay(False)
assert not writer.tcp_nodelay
assert not s.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)


def test_set_nodelay_enable(loop):
transport = mock.Mock()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
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 writer.tcp_nodelay
assert s.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)


def test_set_nodelay_enable_and_disable(loop):
transport = mock.Mock()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
transport.get_extra_info.return_value = s
proto = mock.Mock()
reader = mock.Mock()
writer = StreamWriter(transport, proto, reader, loop)
writer.set_tcp_nodelay(True)
writer.set_tcp_nodelay(False)
assert not writer.tcp_nodelay
assert not s.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)


def test_set_nodelay_enable_ipv6(loop):
transport = mock.Mock()
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
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 writer.tcp_nodelay
assert s.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)


@pytest.mark.skipif(not hasattr(socket, 'AF_UNIX'),
reason="requires unix sockets")
def test_set_nodelay_enable_unix(loop):
transport = mock.Mock()
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
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 writer.tcp_nodelay


def test_set_nodelay_enable_no_socket(loop):
transport = mock.Mock()
transport.get_extra_info.return_value = None
proto = mock.Mock()
reader = mock.Mock()
writer = StreamWriter(transport, proto, reader, loop)
writer.set_tcp_nodelay(True)
assert writer.tcp_nodelay
assert writer._socket is None
42 changes: 42 additions & 0 deletions tests/test_web_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,48 @@ def test_prepare_calls_signal():
sig.assert_called_with(req, resp)


def test_default_nodelay():
resp = StreamResponse()
assert resp.tcp_nodelay


def test_set_tcp_nodelay_before_start():
resp = StreamResponse()
resp.set_tcp_nodelay(False)
assert not resp.tcp_nodelay
resp.set_tcp_nodelay(True)
assert resp.tcp_nodelay


@pytest.mark.run_loop
def test_set_tcp_nodelay_on_start():
req = make_request('GET', '/')
resp = StreamResponse()

with mock.patch('aiohttp.web_reqrep.ResponseImpl'):
resp_impl = yield from resp.prepare(req)
resp_impl.transport.set_tcp_nodelay.assert_called_with(True)


@pytest.mark.run_loop
def test_set_tcp_nodelay_after_start():
req = make_request('GET', '/')
resp = StreamResponse()

with mock.patch('aiohttp.web_reqrep.ResponseImpl'):
resp_impl = yield from resp.prepare(req)
resp_impl.transport.set_tcp_nodelay.assert_called_with(True)
resp.set_tcp_nodelay(False)
assert not resp.tcp_nodelay
resp_impl.transport.set_tcp_nodelay.assert_called_with(False)
resp.set_tcp_nodelay(True)
assert resp.tcp_nodelay
resp_impl.transport.set_tcp_nodelay.assert_called_with(True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True ? maybe 1 or 0 ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bool is subclass of int



# Response class


def test_response_ctor():
resp = Response()

Expand Down