22import logging
33from urllib .parse import urlsplit , urlunsplit
44
5- from localstack . config import LEGACY_S3_PROVIDER
5+ from localstack import config
66from localstack .constants import LOCALHOST_HOSTNAME
77from localstack .http import Request , Response
88from localstack .http .proxy import Proxy
1313
1414LOG = logging .getLogger (__name__ )
1515
16- # virtual-host style: https://{bucket-name}.s3.{region}.localhost.localstack.cloud.com/{key-name}
17- VHOST_REGEX_PATTERN = f"<regex('.*'):bucket>.s3.<regex('({ AWS_REGION_REGEX } \\ .)?'):region>{ LOCALHOST_HOSTNAME } <regex('(?::\\ d+)?'):port>"
16+ # virtual-host style: https://{bucket-name}.s3.{region?}.{domain}:{port?}/{key-name}
17+ # ex: https://{bucket-name}.s3.{region}.localhost.localstack.cloud.com:4566/{key-name}
18+ # ex: https://{bucket-name}.s3.{region}.amazonaws.com/{key-name}
19+ VHOST_REGEX_PATTERN = f"<regex('.*'):bucket>.s3.<regex('({ AWS_REGION_REGEX } \\ .)?'):region><regex('.*'):domain><regex('(?::\\ d+)?'):port>"
1820
1921# path addressed request with the region in the hostname
2022# https://s3.{region}.localhost.localstack.cloud.com/{bucket-name}/{key-name}
2123PATH_WITH_REGION_PATTERN = (
22- f"s3.<regex('({ AWS_REGION_REGEX } \\ .)'):region>{ LOCALHOST_HOSTNAME } <regex('(?::\\ d+)?'):port>"
24+ f"s3.<regex('({ AWS_REGION_REGEX } \\ .)'):region><regex('.*'):domain> <regex('(?::\\ d+)?'):port>"
2325)
2426
2527
@@ -31,7 +33,7 @@ class S3VirtualHostProxyHandler:
3133
3234 def __call__ (self , request : Request , ** kwargs ) -> Response :
3335 # TODO region pattern currently not working -> removing it from url
34- rewritten_url = self ._rewrite_url (request .url , kwargs . get ( "bucket" ), kwargs . get ( "region" ) )
36+ rewritten_url = self ._rewrite_url (url = request .url , ** kwargs )
3537
3638 LOG .debug (f"Rewritten original host url: { request .url } to path-style url: { rewritten_url } " )
3739
@@ -41,7 +43,7 @@ def __call__(self, request: Request, **kwargs) -> Response:
4143 copied_headers [S3_VIRTUAL_HOST_FORWARDED_HEADER ] = request .headers ["host" ]
4244 # do not preserve the Host when forwarding (to avoid an endless loop)
4345 with Proxy (
44- forward_base_url = f" { forward_to_url . scheme } :// { forward_to_url . netloc } " ,
46+ forward_base_url = config . get_edge_url () ,
4547 preserve_host = False ,
4648 ) as proxy :
4749 forwarded = proxy .forward (
@@ -53,18 +55,21 @@ def __call__(self, request: Request, **kwargs) -> Response:
5355 return forwarded
5456
5557 @staticmethod
56- def _rewrite_url (url : str , bucket : str , region : str ) -> str :
58+ def _rewrite_url (url : str , domain : str , bucket : str , region : str , port : str , ** kwargs ) -> str :
5759 """
5860 Rewrites the url so that it can be forwarded to moto. Used for vhost-style and for any url that contains the region.
5961
6062 For vhost style: removes the bucket-name from the host-name and adds it as path
61- E.g. http://my-bucket.s3.localhost.localstack.cloud:4566 -> http://s3.localhost.localstack.cloud:4566/my-bucket
63+ E.g. https://bucket.s3.localhost.localstack.cloud:4566 -> https://s3.localhost.localstack.cloud:4566/bucket
64+ E.g. https://bucket.s3.amazonaws.com -> https://s3.localhost.localstack.cloud:4566/bucket
6265
6366 If the region is contained in the host-name we remove it (for now) as moto cannot handle the region correctly
6467
6568 :param url: the original url
69+ :param domain: the domain name
6670 :param bucket: the bucket name
6771 :param region: the region name
72+ :param port: the port number (if specified in the original request URL), or an empty string
6873 :return: re-written url as string
6974 """
7075 splitted = urlsplit (url )
@@ -79,10 +84,17 @@ def _rewrite_url(url: str, bucket: str, region: str) -> str:
7984 if region :
8085 netloc = netloc .replace (f"{ region } " , "" )
8186
87+ # the user can specify whatever domain & port he wants in the Host header
88+ # we need to make sure we're redirecting the request to our edge URL, possibly s3.localhost.localstack.cloud
89+ host = f"{ domain } :{ port } " if port else domain
90+ edge_host = f"{ LOCALHOST_HOSTNAME } :{ config .get_edge_port_http ()} "
91+ if host != edge_host :
92+ netloc = netloc .replace (host , edge_host )
93+
8294 return urlunsplit ((splitted .scheme , netloc , path , splitted .query , splitted .fragment ))
8395
8496
85- @hooks .on_infra_ready (should_load = not LEGACY_S3_PROVIDER )
97+ @hooks .on_infra_ready (should_load = not config . LEGACY_S3_PROVIDER )
8698def register_virtual_host_routes ():
8799 """
88100 Registers the S3 virtual host handler into the edge router.
0 commit comments