@@ -494,6 +494,12 @@ windows_request_context::~windows_request_context()
494494 // the lock then setting of the event has completed.
495495 std::unique_lock<std::mutex> lock (m_responseCompletedLock);
496496
497+ // Add a task-based continuation so no exceptions thrown from the task go 'unobserved'.
498+ pplx::create_task (m_response_completed).then ([](pplx::task<void > t)
499+ {
500+ try { t.wait (); } catch (...) {}
501+ });
502+
497503 auto *pServer = static_cast <http_windows_server *>(http_server_api::server_api ());
498504 if (--pServer->m_numOutstandingRequests == 0 )
499505 {
@@ -530,6 +536,7 @@ void windows_request_context::async_process_request(HTTP_REQUEST_ID request_id,
530536 {
531537 CancelThreadpoolIo (pServer->m_threadpool_io );
532538 m_msg.reply (status_codes::InternalError);
539+ do_response (true );
533540 }
534541}
535542
@@ -541,6 +548,7 @@ void windows_request_context::read_headers_io_completion(DWORD error_code, DWORD
541548 if (error_code != NO_ERROR)
542549 {
543550 m_msg.reply (status_codes::InternalError);
551+ do_response (true );
544552 }
545553 else
546554 {
@@ -574,6 +582,7 @@ void windows_request_context::read_headers_io_completion(DWORD error_code, DWORD
574582 else
575583 {
576584 m_msg.reply (status_codes::BadRequest, badRequestMsg);
585+ do_response (true );
577586 }
578587 }
579588}
@@ -614,6 +623,7 @@ void windows_request_context::read_request_body_chunk()
614623 else
615624 {
616625 m_msg._get_impl ()->_complete (0 , std::make_exception_ptr (http_exception (error_code)));
626+ m_msg._reply_if_not_already (status_codes::InternalError);
617627 }
618628 }
619629}
@@ -639,6 +649,7 @@ void windows_request_context::read_body_io_completion(DWORD error_code, DWORD by
639649 {
640650 request_body_buf.commit (0 );
641651 m_msg._get_impl ()->_complete (0 , std::make_exception_ptr (http_exception (error_code)));
652+ m_msg._reply_if_not_already (status_codes::InternalError);
642653 }
643654}
644655
@@ -649,46 +660,7 @@ void windows_request_context::dispatch_request_to_listener(_In_ web::http::exper
649660 // Save http_request copy to dispatch to user's handler in case content_ready() completes before.
650661 http_request request = m_msg;
651662
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- });
663+ do_response (false );
692664
693665 // Look up the lock for the http_listener.
694666 auto *pServer = static_cast <http_windows_server *>(http_server_api::server_api ());
@@ -721,6 +693,69 @@ void windows_request_context::dispatch_request_to_listener(_In_ web::http::exper
721693 }
722694}
723695
696+ void windows_request_context::do_response (bool bad_request)
697+ {
698+ // Use a proxy event so we're not causing a circular reference between the http_request and the response task
699+ pplx::task_completion_event<void > proxy_content_ready;
700+
701+ auto content_ready_task = m_msg.content_ready ();
702+ auto get_response_task = m_msg.get_response ();
703+
704+ content_ready_task.then ([this , proxy_content_ready](pplx::task<http_request> requestBody)
705+ {
706+ // If an exception occurred while processing the body then there is no reason
707+ // to even try sending the response, just re-surface the same exception.
708+ try
709+ {
710+ requestBody.wait ();
711+ }
712+ catch (...)
713+ {
714+ m_msg = http_request ();
715+ proxy_content_ready.set_exception (std::current_exception ());
716+ cancel_request (std::current_exception ());
717+ return ;
718+ }
719+
720+ // At this point the user entirely controls the lifetime of the http_request.
721+ m_msg = http_request ();
722+ proxy_content_ready.set ();
723+ });
724+
725+ get_response_task.then ([this , bad_request, proxy_content_ready](pplx::task<http::http_response> responseTask)
726+ {
727+ // Don't let an exception from sending the response bring down the server.
728+ try
729+ {
730+ m_response = responseTask.get ();
731+ }
732+ catch (const pplx::task_canceled &)
733+ {
734+ // This means the user didn't respond to the request, allowing the
735+ // http_request instance to be destroyed. There is nothing to do then
736+ // so don't send a response.
737+ return ;
738+ }
739+ catch (...)
740+ {
741+ m_response = http::http_response (status_codes::InternalError);
742+ }
743+
744+ if (bad_request)
745+ {
746+ async_process_response ();
747+ }
748+ else
749+ {
750+ // Wait until the content download finished before replying.
751+ pplx::create_task (proxy_content_ready).then ([this ](pplx::task<void > requestBody)
752+ {
753+ async_process_response ();
754+ });
755+ }
756+ });
757+ }
758+
724759void windows_request_context::async_process_response ()
725760{
726761 auto *pServer = static_cast <http_windows_server *>(http_server_api::server_api ());
0 commit comments