Skip to content

Commit 673bd6d

Browse files
authored
[k2] fix support http content-encoding on ob_gzhandler (#1523)
1 parent ed78577 commit 673bd6d

6 files changed

Lines changed: 54 additions & 5 deletions

File tree

runtime-light/server/http/http-server-state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct HttpServerInstanceState final : private vk::not_copyable {
5959

6060
std::optional<string> opt_raw_post_data;
6161

62+
bool auto_encoding_enabled{};
6263
uint32_t encoding{};
6364
uint64_t status_code{kphp::http::status::NO_STATUS};
6465
kphp::http::method http_method{kphp::http::method::other};

runtime-light/server/http/init-functions.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,11 @@ string get_http_response_body(HttpServerInstanceState& http_server_instance_st)
212212
const auto appender{[&body](const auto& buffer) noexcept { body.append(buffer.buffer(), buffer.size()); }};
213213
std::ranges::for_each(user_buffers | std::views::filter([](const auto& buffer) noexcept { return buffer.size() > 0; }), appender);
214214

215+
const bool auto_encoding_enabled{http_server_instance_st.auto_encoding_enabled};
215216
const bool gzip_encoded{static_cast<bool>(http_server_instance_st.encoding & HttpServerInstanceState::ENCODING_GZIP)};
216217
const bool deflate_encoded{static_cast<bool>(http_server_instance_st.encoding & HttpServerInstanceState::ENCODING_DEFLATE)};
217218
// compress body if needed
218-
if (gzip_encoded || deflate_encoded) {
219+
if (auto_encoding_enabled && (gzip_encoded || deflate_encoded)) {
219220
auto encoded_body{kphp::zlib::encode({body.c_str(), static_cast<size_t>(body.size())}, kphp::zlib::DEFAULT_COMPRESSION_LEVEL,
220221
gzip_encoded ? kphp::zlib::ENCODING_GZIP : kphp::zlib::ENCODING_DEFLATE)};
221222
if (encoded_body.has_value()) [[likely]] {
@@ -389,9 +390,10 @@ kphp::coro::task<> finalize_server() noexcept {
389390
}
390391
[[fallthrough]];
391392
case kphp::http::response_state::sending_headers: {
393+
const bool auto_encoding_enabled{http_server_instance_st.auto_encoding_enabled};
392394
const bool gzip_encoded{static_cast<bool>(http_server_instance_st.encoding & HttpServerInstanceState::ENCODING_GZIP)};
393395
const bool deflate_encoded{static_cast<bool>(http_server_instance_st.encoding & HttpServerInstanceState::ENCODING_DEFLATE)};
394-
if (gzip_encoded || deflate_encoded) {
396+
if (auto_encoding_enabled && (gzip_encoded || deflate_encoded)) {
395397
auto& static_SB{RuntimeContext::get().static_SB};
396398
static_SB.clean() << kphp::http::headers::CONTENT_ENCODING.data() << ": " << (gzip_encoded ? ENCODING_GZIP.data() : ENCODING_DEFLATE.data());
397399
kphp::http::header({static_SB.c_str(), static_SB.size()}, true, kphp::http::status::NO_STATUS);

runtime-light/stdlib/output/output-buffer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void f$ob_start(const string& callback) noexcept {
2424
if (!callback.empty()) {
2525
if (current_buffering_level == 0 && std::string_view{callback.c_str(), callback.size()} == ob_gzhandler_name) {
2626
auto& http_server_instance_st{HttpServerInstanceState::get()};
27-
http_server_instance_st.encoding |= HttpServerInstanceState::ENCODING_GZIP;
27+
http_server_instance_st.auto_encoding_enabled = true;
2828
} else {
2929
kphp::log::error("unsupported callback {} at buffering level {}", callback.c_str(), output_instance_st.output_buffers.user_level());
3030
}

runtime-light/stdlib/output/output-buffer.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ inline bool f$ob_end_clean() noexcept {
4242

4343
output_instance_st.output_buffers.prev_user_buffer();
4444
auto& http_server_instance_st{HttpServerInstanceState::get()};
45-
http_server_instance_st.encoding &= ~HttpServerInstanceState::ENCODING_GZIP;
45+
if (const auto current_buffering_level{output_instance_st.output_buffers.user_level()}; current_buffering_level == 0) {
46+
http_server_instance_st.auto_encoding_enabled = false;
47+
}
4648
return true;
4749
}
4850

@@ -56,7 +58,10 @@ inline Optional<string> f$ob_get_clean() noexcept {
5658
string result{(*opt_user_buffer).get().str()};
5759
output_instance_st.output_buffers.prev_user_buffer();
5860
auto& http_server_instance_st{HttpServerInstanceState::get()};
59-
http_server_instance_st.encoding &= ~HttpServerInstanceState::ENCODING_GZIP;
61+
if (const auto current_buffering_level{output_instance_st.output_buffers.user_level()}; current_buffering_level == 0) {
62+
http_server_instance_st.auto_encoding_enabled = false;
63+
}
64+
6065
return result;
6166
}
6267

tests/python/tests/http_server/php/index.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,23 @@ public function work(string $output) {
190190
echo 'OK';
191191
ob_end_flush();
192192
break;
193+
case "gzhandler-after-reset":
194+
ob_start("ob_gzhandler");
195+
echo 'OK';
196+
ob_end_flush();
197+
198+
ob_start("ob_gzhandler");
199+
break;
200+
case "gzhandler-with-nested-buffer":
201+
ob_start("ob_gzhandler");
202+
echo 'OK';
203+
ob_start();
204+
ob_end_clean();
205+
206+
break;
207+
case "gzhandler-absent":
208+
echo 'OK';
209+
break;
193210
case "ignore-second-handler":
194211
header('Transfer-Encoding: chunked');
195212
$chunk = "OK";

tests/python/tests/http_server/test_gzip_header_reset.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,36 @@ def test_single_gzip_buffer_closed(self):
1313
with self.assertRaises(KeyError):
1414
_ = response.headers["Content-Encoding"]
1515

16+
def test_gzip_handler_after_reset(self):
17+
response = self.gzip_request("gzhandler-after-reset")
18+
self.assertEqual(response.headers["Content-Encoding"], "gzip")
19+
20+
def test_deflate_handler_after_reset(self):
21+
response = self.deflate_request("gzhandler-after-reset")
22+
self.assertEqual(response.headers["Content-Encoding"], "deflate")
23+
24+
def test_gzip_handler_with_nested_buffer(self):
25+
response = self.gzip_request("gzhandler-with-nested-buffer")
26+
self.assertEqual(response.headers["Content-Encoding"], "gzip")
27+
28+
def test_gzip_without_handler(self):
29+
response = self.gzip_request("gzhandler-absent")
30+
with self.assertRaises(KeyError):
31+
_ = response.headers["Content-Encoding"]
32+
1633
@pytest.mark.k2_skip
1734
def test_ignore_second_gzip_handler(self):
1835
response = self.gzip_request("ignore-second-handler")
1936
with self.assertRaises(KeyError):
2037
_ = response.headers["Content-Encoding"]
2138

39+
def deflate_request(self, type):
40+
url = "/test_script_gzip_header?type=" + type
41+
return self.web_server.http_get(url, headers={
42+
"Host": "localhost",
43+
"Accept-Encoding": "deflate"
44+
})
45+
2246
def gzip_request(self, type):
2347
url = "/test_script_gzip_header?type=" + type
2448
return self.web_server.http_get(url, headers={

0 commit comments

Comments
 (0)