@@ -195,6 +195,35 @@ std::string_view process_headers(const tl::K2InvokeHttp& invoke_http, PhpScriptB
195195 return content_type;
196196}
197197
198+ string get_http_response_body (HttpServerInstanceState& http_server_instance_st) noexcept {
199+ string body{};
200+ if (http_server_instance_st.http_method != kphp::http::method::head) {
201+ auto & output_instance_st{OutputInstanceState::get ()};
202+ const auto system_buffer{output_instance_st.output_buffers .system_buffer ()};
203+ const auto user_buffers{output_instance_st.output_buffers .user_buffers ()};
204+
205+ const auto body_size{std::ranges::fold_left (user_buffers | std::views::transform ([](const auto & buffer) noexcept { return buffer.size (); }),
206+ system_buffer.get ().size (), std::plus<string::size_type>{})};
207+ body.reserve_at_least (body_size);
208+
209+ body.append (system_buffer.get ().buffer (), system_buffer.get ().size ());
210+ const auto appender{[&body](const auto & buffer) noexcept { body.append (buffer.buffer (), buffer.size ()); }};
211+ std::ranges::for_each (user_buffers | std::views::filter ([](const auto & buffer) noexcept { return buffer.size () > 0 ; }), appender);
212+
213+ const bool gzip_encoded{static_cast <bool >(http_server_instance_st.encoding & HttpServerInstanceState::ENCODING_GZIP)};
214+ const bool deflate_encoded{static_cast <bool >(http_server_instance_st.encoding & HttpServerInstanceState::ENCODING_DEFLATE)};
215+ // compress body if needed
216+ if (gzip_encoded || deflate_encoded) {
217+ auto encoded_body{kphp::zlib::encode ({body.c_str (), static_cast <size_t >(body.size ())}, kphp::zlib::DEFAULT_COMPRESSION_LEVEL,
218+ gzip_encoded ? kphp::zlib::ENCODING_GZIP : kphp::zlib::ENCODING_DEFLATE)};
219+ if (encoded_body.has_value ()) [[likely]] {
220+ body = std::move (*encoded_body);
221+ }
222+ }
223+ }
224+ return body;
225+ }
226+
198227} // namespace
199228
200229namespace kphp ::http {
@@ -337,60 +366,59 @@ void init_server(kphp::component::stream request_stream) noexcept {
337366kphp::coro::task<> finalize_server () noexcept {
338367 auto & http_server_instance_st{HttpServerInstanceState::get ()};
339368
340- string body{};
341- if (http_server_instance_st.http_method != method::head) {
342- auto & output_instance_st{OutputInstanceState::get ()};
343- const auto system_buffer{output_instance_st.output_buffers .system_buffer ()};
344- const auto user_buffers{output_instance_st.output_buffers .user_buffers ()};
345-
346- const auto body_size{std::ranges::fold_left (user_buffers | std::views::transform ([](const auto & buffer) noexcept { return buffer.size (); }),
347- system_buffer.get ().size (), std::plus<string::size_type>{})};
348- body.reserve_at_least (body_size);
349-
350- body.append (system_buffer.get ().buffer (), system_buffer.get ().size ());
351- const auto appender{[&body](const auto & buffer) noexcept { body.append (buffer.buffer (), buffer.size ()); }};
352- std::ranges::for_each (user_buffers | std::views::filter ([](const auto & buffer) noexcept { return buffer.size () > 0 ; }), appender);
353-
369+ string response_body{};
370+ tl::HttpResponse http_response{};
371+ switch (http_server_instance_st.response_state ) {
372+ case kphp::http::response_state::not_started:
373+ http_server_instance_st.response_state = kphp::http::response_state::sending_headers;
374+ if (http_server_instance_st.headers_registered_callback .has_value ()) {
375+ co_await *std::exchange (http_server_instance_st.headers_registered_callback , std::nullopt );
376+ }
377+ [[fallthrough]];
378+ case kphp::http::response_state::sending_headers: {
354379 const bool gzip_encoded{static_cast <bool >(http_server_instance_st.encoding & HttpServerInstanceState::ENCODING_GZIP)};
355380 const bool deflate_encoded{static_cast <bool >(http_server_instance_st.encoding & HttpServerInstanceState::ENCODING_DEFLATE)};
356- // compress body if needed
357381 if (gzip_encoded || deflate_encoded) {
358- auto encoded_body{kphp::zlib::encode ({body.c_str (), static_cast <size_t >(body.size ())}, kphp::zlib::DEFAULT_COMPRESSION_LEVEL,
359- gzip_encoded ? kphp::zlib::ENCODING_GZIP : kphp::zlib::ENCODING_DEFLATE)};
360- if (encoded_body.has_value ()) [[likely]] {
361- body = std::move (*encoded_body);
362-
363- auto & static_SB{RuntimeContext::get ().static_SB };
364- static_SB.clean () << headers::CONTENT_ENCODING.data () << " : " << (gzip_encoded ? ENCODING_GZIP.data () : ENCODING_DEFLATE.data ());
365- kphp::http::header ({static_SB.c_str (), static_SB.size ()}, true , status::NO_STATUS);
366- }
382+ auto & static_SB{RuntimeContext::get ().static_SB };
383+ static_SB.clean () << kphp::http::headers::CONTENT_ENCODING.data () << " : " << (gzip_encoded ? ENCODING_GZIP.data () : ENCODING_DEFLATE.data ());
384+ kphp::http::header ({static_SB.c_str (), static_SB.size ()}, true , kphp::http::status::NO_STATUS);
367385 }
386+ // fill headers
387+ http_response.http_response .headers .value .reserve (http_server_instance_st.headers ().size ());
388+ std::transform (http_server_instance_st.headers ().cbegin (), http_server_instance_st.headers ().cend (),
389+ std::back_inserter (http_response.http_response .headers .value ), [](const auto & header_entry) noexcept {
390+ const auto & [name, value]{header_entry};
391+ return tl::httpHeaderEntry{
392+ .is_sensitive = {}, .name = {.value = {name.data (), name.size ()}}, .value = {.value = {value.data (), value.size ()}}};
393+ });
394+ http_server_instance_st.response_state = kphp::http::response_state::headers_sent;
395+ [[fallthrough]];
368396 }
397+ case kphp::http::response_state::headers_sent: {
398+ response_body = get_http_response_body (http_server_instance_st);
399+ const auto status_code{http_server_instance_st.status_code == status::NO_STATUS ? status::OK : http_server_instance_st.status_code };
400+ http_response.http_response .version = tl::HttpVersion{.version = tl::HttpVersion::Version::V11};
401+ http_response.http_response .status_code = {.value = static_cast <int32_t >(status_code)};
402+ http_response.http_response .body = {reinterpret_cast <const std::byte*>(response_body.c_str ()), response_body.size ()};
403+ http_server_instance_st.response_state = kphp::http::response_state::sending_body;
404+ [[fallthrough]];
405+ }
406+ case kphp::http::response_state::sending_body: {
407+ tl::storer tls{http_response.footprint ()};
408+ http_response.store (tls);
369409
370- const auto status_code{http_server_instance_st.status_code == status::NO_STATUS ? status::OK : http_server_instance_st.status_code };
371-
372- tl::HttpResponse http_response{.http_response = tl::httpResponse{.version = tl::HttpVersion{.version = tl::HttpVersion::Version::V11},
373- .status_code = {.value = static_cast <int32_t >(status_code)},
374- .headers = {},
375- .body = {reinterpret_cast <const std::byte*>(body.c_str ()), body.size ()}}};
376- // fill headers
377- http_response.http_response .headers .value .reserve (http_server_instance_st.headers ().size ());
378- std::transform (http_server_instance_st.headers ().cbegin (), http_server_instance_st.headers ().cend (),
379- std::back_inserter (http_response.http_response .headers .value ), [](const auto & header_entry) noexcept {
380- const auto & [name, value]{header_entry};
381- return tl::httpHeaderEntry{
382- .is_sensitive = {}, .name = {.value = {name.data (), name.size ()}}, .value = {.value = {value.data (), value.size ()}}};
383- });
384-
385- tl::storer tls{http_response.footprint ()};
386- http_response.store (tls);
387-
388- if (!http_server_instance_st.request_stream .has_value ()) [[unlikely]] {
389- kphp::log::error (" can't send HTTP response since there is no available stream" );
410+ if (!http_server_instance_st.request_stream .has_value ()) [[unlikely]] {
411+ kphp::log::error (" can't send HTTP response since there is no available stream" );
412+ }
413+ const auto & request_stream{*http_server_instance_st.request_stream };
414+ if (auto expected{co_await kphp::component::send_response (request_stream, tls.view ())}; !expected) [[unlikely]] {
415+ kphp::log::error (" can't write HTTP response: stream -> {}, error code -> {}" , request_stream.descriptor (), expected.error ());
416+ }
417+ http_server_instance_st.response_state = kphp::http::response_state::completed;
418+ [[fallthrough]];
390419 }
391- const auto & request_stream{*http_server_instance_st.request_stream };
392- if (auto expected{co_await kphp::component::send_response (request_stream, tls.view ())}; !expected) [[unlikely]] {
393- kphp::log::error (" can't write HTTP response: stream -> {}, error code -> {}" , request_stream.descriptor (), expected.error ());
420+ case kphp::http::response_state::completed:
421+ co_return ;
394422 }
395423}
396424
0 commit comments