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
25 changes: 23 additions & 2 deletions moto/moto_server/threaded_moto_server.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
from threading import Event, Thread
from typing import Optional

from werkzeug.serving import BaseWSGIServer, make_server
from werkzeug.serving import BaseWSGIServer, WSGIRequestHandler, make_server

from .werkzeug_app import DomainDispatcherApplication, create_backend_app


class _MotoRequestHandler(WSGIRequestHandler):
def handle_expect_100(self) -> bool:
"""Suppress the duplicate ``100 Continue`` from BaseHTTPRequestHandler.

Werkzeug's ``run_wsgi()`` already sends its own ``100 Continue``
when it sees the ``Expect`` header. The default
``BaseHTTPRequestHandler.parse_request()`` sends a second one,
resulting in two ``100 Continue`` responses on the wire.

Returning ``True`` without writing anything prevents the duplicate
while letting werkzeug send exactly one ``100 Continue`` later.
"""
return True


class ThreadedMotoServer:
def __init__(
self, ip_address: str = "0.0.0.0", port: int = 5000, verbose: bool = True
Expand All @@ -21,7 +36,13 @@ def __init__(
def _server_entry(self) -> None:
app = DomainDispatcherApplication(create_backend_app)

self._server = make_server(self._ip_address, self._port, app, True)
self._server = make_server(
self._ip_address,
self._port,
app,
threaded=True,
request_handler=_MotoRequestHandler,
)
self._server_ready_event.set()
self._server.serve_forever()

Expand Down
2 changes: 2 additions & 0 deletions moto/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# Expose ThreadedMotoServer as a public symbol according to PEP-561
from moto.moto_server.threaded_moto_server import (
ThreadedMotoServer as ThreadedMotoServer,
_MotoRequestHandler,
)
from moto.moto_server.werkzeug_app import (
DomainDispatcherApplication,
Expand Down Expand Up @@ -83,6 +84,7 @@ def main(argv: Optional[list[str]] = None) -> None:
threaded=True,
use_reloader=args.reload,
ssl_context=ssl_context,
request_handler=_MotoRequestHandler,
)


Expand Down