# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. from tests import unittest, mock, BaseSessionTest import botocore.session from botocore.client import Config from botocore.exceptions import ParamValidationError class TestS3BucketValidation(unittest.TestCase): def test_invalid_bucket_name_raises_error(self): session = botocore.session.get_session() s3 = session.create_client('s3') with self.assertRaises(ParamValidationError): s3.put_object(Bucket='adfgasdfadfs/bucket/name', Key='foo', Body=b'asdf') class BaseS3OperationTest(BaseSessionTest): def setUp(self): super(BaseS3OperationTest, self).setUp() self.region = 'us-west-2' self.client = self.session.create_client( 's3', self.region) self.session_send_patch = mock.patch('botocore.endpoint.Session.send') self.http_session_send_mock = self.session_send_patch.start() def tearDown(self): super(BaseSessionTest, self).tearDown() self.session_send_patch.stop() class TestS3GetBucketLifecycle(BaseS3OperationTest): def test_multiple_transitions_returns_one(self): http_response = mock.Mock() http_response.status_code = 200 http_response.content = ( '' '' ' ' ' transitionRule' ' foo' ' Enabled' ' ' ' 40' ' STANDARD_IA' ' ' ' ' ' 70' ' GLACIER' ' ' ' ' ' ' ' noncurrentVersionRule' ' bar' ' Enabled' ' ' ' 40' ' STANDARD_IA' ' ' ' ' ' 70' ' GLACIER' ' ' ' ' '' ) http_response.headers = {} self.http_session_send_mock.return_value = http_response s3 = self.session.create_client('s3') response = s3.get_bucket_lifecycle(Bucket='mybucket') # Each Transition member should have at least one of the # transitions provided. self.assertEqual( response['Rules'][0]['Transition'], {'Days': 40, 'StorageClass': 'STANDARD_IA'} ) self.assertEqual( response['Rules'][1]['NoncurrentVersionTransition'], {'NoncurrentDays': 40, 'StorageClass': 'STANDARD_IA'} ) class TestS3PutObject(BaseS3OperationTest): def test_500_error_with_non_xml_body(self): # Note: This exact test case may not be applicable from # an integration standpoint if the issue is fixed in the future. # # The issue is that: # S3 returns a 200 response but the received response from urllib3 has # a 500 status code and the headers are in the body of the # the response. Botocore will try to parse out the error body as xml, # but the body is invalid xml because it is full of headers. # So instead of blowing up on an XML parsing error, we # should at least use the 500 status code because that can be # retried. # # We are unsure of what exactly causes the response to be mangled # but we expect it to be how 100 continues are handled. non_xml_content = ( 'x-amz-id-2: foo\r\n' 'x-amz-request-id: bar\n' 'Date: Tue, 06 Oct 2015 03:20:38 GMT\r\n' 'ETag: "a6d856bc171fc6aa1b236680856094e2"\r\n' 'Content-Length: 0\r\n' 'Server: AmazonS3\r\n' ) http_500_response = mock.Mock() http_500_response.status_code = 500 http_500_response.content = non_xml_content http_500_response.headers = {} success_response = mock.Mock() success_response.status_code = 200 success_response.content = b'' success_response.headers = {} self.http_session_send_mock.side_effect = [ http_500_response, success_response ] s3 = self.session.create_client('s3') response = s3.put_object(Bucket='mybucket', Key='mykey', Body=b'foo') # The first response should have been retried even though the xml is # invalid and eventually return the 200 response. self.assertEqual(response['ResponseMetadata']['HTTPStatusCode'], 200) class BaseS3AddressingStyle(BaseSessionTest): def setUp(self): super(BaseS3AddressingStyle, self).setUp() self.http_response = mock.Mock() self.http_response.status_code = 200 self.http_response.headers = {} self.http_response.content = b'' class TestVirtualHostStyle(BaseS3AddressingStyle): def test_default_endpoint_for_virtual_addressing(self): s3 = self.session.create_client( 's3', config=Config(s3={'addressing_style': 'virtual'})) with mock.patch('botocore.endpoint.Session.send') \ as mock_send: mock_send.return_value = self.http_response s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody') request_sent = mock_send.call_args[0][0] self.assertEqual( 'https://mybucket.s3.amazonaws.com/mykey', request_sent.url) def test_provided_endpoint_url_for_virtual_addressing(self): s3 = self.session.create_client( 's3', config=Config(s3={'addressing_style': 'virtual'}), endpoint_url='https://foo.amazonaws.com') with mock.patch('botocore.endpoint.Session.send') \ as mock_send: mock_send.return_value = self.http_response s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody') request_sent = mock_send.call_args[0][0] self.assertEqual( 'https://mybucket.foo.amazonaws.com/mykey', request_sent.url) def test_us_gov_with_virtual_addressing(self): s3 = self.session.create_client( 's3', region_name='us-gov-west-1', config=Config(s3={'addressing_style': 'virtual'})) with mock.patch('botocore.endpoint.Session.send') \ as mock_send: mock_send.return_value = self.http_response s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody') request_sent = mock_send.call_args[0][0] self.assertEqual( 'https://mybucket.s3-us-gov-west-1.amazonaws.com/mykey', request_sent.url) class TestPathHostStyle(BaseS3AddressingStyle): def test_default_endpoint_for_path_addressing(self): s3 = self.session.create_client( 's3', config=Config(s3={'addressing_style': 'path'})) with mock.patch('botocore.endpoint.Session.send') \ as mock_send: mock_send.return_value = self.http_response s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody') request_sent = mock_send.call_args[0][0] self.assertEqual( 'https://s3.amazonaws.com/mybucket/mykey', request_sent.url) def test_provided_endpoint_url_for_path_addressing(self): s3 = self.session.create_client( 's3', config=Config(s3={'addressing_style': 'path'}), endpoint_url='https://foo.amazonaws.com') with mock.patch('botocore.endpoint.Session.send') \ as mock_send: mock_send.return_value = self.http_response s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody') request_sent = mock_send.call_args[0][0] self.assertEqual( 'https://foo.amazonaws.com/mybucket/mykey', request_sent.url)