@@ -465,13 +465,7 @@ void http_windows_server::receive_requests()
465465pplx::task<void > http_windows_server::respond (http::http_response response)
466466{
467467 windows_request_context * p_context = static_cast <windows_request_context *>(response._get_server_context ());
468- return pplx::create_task (p_context->m_response_completed ).then ([p_context](::pplx::task<void > t)
469- {
470- // After response is sent, break circular reference between http_response and the request context.
471- // Otherwise http_listener::close() can hang.
472- p_context->m_response ._get_impl ()->_set_server_context (nullptr );
473- t.get ();
474- });
468+ return pplx::create_task (p_context->m_response_completed );
475469}
476470
477471windows_request_context::windows_request_context ()
@@ -494,6 +488,12 @@ windows_request_context::~windows_request_context()
494488 // the lock then setting of the event has completed.
495489 std::unique_lock<std::mutex> lock (m_responseCompletedLock);
496490
491+ // Add a task-based continuation so no exceptions thrown from the task go 'unobserved'.
492+ pplx::create_task (m_response_completed).then ([](pplx::task<void > t)
493+ {
494+ try { t.wait (); } catch (...) {}
495+ });
496+
497497 auto *pServer = static_cast <http_windows_server *>(http_server_api::server_api ());
498498 if (--pServer->m_numOutstandingRequests == 0 )
499499 {
@@ -530,6 +530,7 @@ void windows_request_context::async_process_request(HTTP_REQUEST_ID request_id,
530530 {
531531 CancelThreadpoolIo (pServer->m_threadpool_io );
532532 m_msg.reply (status_codes::InternalError);
533+ init_response_callbacks (ShouldWaitForBody::DontWait);
533534 }
534535}
535536
@@ -541,6 +542,7 @@ void windows_request_context::read_headers_io_completion(DWORD error_code, DWORD
541542 if (error_code != NO_ERROR)
542543 {
543544 m_msg.reply (status_codes::InternalError);
545+ init_response_callbacks (ShouldWaitForBody::DontWait);
544546 }
545547 else
546548 {
@@ -574,6 +576,9 @@ void windows_request_context::read_headers_io_completion(DWORD error_code, DWORD
574576 else
575577 {
576578 m_msg.reply (status_codes::BadRequest, badRequestMsg);
579+
580+ // Even though we have a bad request, we should wait for the body otherwise we risk racing over m_overlapped
581+ init_response_callbacks (ShouldWaitForBody::Wait);
577582 }
578583 }
579584}
@@ -649,46 +654,7 @@ void windows_request_context::dispatch_request_to_listener(_In_ web::http::exper
649654 // Save http_request copy to dispatch to user's handler in case content_ready() completes before.
650655 http_request request = m_msg;
651656
652- // Wait until the content download finished before replying.
653- request.content_ready ().then ([=](pplx::task<http_request> requestBody)
654- {
655- // If an exception occurred while processing the body then there is no reason
656- // to even try sending the response, just re-surface the same exception.
657- try
658- {
659- requestBody.wait ();
660- }
661- catch (...)
662- {
663- m_msg = http_request ();
664- cancel_request (std::current_exception ());
665- return ;
666- }
667-
668- // At this point the user entirely controls the lifetime of the http_request.
669- m_msg = http_request ();
670-
671- request.get_response ().then ([this ](pplx::task<http::http_response> responseTask)
672- {
673- // Don't let an exception from sending the response bring down the server.
674- try
675- {
676- m_response = responseTask.get ();
677- }
678- catch (const pplx::task_canceled &)
679- {
680- // This means the user didn't respond to the request, allowing the
681- // http_request instance to be destroyed. There is nothing to do then
682- // so don't send a response.
683- return ;
684- }
685- catch (...)
686- {
687- m_response = http::http_response (status_codes::InternalError);
688- }
689- async_process_response ();
690- });
691- });
657+ init_response_callbacks (ShouldWaitForBody::Wait);
692658
693659 // Look up the lock for the http_listener.
694660 auto *pServer = static_cast <http_windows_server *>(http_server_api::server_api ());
@@ -721,6 +687,94 @@ void windows_request_context::dispatch_request_to_listener(_In_ web::http::exper
721687 }
722688}
723689
690+ void windows_request_context::init_response_callbacks (ShouldWaitForBody shouldWait)
691+ {
692+ // Use a proxy event so we're not causing a circular reference between the http_request and the response task
693+ pplx::task_completion_event<void > proxy_content_ready;
694+
695+ auto content_ready_task = m_msg.content_ready ();
696+ auto get_response_task = m_msg.get_response ();
697+
698+ content_ready_task.then ([this , proxy_content_ready](pplx::task<http_request> requestBody)
699+ {
700+ // If an exception occurred while processing the body then there is no reason
701+ // to even try sending the response, just re-surface the same exception.
702+ try
703+ {
704+ requestBody.wait ();
705+ }
706+ catch (...)
707+ {
708+ // Copy the request reference in case it's the last
709+ http_request request = m_msg;
710+ m_msg = http_request ();
711+ auto exc = std::current_exception ();
712+ proxy_content_ready.set_exception (exc);
713+ cancel_request (exc);
714+ return ;
715+ }
716+
717+ // At this point the user entirely controls the lifetime of the http_request.
718+ m_msg = http_request ();
719+ proxy_content_ready.set ();
720+ });
721+
722+ get_response_task.then ([this , proxy_content_ready](pplx::task<http::http_response> responseTask)
723+ {
724+ // Don't let an exception from sending the response bring down the server.
725+ try
726+ {
727+ m_response = responseTask.get ();
728+ }
729+ catch (const pplx::task_canceled &)
730+ {
731+ // This means the user didn't respond to the request, allowing the
732+ // http_request instance to be destroyed. There is nothing to do then
733+ // so don't send a response.
734+ // Avoid unobserved exception handler
735+ pplx::create_task (proxy_content_ready).then ([](pplx::task<void > t)
736+ {
737+ try { t.wait (); } catch (...) {}
738+ });
739+ return ;
740+ }
741+ catch (...)
742+ {
743+ // Should never get here, if we do there's a chance that a circular reference will cause leaks,
744+ // or worse, undefined behaviour as we don't know who owns 'this' anymore
745+ _ASSERTE (false );
746+ m_response = http::http_response (status_codes::InternalError);
747+ }
748+
749+ pplx::create_task (m_response_completed).then ([this ](pplx::task<void > t)
750+ {
751+ // After response is sent, break circular reference between http_response and the request context.
752+ // Otherwise http_listener::close() can hang.
753+ m_response._get_impl ()->_set_server_context (nullptr );
754+ });
755+
756+ // Wait until the content download finished before replying because m_overlapped is reused,
757+ // and we don't want to delete 'this' if the body is still downloading
758+ pplx::create_task (proxy_content_ready).then ([this ](pplx::task<void > t)
759+ {
760+ try
761+ {
762+ t.wait ();
763+ async_process_response ();
764+ }
765+ catch (...)
766+ {
767+ }
768+ }).wait ();
769+ });
770+
771+ if (shouldWait == ShouldWaitForBody::DontWait)
772+ {
773+ // Fake a body completion so the content_ready() task doesn't keep the http_request alive forever
774+ m_msg._get_impl ()->_complete (0 );
775+ }
776+ }
777+
724778void windows_request_context::async_process_response ()
725779{
726780 auto *pServer = static_cast <http_windows_server *>(http_server_api::server_api ());
0 commit comments