Core: Allow request decompression (#6726)
This commit is contained in:
parent
18e382c8ca
commit
59ebe7d6a5
@ -14,6 +14,7 @@ from moto.core.common_types import TYPE_RESPONSE, TYPE_IF_NONE
|
|||||||
from moto.core.exceptions import DryRunClientError
|
from moto.core.exceptions import DryRunClientError
|
||||||
from moto.core.utils import (
|
from moto.core.utils import (
|
||||||
camelcase_to_underscores,
|
camelcase_to_underscores,
|
||||||
|
gzip_decompress,
|
||||||
method_names_from_class,
|
method_names_from_class,
|
||||||
params_sort_function,
|
params_sort_function,
|
||||||
)
|
)
|
||||||
@ -232,6 +233,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||||||
def __init__(self, service_name: Optional[str] = None):
|
def __init__(self, service_name: Optional[str] = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.service_name = service_name
|
self.service_name = service_name
|
||||||
|
self.allow_request_decompression = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def dispatch(cls, *args: Any, **kwargs: Any) -> Any: # type: ignore[misc]
|
def dispatch(cls, *args: Any, **kwargs: Any) -> Any: # type: ignore[misc]
|
||||||
@ -262,6 +264,15 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||||||
querystring[key] = [value]
|
querystring[key] = [value]
|
||||||
|
|
||||||
raw_body = self.body
|
raw_body = self.body
|
||||||
|
|
||||||
|
# https://github.com/getmoto/moto/issues/6692
|
||||||
|
# Content coming from SDK's can be GZipped for performance reasons
|
||||||
|
if (
|
||||||
|
headers.get("Content-Encoding", "") == "gzip"
|
||||||
|
and self.allow_request_decompression
|
||||||
|
):
|
||||||
|
self.body = gzip_decompress(self.body)
|
||||||
|
|
||||||
if isinstance(self.body, bytes) and not use_raw_body:
|
if isinstance(self.body, bytes) and not use_raw_body:
|
||||||
self.body = self.body.decode("utf-8")
|
self.body = self.body.decode("utf-8")
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import inspect
|
|||||||
import re
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
|
from gzip import decompress
|
||||||
from typing import Any, Optional, List, Callable, Dict, Tuple
|
from typing import Any, Optional, List, Callable, Dict, Tuple
|
||||||
from urllib.parse import urlparse, unquote
|
from urllib.parse import urlparse, unquote
|
||||||
from .common_types import TYPE_RESPONSE
|
from .common_types import TYPE_RESPONSE
|
||||||
@ -416,3 +417,7 @@ def _unquote_hex_characters(path: str) -> str:
|
|||||||
)
|
)
|
||||||
char_offset += (combo_end - combo_start) + len(character) - 1
|
char_offset += (combo_end - combo_start) + len(character) - 1
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def gzip_decompress(body: bytes) -> bytes:
|
||||||
|
return decompress(body)
|
||||||
|
@ -158,6 +158,11 @@ def parse_key_name(pth: str) -> str:
|
|||||||
class S3Response(BaseResponse):
|
class S3Response(BaseResponse):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(service_name="s3")
|
super().__init__(service_name="s3")
|
||||||
|
# Whatever format requests come in, we should never touch them
|
||||||
|
# There are some nuances here - this decision should be method-specific, instead of service-specific
|
||||||
|
# E.G.: we don't want to touch put_object(), but we might have to decompress put_object_configuration()
|
||||||
|
# Taking the naive approach to never decompress anything from S3 for now
|
||||||
|
self.allow_request_decompression = False
|
||||||
|
|
||||||
def get_safe_path_from_url(self, url: ParseResult) -> str:
|
def get_safe_path_from_url(self, url: ParseResult) -> str:
|
||||||
return self.get_safe_path(url.path)
|
return self.get_safe_path(url.path)
|
||||||
|
@ -2,11 +2,13 @@ import datetime
|
|||||||
from unittest import SkipTest, mock
|
from unittest import SkipTest, mock
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from gzip import compress as gzip_compress
|
||||||
|
|
||||||
from botocore.awsrequest import AWSPreparedRequest
|
from botocore.awsrequest import AWSPreparedRequest
|
||||||
|
|
||||||
from moto.core.responses import AWSServiceSpec, BaseResponse
|
from moto.core.responses import AWSServiceSpec, BaseResponse
|
||||||
from moto.core.responses import flatten_json_request_body
|
from moto.core.responses import flatten_json_request_body
|
||||||
|
from moto.s3.responses import S3Response
|
||||||
from moto import settings
|
from moto import settings
|
||||||
from freezegun import freeze_time
|
from freezegun import freeze_time
|
||||||
|
|
||||||
@ -244,3 +246,38 @@ def test_response_metadata():
|
|||||||
assert "date" in bc.response_headers
|
assert "date" in bc.response_headers
|
||||||
if not settings.TEST_SERVER_MODE:
|
if not settings.TEST_SERVER_MODE:
|
||||||
assert bc.response_headers["date"] == "Sat, 20 May 2023 10:20:30 GMT"
|
assert bc.response_headers["date"] == "Sat, 20 May 2023 10:20:30 GMT"
|
||||||
|
|
||||||
|
|
||||||
|
def test_compression_gzip():
|
||||||
|
body = '{"key": "%D0"}, "C": "#0 = :0"}'
|
||||||
|
request = AWSPreparedRequest(
|
||||||
|
"GET",
|
||||||
|
url="http://request",
|
||||||
|
headers={"Content-Encoding": "gzip"},
|
||||||
|
body=_gzip_compress_body(body),
|
||||||
|
stream_output=False,
|
||||||
|
)
|
||||||
|
response = BaseResponse()
|
||||||
|
response.setup_class(request, request.url, request.headers)
|
||||||
|
|
||||||
|
assert body == response.body
|
||||||
|
|
||||||
|
|
||||||
|
def test_compression_gzip_in_s3():
|
||||||
|
body = b"some random data"
|
||||||
|
request = AWSPreparedRequest(
|
||||||
|
"GET",
|
||||||
|
url="http://request",
|
||||||
|
headers={"Content-Encoding": "gzip"},
|
||||||
|
body=body,
|
||||||
|
stream_output=False,
|
||||||
|
)
|
||||||
|
response = S3Response()
|
||||||
|
response.setup_class(request, request.url, request.headers)
|
||||||
|
|
||||||
|
assert body == response.body.encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _gzip_compress_body(body: str):
|
||||||
|
assert isinstance(body, str)
|
||||||
|
return gzip_compress(data=body.encode("utf-8"))
|
||||||
|
Loading…
Reference in New Issue
Block a user