From 9fa4ed002627d34f0b469f6d90936a2f0f7260b8 Mon Sep 17 00:00:00 2001 From: Sebastian Lohff Date: Wed, 21 Apr 2021 01:26:29 +0200 Subject: [PATCH] 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. --- ChangeLog | 2 ++ servefile/servefile.py | 2 +- tests/test_servefile.py | 25 +++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index e1ad90b..1d8e8b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,8 @@ Unreleased wished the ports can be set from outside by specifying the environment variables SERVEFILE_DEFAULT_PORT and SERVEFILE_SECONDARY_PORT + * fixed broken redirect when filename contained umlauts or other characters + that should have been quoted 2020-10-30 v0.5.1 diff --git a/servefile/servefile.py b/servefile/servefile.py index 21df9b2..7e7fd48 100755 --- a/servefile/servefile.py +++ b/servefile/servefile.py @@ -60,7 +60,7 @@ class FileBaseHandler(BaseHTTPServer.BaseHTTPRequestHandler): fileName = self.fileName if unquote(self.path) != "/" + fileName: self.send_response(302) - self.send_header('Location', '/' + fileName) + self.send_header('Location', '/' + quote(fileName)) self.end_headers() return True return False diff --git a/tests/test_servefile.py b/tests/test_servefile.py index 22d0098..3d0cf61 100644 --- a/tests/test_servefile.py +++ b/tests/test_servefile.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import io import os import pytest @@ -14,9 +15,11 @@ import urllib3 if sys.version_info.major >= 3: from pathlib import Path + from urllib.parse import quote connrefused_exc = ConnectionRefusedError else: from pathlib2 import Path + from urllib import quote connrefused_exc = socket.error @@ -81,7 +84,7 @@ def datadir(tmp_path): _datadir(v, new_path) else: if hasattr(v, 'decode'): - v = v.decode() # python2 compability + v = v.decode('utf-8') # python2 compability (path / k).write_text(v) return path @@ -162,6 +165,23 @@ def test_redirect_and_download(run_servefile, datadir): 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): data = "NOOT NOOT" p = datadir({'testfile': data}) / 'testfile' @@ -210,6 +230,7 @@ def test_serve_directory(run_servefile, datadir): 'bar': {'thisisaverylongfilenamefortestingthatthisstillworksproperly': 'jup!'}, 'noot': 'still data in here', 'bigfile': 'x' * (10 * 1024 ** 2), + 'möwe': 'KRAKRAKRAKA', } p = datadir(d) run_servefile([str(p), '-l']) @@ -219,7 +240,7 @@ def test_serve_directory(run_servefile, datadir): for path in '/', '/../': r = make_request(path) for k in d: - assert k in r.text + assert quote(k) in r.text for fname, content in d['foo'].items(): check_download(content, '/foo/' + fname)