| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| """Internal module to help with normalizing botocore client args. |
| |
| This module (and all function/classes within this module) should be |
| considered internal, and *not* a public API. |
| |
| """ |
| import copy |
| import logging |
| import socket |
|
|
| import botocore.exceptions |
| import botocore.parsers |
| import botocore.serialize |
| from botocore.config import Config |
| from botocore.endpoint import EndpointCreator |
| from botocore.regions import EndpointResolverBuiltins as EPRBuiltins |
| from botocore.regions import EndpointRulesetResolver |
| from botocore.signers import RequestSigner |
| from botocore.utils import ensure_boolean, is_s3_accelerate_url |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| VALID_REGIONAL_ENDPOINTS_CONFIG = [ |
| 'legacy', |
| 'regional', |
| ] |
| LEGACY_GLOBAL_STS_REGIONS = [ |
| 'ap-northeast-1', |
| 'ap-south-1', |
| 'ap-southeast-1', |
| 'ap-southeast-2', |
| 'aws-global', |
| 'ca-central-1', |
| 'eu-central-1', |
| 'eu-north-1', |
| 'eu-west-1', |
| 'eu-west-2', |
| 'eu-west-3', |
| 'sa-east-1', |
| 'us-east-1', |
| 'us-east-2', |
| 'us-west-1', |
| 'us-west-2', |
| ] |
|
|
|
|
| class ClientArgsCreator: |
| def __init__( |
| self, |
| event_emitter, |
| user_agent, |
| response_parser_factory, |
| loader, |
| exceptions_factory, |
| config_store, |
| ): |
| self._event_emitter = event_emitter |
| self._user_agent = user_agent |
| self._response_parser_factory = response_parser_factory |
| self._loader = loader |
| self._exceptions_factory = exceptions_factory |
| self._config_store = config_store |
|
|
| def get_client_args( |
| self, |
| service_model, |
| region_name, |
| is_secure, |
| endpoint_url, |
| verify, |
| credentials, |
| scoped_config, |
| client_config, |
| endpoint_bridge, |
| auth_token=None, |
| endpoints_ruleset_data=None, |
| partition_data=None, |
| ): |
| final_args = self.compute_client_args( |
| service_model, |
| client_config, |
| endpoint_bridge, |
| region_name, |
| endpoint_url, |
| is_secure, |
| scoped_config, |
| ) |
|
|
| service_name = final_args['service_name'] |
| parameter_validation = final_args['parameter_validation'] |
| endpoint_config = final_args['endpoint_config'] |
| protocol = final_args['protocol'] |
| config_kwargs = final_args['config_kwargs'] |
| s3_config = final_args['s3_config'] |
| partition = endpoint_config['metadata'].get('partition', None) |
| socket_options = final_args['socket_options'] |
|
|
| signing_region = endpoint_config['signing_region'] |
| endpoint_region_name = endpoint_config['region_name'] |
|
|
| event_emitter = copy.copy(self._event_emitter) |
| signer = RequestSigner( |
| service_model.service_id, |
| signing_region, |
| endpoint_config['signing_name'], |
| endpoint_config['signature_version'], |
| credentials, |
| event_emitter, |
| auth_token, |
| ) |
|
|
| config_kwargs['s3'] = s3_config |
| new_config = Config(**config_kwargs) |
| endpoint_creator = EndpointCreator(event_emitter) |
|
|
| endpoint = endpoint_creator.create_endpoint( |
| service_model, |
| region_name=endpoint_region_name, |
| endpoint_url=endpoint_config['endpoint_url'], |
| verify=verify, |
| response_parser_factory=self._response_parser_factory, |
| max_pool_connections=new_config.max_pool_connections, |
| proxies=new_config.proxies, |
| timeout=(new_config.connect_timeout, new_config.read_timeout), |
| socket_options=socket_options, |
| client_cert=new_config.client_cert, |
| proxies_config=new_config.proxies_config, |
| ) |
|
|
| serializer = botocore.serialize.create_serializer( |
| protocol, parameter_validation |
| ) |
| response_parser = botocore.parsers.create_parser(protocol) |
|
|
| ruleset_resolver = self._build_endpoint_resolver( |
| endpoints_ruleset_data, |
| partition_data, |
| client_config, |
| service_model, |
| endpoint_region_name, |
| region_name, |
| endpoint_url, |
| endpoint, |
| is_secure, |
| endpoint_bridge, |
| event_emitter, |
| ) |
|
|
| return { |
| 'serializer': serializer, |
| 'endpoint': endpoint, |
| 'response_parser': response_parser, |
| 'event_emitter': event_emitter, |
| 'request_signer': signer, |
| 'service_model': service_model, |
| 'loader': self._loader, |
| 'client_config': new_config, |
| 'partition': partition, |
| 'exceptions_factory': self._exceptions_factory, |
| 'endpoint_ruleset_resolver': ruleset_resolver, |
| } |
|
|
| def compute_client_args( |
| self, |
| service_model, |
| client_config, |
| endpoint_bridge, |
| region_name, |
| endpoint_url, |
| is_secure, |
| scoped_config, |
| ): |
| service_name = service_model.endpoint_prefix |
| protocol = service_model.metadata['protocol'] |
| parameter_validation = True |
| if client_config and not client_config.parameter_validation: |
| parameter_validation = False |
| elif scoped_config: |
| raw_value = scoped_config.get('parameter_validation') |
| if raw_value is not None: |
| parameter_validation = ensure_boolean(raw_value) |
|
|
| |
| user_agent = self._user_agent |
| if client_config is not None: |
| if client_config.user_agent is not None: |
| user_agent = client_config.user_agent |
| if client_config.user_agent_extra is not None: |
| user_agent += ' %s' % client_config.user_agent_extra |
|
|
| s3_config = self.compute_s3_config(client_config) |
| endpoint_config = self._compute_endpoint_config( |
| service_name=service_name, |
| region_name=region_name, |
| endpoint_url=endpoint_url, |
| is_secure=is_secure, |
| endpoint_bridge=endpoint_bridge, |
| s3_config=s3_config, |
| ) |
| endpoint_variant_tags = endpoint_config['metadata'].get('tags', []) |
| |
| |
| |
| config_kwargs = dict( |
| region_name=endpoint_config['region_name'], |
| signature_version=endpoint_config['signature_version'], |
| user_agent=user_agent, |
| ) |
| if 'dualstack' in endpoint_variant_tags: |
| config_kwargs.update(use_dualstack_endpoint=True) |
| if 'fips' in endpoint_variant_tags: |
| config_kwargs.update(use_fips_endpoint=True) |
| if client_config is not None: |
| config_kwargs.update( |
| connect_timeout=client_config.connect_timeout, |
| read_timeout=client_config.read_timeout, |
| max_pool_connections=client_config.max_pool_connections, |
| proxies=client_config.proxies, |
| proxies_config=client_config.proxies_config, |
| retries=client_config.retries, |
| client_cert=client_config.client_cert, |
| inject_host_prefix=client_config.inject_host_prefix, |
| tcp_keepalive=client_config.tcp_keepalive, |
| ) |
| self._compute_retry_config(config_kwargs) |
| self._compute_connect_timeout(config_kwargs) |
| s3_config = self.compute_s3_config(client_config) |
|
|
| is_s3_service = self._is_s3_service(service_name) |
|
|
| if is_s3_service and 'dualstack' in endpoint_variant_tags: |
| if s3_config is None: |
| s3_config = {} |
| s3_config['use_dualstack_endpoint'] = True |
|
|
| return { |
| 'service_name': service_name, |
| 'parameter_validation': parameter_validation, |
| 'user_agent': user_agent, |
| 'endpoint_config': endpoint_config, |
| 'protocol': protocol, |
| 'config_kwargs': config_kwargs, |
| 's3_config': s3_config, |
| 'socket_options': self._compute_socket_options( |
| scoped_config, client_config |
| ), |
| } |
|
|
| def compute_s3_config(self, client_config): |
| s3_configuration = self._config_store.get_config_variable('s3') |
|
|
| |
| |
| if client_config is not None: |
| if client_config.s3 is not None: |
| if s3_configuration is None: |
| s3_configuration = client_config.s3 |
| else: |
| |
| |
| |
| |
| s3_configuration = s3_configuration.copy() |
| s3_configuration.update(client_config.s3) |
|
|
| return s3_configuration |
|
|
| def _is_s3_service(self, service_name): |
| """Whether the service is S3 or S3 Control. |
| |
| Note that throughout this class, service_name refers to the endpoint |
| prefix, not the folder name of the service in botocore/data. For |
| S3 Control, the folder name is 's3control' but the endpoint prefix is |
| 's3-control'. |
| """ |
| return service_name in ['s3', 's3-control'] |
|
|
| def _compute_endpoint_config( |
| self, |
| service_name, |
| region_name, |
| endpoint_url, |
| is_secure, |
| endpoint_bridge, |
| s3_config, |
| ): |
| resolve_endpoint_kwargs = { |
| 'service_name': service_name, |
| 'region_name': region_name, |
| 'endpoint_url': endpoint_url, |
| 'is_secure': is_secure, |
| 'endpoint_bridge': endpoint_bridge, |
| } |
| if service_name == 's3': |
| return self._compute_s3_endpoint_config( |
| s3_config=s3_config, **resolve_endpoint_kwargs |
| ) |
| if service_name == 'sts': |
| return self._compute_sts_endpoint_config(**resolve_endpoint_kwargs) |
| return self._resolve_endpoint(**resolve_endpoint_kwargs) |
|
|
| def _compute_s3_endpoint_config( |
| self, s3_config, **resolve_endpoint_kwargs |
| ): |
| force_s3_global = self._should_force_s3_global( |
| resolve_endpoint_kwargs['region_name'], s3_config |
| ) |
| if force_s3_global: |
| resolve_endpoint_kwargs['region_name'] = None |
| endpoint_config = self._resolve_endpoint(**resolve_endpoint_kwargs) |
| self._set_region_if_custom_s3_endpoint( |
| endpoint_config, resolve_endpoint_kwargs['endpoint_bridge'] |
| ) |
| |
| |
| |
| |
| |
| if force_s3_global and endpoint_config['region_name'] == 'aws-global': |
| endpoint_config['region_name'] = 'us-east-1' |
| return endpoint_config |
|
|
| def _should_force_s3_global(self, region_name, s3_config): |
| s3_regional_config = 'legacy' |
| if s3_config and 'us_east_1_regional_endpoint' in s3_config: |
| s3_regional_config = s3_config['us_east_1_regional_endpoint'] |
| self._validate_s3_regional_config(s3_regional_config) |
|
|
| is_global_region = region_name in ('us-east-1', None) |
| return s3_regional_config == 'legacy' and is_global_region |
|
|
| def _validate_s3_regional_config(self, config_val): |
| if config_val not in VALID_REGIONAL_ENDPOINTS_CONFIG: |
| raise botocore.exceptions.InvalidS3UsEast1RegionalEndpointConfigError( |
| s3_us_east_1_regional_endpoint_config=config_val |
| ) |
|
|
| def _set_region_if_custom_s3_endpoint( |
| self, endpoint_config, endpoint_bridge |
| ): |
| |
| |
| |
| if ( |
| endpoint_config['signing_region'] is None |
| and endpoint_config['region_name'] is None |
| ): |
| endpoint = endpoint_bridge.resolve('s3') |
| endpoint_config['signing_region'] = endpoint['signing_region'] |
| endpoint_config['region_name'] = endpoint['region_name'] |
|
|
| def _compute_sts_endpoint_config(self, **resolve_endpoint_kwargs): |
| endpoint_config = self._resolve_endpoint(**resolve_endpoint_kwargs) |
| if self._should_set_global_sts_endpoint( |
| resolve_endpoint_kwargs['region_name'], |
| resolve_endpoint_kwargs['endpoint_url'], |
| endpoint_config, |
| ): |
| self._set_global_sts_endpoint( |
| endpoint_config, resolve_endpoint_kwargs['is_secure'] |
| ) |
| return endpoint_config |
|
|
| def _should_set_global_sts_endpoint( |
| self, region_name, endpoint_url, endpoint_config |
| ): |
| has_variant_tags = endpoint_config and endpoint_config.get( |
| 'metadata', {} |
| ).get('tags') |
| if endpoint_url or has_variant_tags: |
| return False |
| return ( |
| self._get_sts_regional_endpoints_config() == 'legacy' |
| and region_name in LEGACY_GLOBAL_STS_REGIONS |
| ) |
|
|
| def _get_sts_regional_endpoints_config(self): |
| sts_regional_endpoints_config = self._config_store.get_config_variable( |
| 'sts_regional_endpoints' |
| ) |
| if not sts_regional_endpoints_config: |
| sts_regional_endpoints_config = 'legacy' |
| if ( |
| sts_regional_endpoints_config |
| not in VALID_REGIONAL_ENDPOINTS_CONFIG |
| ): |
| raise botocore.exceptions.InvalidSTSRegionalEndpointsConfigError( |
| sts_regional_endpoints_config=sts_regional_endpoints_config |
| ) |
| return sts_regional_endpoints_config |
|
|
| def _set_global_sts_endpoint(self, endpoint_config, is_secure): |
| scheme = 'https' if is_secure else 'http' |
| endpoint_config['endpoint_url'] = '%s://sts.amazonaws.com' % scheme |
| endpoint_config['signing_region'] = 'us-east-1' |
|
|
| def _resolve_endpoint( |
| self, |
| service_name, |
| region_name, |
| endpoint_url, |
| is_secure, |
| endpoint_bridge, |
| ): |
| return endpoint_bridge.resolve( |
| service_name, region_name, endpoint_url, is_secure |
| ) |
|
|
| def _compute_socket_options(self, scoped_config, client_config=None): |
| |
| |
| socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] |
| client_keepalive = client_config and client_config.tcp_keepalive |
| scoped_keepalive = scoped_config and self._ensure_boolean( |
| scoped_config.get("tcp_keepalive", False) |
| ) |
| |
| if client_keepalive or scoped_keepalive: |
| socket_options.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)) |
| return socket_options |
|
|
| def _compute_retry_config(self, config_kwargs): |
| self._compute_retry_max_attempts(config_kwargs) |
| self._compute_retry_mode(config_kwargs) |
|
|
| def _compute_retry_max_attempts(self, config_kwargs): |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| retries = config_kwargs.get('retries') |
| if retries is not None: |
| if 'total_max_attempts' in retries: |
| retries.pop('max_attempts', None) |
| return |
| if 'max_attempts' in retries: |
| value = retries.pop('max_attempts') |
| |
| |
| |
| retries['total_max_attempts'] = value + 1 |
| return |
| |
| |
| |
| max_attempts = self._config_store.get_config_variable('max_attempts') |
| if max_attempts is not None: |
| if retries is None: |
| retries = {} |
| config_kwargs['retries'] = retries |
| retries['total_max_attempts'] = max_attempts |
|
|
| def _compute_retry_mode(self, config_kwargs): |
| retries = config_kwargs.get('retries') |
| if retries is None: |
| retries = {} |
| config_kwargs['retries'] = retries |
| elif 'mode' in retries: |
| |
| |
| return |
| retry_mode = self._config_store.get_config_variable('retry_mode') |
| if retry_mode is None: |
| retry_mode = 'legacy' |
| retries['mode'] = retry_mode |
|
|
| def _compute_connect_timeout(self, config_kwargs): |
| |
| |
| |
| connect_timeout = config_kwargs.get('connect_timeout') |
| if connect_timeout is not None: |
| return |
| connect_timeout = self._config_store.get_config_variable( |
| 'connect_timeout' |
| ) |
| if connect_timeout: |
| config_kwargs['connect_timeout'] = connect_timeout |
|
|
| def _ensure_boolean(self, val): |
| if isinstance(val, bool): |
| return val |
| else: |
| return val.lower() == 'true' |
|
|
| def _build_endpoint_resolver( |
| self, |
| endpoints_ruleset_data, |
| partition_data, |
| client_config, |
| service_model, |
| endpoint_region_name, |
| region_name, |
| endpoint_url, |
| endpoint, |
| is_secure, |
| endpoint_bridge, |
| event_emitter, |
| ): |
| if endpoints_ruleset_data is None: |
| return None |
|
|
| |
| |
| |
| |
| |
| s3_config_raw = self.compute_s3_config(client_config) or {} |
| service_name_raw = service_model.endpoint_prefix |
| |
| |
| if service_name_raw in ['s3', 'sts'] or region_name is None: |
| eprv2_region_name = endpoint_region_name |
| else: |
| eprv2_region_name = region_name |
| resolver_builtins = self.compute_endpoint_resolver_builtin_defaults( |
| region_name=eprv2_region_name, |
| service_name=service_name_raw, |
| s3_config=s3_config_raw, |
| endpoint_bridge=endpoint_bridge, |
| client_endpoint_url=endpoint_url, |
| legacy_endpoint_url=endpoint.host, |
| ) |
| |
| |
| |
| |
| |
| client_context = ( |
| s3_config_raw if self._is_s3_service(service_name_raw) else {} |
| ) |
| sig_version = ( |
| client_config.signature_version |
| if client_config is not None |
| else None |
| ) |
| return EndpointRulesetResolver( |
| endpoint_ruleset_data=endpoints_ruleset_data, |
| partition_data=partition_data, |
| service_model=service_model, |
| builtins=resolver_builtins, |
| client_context=client_context, |
| event_emitter=event_emitter, |
| use_ssl=is_secure, |
| requested_auth_scheme=sig_version, |
| ) |
|
|
| def compute_endpoint_resolver_builtin_defaults( |
| self, |
| region_name, |
| service_name, |
| s3_config, |
| endpoint_bridge, |
| client_endpoint_url, |
| legacy_endpoint_url, |
| ): |
| |
| |
| |
| if client_endpoint_url: |
| given_endpoint = client_endpoint_url |
| |
| |
| |
| elif not endpoint_bridge.resolver_uses_builtin_data(): |
| given_endpoint = legacy_endpoint_url |
| else: |
| given_endpoint = None |
|
|
| |
| |
| |
| |
| |
| if s3_config.get('use_accelerate_endpoint', False): |
| force_path_style = False |
| elif client_endpoint_url is not None and not is_s3_accelerate_url( |
| client_endpoint_url |
| ): |
| force_path_style = s3_config.get('addressing_style') != 'virtual' |
| else: |
| force_path_style = s3_config.get('addressing_style') == 'path' |
|
|
| return { |
| EPRBuiltins.AWS_REGION: region_name, |
| EPRBuiltins.AWS_USE_FIPS: ( |
| |
| given_endpoint is None |
| |
| |
| and endpoint_bridge._resolve_endpoint_variant_config_var( |
| 'use_fips_endpoint' |
| ) |
| or False |
| ), |
| EPRBuiltins.AWS_USE_DUALSTACK: ( |
| |
| given_endpoint is None |
| |
| |
| and endpoint_bridge._resolve_use_dualstack_endpoint( |
| service_name |
| ) |
| or False |
| ), |
| EPRBuiltins.AWS_STS_USE_GLOBAL_ENDPOINT: ( |
| self._should_set_global_sts_endpoint( |
| region_name=region_name, |
| endpoint_url=None, |
| endpoint_config=None, |
| ) |
| ), |
| EPRBuiltins.AWS_S3_USE_GLOBAL_ENDPOINT: ( |
| self._should_force_s3_global(region_name, s3_config) |
| ), |
| EPRBuiltins.AWS_S3_ACCELERATE: s3_config.get( |
| 'use_accelerate_endpoint', False |
| ), |
| EPRBuiltins.AWS_S3_FORCE_PATH_STYLE: force_path_style, |
| EPRBuiltins.AWS_S3_USE_ARN_REGION: s3_config.get( |
| 'use_arn_region', True |
| ), |
| EPRBuiltins.AWS_S3CONTROL_USE_ARN_REGION: s3_config.get( |
| 'use_arn_region', False |
| ), |
| EPRBuiltins.AWS_S3_DISABLE_MRAP: s3_config.get( |
| 's3_disable_multiregion_access_points', False |
| ), |
| EPRBuiltins.SDK_ENDPOINT: given_endpoint, |
| } |
|
|