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

Make StaticRoute support Last-Modified and If-Modified-Since headers #386

Merged
merged 4 commits into from
Jun 5, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
17 changes: 13 additions & 4 deletions aiohttp/web_urldispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
import inspect

from urllib.parse import urlencode
from wsgiref.handlers import format_date_time

from . import hdrs
from .abc import AbstractRouter, AbstractMatchInfo
from .protocol import HttpVersion11
from .web_exceptions import HTTPMethodNotAllowed, HTTPNotFound
from .web_exceptions import HTTPMethodNotAllowed, HTTPNotFound, HTTPNotModified
from .web_reqrep import StreamResponse


Expand Down Expand Up @@ -169,22 +170,30 @@ def url(self, *, filename, query=None):

@asyncio.coroutine
def handle(self, request):
resp = StreamResponse()
filename = request.match_info['filename']
filepath = os.path.abspath(os.path.join(self._directory, filename))
if not filepath.startswith(self._directory):
raise HTTPNotFound()
if not os.path.exists(filepath) or not os.path.isfile(filepath):
raise HTTPNotFound()

st = os.stat(filepath)
mtime = format_date_time(st.st_mtime)

if request.headers.get(hdrs.IF_MODIFIED_SINCE) == mtime:
raise HTTPNotModified()

ct, encoding = mimetypes.guess_type(filepath)
if not ct:
ct = 'application/octet-stream'

resp = StreamResponse()
resp.content_type = ct
if encoding:
resp.headers['content-encoding'] = encoding
resp.headers[hdrs.CONTENT_ENCODING] = encoding
resp.headers[hdrs.LAST_MODIFIED] = mtime

file_size = os.stat(filepath).st_size
file_size = st.st_size
single_chunk = file_size < self._chunk_size

if single_chunk:
Expand Down
42 changes: 42 additions & 0 deletions tests/test_web_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,48 @@ def go(dirname, relpath):
filename = '../README.rst'
self.loop.run_until_complete(go(here, filename))

def test_static_file_if_modified_since(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)
lastmod = resp.headers.get('Last-Modified')
self.assertIsNotNone(lastmod)
resp.close()

resp = yield from request('GET', url, loop=self.loop,
Copy link
Member

Choose a reason for hiding this comment

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

Please split the test into several (five?) ones.

headers={'If-Modified-Since': lastmod})
self.assertEqual(304, resp.status)
resp.close()

lastmod = 'Mon, 1 Jan 1990 01:01:01 GMT'
resp = yield from request('GET', url, loop=self.loop,
headers={'If-Modified-Since': lastmod})
self.assertIn(resp.status, (200, 304))
resp.close()

lastmod = 'Fri, 31 Dec 9999 23:59:59 GMT'
resp = yield from request('GET', url, loop=self.loop,
headers={'If-Modified-Since': lastmod})
self.assertEqual(200, resp.status)
resp.close()

lastmod = 'not a valid HTTP-date'
resp = yield from request('GET', url, loop=self.loop,
headers={'If-Modified-Since': lastmod})
self.assertEqual(200, resp.status)
resp.close()

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

def test_static_route_path_existence_check(self):
directory = os.path.dirname(__file__)
web.StaticRoute(None, "/", directory)
Expand Down