We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
bug: SQS performance regression with ASF
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

Hi, I'm testing the image `localstack/localstack:0.14.5`, and setting `LEGACY_EDGE_PROXY=1` makes the following cleanup code much slower:

`for queue in client.list_queues()['QueueUrls']: client.purge_queue(QueueUrl=queue)`

`LEGACY_EDGE_PROXY=1`: `56.2 ms ± 2.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)`

`LEGACY_EDGE_PROXY=0`: `258 ms ± 17.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)`


### Expected Behavior

I expected performance to improve or not to regress.

### How are you starting LocalStack?

With a docker-compose file

### Steps To Reproduce

# Benchmark setup:

Docker-compose file:
```
version: "3.9"
services:
  localstack:
    image: localstack/localstack:latest
    environment:
      - DEFAULT_REGION=eu-west-1
      - SERVICES=s3,sns,sqs
      - LEGACY_EDGE_PROXY=1  # or 0
    ipc: host
```

```
# docker-compose rm -f localstack && docker-compose up localstack
```

```
import boto3

client = boto3.resource(
    'sqs',
    endpoint_url="http://localstack:4566/",
    region_name='eu-west-1',
    aws_secret_access_key="x",
    aws_access_key_id="x",
    use_ssl=False,
).meta.client

for i in range(10):
    client.create_queue(QueueName=str(i))

queues = client.list_queues()['QueueUrls']
assert len(queues) == 10
```

```
In [3]: %timeit for queue in client.list_queues()['QueueUrls']: client.purge_queue(QueueUrl=queue)
```

### Environment

```markdown
- OS: Ubuntu 22.04 LTS, Linux 5.15.0-40-generic #43-Ubuntu SMP Wed Jun 15 12:54:21 UTC 2022 x86_64 
- docker-compose 1.29.2
- Docker: 20.10.12, build 20.10.12-0ubuntu4
- LocalStack: 0.14.5
```


### Anything else?

Results don't improve over time, it's not a cold start issue.

https://github.com/localstack/localstack/issues/6398

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `localstack/aws/app.py`
Content:
```
1 from localstack.aws import handlers
2 from localstack.aws.handlers.metric_handler import MetricHandler
3 from localstack.aws.handlers.service_plugin import ServiceLoader
4 from localstack.services.plugins import SERVICE_PLUGINS, ServiceManager, ServicePluginManager
5 
6 from .gateway import Gateway
7 from .handlers.fallback import EmptyResponseHandler
8 from .handlers.service import ServiceRequestRouter
9 
10 
11 class LocalstackAwsGateway(Gateway):
12     def __init__(self, service_manager: ServiceManager = None) -> None:
13         super().__init__()
14 
15         # basic server components
16         self.service_manager = service_manager or ServicePluginManager()
17         self.service_request_router = ServiceRequestRouter()
18         # lazy-loads services into the router
19         load_service = ServiceLoader(self.service_manager, self.service_request_router)
20 
21         metric_collector = MetricHandler()
22         # the main request handler chain
23         self.request_handlers.extend(
24             [
25                 handlers.push_request_context,
26                 metric_collector.create_metric_handler_item,
27                 handlers.parse_service_name,  # enforce_cors and content_decoder depend on the service name
28                 handlers.enforce_cors,
29                 handlers.content_decoder,
30                 handlers.serve_localstack_resources,  # try to serve internal resources in /_localstack first
31                 handlers.serve_default_listeners,  # legacy proxy default listeners
32                 handlers.serve_edge_router_rules,
33                 # start aws handler chain
34                 handlers.inject_auth_header_if_missing,
35                 handlers.add_region_from_header,
36                 handlers.add_account_id,
37                 handlers.parse_service_request,
38                 metric_collector.record_parsed_request,
39                 handlers.serve_custom_service_request_handlers,
40                 load_service,  # once we have the service request we can make sure we load the service
41                 self.service_request_router,  # once we know the service is loaded we can route the request
42                 # if the chain is still running, set an empty response
43                 EmptyResponseHandler(404, b'{"message": "Not Found"}'),
44             ]
45         )
46 
47         # exception handlers in the chain
48         self.exception_handlers.extend(
49             [
50                 handlers.log_exception,
51                 handlers.handle_service_exception,
52                 handlers.handle_internal_failure,
53             ]
54         )
55 
56         # response post-processing
57         self.response_handlers.extend(
58             [
59                 handlers.parse_service_response,
60                 handlers.run_custom_response_handlers,
61                 handlers.add_cors_response_headers,
62                 handlers.log_response,
63                 handlers.count_service_request,
64                 handlers.pop_request_context,
65                 metric_collector.update_metric_collection,
66             ]
67         )
68 
69 
70 def main():
71     """
72     Serve the LocalstackGateway with the default configuration directly through hypercorn. This is mostly for
73     development purposes and documentation on how to serve the Gateway.
74     """
75     from .serving.hypercorn import serve
76 
77     use_ssl = True
78     port = 4566
79 
80     # serve the LocalStackAwsGateway in a dev app
81     from localstack.utils.bootstrap import setup_logging
82 
83     setup_logging()
84 
85     if use_ssl:
86         from localstack.services.generic_proxy import (
87             GenericProxy,
88             install_predefined_cert_if_available,
89         )
90 
91         install_predefined_cert_if_available()
92         _, cert_file_name, key_file_name = GenericProxy.create_ssl_cert(serial_number=port)
93         ssl_creds = (cert_file_name, key_file_name)
94     else:
95         ssl_creds = None
96 
97     gw = LocalstackAwsGateway(SERVICE_PLUGINS)
98 
99     serve(gw, use_reloader=True, port=port, ssl_creds=ssl_creds)
100 
101 
102 if __name__ == "__main__":
103     main()
104
```

