mirror of
https://github.com/onionshare/onionshare.git
synced 2025-01-10 03:37:28 -03:00
Fixes tests for normal range requests, curl and wget
This commit is contained in:
parent
157ffc9ba5
commit
81700ecaea
1 changed files with 109 additions and 75 deletions
|
@ -11,7 +11,7 @@ import base64
|
||||||
import pytest
|
import pytest
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen, Request
|
||||||
from werkzeug.datastructures import Headers
|
from werkzeug.datastructures import Headers
|
||||||
from werkzeug.exceptions import RequestedRangeNotSatisfiable
|
from werkzeug.exceptions import RequestedRangeNotSatisfiable
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ def check_unsupported(cmd: str, args: list):
|
||||||
except Exception:
|
except Exception:
|
||||||
skip = True
|
skip = True
|
||||||
|
|
||||||
return pytest.mark.skipif(skip, reason='Command {!r} not supported'.format(cmd))
|
return pytest.mark.skipif(skip, reason="Command {!r} not supported".format(cmd))
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
|
@ -255,17 +255,19 @@ def live_server(web):
|
||||||
s.close()
|
s.close()
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
web.app.run(host='127.0.0.1', port=port, debug=False)
|
web.app.run(host="127.0.0.1", port=port, debug=False)
|
||||||
|
|
||||||
proc = Process(target=run)
|
proc = Process(target=run)
|
||||||
proc.start()
|
proc.start()
|
||||||
|
|
||||||
url = 'http://127.0.0.1:{}'.format(port)
|
url = "http://127.0.0.1:{}".format(port)
|
||||||
|
auth = base64.b64encode(b"onionshare:" + web.password.encode()).decode()
|
||||||
|
req = Request(url, headers={"Authorization": "Basic {}".format(auth)})
|
||||||
|
|
||||||
attempts = 20
|
attempts = 20
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
urlopen(url)
|
urlopen(req)
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
attempts -= 1
|
attempts -= 1
|
||||||
|
@ -274,7 +276,7 @@ def live_server(web):
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
yield url + '/download'
|
yield url + "/download"
|
||||||
|
|
||||||
proc.terminate()
|
proc.terminate()
|
||||||
|
|
||||||
|
@ -283,23 +285,23 @@ class TestRangeRequests:
|
||||||
|
|
||||||
VALID_RANGES = [
|
VALID_RANGES = [
|
||||||
(None, 500, [(0, 499)]),
|
(None, 500, [(0, 499)]),
|
||||||
('bytes=0', 500, [(0, 499)]),
|
("bytes=0", 500, [(0, 499)]),
|
||||||
('bytes=100', 500, [(100, 499)]),
|
("bytes=100", 500, [(100, 499)]),
|
||||||
('bytes=100-', 500, [(100, 499)]), # not in the RFC, but how curl sends
|
("bytes=100-", 500, [(100, 499)]), # not in the RFC, but how curl sends
|
||||||
('bytes=0-99', 500, [(0, 99)]),
|
("bytes=0-99", 500, [(0, 99)]),
|
||||||
('bytes=0-599', 500, [(0, 499)]),
|
("bytes=0-599", 500, [(0, 499)]),
|
||||||
('bytes=0-0', 500, [(0, 0)]),
|
("bytes=0-0", 500, [(0, 0)]),
|
||||||
('bytes=-100', 500, [(400, 499)]),
|
("bytes=-100", 500, [(400, 499)]),
|
||||||
('bytes=0-99,100-199', 500, [(0, 199)]),
|
("bytes=0-99,100-199", 500, [(0, 199)]),
|
||||||
('bytes=0-100,100-199', 500, [(0, 199)]),
|
("bytes=0-100,100-199", 500, [(0, 199)]),
|
||||||
('bytes=0-99,101-199', 500, [(0, 99), (101, 199)]),
|
("bytes=0-99,101-199", 500, [(0, 99), (101, 199)]),
|
||||||
('bytes=0-199,100-299', 500, [(0, 299)]),
|
("bytes=0-199,100-299", 500, [(0, 299)]),
|
||||||
('bytes=0-99,200-299', 500, [(0, 99), (200, 299)]),
|
("bytes=0-99,200-299", 500, [(0, 99), (200, 299)]),
|
||||||
]
|
]
|
||||||
|
|
||||||
INVALID_RANGES = [
|
INVALID_RANGES = [
|
||||||
'bytes=200-100',
|
"bytes=200-100",
|
||||||
'bytes=0-100,300-200',
|
"bytes=0-100,300-200",
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_parse_ranges(self):
|
def test_parse_ranges(self):
|
||||||
|
@ -314,22 +316,22 @@ class TestRangeRequests:
|
||||||
|
|
||||||
def test_headers(self, temp_dir, common_obj):
|
def test_headers(self, temp_dir, common_obj):
|
||||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||||
web.stay_open = True
|
web.settings.set("share", "autostop_sharing", False)
|
||||||
url = '/download'
|
url = "/download"
|
||||||
|
|
||||||
with web.app.test_client() as client:
|
with web.app.test_client() as client:
|
||||||
resp = client.get(url, headers=self._make_auth_headers(web.password))
|
resp = client.get(url, headers=self._make_auth_headers(web.password))
|
||||||
assert resp.headers['ETag'].startswith('"sha256:')
|
assert resp.headers["ETag"].startswith('"sha256:')
|
||||||
assert resp.headers['Accept-Ranges'] == 'bytes'
|
assert resp.headers["Accept-Ranges"] == "bytes"
|
||||||
assert resp.headers.get('Last-Modified') is not None
|
assert resp.headers.get("Last-Modified") is not None
|
||||||
assert resp.headers.get('Content-Length') is not None
|
assert resp.headers.get("Content-Length") is not None
|
||||||
assert 'Accept-Encoding' in resp.headers['Vary']
|
assert "Accept-Encoding" in resp.headers["Vary"]
|
||||||
|
|
||||||
def test_basic(self, temp_dir, common_obj):
|
def test_basic(self, temp_dir, common_obj):
|
||||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||||
web.stay_open = True
|
web.settings.set("share", "autostop_sharing", False)
|
||||||
url = '/download'
|
url = "/download"
|
||||||
with open(web.share_mode.download_filename, 'rb') as f:
|
with open(web.share_mode.download_filename, "rb") as f:
|
||||||
contents = f.read()
|
contents = f.read()
|
||||||
|
|
||||||
with web.app.test_client() as client:
|
with web.app.test_client() as client:
|
||||||
|
@ -339,40 +341,45 @@ class TestRangeRequests:
|
||||||
|
|
||||||
def test_reassemble(self, temp_dir, common_obj):
|
def test_reassemble(self, temp_dir, common_obj):
|
||||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||||
web.stay_open = True
|
web.settings.set("share", "autostop_sharing", False)
|
||||||
url = '/download'
|
url = "/download"
|
||||||
with open(web.share_mode.download_filename, 'rb') as f:
|
with open(web.share_mode.download_filename, "rb") as f:
|
||||||
contents = f.read()
|
contents = f.read()
|
||||||
|
|
||||||
with web.app.test_client() as client:
|
with web.app.test_client() as client:
|
||||||
headers = self._make_auth_headers(web.password)
|
headers = self._make_auth_headers(web.password)
|
||||||
headers.extend({'Range': 'bytes=0-10'})
|
headers.extend({"Range": "bytes=0-10"})
|
||||||
resp = client.get(url, headers=headers)
|
resp = client.get(url, headers=headers)
|
||||||
assert resp.status_code == 206
|
assert resp.status_code == 206
|
||||||
content_range = resp.headers['Content-Range']
|
content_range = resp.headers["Content-Range"]
|
||||||
assert content_range == 'bytes {}-{}/{}'.format(0, 10, web.share_mode.download_filesize)
|
assert content_range == "bytes {}-{}/{}".format(
|
||||||
|
0, 10, web.share_mode.download_filesize
|
||||||
|
)
|
||||||
bytes_out = resp.data
|
bytes_out = resp.data
|
||||||
|
|
||||||
headers.update({'Range': 'bytes=11-100000'})
|
headers.update({"Range": "bytes=11-100000"})
|
||||||
resp = client.get(url, headers=headers)
|
resp = client.get(url, headers=headers)
|
||||||
assert resp.status_code == 206
|
assert resp.status_code == 206
|
||||||
content_range = resp.headers['Content-Range']
|
content_range = resp.headers["Content-Range"]
|
||||||
assert content_range == 'bytes {}-{}/{}'.format(
|
assert content_range == "bytes {}-{}/{}".format(
|
||||||
11, web.share_mode.download_filesize - 1, web.share_mode.download_filesize)
|
11,
|
||||||
|
web.share_mode.download_filesize - 1,
|
||||||
|
web.share_mode.download_filesize,
|
||||||
|
)
|
||||||
bytes_out += resp.data
|
bytes_out += resp.data
|
||||||
|
|
||||||
assert bytes_out == contents
|
assert bytes_out == contents
|
||||||
|
|
||||||
def test_mismatched_etags(self, temp_dir, common_obj):
|
def test_mismatched_etags(self, temp_dir, common_obj):
|
||||||
'''RFC 7233 Section 3.2
|
"""RFC 7233 Section 3.2
|
||||||
The "If-Range" header field allows a client to "short-circuit" the second request.
|
The "If-Range" header field allows a client to "short-circuit" the second request.
|
||||||
Informally, its meaning is as follows: if the representation is unchanged, send me the
|
Informally, its meaning is as follows: if the representation is unchanged, send me the
|
||||||
part(s) that I am requesting in Range; otherwise, send me the entire representation.
|
part(s) that I am requesting in Range; otherwise, send me the entire representation.
|
||||||
'''
|
"""
|
||||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||||
web.stay_open = True
|
web.settings.set("share", "autostop_sharing", False)
|
||||||
url = '/download'
|
url = "/download"
|
||||||
with open(web.share_mode.download_filename, 'rb') as f:
|
with open(web.share_mode.download_filename, "rb") as f:
|
||||||
contents = f.read()
|
contents = f.read()
|
||||||
|
|
||||||
with web.app.test_client() as client:
|
with web.app.test_client() as client:
|
||||||
|
@ -380,31 +387,30 @@ class TestRangeRequests:
|
||||||
resp = client.get(url, headers=headers)
|
resp = client.get(url, headers=headers)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
|
|
||||||
headers.extend({'If-Range': 'mismatched etag',
|
headers.extend({"If-Range": "mismatched etag", "Range": "bytes=10-100"})
|
||||||
'Range': 'bytes=10-100'})
|
|
||||||
resp = client.get(url, headers=headers)
|
resp = client.get(url, headers=headers)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert resp.data == contents
|
assert resp.data == contents
|
||||||
|
|
||||||
def test_if_unmodified_since(self, temp_dir, common_obj):
|
def test_if_unmodified_since(self, temp_dir, common_obj):
|
||||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||||
web.stay_open = True
|
web.settings.set("share", "autostop_sharing", False)
|
||||||
url = '/download'
|
url = "/download"
|
||||||
|
|
||||||
with web.app.test_client() as client:
|
with web.app.test_client() as client:
|
||||||
headers = self._make_auth_headers(web.password)
|
headers = self._make_auth_headers(web.password)
|
||||||
resp = client.get(url, headers=headers)
|
resp = client.get(url, headers=headers)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
last_mod = resp.headers['Last-Modified']
|
last_mod = resp.headers["Last-Modified"]
|
||||||
|
|
||||||
headers.extend({'If-Unmodified-Since': last_mod})
|
headers.extend({"If-Unmodified-Since": last_mod})
|
||||||
resp = client.get(url, headers=headers)
|
resp = client.get(url, headers=headers)
|
||||||
assert resp.status_code == 304
|
assert resp.status_code == 304
|
||||||
|
|
||||||
def test_firefox_like_behavior(self, temp_dir, common_obj):
|
def test_firefox_like_behavior(self, temp_dir, common_obj):
|
||||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||||
web.stay_open = True
|
web.settings.set("share", "autostop_sharing", False)
|
||||||
url = '/download'
|
url = "/download"
|
||||||
|
|
||||||
with web.app.test_client() as client:
|
with web.app.test_client() as client:
|
||||||
headers = self._make_auth_headers(web.password)
|
headers = self._make_auth_headers(web.password)
|
||||||
|
@ -412,49 +418,77 @@ class TestRangeRequests:
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
|
|
||||||
# Firefox sends these with all range requests
|
# Firefox sends these with all range requests
|
||||||
etag = resp.headers['ETag']
|
etag = resp.headers["ETag"]
|
||||||
last_mod = resp.headers['Last-Modified']
|
last_mod = resp.headers["Last-Modified"]
|
||||||
|
|
||||||
# make a request that uses the full header set
|
# make a request that uses the full header set
|
||||||
headers.extend({'Range': 'bytes=0-10',
|
headers.extend(
|
||||||
'If-Unmodified-Since': last_mod,
|
{
|
||||||
'If-Range': etag})
|
"Range": "bytes=0-10",
|
||||||
|
"If-Unmodified-Since": last_mod,
|
||||||
|
"If-Range": etag,
|
||||||
|
}
|
||||||
|
)
|
||||||
resp = client.get(url, headers=headers)
|
resp = client.get(url, headers=headers)
|
||||||
assert resp.status_code == 206
|
assert resp.status_code == 206
|
||||||
|
|
||||||
def _make_auth_headers(self, password):
|
def _make_auth_headers(self, password):
|
||||||
auth = base64.b64encode(b"onionshare:" + password.encode()).decode()
|
auth = base64.b64encode(b"onionshare:" + password.encode()).decode()
|
||||||
h = Headers()
|
h = Headers()
|
||||||
h.add("Authorization", "Basic " + auth)
|
h.add("Authorization", "Basic " + auth)
|
||||||
return h
|
return h
|
||||||
|
|
||||||
@check_unsupported('curl', ['--version'])
|
@check_unsupported("curl", ["--version"])
|
||||||
def test_curl(self, temp_dir, common_obj):
|
def test_curl(self, temp_dir, tmpdir, common_obj):
|
||||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||||
web.stay_open = True
|
web.settings.set("share", "autostop_sharing", False)
|
||||||
|
|
||||||
|
download = tmpdir.join("download")
|
||||||
|
|
||||||
with live_server(web) as url:
|
with live_server(web) as url:
|
||||||
# Debugging help from `man curl`, on error 33
|
# Debugging help from `man curl`, on error 33
|
||||||
# 33 HTTP range error. The range "command" didn't work.
|
# 33 HTTP range error. The range "command" didn't work.
|
||||||
subprocess.check_call(['curl', '--continue-at', '10', url])
|
auth_header = self._make_auth_headers(web.password)
|
||||||
|
subprocess.check_call(
|
||||||
|
[
|
||||||
|
"curl",
|
||||||
|
"-H",
|
||||||
|
str(auth_header).strip(),
|
||||||
|
"--output",
|
||||||
|
str(download),
|
||||||
|
"--continue-at",
|
||||||
|
"10",
|
||||||
|
url,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
@check_unsupported('wget', ['--version'])
|
@check_unsupported("wget", ["--version"])
|
||||||
def test_wget(self, temp_dir, tmpdir, common_obj):
|
def test_wget(self, temp_dir, tmpdir, common_obj):
|
||||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||||
web.stay_open = True
|
web.settings.set("share", "autostop_sharing", False)
|
||||||
|
|
||||||
# wget needs a file to exist to continue
|
# wget needs a file to exist to continue
|
||||||
download = tmpdir.join('download')
|
download = tmpdir.join("download")
|
||||||
download.write('x' * 10)
|
download.write("x" * 10)
|
||||||
|
|
||||||
with live_server(web) as url:
|
with live_server(web) as url:
|
||||||
subprocess.check_call(['wget', '--continue', '-O', str(download), url])
|
auth_header = self._make_auth_headers(web.password)
|
||||||
|
subprocess.check_call(
|
||||||
|
[
|
||||||
|
"wget",
|
||||||
|
"--header",
|
||||||
|
str(auth_header).strip(),
|
||||||
|
"--continue",
|
||||||
|
"-O",
|
||||||
|
str(download),
|
||||||
|
url,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_unsupported("http", ["--version"])
|
||||||
@check_unsupported('http', ['--version'])
|
|
||||||
def test_httpie(self, temp_dir, common_obj):
|
def test_httpie(self, temp_dir, common_obj):
|
||||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||||
web.stay_open = True
|
web.settings.set("share", "autostop_sharing", False)
|
||||||
|
|
||||||
with live_server(web) as url:
|
with live_server(web) as url:
|
||||||
subprocess.check_call(['http', url, 'Range: bytes=10'])
|
subprocess.check_call(["http", url, "Range: bytes=10"])
|
||||||
|
|
Loading…
Reference in a new issue