S3 - Add more detail to error responses
This commit is contained in:
parent
a5fc14b5bc
commit
cc27f1ef0c
@ -14,6 +14,11 @@ ERROR_WITH_CONDITION_NAME = """{% extends 'single_error' %}
|
||||
{% block extra %}<Condition>{{ condition }}</Condition>{% endblock %}
|
||||
"""
|
||||
|
||||
ERROR_WITH_RANGE = """{% extends 'single_error' %}
|
||||
{% block extra %}<ActualObjectSize>{{ actual_size }}</ActualObjectSize>
|
||||
<RangeRequested>{{ range_requested }}</RangeRequested>{% endblock %}
|
||||
"""
|
||||
|
||||
|
||||
class S3ClientError(RESTError):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -404,3 +409,18 @@ class PreconditionFailed(S3ClientError):
|
||||
condition=failed_condition,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class InvalidRange(S3ClientError):
|
||||
code = 416
|
||||
|
||||
def __init__(self, range_requested, actual_size, **kwargs):
|
||||
kwargs.setdefault("template", "range_error")
|
||||
self.templates["range_error"] = ERROR_WITH_RANGE
|
||||
super(InvalidRange, self).__init__(
|
||||
"InvalidRange",
|
||||
"The requested range is not satisfiable",
|
||||
range_requested=range_requested,
|
||||
actual_size=actual_size,
|
||||
**kwargs
|
||||
)
|
||||
|
@ -37,6 +37,7 @@ from .exceptions import (
|
||||
ObjectNotInActiveTierError,
|
||||
NoSystemTags,
|
||||
PreconditionFailed,
|
||||
InvalidRange,
|
||||
)
|
||||
from .models import (
|
||||
s3_backend,
|
||||
@ -936,11 +937,15 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
else:
|
||||
return 400, response_headers, ""
|
||||
if begin < 0 or end > last or begin > min(end, last):
|
||||
return 416, response_headers, ""
|
||||
raise InvalidRange(
|
||||
actual_size=str(length), range_requested=request.headers.get("range")
|
||||
)
|
||||
response_headers["content-range"] = "bytes {0}-{1}/{2}".format(
|
||||
begin, end, length
|
||||
)
|
||||
return 206, response_headers, response_content[begin : end + 1]
|
||||
content = response_content[begin : end + 1]
|
||||
response_headers["content-length"] = len(content)
|
||||
return 206, response_headers, content
|
||||
|
||||
def key_or_control_response(self, request, full_url, headers):
|
||||
# Key and Control are lumped in because splitting out the regex is too much of a pain :/
|
||||
@ -967,9 +972,12 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
status_code, response_headers, response_content = response
|
||||
|
||||
if status_code == 200 and "range" in request.headers:
|
||||
try:
|
||||
return self._handle_range_header(
|
||||
request, response_headers, response_content
|
||||
)
|
||||
except S3ClientError as s3error:
|
||||
return s3error.code, {}, s3error.description
|
||||
return status_code, response_headers, response_content
|
||||
|
||||
def _control_response(self, request, full_url, headers):
|
||||
|
BIN
tests/test_s3/red.jpg
Normal file
BIN
tests/test_s3/red.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 633 B |
@ -4889,3 +4889,37 @@ def test_presigned_put_url_with_custom_headers():
|
||||
|
||||
s3.delete_object(Bucket=bucket, Key=key)
|
||||
s3.delete_bucket(Bucket=bucket)
|
||||
|
||||
|
||||
@mock_s3
|
||||
def test_request_partial_content_should_contain_content_length():
|
||||
bucket = "bucket"
|
||||
object_key = "key"
|
||||
s3 = boto3.resource("s3")
|
||||
s3.create_bucket(Bucket=bucket)
|
||||
s3.Object(bucket, object_key).put(Body="some text")
|
||||
|
||||
file = s3.Object(bucket, object_key)
|
||||
response = file.get(Range="bytes=0-1024")
|
||||
response["ContentLength"].should.equal(9)
|
||||
|
||||
|
||||
@mock_s3
|
||||
def test_request_partial_content_should_contain_actual_content_length():
|
||||
bucket = "bucket"
|
||||
object_key = "key"
|
||||
s3 = boto3.resource("s3")
|
||||
s3.create_bucket(Bucket=bucket)
|
||||
s3.Object(bucket, object_key).put(Body="some text")
|
||||
|
||||
file = s3.Object(bucket, object_key)
|
||||
requested_range = "bytes=1024-"
|
||||
try:
|
||||
file.get(Range=requested_range)
|
||||
except botocore.client.ClientError as e:
|
||||
e.response["Error"]["Code"].should.equal("InvalidRange")
|
||||
e.response["Error"]["Message"].should.equal(
|
||||
"The requested range is not satisfiable"
|
||||
)
|
||||
e.response["Error"]["ActualObjectSize"].should.equal("9")
|
||||
e.response["Error"]["RangeRequested"].should.equal(requested_range)
|
||||
|
Loading…
Reference in New Issue
Block a user