Merge pull request #1297 from tahoe-lafs/4016-http-storage-content-type

HTTP storage content type correctness

Fixes ticket:4016
This commit is contained in:
Itamar Turner-Trauring 2023-05-04 13:16:07 -04:00 committed by GitHub
commit 0b992c498a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 26 additions and 5 deletions

0
newsfragments/4016.minor Normal file
View File

View File

@ -443,11 +443,20 @@ class StorageClient(object):
kwargs["data"] = dumps(message_to_serialize) kwargs["data"] = dumps(message_to_serialize)
headers.addRawHeader("Content-Type", CBOR_MIME_TYPE) headers.addRawHeader("Content-Type", CBOR_MIME_TYPE)
return await self._treq.request( response = await self._treq.request(
method, url, headers=headers, timeout=timeout, **kwargs method, url, headers=headers, timeout=timeout, **kwargs
) )
async def decode_cbor(self, response, schema: Schema) -> object: if self.TEST_MODE_REGISTER_HTTP_POOL is not None:
if response.code != 404:
# We're doing API queries, HTML is never correct except in 404, but
# it's the default for Twisted's web server so make sure nothing
# unexpected happened.
assert get_content_type(response.headers) != "text/html"
return response
async def decode_cbor(self, response: IResponse, schema: Schema) -> object:
"""Given HTTP response, return decoded CBOR body.""" """Given HTTP response, return decoded CBOR body."""
with start_action(action_type="allmydata:storage:http-client:decode-cbor"): with start_action(action_type="allmydata:storage:http-client:decode-cbor"):
if response.code > 199 and response.code < 300: if response.code > 199 and response.code < 300:
@ -587,6 +596,12 @@ def read_share_chunk(
if response.code == http.NO_CONTENT: if response.code == http.NO_CONTENT:
return b"" return b""
content_type = get_content_type(response.headers)
if content_type != "application/octet-stream":
raise ValueError(
f"Content-type was wrong: {content_type}, should be application/octet-stream"
)
if response.code == http.PARTIAL_CONTENT: if response.code == http.PARTIAL_CONTENT:
content_range = parse_content_range_header( content_range = parse_content_range_header(
response.headers.getRawHeaders("content-range")[0] or "" response.headers.getRawHeaders("content-range")[0] or ""

View File

@ -106,6 +106,9 @@ def _authorization_decorator(required_secrets):
def decorator(f): def decorator(f):
@wraps(f) @wraps(f)
def route(self, request, *args, **kwargs): def route(self, request, *args, **kwargs):
# Don't set text/html content type by default:
request.defaultContentType = None
with start_action( with start_action(
action_type="allmydata:storage:http-server:handle-request", action_type="allmydata:storage:http-server:handle-request",
method=request.method, method=request.method,
@ -114,9 +117,9 @@ def _authorization_decorator(required_secrets):
try: try:
# Check Authorization header: # Check Authorization header:
if not timing_safe_compare( if not timing_safe_compare(
request.requestHeaders.getRawHeaders("Authorization", [""])[0].encode( request.requestHeaders.getRawHeaders("Authorization", [""])[
"utf-8" 0
), ].encode("utf-8"),
swissnum_auth_header(self._swissnum), swissnum_auth_header(self._swissnum),
): ):
raise _HTTPError(http.UNAUTHORIZED) raise _HTTPError(http.UNAUTHORIZED)
@ -491,6 +494,7 @@ def read_range(
def _add_error_handling(app: Klein): def _add_error_handling(app: Klein):
"""Add exception handlers to a Klein app.""" """Add exception handlers to a Klein app."""
@app.handle_errors(_HTTPError) @app.handle_errors(_HTTPError)
def _http_error(_, request, failure): def _http_error(_, request, failure):
"""Handle ``_HTTPError`` exceptions.""" """Handle ``_HTTPError`` exceptions."""
@ -775,6 +779,7 @@ class HTTPServer(object):
) )
def read_share_chunk(self, request, authorization, storage_index, share_number): def read_share_chunk(self, request, authorization, storage_index, share_number):
"""Read a chunk for an already uploaded immutable.""" """Read a chunk for an already uploaded immutable."""
request.setHeader("content-type", "application/octet-stream")
try: try:
bucket = self._storage_server.get_buckets(storage_index)[share_number] bucket = self._storage_server.get_buckets(storage_index)[share_number]
except KeyError: except KeyError:
@ -880,6 +885,7 @@ class HTTPServer(object):
) )
def read_mutable_chunk(self, request, authorization, storage_index, share_number): def read_mutable_chunk(self, request, authorization, storage_index, share_number):
"""Read a chunk from a mutable.""" """Read a chunk from a mutable."""
request.setHeader("content-type", "application/octet-stream")
try: try:
share_length = self._storage_server.get_mutable_share_length( share_length = self._storage_server.get_mutable_share_length(