mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Fix HTTP Range requests on dataset files (non-nginx deployments)
send_file read file_size from the content-length response header, but files served from a dataset's extra_files_path (e.g. JBrowse track data) don't set it, so any Range request 500'd with KeyError. nginx/apache deployments dodged this via the X-Accel-Redirect/X-Sendfile early return. Use os.path.getsize(body.name) instead, and honor the requested range end (the old 'end is None' check was always true, so it ignored the client's end and always served to EOF). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
19c75c6852
commit
9e4be516e7
@@ -569,8 +569,8 @@ def send_file(start_response, trans, body):
|
||||
body = b""
|
||||
if trans.request.range:
|
||||
start = int(trans.request.range.start)
|
||||
file_size = int(trans.response.headers["content-length"])
|
||||
end = int(file_size if end is None else trans.request.range.end)
|
||||
file_size = os.path.getsize(body.name)
|
||||
end = file_size if trans.request.range.end is None else min(int(trans.request.range.end), file_size)
|
||||
trans.response.headers["content-length"] = str(end - start)
|
||||
trans.response.headers["content-range"] = f"bytes {start}-{end - 1}/{file_size}"
|
||||
trans.response.status = 206
|
||||
|
||||
@@ -15,7 +15,7 @@ from galaxy.web.framework.base import (
|
||||
CONTENT = "content"
|
||||
|
||||
|
||||
def setup_fastAPI(fh, nginx_x_accel_redirect_base=None, apache_xsendfile=None):
|
||||
def setup_fastAPI(fh, nginx_x_accel_redirect_base=None, apache_xsendfile=None, set_content_length=True):
|
||||
def wsgi_application(env, start_response):
|
||||
trans = Bunch(
|
||||
response=Response(),
|
||||
@@ -24,7 +24,8 @@ def setup_fastAPI(fh, nginx_x_accel_redirect_base=None, apache_xsendfile=None):
|
||||
config=Bunch(nginx_x_accel_redirect_base=nginx_x_accel_redirect_base, apache_xsendfile=apache_xsendfile)
|
||||
),
|
||||
)
|
||||
trans.response.headers["content-length"] = len(CONTENT)
|
||||
if set_content_length:
|
||||
trans.response.headers["content-length"] = len(CONTENT)
|
||||
trans.response.set_content_type("application/octet-stream")
|
||||
return send_file(start_response, trans, fh)
|
||||
|
||||
@@ -72,3 +73,34 @@ def test_sendfile_in_process(test_file_handle):
|
||||
assert "x-accel-redirect" not in response.headers
|
||||
assert response.headers["content-length"] == str(len(CONTENT))
|
||||
assert response.content.decode() == "content"
|
||||
|
||||
|
||||
def test_sendfile_range(test_file_handle):
|
||||
app = setup_fastAPI(test_file_handle)
|
||||
client = TestClient(app)
|
||||
response = client.get("/test/send_file", headers={"Range": "bytes=0-3"})
|
||||
assert response.status_code == 206
|
||||
assert response.headers["content-range"] == f"bytes 0-3/{len(CONTENT)}"
|
||||
assert response.headers["content-length"] == "4"
|
||||
assert response.content.decode() == "cont"
|
||||
|
||||
|
||||
def test_sendfile_range_open_ended(test_file_handle):
|
||||
app = setup_fastAPI(test_file_handle)
|
||||
client = TestClient(app)
|
||||
response = client.get("/test/send_file", headers={"Range": "bytes=2-"})
|
||||
assert response.status_code == 206
|
||||
assert response.headers["content-range"] == f"bytes 2-{len(CONTENT) - 1}/{len(CONTENT)}"
|
||||
assert response.headers["content-length"] == str(len(CONTENT) - 2)
|
||||
assert response.content.decode() == "ntent"
|
||||
|
||||
|
||||
def test_sendfile_range_without_content_length(test_file_handle):
|
||||
# Files served out of a dataset's extra_files_path (e.g. JBrowse track data) don't set
|
||||
# a content-length header; a Range request on a non-nginx deployment must still succeed.
|
||||
app = setup_fastAPI(test_file_handle, set_content_length=False)
|
||||
client = TestClient(app)
|
||||
response = client.get("/test/send_file", headers={"Range": "bytes=0-3"})
|
||||
assert response.status_code == 206
|
||||
assert response.headers["content-range"] == f"bytes 0-3/{len(CONTENT)}"
|
||||
assert response.content.decode() == "cont"
|
||||
|
||||
Reference in New Issue
Block a user