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

Use loop.sendfile() for file sending on Python 3.7+ #4269 #4328

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/4269.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use loop.sendfile() for file sending on Python 3.7+
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Alexander Shorin
Alexander Travov
Alexandru Mihai
Alexey Firsov
Alexey Kostyrin
Alexey Popravka
Alexey Stepanov
Amin Etesamian
Expand Down
27 changes: 21 additions & 6 deletions aiohttp/web_fileresponse.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import pathlib
from functools import partial
from socket import SocketType
from typing import ( # noqa
IO,
TYPE_CHECKING,
Expand Down Expand Up @@ -56,6 +57,8 @@ def __init__(self,
self._count = count
self._offset = fobj.tell()
self._in_fd = fobj.fileno()
self._sendfile_impl = self._sendfile_aiohttp \
if not getattr(loop, 'sendfile') else self._sendfile_asyncio

def _write(self, chunk: bytes) -> None:
# we overwrite StreamWriter._write, so nothing can be appended to
Expand Down Expand Up @@ -92,21 +95,33 @@ def _do_sendfile(self, out_fd: int) -> bool:
def _done_fut(self, out_fd: int, fut: 'asyncio.Future[None]') -> None:
self.loop.remove_writer(out_fd)

async def _sendfile_aiohttp(self, out_socket: SocketType) -> None:
out_fd = out_socket.fileno()
loop = self.loop
if not self._do_sendfile(out_fd):
fut = loop.create_future()
fut.add_done_callback(partial(self._done_fut, out_fd))
loop.add_writer(out_fd, self._sendfile_cb, fut, out_fd)
await fut

async def _sendfile_asyncio(self, _: SocketType) -> None:
loop = self.loop
assert self.transport is not None
n = await loop.sendfile(self.transport, self._fobj,
self._offset, self._count)
assert n == self._count
self._count = 0

async def sendfile(self) -> None:
assert self.transport is not None
out_socket = self.transport.get_extra_info('socket').dup()
out_socket.setblocking(False)
out_fd = out_socket.fileno()

loop = self.loop
data = b''.join(self._sendfile_buffer)
try:
await loop.sock_sendall(out_socket, data)
if not self._do_sendfile(out_fd):
fut = loop.create_future()
fut.add_done_callback(partial(self._done_fut, out_fd))
loop.add_writer(out_fd, self._sendfile_cb, fut, out_fd)
await fut
await self._sendfile_impl(out_socket)
except asyncio.CancelledError:
raise
except Exception:
Expand Down