Quote filenames in Location header on redirect

When we redirect the user to the "correct" file name this name should
end up quoted in the header, else we would end up in an infinite
redirect loop.
This commit is contained in:
Sebastian Lohff 2021-04-21 01:26:29 +02:00
parent 1f451e0f29
commit 9fa4ed0026
3 changed files with 26 additions and 3 deletions

View File

@ -11,6 +11,8 @@ Unreleased
wished the ports can be set from outside by specifying the wished the ports can be set from outside by specifying the
environment variables SERVEFILE_DEFAULT_PORT and environment variables SERVEFILE_DEFAULT_PORT and
SERVEFILE_SECONDARY_PORT SERVEFILE_SECONDARY_PORT
* fixed broken redirect when filename contained umlauts or other characters
that should have been quoted
2020-10-30 v0.5.1 2020-10-30 v0.5.1

View File

@ -60,7 +60,7 @@ class FileBaseHandler(BaseHTTPServer.BaseHTTPRequestHandler):
fileName = self.fileName fileName = self.fileName
if unquote(self.path) != "/" + fileName: if unquote(self.path) != "/" + fileName:
self.send_response(302) self.send_response(302)
self.send_header('Location', '/' + fileName) self.send_header('Location', '/' + quote(fileName))
self.end_headers() self.end_headers()
return True return True
return False return False

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import io import io
import os import os
import pytest import pytest
@ -14,9 +15,11 @@ import urllib3
if sys.version_info.major >= 3: if sys.version_info.major >= 3:
from pathlib import Path from pathlib import Path
from urllib.parse import quote
connrefused_exc = ConnectionRefusedError connrefused_exc = ConnectionRefusedError
else: else:
from pathlib2 import Path from pathlib2 import Path
from urllib import quote
connrefused_exc = socket.error connrefused_exc = socket.error
@ -81,7 +84,7 @@ def datadir(tmp_path):
_datadir(v, new_path) _datadir(v, new_path)
else: else:
if hasattr(v, 'decode'): if hasattr(v, 'decode'):
v = v.decode() # python2 compability v = v.decode('utf-8') # python2 compability
(path / k).write_text(v) (path / k).write_text(v)
return path return path
@ -162,6 +165,23 @@ def test_redirect_and_download(run_servefile, datadir):
check_download(data, fname='testfile') check_download(data, fname='testfile')
def test_redirect_and_download_with_umlaut(run_servefile, datadir):
data = "NÖÖT NÖÖT"
filename = "tästføile"
p = datadir({filename: data}) / filename
run_servefile(str(p))
# redirect
r = make_request(allow_redirects=False)
assert r.status_code == 302
assert r.headers.get('Location') == '/{}'.format(quote(filename))
# normal download
if sys.version_info.major < 3:
data = unicode(data, 'utf-8')
check_download(data, fname=filename)
def test_specify_port(run_servefile, datadir): def test_specify_port(run_servefile, datadir):
data = "NOOT NOOT" data = "NOOT NOOT"
p = datadir({'testfile': data}) / 'testfile' p = datadir({'testfile': data}) / 'testfile'
@ -210,6 +230,7 @@ def test_serve_directory(run_servefile, datadir):
'bar': {'thisisaverylongfilenamefortestingthatthisstillworksproperly': 'jup!'}, 'bar': {'thisisaverylongfilenamefortestingthatthisstillworksproperly': 'jup!'},
'noot': 'still data in here', 'noot': 'still data in here',
'bigfile': 'x' * (10 * 1024 ** 2), 'bigfile': 'x' * (10 * 1024 ** 2),
'möwe': 'KRAKRAKRAKA',
} }
p = datadir(d) p = datadir(d)
run_servefile([str(p), '-l']) run_servefile([str(p), '-l'])
@ -219,7 +240,7 @@ def test_serve_directory(run_servefile, datadir):
for path in '/', '/../': for path in '/', '/../':
r = make_request(path) r = make_request(path)
for k in d: for k in d:
assert k in r.text assert quote(k) in r.text
for fname, content in d['foo'].items(): for fname, content in d['foo'].items():
check_download(content, '/foo/' + fname) check_download(content, '/foo/' + fname)