Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion localstack/aws/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@

from botocore.model import OperationModel
from botocore.parsers import ResponseParser, ResponseParserFactory
from werkzeug import Response
from werkzeug import Request, Response

from localstack.aws.api import CommonServiceException, ServiceException, ServiceResponse
from localstack.aws.gateway import Gateway
from localstack.http.client import HttpClient
from localstack.runtime import hooks
from localstack.utils.patch import patch

Expand Down Expand Up @@ -220,3 +222,21 @@ def raise_service_exception(response: Response, parsed_response: Dict) -> None:
"""
if service_exception := parse_service_exception(response, parsed_response):
raise service_exception


class GatewayClient(HttpClient):
"""
HttpClient implementation that injects the request directly into the Gateway.
"""

gateway: Gateway

def __init__(self, gateway: Gateway):
self.gateway = gateway

def request(self, request: Request, server: str | None = None) -> Response:
from localstack.http.response import Response

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: This import should be at the top of the file with other imports


response = Response()
self.gateway.process(request, response)
return response
7 changes: 2 additions & 5 deletions localstack/aws/serving/edge.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
from localstack.http.hypercorn import GatewayServer
from localstack.runtime.shutdown import SHUTDOWN_HANDLERS
from localstack.services.plugins import SERVICE_PLUGINS


def serve_gateway(bind_address, port, use_ssl, asynchronous=False):
"""
Implementation of the edge.do_start_edge_proxy interface to start a Hypercorn server instance serving the
LocalstackAwsGateway.
"""
from localstack.aws.app import LocalstackAwsGateway

gateway = LocalstackAwsGateway(SERVICE_PLUGINS)
from localstack.runtime import components

# start serving gateway
server = GatewayServer(gateway, port, bind_address, use_ssl)
server = GatewayServer(components.gateway, port, bind_address, use_ssl)
server.start()

# with the current way the infrastructure is started, this is the easiest way to shut down the server correctly
Expand Down
57 changes: 57 additions & 0 deletions localstack/runtime/components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Core components of the runtime available as global singletons.
"""
from functools import cached_property
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
from localstack.aws.app import LocalstackAwsGateway
from localstack.services.plugins import ServicePluginManager

gateway: "LocalstackAwsGateway"
"""The Gateway processes incoming HTTP request and dispatches them to AWS services or internal resources"""

service_plugin_manager: "ServicePluginManager"
"""The plugin manager """

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: The docstring for service_plugin_manager is incomplete



class RuntimeContainer:
"""
Class that provides the core runtime components as singleton factories.
"""

@cached_property
def service_plugin_manager(self) -> "ServicePluginManager":
# TODO: migrate creation here
from localstack.services.plugins import SERVICE_PLUGINS

return SERVICE_PLUGINS

@cached_property
def gateway(self) -> "LocalstackAwsGateway":
from localstack.aws.app import LocalstackAwsGateway

return LocalstackAwsGateway(self.service_plugin_manager)


_container = RuntimeContainer()


def __getattr__(name) -> Any:
"""
Provides dynamic and lazy access to runtime components. Can be used like this::

from localstack.runtime import components

assert isinstance(components.gateway, LocalstackAwsGateway)

or even::

from localstack.runtime.components import gateway

assert isinstance(gateway, LocalstackAwsGateway)

:param name: the component to get
:return: the component if it exists
"""
return getattr(_container, name)
9 changes: 6 additions & 3 deletions localstack/services/s3/virtual_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
from urllib.parse import urlsplit, urlunsplit

from localstack import config
from localstack.aws.client import GatewayClient
from localstack.constants import LOCALHOST_HOSTNAME
from localstack.http import Request, Response
from localstack.http.client import SimpleStreamingRequestsClient
from localstack.http.proxy import Proxy
from localstack.runtime import hooks
from localstack.services.edge import ROUTER
Expand Down Expand Up @@ -54,15 +54,18 @@ def __call__(self, request: Request, **kwargs) -> Response:

def _create_proxy(self) -> Proxy:
"""
Factory for creating proxy instance used when proxying s3 calls.
Factory for creating proxy instance used when proxying s3 calls, injects directly into the handler
chain.

:return: a proxy instance
"""
from localstack.runtime import components

return Proxy(
forward_base_url=config.get_edge_url(),
# do not preserve the Host when forwarding (to avoid an endless loop)
preserve_host=False,
client=SimpleStreamingRequestsClient(),
client=GatewayClient(components.gateway),
)

@staticmethod
Expand Down