From e3eb4d1809149447c87a27fb47828a1ea95fc7d2 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Sun, 21 Jul 2019 21:31:19 -0500 Subject: [PATCH 1/4] Cleanup host parsing. --- moto/server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/moto/server.py b/moto/server.py index 971589cac..7e251e2f5 100644 --- a/moto/server.py +++ b/moto/server.py @@ -95,13 +95,14 @@ class DomainDispatcherApplication(object): if six.PY3 and isinstance(path_info, six.binary_type): path_info = path_info.decode('utf-8') + host = None if path_info.startswith("/moto-api") or path_info == "/favicon.ico": host = "moto_api" elif path_info.startswith("/latest/meta-data/"): host = "instance_metadata" else: host = environ['HTTP_HOST'].split(':')[0] - if host in {'localhost', 'motoserver'} or host.startswith("192.168."): + if host is None: service, region = self.infer_service_region(environ) if service == 'dynamodb': if environ['HTTP_X_AMZ_TARGET'].startswith('DynamoDBStreams'): From 4dd2b66b04e85937b6390aac80c35d991da71047 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Sun, 21 Jul 2019 22:07:58 -0500 Subject: [PATCH 2/4] Refactor backend parsing. --- moto/server.py | 33 +++++++++++++++++---------------- tests/test_core/test_server.py | 6 ------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/moto/server.py b/moto/server.py index 7e251e2f5..183c8c1f7 100644 --- a/moto/server.py +++ b/moto/server.py @@ -58,8 +58,6 @@ class DomainDispatcherApplication(object): if re.match(url_base, 'http://%s' % host): return backend_name - raise RuntimeError('Invalid host: "%s"' % host) - def infer_service_region(self, environ): auth = environ.get('HTTP_AUTHORIZATION') if auth: @@ -95,29 +93,32 @@ class DomainDispatcherApplication(object): if six.PY3 and isinstance(path_info, six.binary_type): path_info = path_info.decode('utf-8') - host = None if path_info.startswith("/moto-api") or path_info == "/favicon.ico": host = "moto_api" elif path_info.startswith("/latest/meta-data/"): host = "instance_metadata" else: host = environ['HTTP_HOST'].split(':')[0] - if host is None: - service, region = self.infer_service_region(environ) - if service == 'dynamodb': - if environ['HTTP_X_AMZ_TARGET'].startswith('DynamoDBStreams'): - host = 'dynamodbstreams' - else: - dynamo_api_version = environ['HTTP_X_AMZ_TARGET'].split("_")[1].split(".")[0] - # If Newer API version, use dynamodb2 - if dynamo_api_version > "20111205": - host = "dynamodb2" - else: - host = "{service}.{region}.amazonaws.com".format( - service=service, region=region) with self.lock: backend = self.get_backend_for_host(host) + if not backend: + service, region = self.infer_service_region(environ) + if service == 'dynamodb': + if environ['HTTP_X_AMZ_TARGET'].startswith('DynamoDBStreams'): + host = 'dynamodbstreams' + else: + dynamo_api_version = environ['HTTP_X_AMZ_TARGET'].split("_")[1].split(".")[0] + # If Newer API version, use dynamodb2 + if dynamo_api_version > "20111205": + host = "dynamodb2" + else: + host = "{service}.{region}.amazonaws.com".format( + service=service, region=region) + backend = self.get_backend_for_host(host) + if not backend: + raise RuntimeError('Invalid host: "%s"' % host) + app = self.app_instances.get(backend, None) if app is None: app = self.create_app(backend) diff --git a/tests/test_core/test_server.py b/tests/test_core/test_server.py index b7290e351..bd00b17c3 100644 --- a/tests/test_core/test_server.py +++ b/tests/test_core/test_server.py @@ -38,12 +38,6 @@ def test_domain_dispatched(): keys[0].should.equal('EmailResponse.dispatch') -def test_domain_without_matches(): - dispatcher = DomainDispatcherApplication(create_backend_app) - dispatcher.get_application.when.called_with( - {"HTTP_HOST": "not-matching-anything.com"}).should.throw(RuntimeError) - - def test_domain_dispatched_with_service(): # If we pass a particular service, always return that. dispatcher = DomainDispatcherApplication(create_backend_app, service="s3") From 5de95b026ac7474d8edbce20666531388babc912 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Sun, 21 Jul 2019 22:30:35 -0500 Subject: [PATCH 3/4] More refactoring. --- moto/server.py | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/moto/server.py b/moto/server.py index 183c8c1f7..907681f09 100644 --- a/moto/server.py +++ b/moto/server.py @@ -58,7 +58,7 @@ class DomainDispatcherApplication(object): if re.match(url_base, 'http://%s' % host): return backend_name - def infer_service_region(self, environ): + def infer_service_region_host(self, environ): auth = environ.get('HTTP_AUTHORIZATION') if auth: # Signed request @@ -68,21 +68,34 @@ class DomainDispatcherApplication(object): try: credential_scope = auth.split(",")[0].split()[1] _, _, region, service, _ = credential_scope.split("/") - return service, region except ValueError: # Signature format does not match, this is exceptional and we can't # infer a service-region. A reduced set of services still use # the deprecated SigV2, ergo prefer S3 as most likely default. # https://docs.aws.amazon.com/general/latest/gr/signature-version-2.html - return DEFAULT_SERVICE_REGION + service, region = DEFAULT_SERVICE_REGION else: # Unsigned request target = environ.get('HTTP_X_AMZ_TARGET') if target: service, _ = target.split('.', 1) - return UNSIGNED_REQUESTS.get(service, DEFAULT_SERVICE_REGION) + service, region = UNSIGNED_REQUESTS.get(service, DEFAULT_SERVICE_REGION) # S3 is the last resort when the target is also unknown - return DEFAULT_SERVICE_REGION + service, region = DEFAULT_SERVICE_REGION + + if service == 'dynamodb': + if environ['HTTP_X_AMZ_TARGET'].startswith('DynamoDBStreams'): + host = 'dynamodbstreams' + else: + dynamo_api_version = environ['HTTP_X_AMZ_TARGET'].split("_")[1].split(".")[0] + # If Newer API version, use dynamodb2 + if dynamo_api_version > "20111205": + host = "dynamodb2" + else: + host = "{service}.{region}.amazonaws.com".format( + service=service, region=region) + + return host def get_application(self, environ): path_info = environ.get('PATH_INFO', '') @@ -103,21 +116,9 @@ class DomainDispatcherApplication(object): with self.lock: backend = self.get_backend_for_host(host) if not backend: - service, region = self.infer_service_region(environ) - if service == 'dynamodb': - if environ['HTTP_X_AMZ_TARGET'].startswith('DynamoDBStreams'): - host = 'dynamodbstreams' - else: - dynamo_api_version = environ['HTTP_X_AMZ_TARGET'].split("_")[1].split(".")[0] - # If Newer API version, use dynamodb2 - if dynamo_api_version > "20111205": - host = "dynamodb2" - else: - host = "{service}.{region}.amazonaws.com".format( - service=service, region=region) + # No regular backend found; try parsing other headers + host = self.infer_service_region_host(environ) backend = self.get_backend_for_host(host) - if not backend: - raise RuntimeError('Invalid host: "%s"' % host) app = self.app_instances.get(backend, None) if app is None: From 1fb06e6a085d52e7e237a0b3d8f833739a016cfe Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Sun, 21 Jul 2019 23:03:36 -0500 Subject: [PATCH 4/4] Cleanup refactor. --- moto/server.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/moto/server.py b/moto/server.py index 907681f09..89be47093 100644 --- a/moto/server.py +++ b/moto/server.py @@ -80,8 +80,9 @@ class DomainDispatcherApplication(object): if target: service, _ = target.split('.', 1) service, region = UNSIGNED_REQUESTS.get(service, DEFAULT_SERVICE_REGION) - # S3 is the last resort when the target is also unknown - service, region = DEFAULT_SERVICE_REGION + else: + # S3 is the last resort when the target is also unknown + service, region = DEFAULT_SERVICE_REGION if service == 'dynamodb': if environ['HTTP_X_AMZ_TARGET'].startswith('DynamoDBStreams'):