@@ -432,25 +432,38 @@ async def shutdown_integration_delete(req):
432432 req (HTTPRequest): HTTPRequest Object
433433 """
434434 state = req .app ["state" ]
435-
436435 logger .info (f"Shutting down { state .settings ['integration_name' ]} ..." )
437436
438- # Send response manually because this has to happen before the application exits
439437 res = create_status_response (req .app , "../" )
440- await res .prepare (req )
441- await res .write_eof ()
442438
443- # Gracefully shutdown the server
444- await req .app .shutdown ()
445- await req .app .cleanup ()
439+ # Schedule the shutdown to happen after the response is sent
440+ asyncio .create_task (_shutdown_after_response (req .app ))
441+
442+ return res
443+
444+
445+ async def _shutdown_after_response (app ):
446+ # Shutdown the application after a short delay to allow the response to be fully sent
447+ # back to the client before the server is stopped
448+ await asyncio .sleep (0.1 )
449+
450+ # aiohttp shutdown to be invoked before cleanup -
451+ # https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.Application.shutdown
452+ await app .shutdown ()
453+ await app .cleanup ()
446454
447455 loop = util .get_event_loop ()
448- # Run the current batch of coroutines in the event loop and then exit.
449- # This completes the loop.run_forever() blocking call and subsequent code
450- # in create_and_start_app() resumes execution.
451- loop .stop ()
452456
453- return res
457+ # Cancel remaining tasks (except this one: _shutdown_after_response)
458+ running_tasks = asyncio .all_tasks (loop )
459+ current_task = asyncio .current_task ()
460+ if current_task :
461+ running_tasks .discard (current_task )
462+
463+ await util .cancel_tasks (running_tasks )
464+
465+ # Stop the event loop from this task
466+ loop .call_soon_threadsafe (loop .stop )
454467
455468
456469# @token_auth.authenticate_access_decorator
@@ -873,8 +886,6 @@ def configure_and_start(app):
873886 """
874887 loop = util .get_event_loop ()
875888
876- web_logger = None if not mwi_env .is_web_logging_enabled () else logger
877-
878889 # Setup the session storage,
879890 # Uniqified per session to prevent multiple proxy servers on the same FQDN from interfering with each other.
880891 uniqify_session_cookie = secrets .token_hex ()
@@ -888,7 +899,9 @@ def configure_and_start(app):
888899 )
889900
890901 # Setup runner
891- runner = web .AppRunner (app , logger = web_logger , access_log = web_logger )
902+ runner = web .AppRunner (
903+ app , access_log = logger if mwi_env .is_web_logging_enabled () else None
904+ )
892905 loop .run_until_complete (runner .setup ())
893906
894907 # Prepare site to start, then set port of the app.
@@ -961,7 +974,7 @@ def create_app(config_name=matlab_proxy.get_default_config_name()):
961974 app .router .add_route ("*" , f"{ base_url } " , root_redirect )
962975
963976 app .router .add_route ("*" , f"{ base_url } /{{proxyPath:.*}}" , matlab_view )
964- app .on_cleanup .append (cleanup_background_tasks )
977+ app .on_shutdown .append (cleanup_background_tasks )
965978
966979 return app
967980
@@ -1011,15 +1024,15 @@ def create_and_start_app(config_name):
10111024
10121025 # After handling the interrupt, proceed with shutting down the server gracefully.
10131026 try :
1027+ # aiohttp shutdown to be invoked before cleanup -
1028+ # https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.Application.shutdown
1029+ loop .run_until_complete (app .shutdown ())
1030+ loop .run_until_complete (app .cleanup ())
1031+
10141032 running_tasks = asyncio .all_tasks (loop )
1015- loop .run_until_complete (
1016- asyncio .gather (
1017- app .shutdown (),
1018- app .cleanup (),
1019- util .cancel_tasks (running_tasks ),
1020- return_exceptions = False ,
1021- )
1022- )
1033+
1034+ # Gracefully cancel all running background tasks
1035+ loop .run_until_complete (util .cancel_tasks (running_tasks ))
10231036
10241037 except Exception :
10251038 pass
0 commit comments