diff --git a/moto/s3/responses.py b/moto/s3/responses.py index 7dc682758..39fea7f45 100644 --- a/moto/s3/responses.py +++ b/moto/s3/responses.py @@ -319,7 +319,7 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin): elif method == "POST": return self._bucket_response_post(request, body, bucket_name) elif method == "OPTIONS": - return self._bucket_response_options(bucket_name) + return self._response_options(bucket_name) else: raise NotImplementedError( "Method {0} has not been implemented in the S3 backend yet".format( @@ -389,7 +389,7 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin): return self.headers - def _bucket_response_options(self, bucket_name): + def _response_options(self, bucket_name): # Return 200 with the headers from the bucket CORS configuration self._authenticate_and_authorize_s3_action() try: @@ -1294,6 +1294,9 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin): return self._key_response_delete(headers, bucket_name, query, key_name) elif method == "POST": return self._key_response_post(request, body, bucket_name, query, key_name) + elif method == "OPTIONS": + # OPTIONS response doesn't depend on the key_name: always return 200 with CORS headers + return self._response_options(bucket_name) else: raise NotImplementedError( "Method {0} has not been implemented in the S3 backend yet".format( diff --git a/tests/test_s3/test_server.py b/tests/test_s3/test_server.py index 6b1aea2ca..a964b358e 100644 --- a/tests/test_s3/test_server.py +++ b/tests/test_s3/test_server.py @@ -223,17 +223,19 @@ def test_s3_server_post_cors_exposed_header(): cors_res = test_client.get("/?cors", "http://testcors.localhost:5000") assert b"ETag" in cors_res.data - preflight_response = test_client.options( - "/", "http://testcors.localhost:5000/", headers=preflight_headers - ) - assert preflight_response.status_code == 200 - expected_cors_headers = { - "Access-Control-Allow-Methods": "HEAD, GET, PUT, POST, DELETE", - "Access-Control-Allow-Origin": "https://example.org", - "Access-Control-Allow-Headers": "*", - "Access-Control-Expose-Headers": "ETag", - "Access-Control-Max-Age": "3000", - } - for header_name, header_value in expected_cors_headers.items(): - assert header_name in preflight_response.headers - assert preflight_response.headers[header_name] == header_value + # Test OPTIONS bucket response and key response + for key_name in ("/", "/test"): + preflight_response = test_client.options( + key_name, "http://testcors.localhost:5000/", headers=preflight_headers + ) + assert preflight_response.status_code == 200 + expected_cors_headers = { + "Access-Control-Allow-Methods": "HEAD, GET, PUT, POST, DELETE", + "Access-Control-Allow-Origin": "https://example.org", + "Access-Control-Allow-Headers": "*", + "Access-Control-Expose-Headers": "ETag", + "Access-Control-Max-Age": "3000", + } + for header_name, header_value in expected_cors_headers.items(): + assert header_name in preflight_response.headers + assert preflight_response.headers[header_name] == header_value