Skip to content

Commit e936dbb

Browse files
committed
allow add middleware after app has started
this should completely fix "Cannot add middleware after an application has started" which can occur due to a race condition
1 parent 813c391 commit e936dbb

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

modules/initialize.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def check_versions():
5050

5151
def initialize():
5252
from modules import initialize_util
53+
initialize_util.allow_add_middleware_after_start()
5354
initialize_util.fix_torch_version()
5455
initialize_util.fix_pytorch_lightning()
5556
initialize_util.fix_asyncio_event_loop_policy()

modules/initialize_util.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import re
66

77
from modules.timer import startup_timer
8+
from modules import patches
9+
from functools import wraps
810

911

1012
def gradio_server_name():
@@ -191,11 +193,8 @@ def configure_opts_onchange():
191193

192194
def setup_middleware(app):
193195
from starlette.middleware.gzip import GZipMiddleware
194-
195-
app.middleware_stack = None # reset current middleware to allow modifying user provided list
196196
app.add_middleware(GZipMiddleware, minimum_size=1000)
197197
configure_cors_middleware(app)
198-
app.build_middleware_stack() # rebuild middleware stack on-the-fly
199198

200199

201200
def configure_cors_middleware(app):
@@ -213,3 +212,38 @@ def configure_cors_middleware(app):
213212
cors_options["allow_origin_regex"] = cmd_opts.cors_allow_origins_regex
214213
app.add_middleware(CORSMiddleware, **cors_options)
215214

215+
216+
def allow_add_middleware_after_start():
217+
from starlette.applications import Starlette
218+
219+
def add_middleware_wrapper(func):
220+
"""Patch Starlette.add_middleware to allow for middleware to be added after the app has started
221+
222+
Starlette.add_middleware raises RuntimeError("Cannot add middleware after an application has started") if middleware_stack is not None.
223+
We can force add new middleware by first setting middleware_stack to None, then adding the middleware.
224+
When middleware_stack is None, it will rebuild the middleware_stack on the next request (Lazily build middleware stack).
225+
226+
If packages are updated in the future, things may break, so we have two ways to add middleware after the app has started:
227+
the first way is to just set middleware_stack to None and then retry
228+
the second manually insert the middleware into the user_middleware list without calling add_middleware
229+
"""
230+
231+
@wraps(func)
232+
def wrapper(self, *args, **kwargs):
233+
res = None
234+
try:
235+
res = func(self, *args, **kwargs)
236+
except RuntimeError as _:
237+
try:
238+
self.middleware_stack = None
239+
res = func(self, *args, **kwargs)
240+
except RuntimeError as e:
241+
print(f'Warning: "{e}", Retrying...')
242+
from starlette.middleware import Middleware
243+
self.user_middleware.insert(0, Middleware(*args, **kwargs))
244+
self.middleware_stack = None # ensure middleware_stack in the event of concurrent requests
245+
return res
246+
247+
return wrapper
248+
249+
patches.patch(__name__, obj=Starlette, field="add_middleware", replacement=add_middleware_wrapper(Starlette.add_middleware))

0 commit comments

Comments
 (0)