Path: `localstack/aws/handlers/legacy.py`
Content:
```
1 """ Handlers for compatibility with legacy edge proxy and the quart http framework."""
2 
3 import logging
4 import re
5 from typing import Mapping
6 
7 from requests import Response as RequestsResponse
8 
9 from localstack.constants import HEADER_LOCALSTACK_EDGE_URL, HEADER_LOCALSTACK_REQUEST_URL
10 from localstack.http import Response
11 from localstack.http.request import restore_payload
12 from localstack.services.generic_proxy import ProxyListener, modify_and_forward
13 
14 from ..api import RequestContext
15 from ..chain import Handler, HandlerChain
16 from .routes import RouterHandler
17 
18 LOG = logging.getLogger(__name__)
19 
20 
21 def push_request_context(_chain: HandlerChain, context: RequestContext, _response: Response):
22     # hack for legacy compatibility. various parts of localstack access the global flask/quart/our own request
23     # context. since we're neither in a flask nor a quart context, we're pushing our own context object into their
24     # proxy objects, which is terrible, but works because mostly code just accesses "context.request", so we don't
25     # have to bother pushing a real quart/flask context.
26     import flask.globals
27     import quart.globals
28 
29     from localstack.utils.aws import request_context
30 
31     quart.globals._request_ctx_stack.push(context)
32     flask.globals._request_ctx_stack.push(context)
33     request_context.THREAD_LOCAL.request_context = context.request
34 
35 
36 def pop_request_context(_chain: HandlerChain, _context: RequestContext, _response: Response):
37     # hack for legacy compatibility
38     import flask.globals
39     import quart.globals
40 
41     from localstack.utils.aws import request_context
42 
43     quart.globals._request_ctx_stack.pop()
44     flask.globals._request_ctx_stack.pop()
45     request_context.THREAD_LOCAL.request_context = None
46 
47 
48 class EdgeRouterHandler(RouterHandler):
49     def __init__(self, respond_not_found=False) -> None:
50         from localstack.services.edge import ROUTER
51 
52         super().__init__(ROUTER, respond_not_found)
53 
54 
55 class GenericProxyHandler(Handler):
56     """
57     This handler maps HandlerChain requests to the generic proxy ProxyListener interface `forward_request`.
58     """
59 
60     def __call__(self, chain: HandlerChain, context: RequestContext, response: Response):
61         request = context.request
62 
63         # a werkzeug Request consumes form/multipart data from the socket stream, so we need to restore the payload here
64         data = restore_payload(request)
65 
66         # TODO: rethink whether this proxy handling is necessary
67         context.request.headers[HEADER_LOCALSTACK_REQUEST_URL] = context.request.base_url
68 
69         result = self.forward_request(
70             context,
71             method=request.method,
72             path=request.full_path if request.query_string else request.path,
73             data=data,
74             headers=request.headers,
75         )
76 
77         if type(result) == int:
78             chain.respond(status_code=result)
79             return
80 
81         if isinstance(result, tuple):
82             # special case for Kinesis SubscribeToShard
83             if len(result) == 2:
84                 response.status_code = 200
85                 response.set_response(result[0])
86                 response.headers.update(dict(result[1]))
87                 chain.stop()
88                 return
89 
90         if isinstance(result, RequestsResponse):
91             response.status_code = result.status_code
92             response.set_response(result.content)
93             # make sure headers are set after the content, so potential content-length headers are overwritten
94             response.headers.update(dict(result.headers))
95 
96             # make sure content-length is re-calculated correctly, unless it's a HEAD request
97             if request.method != "HEAD":
98                 length = response.calculate_content_length()
99                 if length is not None:
100                     response.headers["Content-Length"] = str(length)
101             chain.stop()
102             return
103 
104         raise ValueError("cannot create response for result %s" % result)
105 
106     def forward_request(
107         self, context: RequestContext, method: str, path: str, data: bytes, headers: Mapping
108     ):
109         raise NotImplementedError
110 
111 
112 class LegacyPluginHandler(GenericProxyHandler):
113     """
114     This adapter exposes Services that are developed as ProxyListener as Handler.
115     """
116 
117     def forward_request(
118         self, context: RequestContext, method: str, path: str, data: bytes, headers: Mapping
119     ):
120         from localstack.services.edge import do_forward_request
121 
122         # TODO: rethink whether this proxy handling is necessary
123         request = context.request
124         orig_req_url = request.headers.pop(HEADER_LOCALSTACK_REQUEST_URL, "")
125         request.headers[HEADER_LOCALSTACK_EDGE_URL] = (
126             re.sub(r"^([^:]+://[^/]+).*", r"\1", orig_req_url) or request.host_url
127         )
128 
129         return do_forward_request(
130             api=context.service.service_name,
131             method=method,
132             path=path,
133             data=data,
134             headers=headers,
135             port=None,
136         )
137 
138 
139 class _NoHandlerCalled(Exception):
140     pass
141 
142 
143 class _DummyProxyListener(ProxyListener):
144     def forward_request(self, method, path, data, headers):
145         raise _NoHandlerCalled
146 
147 
148 class DefaultListenerHandler(GenericProxyHandler):
149     """
150     Adapter that exposes the ProxyListener.DEFAULT_LISTENERS as a Handler.
151     """
152 
153     def __call__(self, chain: HandlerChain, context: RequestContext, response: Response):
154         if not ProxyListener.DEFAULT_LISTENERS:
155             return
156 
157         try:
158             super(DefaultListenerHandler, self).__call__(chain, context, response)
159         except _NoHandlerCalled:
160             # may be raised by the _DummyProxyListener, which is reached if no other listener is called,
161             # in which case we don't want to return a result or stop the chain.
162             return
163 
164     def forward_request(
165         self, context: RequestContext, method: str, path: str, data: bytes, headers: Mapping
166     ):
167         request = context.request
168 
169         return modify_and_forward(
170             method=method,
171             path=path,
172             data_bytes=data,
173             headers=headers,
174             forward_base_url=None,
175             listeners=[_DummyProxyListener()],
176             client_address=request.remote_addr,
177             server_address=request.host,
178         )
179
```

