Skip to content

Commit b21ad25

Browse files
committed
[SERVER] Support gzip encoding instead of deflate.
The `compress` function is copied from httplib
1 parent c43c637 commit b21ad25

File tree

5 files changed

+57
-38
lines changed

5 files changed

+57
-38
lines changed

src/server/internalServer.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ extern "C" {
7878
#include "response.h"
7979

8080
#define MAX_SEARCH_LEN 140
81-
#define KIWIX_MIN_CONTENT_SIZE_TO_DEFLATE 100
8281
#define DEFAULT_CACHE_SIZE 2
8382

8483
namespace kiwix {

src/server/request_context.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,15 @@ RequestContext::RequestContext(struct MHD_Connection* connection,
7575
method(str2RequestMethod(_method)),
7676
version(version),
7777
requestIndex(s_requestIndex++),
78-
acceptEncodingDeflate(false),
78+
acceptEncodingGzip(false),
7979
byteRange_()
8080
{
8181
MHD_get_connection_values(connection, MHD_HEADER_KIND, &RequestContext::fill_header, this);
8282
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &RequestContext::fill_argument, this);
8383

8484
try {
85-
acceptEncodingDeflate =
86-
(get_header(MHD_HTTP_HEADER_ACCEPT_ENCODING).find("deflate") != std::string::npos);
85+
acceptEncodingGzip =
86+
(get_header(MHD_HTTP_HEADER_ACCEPT_ENCODING).find("gzip") != std::string::npos);
8787
} catch (const std::out_of_range&) {}
8888

8989
try {
@@ -127,7 +127,7 @@ void RequestContext::print_debug_info() const {
127127
printf("Parsed : \n");
128128
printf("full_url: %s\n", full_url.c_str());
129129
printf("url : %s\n", url.c_str());
130-
printf("acceptEncodingDeflate : %d\n", acceptEncodingDeflate);
130+
printf("acceptEncodingGzip : %d\n", acceptEncodingGzip);
131131
printf("has_range : %d\n", byteRange_.kind() != ByteRange::NONE);
132132
printf("is_valid_url : %d\n", is_valid_url());
133133
printf(".............\n");

src/server/request_context.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class RequestContext {
9292

9393
ByteRange get_range() const;
9494

95-
bool can_compress() const { return acceptEncodingDeflate; }
95+
bool can_compress() const { return acceptEncodingGzip; }
9696

9797
std::string get_user_language() const;
9898

@@ -103,7 +103,7 @@ class RequestContext {
103103
std::string version;
104104
unsigned long long requestIndex;
105105

106-
bool acceptEncodingDeflate;
106+
bool acceptEncodingGzip;
107107

108108
ByteRange byteRange_;
109109
std::map<std::string, std::string> headers;

src/server/response.cpp

+43-24
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
#include <mustache.hpp>
3232
#include <zlib.h>
3333

34+
#include <array>
3435

35-
#define KIWIX_MIN_CONTENT_SIZE_TO_DEFLATE 100
36+
37+
#define KIWIX_MIN_CONTENT_SIZE_TO_COMPRESS 100
3638

3739
namespace kiwix {
3840

@@ -58,6 +60,41 @@ bool is_compressible_mime_type(const std::string& mimeType)
5860
|| mimeType.find("application/json") != string::npos;
5961
}
6062

63+
inline bool compress(std::string &content) {
64+
z_stream strm;
65+
strm.zalloc = Z_NULL;
66+
strm.zfree = Z_NULL;
67+
strm.opaque = Z_NULL;
68+
69+
auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
70+
Z_DEFAULT_STRATEGY);
71+
if (ret != Z_OK) { return false; }
72+
73+
strm.avail_in = static_cast<decltype(strm.avail_in)>(content.size());
74+
strm.next_in =
75+
const_cast<Bytef *>(reinterpret_cast<const Bytef *>(content.data()));
76+
77+
std::string compressed;
78+
79+
std::array<char, 16384> buff{};
80+
do {
81+
strm.avail_out = buff.size();
82+
strm.next_out = reinterpret_cast<Bytef *>(buff.data());
83+
ret = deflate(&strm, Z_FINISH);
84+
assert(ret != Z_STREAM_ERROR);
85+
compressed.append(buff.data(), buff.size() - strm.avail_out);
86+
} while (strm.avail_out == 0);
87+
88+
assert(ret == Z_STREAM_END);
89+
assert(strm.avail_in == 0);
90+
91+
content.swap(compressed);
92+
93+
deflateEnd(&strm);
94+
return true;
95+
}
96+
97+
6198

6299
} // unnamed namespace
63100

@@ -331,7 +368,7 @@ ContentResponse::can_compress(const RequestContext& request) const
331368
{
332369
return request.can_compress()
333370
&& is_compressible_mime_type(m_mimeType)
334-
&& (m_content.size() > KIWIX_MIN_CONTENT_SIZE_TO_DEFLATE);
371+
&& (m_content.size() > KIWIX_MIN_CONTENT_SIZE_TO_COMPRESS);
335372
}
336373

337374
bool
@@ -365,35 +402,17 @@ ContentResponse::create_mhd_response(const RequestContext& request)
365402
}
366403
}
367404

368-
bool shouldCompress = can_compress(request);
369-
if (shouldCompress) {
370-
std::vector<Bytef> compr_buffer(compressBound(m_content.size()));
371-
uLongf comprLen = compr_buffer.capacity();
372-
int err = compress(&compr_buffer[0],
373-
&comprLen,
374-
(const Bytef*)(m_content.data()),
375-
m_content.size());
376-
if (err == Z_OK && comprLen > 2 && comprLen < (m_content.size() + 2)) {
377-
/* /!\ Internet Explorer has a bug with deflate compression.
378-
It can not handle the first two bytes (compression headers)
379-
We need to chunk them off (move the content 2bytes)
380-
It has no incidence on other browsers
381-
See http://www.subbu.org/blog/2008/03/ie7-deflate-or-not and comments */
382-
m_content = string((char*)&compr_buffer[2], comprLen - 2);
383-
m_etag.set_option(ETag::COMPRESSED_CONTENT);
384-
} else {
385-
shouldCompress = false;
386-
}
387-
}
405+
bool isCompressed = can_compress(request) && compress(m_content);
388406

389407
MHD_Response* response = MHD_create_response_from_buffer(
390408
m_content.size(), const_cast<char*>(m_content.data()), MHD_RESPMEM_MUST_COPY);
391409

392-
if (shouldCompress) {
410+
if (isCompressed) {
411+
m_etag.set_option(ETag::COMPRESSED_CONTENT);
393412
MHD_add_response_header(
394413
response, MHD_HTTP_HEADER_VARY, "Accept-Encoding");
395414
MHD_add_response_header(
396-
response, MHD_HTTP_HEADER_CONTENT_ENCODING, "deflate");
415+
response, MHD_HTTP_HEADER_CONTENT_ENCODING, "gzip");
397416
}
398417
return response;
399418
}

test/server.cpp

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
#define CPPHTTPLIB_ZLIB_SUPPORT 1
23
#include "./httplib.h"
34
#include "gtest/gtest.h"
45

@@ -271,17 +272,17 @@ TEST_F(ServerTest, 200)
271272
TEST_F(ServerTest, CompressibleContentIsCompressedIfAcceptable)
272273
{
273274
for ( const Resource& res : resources200Compressible ) {
274-
const auto x = zfs1_->GET(res.url, { {"Accept-Encoding", "deflate"} });
275+
const auto x = zfs1_->GET(res.url, { {"Accept-Encoding", "gzip"} });
275276
EXPECT_EQ(200, x->status) << res;
276-
EXPECT_EQ("deflate", x->get_header_value("Content-Encoding")) << res;
277+
EXPECT_EQ("gzip", x->get_header_value("Content-Encoding")) << res;
277278
EXPECT_EQ("Accept-Encoding", x->get_header_value("Vary")) << res;
278279
}
279280
}
280281

281282
TEST_F(ServerTest, UncompressibleContentIsNotCompressed)
282283
{
283284
for ( const Resource& res : resources200Uncompressible ) {
284-
const auto x = zfs1_->GET(res.url, { {"Accept-Encoding", "deflate"} });
285+
const auto x = zfs1_->GET(res.url, { {"Accept-Encoding", "gzip"} });
285286
EXPECT_EQ(200, x->status) << res;
286287
EXPECT_EQ("", x->get_header_value("Content-Encoding")) << res;
287288
}
@@ -1062,7 +1063,7 @@ TEST_F(ServerTest, CompressionInfluencesETag)
10621063
if ( ! res.etag_expected ) continue;
10631064
const auto g1 = zfs1_->GET(res.url);
10641065
const auto g2 = zfs1_->GET(res.url, { {"Accept-Encoding", ""} } );
1065-
const auto g3 = zfs1_->GET(res.url, { {"Accept-Encoding", "deflate"} } );
1066+
const auto g3 = zfs1_->GET(res.url, { {"Accept-Encoding", "gzip"} } );
10661067
const auto etag = g1->get_header_value("ETag");
10671068
EXPECT_EQ(etag, g2->get_header_value("ETag"));
10681069
EXPECT_NE(etag, g3->get_header_value("ETag"));
@@ -1075,7 +1076,7 @@ TEST_F(ServerTest, ETagOfUncompressibleContentIsNotAffectedByAcceptEncoding)
10751076
if ( ! res.etag_expected ) continue;
10761077
const auto g1 = zfs1_->GET(res.url);
10771078
const auto g2 = zfs1_->GET(res.url, { {"Accept-Encoding", ""} } );
1078-
const auto g3 = zfs1_->GET(res.url, { {"Accept-Encoding", "deflate"} } );
1079+
const auto g3 = zfs1_->GET(res.url, { {"Accept-Encoding", "gzip"} } );
10791080
const auto etag = g1->get_header_value("ETag");
10801081
EXPECT_EQ(etag, g2->get_header_value("ETag")) << res;
10811082
EXPECT_EQ(etag, g3->get_header_value("ETag")) << res;
@@ -1114,7 +1115,7 @@ std::string make_etag_list(const std::string& etag)
11141115

11151116
TEST_F(ServerTest, IfNoneMatchRequestsWithMatchingETagResultIn304Responses)
11161117
{
1117-
const char* const encodings[] = { "", "deflate" };
1118+
const char* const encodings[] = { "", "gzip" };
11181119
for ( const Resource& res : all200Resources() ) {
11191120
for ( const char* enc: encodings ) {
11201121
if ( ! res.etag_expected ) continue;
@@ -1245,7 +1246,7 @@ TEST_F(ServerTest, RangeHasPrecedenceOverCompression)
12451246

12461247
const Headers onlyRange{ {"Range", "bytes=123-456"} };
12471248
Headers rangeAndCompression(onlyRange);
1248-
rangeAndCompression.insert({"Accept-Encoding", "deflate"});
1249+
rangeAndCompression.insert({"Accept-Encoding", "gzip"});
12491250

12501251
const auto p1 = zfs1_->GET(url, onlyRange);
12511252
const auto p2 = zfs1_->GET(url, rangeAndCompression);

0 commit comments

Comments
 (0)