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 a mmap file to read data in StaticRoute #517

Merged
merged 7 commits into from
Oct 1, 2015
20 changes: 12 additions & 8 deletions aiohttp/web_urldispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import re
import os
import inspect
import mmap

from urllib.parse import urlencode, unquote

Expand Down Expand Up @@ -235,19 +236,18 @@ def _sendfile_fallback(self, req, resp, fobj, count):
transferred in chunks controlled by the `chunk_size` argument to
:class:`StaticRoute`.
"""
f_mm = mmap.mmap(fobj.fileno(), count, access=mmap.ACCESS_READ)
chunk_size = self._chunk_size

chunk = fobj.read(chunk_size)
while chunk and count > chunk_size:
resp.write(chunk)
yield from resp.drain()
count = count - chunk_size
chunk = fobj.read(chunk_size)
chunk_starts = range(0, count, chunk_size)
chunk_stops = range(chunk_size, count+chunk_size, chunk_size)

if chunk:
resp.write(chunk[:count])
for i, j in zip(chunk_starts, chunk_stops):
resp.write(f_mm[i:j])
yield from resp.drain()

f_mm.close()

if hasattr(os, "sendfile"): # pragma: no cover
_sendfile = _sendfile_system
else: # pragma: no cover
Expand Down Expand Up @@ -281,6 +281,10 @@ def handle(self, request):
file_size = st.st_size

resp.content_length = file_size

if file_size == 0:
return resp

yield from resp.prepare(request)

with open(filepath, 'rb') as f:
Expand Down
Empty file added tests/empty.dat
Empty file.
20 changes: 20 additions & 0 deletions tests/test_web_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,26 @@ def go(dirname, filename):

self.loop.run_until_complete(go(here, filename))

def test_static_file_empty(self):

@asyncio.coroutine
def go(dirname, filename):
app, _, url = yield from self.create_server(
'GET', '/static/' + filename
)
app.router.add_static('/static', dirname)

resp = yield from request('GET', url, loop=self.loop)
self.assertEqual(200, resp.status)
txt = yield from resp.text()
self.assertEqual('', txt)
self.assertEqual('0', resp.headers['CONTENT-LENGTH'])
resp.close()

here = os.path.dirname(__file__)
filename = 'empty.dat'
self.loop.run_until_complete(go(here, filename))


class TestStaticFileSendfileFallback(StaticFileMixin,
unittest.TestCase):
Expand Down