Path: `localstack/aws/handlers/__init__.py`
Content:
```
1 """ A set of common handlers to build an AWS server application."""
2 
3 from .. import chain
4 from . import analytics, auth, codec, cors, fallback, internal, legacy, logging, region, service
5 
6 enforce_cors = cors.CorsEnforcer()
7 add_cors_response_headers = cors.CorsResponseEnricher()
8 content_decoder = codec.ContentDecoder()
9 parse_service_name = service.ServiceNameParser()
10 parse_service_request = service.ServiceRequestParser()
11 add_account_id = auth.AccountIdEnricher()
12 inject_auth_header_if_missing = auth.MissingAuthHeaderInjector()
13 add_region_from_header = region.RegionContextEnricher()
14 log_exception = logging.ExceptionLogger()
15 log_response = logging.ResponseLogger()
16 count_service_request = analytics.ServiceRequestCounter()
17 handle_service_exception = service.ServiceExceptionSerializer()
18 handle_internal_failure = fallback.InternalFailureHandler()
19 serve_custom_service_request_handlers = chain.CompositeHandler()
20 serve_localstack_resources = internal.LocalstackResourceHandler()
21 run_custom_response_handlers = chain.CompositeResponseHandler()
22 parse_service_response = service.ServiceResponseParser()
23 # legacy compatibility handlers
24 serve_edge_router_rules = legacy.EdgeRouterHandler()
25 serve_default_listeners = legacy.DefaultListenerHandler()
26 pop_request_context = legacy.pop_request_context
27 push_request_context = legacy.push_request_context
28
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/localstack/aws/app.py b/localstack/aws/app.py
--- a/localstack/aws/app.py
+++ b/localstack/aws/app.py
@@ -57,6 +57,7 @@
         self.response_handlers.extend(
             [
                 handlers.parse_service_response,
+                handlers.set_close_connection_header,
                 handlers.run_custom_response_handlers,
                 handlers.add_cors_response_headers,
                 handlers.log_response,
diff --git a/localstack/aws/handlers/__init__.py b/localstack/aws/handlers/__init__.py
--- a/localstack/aws/handlers/__init__.py
+++ b/localstack/aws/handlers/__init__.py
@@ -23,5 +23,6 @@
 # legacy compatibility handlers
 serve_edge_router_rules = legacy.EdgeRouterHandler()
 serve_default_listeners = legacy.DefaultListenerHandler()
+set_close_connection_header = legacy.set_close_connection_header
 pop_request_context = legacy.pop_request_context
 push_request_context = legacy.push_request_context
diff --git a/localstack/aws/handlers/legacy.py b/localstack/aws/handlers/legacy.py
--- a/localstack/aws/handlers/legacy.py
+++ b/localstack/aws/handlers/legacy.py
@@ -45,6 +45,18 @@
     request_context.THREAD_LOCAL.request_context = None
 
 
+def set_close_connection_header(_chain: HandlerChain, context: RequestContext, response: Response):
+    """This is a hack to work around performance issues with h11 and boto. See
+    https://github.com/localstack/localstack/issues/6557"""
+    if conn := context.request.headers.get("Connection"):
+        if conn.lower() == "keep-alive":
+            # don't set Connection: close header if keep-alive is explicitly asked for
+            return
+
+    if "Connection" not in response.headers:
+        response.headers["Connection"] = "close"
+
+
 class EdgeRouterHandler(RouterHandler):
     def __init__(self, respond_not_found=False) -> None:
         from localstack.services.edge import ROUTER

{"golden_diff": "diff --git a/localstack/aws/app.py b/localstack/aws/app.py\n--- a/localstack/aws/app.py\n+++ b/localstack/aws/app.py\n@@ -57,6 +57,7 @@\n         self.response_handlers.extend(\n             [\n                 handlers.parse_service_response,\n+                handlers.set_close_connection_header,\n                 handlers.run_custom_response_handlers,\n                 handlers.add_cors_response_headers,\n                 handlers.log_response,\ndiff --git a/localstack/aws/handlers/__init__.py b/localstack/aws/handlers/__init__.py\n--- a/localstack/aws/handlers/__init__.py\n+++ b/localstack/aws/handlers/__init__.py\n@@ -23,5 +23,6 @@\n # legacy compatibility handlers\n serve_edge_router_rules = legacy.EdgeRouterHandler()\n serve_default_listeners = legacy.DefaultListenerHandler()\n+set_close_connection_header = legacy.set_close_connection_header\n pop_request_context = legacy.pop_request_context\n push_request_context = legacy.push_request_context\ndiff --git a/localstack/aws/handlers/legacy.py b/localstack/aws/handlers/legacy.py\n--- a/localstack/aws/handlers/legacy.py\n+++ b/localstack/aws/handlers/legacy.py\n@@ -45,6 +45,18 @@\n     request_context.THREAD_LOCAL.request_context = None\n \n \n+def set_close_connection_header(_chain: HandlerChain, context: RequestContext, response: Response):\n+    \"\"\"This is a hack to work around performance issues with h11 and boto. See\n+    https://github.com/localstack/localstack/issues/6557\"\"\"\n+    if conn := context.request.headers.get(\"Connection\"):\n+        if conn.lower() == \"keep-alive\":\n+            # don't set Connection: close header if keep-alive is explicitly asked for\n+            return\n+\n+    if \"Connection\" not in response.headers:\n+        response.headers[\"Connection\"] = \"close\"\n+\n+\n class EdgeRouterHandler(RouterHandler):\n     def __init__(self, respond_not_found=False) -> None:\n         from localstack.services.edge import ROUTER\n", "issue": "bug: SQS performance regression with ASF\n### Is there an existing issue for this?\n\n- [X] I have searched the existing issues\n\n### Current Behavior\n\nHi, I'm testing the image `localstack/localstack:0.14.5`, and setting `LEGACY_EDGE_PROXY=1` makes the following cleanup code much slower:\r\n\r\n`for queue in client.list_queues()['QueueUrls']: client.purge_queue(QueueUrl=queue)`\r\n\r\n`LEGACY_EDGE_PROXY=1`: `56.2 ms \u00b1 2.9 ms per loop (mean \u00b1 std. dev. of 7 runs, 10 loops each)`\r\n\r\n`LEGACY_EDGE_PROXY=0`: `258 ms \u00b1 17.6 ms per loop (mean \u00b1 std. dev. of 7 runs, 1 loop each)`\r\n\n\n### Expected Behavior\n\nI expected performance to improve or not to regress.\n\n### How are you starting LocalStack?\n\nWith a docker-compose file\n\n### Steps To Reproduce\n\n# Benchmark setup:\r\n\r\nDocker-compose file:\r\n```\r\nversion: \"3.9\"\r\nservices:\r\n  localstack:\r\n    image: localstack/localstack:latest\r\n    environment:\r\n      - DEFAULT_REGION=eu-west-1\r\n      - SERVICES=s3,sns,sqs\r\n      - LEGACY_EDGE_PROXY=1  # or 0\r\n    ipc: host\r\n```\r\n\r\n```\r\n# docker-compose rm -f localstack && docker-compose up localstack\r\n```\r\n\r\n```\r\nimport boto3\r\n\r\nclient = boto3.resource(\r\n    'sqs',\r\n    endpoint_url=\"http://localstack:4566/\",\r\n    region_name='eu-west-1',\r\n    aws_secret_access_key=\"x\",\r\n    aws_access_key_id=\"x\",\r\n    use_ssl=False,\r\n).meta.client\r\n\r\nfor i in range(10):\r\n    client.create_queue(QueueName=str(i))\r\n\r\nqueues = client.list_queues()['QueueUrls']\r\nassert len(queues) == 10\r\n```\r\n\r\n```\r\nIn [3]: %timeit for queue in client.list_queues()['QueueUrls']: client.purge_queue(QueueUrl=queue)\r\n```\n\n### Environment\n\n```markdown\n- OS: Ubuntu 22.04 LTS, Linux 5.15.0-40-generic #43-Ubuntu SMP Wed Jun 15 12:54:21 UTC 2022 x86_64 \r\n- docker-compose 1.29.2\r\n- Docker: 20.10.12, build 20.10.12-0ubuntu4\r\n- LocalStack: 0.14.5\n```\n\n\n### Anything else?\n\nResults don't improve over time, it's not a cold start issue.\r\n\r\nhttps://github.com/localstack/localstack/issues/6398\n", "before_files": [{"content": "from localstack.aws import handlers\nfrom localstack.aws.handlers.metric_handler import MetricHandler\nfrom localstack.aws.handlers.service_plugin import ServiceLoader\nfrom localstack.services.plugins import SERVICE_PLUGINS, ServiceManager, ServicePluginManager\n\nfrom .gateway import Gateway\nfrom .handlers.fallback import EmptyResponseHandler\nfrom .handlers.service import ServiceRequestRouter\n\n\nclass LocalstackAwsGateway(Gateway):\n    def __init__(self, service_manager: ServiceManager = None) -> None:\n        super().__init__()\n\n        # basic server components\n        self.service_manager = service_manager or ServicePluginManager()\n        self.service_request_router = ServiceRequestRouter()\n        # lazy-loads services into the router\n        load_service = ServiceLoader(self.service_manager, self.service_request_router)\n\n        metric_collector = MetricHandler()\n        # the main request handler chain\n        self.request_handlers.extend(\n            [\n                handlers.push_request_context,\n                metric_collector.create_metric_handler_item,\n                handlers.parse_service_name,  # enforce_cors and content_decoder depend on the service name\n                handlers.enforce_cors,\n                handlers.content_decoder,\n                handlers.serve_localstack_resources,  # try to serve internal resources in /_localstack first\n                handlers.serve_default_listeners,  # legacy proxy default listeners\n                handlers.serve_edge_router_rules,\n                # start aws handler chain\n                handlers.inject_auth_header_if_missing,\n                handlers.add_region_from_header,\n                handlers.add_account_id,\n                handlers.parse_service_request,\n                metric_collector.record_parsed_request,\n                handlers.serve_custom_service_request_handlers,\n                load_service,  # once we have the service request we can make sure we load the service\n                self.service_request_router,  # once we know the service is loaded we can route the request\n                # if the chain is still running, set an empty response\n                EmptyResponseHandler(404, b'{\"message\": \"Not Found\"}'),\n            ]\n        )\n\n        # exception handlers in the chain\n        self.exception_handlers.extend(\n            [\n                handlers.log_exception,\n                handlers.handle_service_exception,\n                handlers.handle_internal_failure,\n            ]\n        )\n\n        # response post-processing\n        self.response_handlers.extend(\n            [\n                handlers.parse_service_response,\n                handlers.run_custom_response_handlers,\n                handlers.add_cors_response_headers,\n                handlers.log_response,\n                handlers.count_service_request,\n                handlers.pop_request_context,\n                metric_collector.update_metric_collection,\n            ]\n        )\n\n\ndef main():\n    \"\"\"\n    Serve the LocalstackGateway with the default configuration directly through hypercorn. This is mostly for\n    development purposes and documentation on how to serve the Gateway.\n    \"\"\"\n    from .serving.hypercorn import serve\n\n    use_ssl = True\n    port = 4566\n\n    # serve the LocalStackAwsGateway in a dev app\n    from localstack.utils.bootstrap import setup_logging\n\n    setup_logging()\n\n    if use_ssl:\n        from localstack.services.generic_proxy import (\n            GenericProxy,\n            install_predefined_cert_if_available,\n        )\n\n        install_predefined_cert_if_available()\n        _, cert_file_name, key_file_name = GenericProxy.create_ssl_cert(serial_number=port)\n        ssl_creds = (cert_file_name, key_file_name)\n    else:\n        ssl_creds = None\n\n    gw = LocalstackAwsGateway(SERVICE_PLUGINS)\n\n    serve(gw, use_reloader=True, port=port, ssl_creds=ssl_creds)\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "localstack/aws/app.py"}, {"content": "\"\"\" Handlers for compatibility with legacy edge proxy and the quart http framework.\"\"\"\n\nimport logging\nimport re\nfrom typing import Mapping\n\nfrom requests import Response as RequestsResponse\n\nfrom localstack.constants import HEADER_LOCALSTACK_EDGE_URL, HEADER_LOCALSTACK_REQUEST_URL\nfrom localstack.http import Response\nfrom localstack.http.request import restore_payload\nfrom localstack.services.generic_proxy import ProxyListener, modify_and_forward\n\nfrom ..api import RequestContext\nfrom ..chain import Handler, HandlerChain\nfrom .routes import RouterHandler\n\nLOG = logging.getLogger(__name__)\n\n\ndef push_request_context(_chain: HandlerChain, context: RequestContext, _response: Response):\n    # hack for legacy compatibility. various parts of localstack access the global flask/quart/our own request\n    # context. since we're neither in a flask nor a quart context, we're pushing our own context object into their\n    # proxy objects, which is terrible, but works because mostly code just accesses \"context.request\", so we don't\n    # have to bother pushing a real quart/flask context.\n    import flask.globals\n    import quart.globals\n\n    from localstack.utils.aws import request_context\n\n    quart.globals._request_ctx_stack.push(context)\n    flask.globals._request_ctx_stack.push(context)\n    request_context.THREAD_LOCAL.request_context = context.request\n\n\ndef pop_request_context(_chain: HandlerChain, _context: RequestContext, _response: Response):\n    # hack for legacy compatibility\n    import flask.globals\n    import quart.globals\n\n    from localstack.utils.aws import request_context\n\n    quart.globals._request_ctx_stack.pop()\n    flask.globals._request_ctx_stack.pop()\n    request_context.THREAD_LOCAL.request_context = None\n\n\nclass EdgeRouterHandler(RouterHandler):\n    def __init__(self, respond_not_found=False) -> None:\n        from localstack.services.edge import ROUTER\n\n        super().__init__(ROUTER, respond_not_found)\n\n\nclass GenericProxyHandler(Handler):\n    \"\"\"\n    This handler maps HandlerChain requests to the generic proxy ProxyListener interface `forward_request`.\n    \"\"\"\n\n    def __call__(self, chain: HandlerChain, context: RequestContext, response: Response):\n        request = context.request\n\n        # a werkzeug Request consumes form/multipart data from the socket stream, so we need to restore the payload here\n        data = restore_payload(request)\n\n        # TODO: rethink whether this proxy handling is necessary\n        context.request.headers[HEADER_LOCALSTACK_REQUEST_URL] = context.request.base_url\n\n        result = self.forward_request(\n            context,\n            method=request.method,\n            path=request.full_path if request.query_string else request.path,\n            data=data,\n            headers=request.headers,\n        )\n\n        if type(result) == int:\n            chain.respond(status_code=result)\n            return\n\n        if isinstance(result, tuple):\n            # special case for Kinesis SubscribeToShard\n            if len(result) == 2:\n                response.status_code = 200\n                response.set_response(result[0])\n                response.headers.update(dict(result[1]))\n                chain.stop()\n                return\n\n        if isinstance(result, RequestsResponse):\n            response.status_code = result.status_code\n            response.set_response(result.content)\n            # make sure headers are set after the content, so potential content-length headers are overwritten\n            response.headers.update(dict(result.headers))\n\n            # make sure content-length is re-calculated correctly, unless it's a HEAD request\n            if request.method != \"HEAD\":\n                length = response.calculate_content_length()\n                if length is not None:\n                    response.headers[\"Content-Length\"] = str(length)\n            chain.stop()\n            return\n\n        raise ValueError(\"cannot create response for result %s\" % result)\n\n    def forward_request(\n        self, context: RequestContext, method: str, path: str, data: bytes, headers: Mapping\n    ):\n        raise NotImplementedError\n\n\nclass LegacyPluginHandler(GenericProxyHandler):\n    \"\"\"\n    This adapter exposes Services that are developed as ProxyListener as Handler.\n    \"\"\"\n\n    def forward_request(\n        self, context: RequestContext, method: str, path: str, data: bytes, headers: Mapping\n    ):\n        from localstack.services.edge import do_forward_request\n\n        # TODO: rethink whether this proxy handling is necessary\n        request = context.request\n        orig_req_url = request.headers.pop(HEADER_LOCALSTACK_REQUEST_URL, \"\")\n        request.headers[HEADER_LOCALSTACK_EDGE_URL] = (\n            re.sub(r\"^([^:]+://[^/]+).*\", r\"\\1\", orig_req_url) or request.host_url\n        )\n\n        return do_forward_request(\n            api=context.service.service_name,\n            method=method,\n            path=path,\n            data=data,\n            headers=headers,\n            port=None,\n        )\n\n\nclass _NoHandlerCalled(Exception):\n    pass\n\n\nclass _DummyProxyListener(ProxyListener):\n    def forward_request(self, method, path, data, headers):\n        raise _NoHandlerCalled\n\n\nclass DefaultListenerHandler(GenericProxyHandler):\n    \"\"\"\n    Adapter that exposes the ProxyListener.DEFAULT_LISTENERS as a Handler.\n    \"\"\"\n\n    def __call__(self, chain: HandlerChain, context: RequestContext, response: Response):\n        if not ProxyListener.DEFAULT_LISTENERS:\n            return\n\n        try:\n            super(DefaultListenerHandler, self).__call__(chain, context, response)\n        except _NoHandlerCalled:\n            # may be raised by the _DummyProxyListener, which is reached if no other listener is called,\n            # in which case we don't want to return a result or stop the chain.\n            return\n\n    def forward_request(\n        self, context: RequestContext, method: str, path: str, data: bytes, headers: Mapping\n    ):\n        request = context.request\n\n        return modify_and_forward(\n            method=method,\n            path=path,\n            data_bytes=data,\n            headers=headers,\n            forward_base_url=None,\n            listeners=[_DummyProxyListener()],\n            client_address=request.remote_addr,\n            server_address=request.host,\n        )\n", "path": "localstack/aws/handlers/legacy.py"}, {"content": "\"\"\" A set of common handlers to build an AWS server application.\"\"\"\n\nfrom .. import chain\nfrom . import analytics, auth, codec, cors, fallback, internal, legacy, logging, region, service\n\nenforce_cors = cors.CorsEnforcer()\nadd_cors_response_headers = cors.CorsResponseEnricher()\ncontent_decoder = codec.ContentDecoder()\nparse_service_name = service.ServiceNameParser()\nparse_service_request = service.ServiceRequestParser()\nadd_account_id = auth.AccountIdEnricher()\ninject_auth_header_if_missing = auth.MissingAuthHeaderInjector()\nadd_region_from_header = region.RegionContextEnricher()\nlog_exception = logging.ExceptionLogger()\nlog_response = logging.ResponseLogger()\ncount_service_request = analytics.ServiceRequestCounter()\nhandle_service_exception = service.ServiceExceptionSerializer()\nhandle_internal_failure = fallback.InternalFailureHandler()\nserve_custom_service_request_handlers = chain.CompositeHandler()\nserve_localstack_resources = internal.LocalstackResourceHandler()\nrun_custom_response_handlers = chain.CompositeResponseHandler()\nparse_service_response = service.ServiceResponseParser()\n# legacy compatibility handlers\nserve_edge_router_rules = legacy.EdgeRouterHandler()\nserve_default_listeners = legacy.DefaultListenerHandler()\npop_request_context = legacy.pop_request_context\npush_request_context = legacy.push_request_context\n", "path": "localstack/aws/handlers/__init__.py"}], "after_files": [{"content": "from localstack.aws import handlers\nfrom localstack.aws.handlers.metric_handler import MetricHandler\nfrom localstack.aws.handlers.service_plugin import ServiceLoader\nfrom localstack.services.plugins import SERVICE_PLUGINS, ServiceManager, ServicePluginManager\n\nfrom .gateway import Gateway\nfrom .handlers.fallback import EmptyResponseHandler\nfrom .handlers.service import ServiceRequestRouter\n\n\nclass LocalstackAwsGateway(Gateway):\n    def __init__(self, service_manager: ServiceManager = None) -> None:\n        super().__init__()\n\n        # basic server components\n        self.service_manager = service_manager or ServicePluginManager()\n        self.service_request_router = ServiceRequestRouter()\n        # lazy-loads services into the router\n        load_service = ServiceLoader(self.service_manager, self.service_request_router)\n\n        metric_collector = MetricHandler()\n        # the main request handler chain\n        self.request_handlers.extend(\n            [\n                handlers.push_request_context,\n                metric_collector.create_metric_handler_item,\n                handlers.parse_service_name,  # enforce_cors and content_decoder depend on the service name\n                handlers.enforce_cors,\n                handlers.content_decoder,\n                handlers.serve_localstack_resources,  # try to serve internal resources in /_localstack first\n                handlers.serve_default_listeners,  # legacy proxy default listeners\n                handlers.serve_edge_router_rules,\n                # start aws handler chain\n                handlers.inject_auth_header_if_missing,\n                handlers.add_region_from_header,\n                handlers.add_account_id,\n                handlers.parse_service_request,\n                metric_collector.record_parsed_request,\n                handlers.serve_custom_service_request_handlers,\n                load_service,  # once we have the service request we can make sure we load the service\n                self.service_request_router,  # once we know the service is loaded we can route the request\n                # if the chain is still running, set an empty response\n                EmptyResponseHandler(404, b'{\"message\": \"Not Found\"}'),\n            ]\n        )\n\n        # exception handlers in the chain\n        self.exception_handlers.extend(\n            [\n                handlers.log_exception,\n                handlers.handle_service_exception,\n                handlers.handle_internal_failure,\n            ]\n        )\n\n        # response post-processing\n        self.response_handlers.extend(\n            [\n                handlers.parse_service_response,\n                handlers.set_close_connection_header,\n                handlers.run_custom_response_handlers,\n                handlers.add_cors_response_headers,\n                handlers.log_response,\n                handlers.count_service_request,\n                handlers.pop_request_context,\n                metric_collector.update_metric_collection,\n            ]\n        )\n\n\ndef main():\n    \"\"\"\n    Serve the LocalstackGateway with the default configuration directly through hypercorn. This is mostly for\n    development purposes and documentation on how to serve the Gateway.\n    \"\"\"\n    from .serving.hypercorn import serve\n\n    use_ssl = True\n    port = 4566\n\n    # serve the LocalStackAwsGateway in a dev app\n    from localstack.utils.bootstrap import setup_logging\n\n    setup_logging()\n\n    if use_ssl:\n        from localstack.services.generic_proxy import (\n            GenericProxy,\n            install_predefined_cert_if_available,\n        )\n\n        install_predefined_cert_if_available()\n        _, cert_file_name, key_file_name = GenericProxy.create_ssl_cert(serial_number=port)\n        ssl_creds = (cert_file_name, key_file_name)\n    else:\n        ssl_creds = None\n\n    gw = LocalstackAwsGateway(SERVICE_PLUGINS)\n\n    serve(gw, use_reloader=True, port=port, ssl_creds=ssl_creds)\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "localstack/aws/app.py"}, {"content": "\"\"\" Handlers for compatibility with legacy edge proxy and the quart http framework.\"\"\"\n\nimport logging\nimport re\nfrom typing import Mapping\n\nfrom requests import Response as RequestsResponse\n\nfrom localstack.constants import HEADER_LOCALSTACK_EDGE_URL, HEADER_LOCALSTACK_REQUEST_URL\nfrom localstack.http import Response\nfrom localstack.http.request import restore_payload\nfrom localstack.services.generic_proxy import ProxyListener, modify_and_forward\n\nfrom ..api import RequestContext\nfrom ..chain import Handler, HandlerChain\nfrom .routes import RouterHandler\n\nLOG = logging.getLogger(__name__)\n\n\ndef push_request_context(_chain: HandlerChain, context: RequestContext, _response: Response):\n    # hack for legacy compatibility. various parts of localstack access the global flask/quart/our own request\n    # context. since we're neither in a flask nor a quart context, we're pushing our own context object into their\n    # proxy objects, which is terrible, but works because mostly code just accesses \"context.request\", so we don't\n    # have to bother pushing a real quart/flask context.\n    import flask.globals\n    import quart.globals\n\n    from localstack.utils.aws import request_context\n\n    quart.globals._request_ctx_stack.push(context)\n    flask.globals._request_ctx_stack.push(context)\n    request_context.THREAD_LOCAL.request_context = context.request\n\n\ndef pop_request_context(_chain: HandlerChain, _context: RequestContext, _response: Response):\n    # hack for legacy compatibility\n    import flask.globals\n    import quart.globals\n\n    from localstack.utils.aws import request_context\n\n    quart.globals._request_ctx_stack.pop()\n    flask.globals._request_ctx_stack.pop()\n    request_context.THREAD_LOCAL.request_context = None\n\n\ndef set_close_connection_header(_chain: HandlerChain, context: RequestContext, response: Response):\n    \"\"\"This is a hack to work around performance issues with h11 and boto. See\n    https://github.com/localstack/localstack/issues/6557\"\"\"\n    if conn := context.request.headers.get(\"Connection\"):\n        if conn.lower() == \"keep-alive\":\n            # don't set Connection: close header if keep-alive is explicitly asked for\n            return\n\n    if \"Connection\" not in response.headers:\n        response.headers[\"Connection\"] = \"close\"\n\n\nclass EdgeRouterHandler(RouterHandler):\n    def __init__(self, respond_not_found=False) -> None:\n        from localstack.services.edge import ROUTER\n\n        super().__init__(ROUTER, respond_not_found)\n\n\nclass GenericProxyHandler(Handler):\n    \"\"\"\n    This handler maps HandlerChain requests to the generic proxy ProxyListener interface `forward_request`.\n    \"\"\"\n\n    def __call__(self, chain: HandlerChain, context: RequestContext, response: Response):\n        request = context.request\n\n        # a werkzeug Request consumes form/multipart data from the socket stream, so we need to restore the payload here\n        data = restore_payload(request)\n\n        # TODO: rethink whether this proxy handling is necessary\n        context.request.headers[HEADER_LOCALSTACK_REQUEST_URL] = context.request.base_url\n\n        result = self.forward_request(\n            context,\n            method=request.method,\n            path=request.full_path if request.query_string else request.path,\n            data=data,\n            headers=request.headers,\n        )\n\n        if type(result) == int:\n            chain.respond(status_code=result)\n            return\n\n        if isinstance(result, tuple):\n            # special case for Kinesis SubscribeToShard\n            if len(result) == 2:\n                response.status_code = 200\n                response.set_response(result[0])\n                response.headers.update(dict(result[1]))\n                chain.stop()\n                return\n\n        if isinstance(result, RequestsResponse):\n            response.status_code = result.status_code\n            response.set_response(result.content)\n            # make sure headers are set after the content, so potential content-length headers are overwritten\n            response.headers.update(dict(result.headers))\n\n            # make sure content-length is re-calculated correctly, unless it's a HEAD request\n            if request.method != \"HEAD\":\n                length = response.calculate_content_length()\n                if length is not None:\n                    response.headers[\"Content-Length\"] = str(length)\n            chain.stop()\n            return\n\n        raise ValueError(\"cannot create response for result %s\" % result)\n\n    def forward_request(\n        self, context: RequestContext, method: str, path: str, data: bytes, headers: Mapping\n    ):\n        raise NotImplementedError\n\n\nclass LegacyPluginHandler(GenericProxyHandler):\n    \"\"\"\n    This adapter exposes Services that are developed as ProxyListener as Handler.\n    \"\"\"\n\n    def forward_request(\n        self, context: RequestContext, method: str, path: str, data: bytes, headers: Mapping\n    ):\n        from localstack.services.edge import do_forward_request\n\n        # TODO: rethink whether this proxy handling is necessary\n        request = context.request\n        orig_req_url = request.headers.pop(HEADER_LOCALSTACK_REQUEST_URL, \"\")\n        request.headers[HEADER_LOCALSTACK_EDGE_URL] = (\n            re.sub(r\"^([^:]+://[^/]+).*\", r\"\\1\", orig_req_url) or request.host_url\n        )\n\n        return do_forward_request(\n            api=context.service.service_name,\n            method=method,\n            path=path,\n            data=data,\n            headers=headers,\n            port=None,\n        )\n\n\nclass _NoHandlerCalled(Exception):\n    pass\n\n\nclass _DummyProxyListener(ProxyListener):\n    def forward_request(self, method, path, data, headers):\n        raise _NoHandlerCalled\n\n\nclass DefaultListenerHandler(GenericProxyHandler):\n    \"\"\"\n    Adapter that exposes the ProxyListener.DEFAULT_LISTENERS as a Handler.\n    \"\"\"\n\n    def __call__(self, chain: HandlerChain, context: RequestContext, response: Response):\n        if not ProxyListener.DEFAULT_LISTENERS:\n            return\n\n        try:\n            super(DefaultListenerHandler, self).__call__(chain, context, response)\n        except _NoHandlerCalled:\n            # may be raised by the _DummyProxyListener, which is reached if no other listener is called,\n            # in which case we don't want to return a result or stop the chain.\n            return\n\n    def forward_request(\n        self, context: RequestContext, method: str, path: str, data: bytes, headers: Mapping\n    ):\n        request = context.request\n\n        return modify_and_forward(\n            method=method,\n            path=path,\n            data_bytes=data,\n            headers=headers,\n            forward_base_url=None,\n            listeners=[_DummyProxyListener()],\n            client_address=request.remote_addr,\n            server_address=request.host,\n        )\n", "path": "localstack/aws/handlers/legacy.py"}, {"content": "\"\"\" A set of common handlers to build an AWS server application.\"\"\"\n\nfrom .. import chain\nfrom . import analytics, auth, codec, cors, fallback, internal, legacy, logging, region, service\n\nenforce_cors = cors.CorsEnforcer()\nadd_cors_response_headers = cors.CorsResponseEnricher()\ncontent_decoder = codec.ContentDecoder()\nparse_service_name = service.ServiceNameParser()\nparse_service_request = service.ServiceRequestParser()\nadd_account_id = auth.AccountIdEnricher()\ninject_auth_header_if_missing = auth.MissingAuthHeaderInjector()\nadd_region_from_header = region.RegionContextEnricher()\nlog_exception = logging.ExceptionLogger()\nlog_response = logging.ResponseLogger()\ncount_service_request = analytics.ServiceRequestCounter()\nhandle_service_exception = service.ServiceExceptionSerializer()\nhandle_internal_failure = fallback.InternalFailureHandler()\nserve_custom_service_request_handlers = chain.CompositeHandler()\nserve_localstack_resources = internal.LocalstackResourceHandler()\nrun_custom_response_handlers = chain.CompositeResponseHandler()\nparse_service_response = service.ServiceResponseParser()\n# legacy compatibility handlers\nserve_edge_router_rules = legacy.EdgeRouterHandler()\nserve_default_listeners = legacy.DefaultListenerHandler()\nset_close_connection_header = legacy.set_close_connection_header\npop_request_context = legacy.pop_request_context\npush_request_context = legacy.push_request_context\n", "path": "localstack/aws/handlers/__init__.py"}]}