#2567 - When mocking URLs, always return the first match

This commit is contained in:
Bert Blommers 2020-02-27 08:54:57 +00:00
parent f009f7da8c
commit 47349b30df
2 changed files with 32 additions and 2 deletions

View File

@ -7,6 +7,7 @@ import inspect
import os import os
import re import re
import six import six
import types
from io import BytesIO from io import BytesIO
from collections import defaultdict from collections import defaultdict
from botocore.handlers import BUILTIN_HANDLERS from botocore.handlers import BUILTIN_HANDLERS
@ -217,12 +218,29 @@ botocore_mock = responses.RequestsMock(
assert_all_requests_are_fired=False, assert_all_requests_are_fired=False,
target="botocore.vendored.requests.adapters.HTTPAdapter.send", target="botocore.vendored.requests.adapters.HTTPAdapter.send",
) )
responses_mock = responses._default_mock responses_mock = responses._default_mock
# Add passthrough to allow any other requests to work # Add passthrough to allow any other requests to work
# Since this uses .startswith, it applies to http and https requests. # Since this uses .startswith, it applies to http and https requests.
responses_mock.add_passthru("http") responses_mock.add_passthru("http")
def _find_first_match(self, request):
for i, match in enumerate(self._matches):
if match.matches(request):
return match
return None
# Modify behaviour of the matcher to only/always return the first match
# Default behaviour is to return subsequent matches for subsequent requests, which leads to https://github.com/spulec/moto/issues/2567
# - First request matches on the appropriate S3 URL
# - Same request, executed again, will be matched on the subsequent match, which happens to be the catch-all, not-yet-implemented, callback
# Fix: Always return the first match
responses_mock._find_match = types.MethodType(_find_first_match, responses_mock)
BOTOCORE_HTTP_METHODS = ["GET", "DELETE", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"] BOTOCORE_HTTP_METHODS = ["GET", "DELETE", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]

View File

@ -39,6 +39,7 @@ import moto.s3.models as s3model
from moto.core.exceptions import InvalidNextTokenException from moto.core.exceptions import InvalidNextTokenException
from moto.core.utils import py2_strip_unicode_keys from moto.core.utils import py2_strip_unicode_keys
if settings.TEST_SERVER_MODE: if settings.TEST_SERVER_MODE:
REDUCED_PART_SIZE = s3model.UPLOAD_PART_MIN_SIZE REDUCED_PART_SIZE = s3model.UPLOAD_PART_MIN_SIZE
EXPECTED_ETAG = '"140f92a6df9f9e415f74a1463bcee9bb-2"' EXPECTED_ETAG = '"140f92a6df9f9e415f74a1463bcee9bb-2"'
@ -1018,12 +1019,23 @@ def test_s3_object_in_public_bucket():
s3_anonymous.Object(key="file.txt", bucket_name="test-bucket").get() s3_anonymous.Object(key="file.txt", bucket_name="test-bucket").get()
exc.exception.response["Error"]["Code"].should.equal("403") exc.exception.response["Error"]["Code"].should.equal("403")
@mock_s3
def test_s3_object_in_public_bucket_using_multiple_presigned_urls():
s3 = boto3.resource("s3")
bucket = s3.Bucket("test-bucket")
bucket.create(
ACL="public-read", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
)
bucket.put_object(Body=b"ABCD", Key="file.txt")
params = {"Bucket": "test-bucket", "Key": "file.txt"} params = {"Bucket": "test-bucket", "Key": "file.txt"}
presigned_url = boto3.client("s3").generate_presigned_url( presigned_url = boto3.client("s3").generate_presigned_url(
"get_object", params, ExpiresIn=900 "get_object", params, ExpiresIn=900
) )
for i in range(1, 10):
response = requests.get(presigned_url) response = requests.get(presigned_url)
assert response.status_code == 200 assert response.status_code == 200, "Failed on req number {}".format(i)
@mock_s3 @mock_s3