From 899c383f2fed3720594a1cacfb1c4f8dffb4290b Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Sat, 28 Sep 2024 09:24:14 -0700 Subject: [PATCH 01/12] updated meson_options.txt --- meson_options.txt | 1 + version.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index 89322d6a..4be0675f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -9,6 +9,7 @@ option('PISTACHE_INSTALL', type: 'boolean', value: true, description: 'add pista option('PISTACHE_USE_SSL', type: 'boolean', value: false, description: 'add support for SSL server') option('PISTACHE_USE_RAPIDJSON', type: 'boolean', value: true, description: 'add support for rapidjson') option('PISTACHE_USE_CONTENT_ENCODING_BROTLI', type: 'boolean', value: false, description: 'add support for Brotli compressed content encoding') +option('PISTACHE_USE_CONTENT_ENCODING_ZSTD', type: 'boolean', value: false, description: 'add support for ZSTD compressed content encoding') option('PISTACHE_USE_CONTENT_ENCODING_DEFLATE', type: 'boolean', value: false, description: 'add support for deflate compressed content encoding') option('PISTACHE_DEBUG', type: 'boolean', value: false, description: 'with debugging code') option('PISTACHE_LOG_AND_STDOUT', type: 'boolean', value: false, description: 'send log msgs to stdout too') diff --git a/version.txt b/version.txt index ba40da52..44c12a6c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.4.6.20240919 +0.4.7.20240928 From 82c372f5fe66bc7c17d3545b9afc0535f991f2f9 Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Mon, 30 Sep 2024 18:43:46 -0700 Subject: [PATCH 02/12] added pistache_use_content_encoding_zstd to build and common http --- include/pistache/http_header.h | 1 + meson.build | 6 + src/common/http.cc | 7 + src/meson.build | 4 + tests/http_server_test.cc | 1420 ++++++++++++++++---------------- tests/meson.build | 7 +- version.txt | 2 +- 7 files changed, 743 insertions(+), 704 deletions(-) diff --git a/include/pistache/http_header.h b/include/pistache/http_header.h index 08315aec..48fdba83 100644 --- a/include/pistache/http_header.h +++ b/include/pistache/http_header.h @@ -68,6 +68,7 @@ namespace Pistache::Http::Header Br, Compress, Deflate, + Zstd, Identity, Chunked, Unknown }; diff --git a/meson.build b/meson.build index 4fb55ba8..86cc91e7 100644 --- a/meson.build +++ b/meson.build @@ -62,6 +62,12 @@ if get_option('PISTACHE_USE_RAPIDJSON') public_deps += rapidjson_dep endif +if get_option('PISTACHE_USE_CONTENT_ENCODING_ZSTD') + zstd_dep = dependency('libzstd') + deps_libpistache += zstd_dep + public_deps += zstd_dep +endif + # Support Brotli compressed Content-Encoding responses... if get_option('PISTACHE_USE_CONTENT_ENCODING_BROTLI') diff --git a/src/common/http.cc b/src/common/http.cc index d4293605..b48f0bea 100644 --- a/src/common/http.cc +++ b/src/common/http.cc @@ -922,6 +922,10 @@ namespace Pistache::Http } #endif +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD + +#endif + #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE // User requested deflate compression... case Http::Header::Encoding::Deflate: { @@ -1084,6 +1088,9 @@ namespace Pistache::Http break; #endif +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD +#endif + #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE // Application requested deflate compression... case Http::Header::Encoding::Deflate: diff --git a/src/meson.build b/src/meson.build index 5f9eccf4..92de9438 100644 --- a/src/meson.build +++ b/src/meson.build @@ -45,6 +45,10 @@ if get_option('PISTACHE_USE_CONTENT_ENCODING_BROTLI') public_args += '-DPISTACHE_USE_CONTENT_ENCODING_BROTLI' endif +if get_option('PISTACHE_USE_CONTENT_ENCODING_ZSTD') + public_args += '-DPISTACHE_USE_CONTENT_ENCODING_ZSTD' +endif + if get_option('PISTACHE_USE_CONTENT_ENCODING_DEFLATE') public_args += '-DPISTACHE_USE_CONTENT_ENCODING_DEFLATE' endif diff --git a/tests/http_server_test.cc b/tests/http_server_test.cc index b57771d2..459e1c90 100644 --- a/tests/http_server_test.cc +++ b/tests/http_server_test.cc @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "pistache/http_header.h" +#include #include #include #include @@ -302,31 +304,31 @@ TEST(http_server_test, #endif { // encapsulate - - const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); + const Pistache::Address address("localhost", Pistache::Port(0)); - LOGGER("test", "Trying to run server..."); - const int ONE_SECOND_TIMEOUT = 1; - const int SIX_SECONDS_DELAY = 6; - server.setHandler( - Http::make_handler(SIX_SECONDS_DELAY)); - server.serveThreaded(); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + LOGGER("test", "Trying to run server..."); + const int ONE_SECOND_TIMEOUT = 1; + const int SIX_SECONDS_DELAY = 6; + server.setHandler( + Http::make_handler(SIX_SECONDS_DELAY)); + server.serveThreaded(); - const int CLIENT_REQUEST_SIZE = 1; - int counter = clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, - ONE_SECOND_TIMEOUT, SIX_SECONDS_DELAY); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - server.shutdown(); + const int CLIENT_REQUEST_SIZE = 1; + int counter = clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, + ONE_SECOND_TIMEOUT, SIX_SECONDS_DELAY); - ASSERT_EQ(counter, 0); + server.shutdown(); + + ASSERT_EQ(counter, 0); } // end encapsulate @@ -355,33 +357,33 @@ TEST( { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); - LOGGER("test", "Trying to run server..."); - const int ONE_SECOND_TIMEOUT = 1; - const int SIX_SECONDS_DELAY = 6; - server.setHandler( - Http::make_handler(SIX_SECONDS_DELAY)); - server.serveThreaded(); + LOGGER("test", "Trying to run server..."); + const int ONE_SECOND_TIMEOUT = 1; + const int SIX_SECONDS_DELAY = 6; + server.setHandler( + Http::make_handler(SIX_SECONDS_DELAY)); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - const int CLIENT_REQUEST_SIZE = 3; - int counter = clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, - ONE_SECOND_TIMEOUT, SIX_SECONDS_DELAY); + const int CLIENT_REQUEST_SIZE = 3; + int counter = clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, + ONE_SECOND_TIMEOUT, SIX_SECONDS_DELAY); - server.shutdown(); + server.shutdown(); - ASSERT_EQ(counter, 0); + ASSERT_EQ(counter, 0); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -405,40 +407,40 @@ TEST(http_server_test, multiple_client_with_requests_to_multithreaded_server) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags).threads(3); - server.init(server_opts); - LOGGER("test", "Trying to run server..."); - server.setHandler(Http::make_handler()); - ASSERT_NO_THROW(server.serveThreaded()); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags).threads(3); + server.init(server_opts); + LOGGER("test", "Trying to run server..."); + server.setHandler(Http::make_handler()); + ASSERT_NO_THROW(server.serveThreaded()); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - const int NO_TIMEOUT = 0; - const int SIX_SECONDS_TIMOUT = 6; - const int FIRST_CLIENT_REQUEST_SIZE = 4; - std::future result1(std::async(clientLogicFunc, - FIRST_CLIENT_REQUEST_SIZE, server_address, - NO_TIMEOUT, SIX_SECONDS_TIMOUT)); - const int SECOND_CLIENT_REQUEST_SIZE = 5; - std::future result2( - std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, - NO_TIMEOUT, SIX_SECONDS_TIMOUT)); + const int NO_TIMEOUT = 0; + const int SIX_SECONDS_TIMOUT = 6; + const int FIRST_CLIENT_REQUEST_SIZE = 4; + std::future result1(std::async(clientLogicFunc, + FIRST_CLIENT_REQUEST_SIZE, server_address, + NO_TIMEOUT, SIX_SECONDS_TIMOUT)); + const int SECOND_CLIENT_REQUEST_SIZE = 5; + std::future result2( + std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, + NO_TIMEOUT, SIX_SECONDS_TIMOUT)); - int res1 = result1.get(); - int res2 = result2.get(); + int res1 = result1.get(); + int res2 = result2.get(); - server.shutdown(); + server.shutdown(); - ASSERT_EQ(res1, FIRST_CLIENT_REQUEST_SIZE); - ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); + ASSERT_EQ(res1, FIRST_CLIENT_REQUEST_SIZE); + ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -462,40 +464,40 @@ TEST(http_server_test, many_client_with_requests_to_multithreaded_server) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags).threads(6); - server.init(server_opts); - LOGGER("test", "Trying to run server..."); - server.setHandler(Http::make_handler()); - ASSERT_NO_THROW(server.serveThreaded()); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags).threads(6); + server.init(server_opts); + LOGGER("test", "Trying to run server..."); + server.setHandler(Http::make_handler()); + ASSERT_NO_THROW(server.serveThreaded()); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - const int NO_TIMEOUT = 0; - const int SECONDS_TIMOUT = 20; - const int FIRST_CLIENT_REQUEST_SIZE = 128; - std::future result1(std::async(clientLogicFunc, - FIRST_CLIENT_REQUEST_SIZE, server_address, - NO_TIMEOUT, SECONDS_TIMOUT)); - const int SECOND_CLIENT_REQUEST_SIZE = 192; - std::future result2( - std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, - NO_TIMEOUT, 3 * SECONDS_TIMOUT)); + const int NO_TIMEOUT = 0; + const int SECONDS_TIMOUT = 20; + const int FIRST_CLIENT_REQUEST_SIZE = 128; + std::future result1(std::async(clientLogicFunc, + FIRST_CLIENT_REQUEST_SIZE, server_address, + NO_TIMEOUT, SECONDS_TIMOUT)); + const int SECOND_CLIENT_REQUEST_SIZE = 192; + std::future result2( + std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, + NO_TIMEOUT, 3 * SECONDS_TIMOUT)); - int res1 = result1.get(); - int res2 = result2.get(); + int res1 = result1.get(); + int res2 = result2.get(); - server.shutdown(); + server.shutdown(); - ASSERT_EQ(res1, FIRST_CLIENT_REQUEST_SIZE); - ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); + ASSERT_EQ(res1, FIRST_CLIENT_REQUEST_SIZE); + ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -520,47 +522,47 @@ TEST(http_server_test, { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags).threads(4); - server.init(server_opts); - const int SIX_SECONDS_DELAY = 6; - server.setHandler(Http::make_handler(SIX_SECONDS_DELAY)); - server.serveThreaded(); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags).threads(4); + server.init(server_opts); + const int SIX_SECONDS_DELAY = 6; + server.setHandler(Http::make_handler(SIX_SECONDS_DELAY)); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - const int FIRST_CLIENT_REQUEST_SIZE = 1; - const int FIRST_CLIENT_TIMEOUT = SIX_SECONDS_DELAY / 2; - std::future result1(std::async( - clientLogicFunc, FIRST_CLIENT_REQUEST_SIZE, server_address + SLOW_PAGE, - FIRST_CLIENT_TIMEOUT, SIX_SECONDS_DELAY)); - const int SECOND_CLIENT_REQUEST_SIZE = 2; - const int SECOND_CLIENT_TIMEOUT = SIX_SECONDS_DELAY * 2; - std::future result2( - std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, - SECOND_CLIENT_TIMEOUT, 2 * SIX_SECONDS_DELAY)); + const int FIRST_CLIENT_REQUEST_SIZE = 1; + const int FIRST_CLIENT_TIMEOUT = SIX_SECONDS_DELAY / 2; + std::future result1(std::async( + clientLogicFunc, FIRST_CLIENT_REQUEST_SIZE, server_address + SLOW_PAGE, + FIRST_CLIENT_TIMEOUT, SIX_SECONDS_DELAY)); + const int SECOND_CLIENT_REQUEST_SIZE = 2; + const int SECOND_CLIENT_TIMEOUT = SIX_SECONDS_DELAY * 2; + std::future result2( + std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, + SECOND_CLIENT_TIMEOUT, 2 * SIX_SECONDS_DELAY)); - int res1 = result1.get(); - int res2 = result2.get(); + int res1 = result1.get(); + int res2 = result2.get(); - server.shutdown(); + server.shutdown(); - if (hardware_concurrency() > 1) - { - ASSERT_EQ(res1, 0); - ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); - } - else - { - ASSERT_TRUE(true); - } + if (hardware_concurrency() > 1) + { + ASSERT_EQ(res1, 0); + ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); + } + else + { + ASSERT_TRUE(true); + } } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -584,66 +586,66 @@ TEST(http_server_test, server_with_static_file) { // encapsulate - const std::string data("Hello, World!"); - char fileName[PATH_MAX] = "/tmp/pistacheioXXXXXX"; - if (!mkstemp(fileName)) - { - std::cerr << "No suitable filename can be generated!" << std::endl; - } - LOGGER("test", "Creating temporary file: " << fileName); + const std::string data("Hello, World!"); + char fileName[PATH_MAX] = "/tmp/pistacheioXXXXXX"; + if (!mkstemp(fileName)) + { + std::cerr << "No suitable filename can be generated!" << std::endl; + } + LOGGER("test", "Creating temporary file: " << fileName); - std::ofstream tmpFile; - tmpFile.open(fileName); - tmpFile << data; - tmpFile.close(); + std::ofstream tmpFile; + tmpFile.open(fileName); + tmpFile << data; + tmpFile.close(); - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); - server.setHandler(Http::make_handler(fileName)); - server.serveThreaded(); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); + server.setHandler(Http::make_handler(fileName)); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - Http::Experimental::Client client; - client.init(); - auto rb = client.get(server_address); - PS_LOG_DEBUG("Calling send"); - - auto response = rb.send(); - std::string resultData; - PS_LOG_DEBUG("About to wait for response"); - response.then( - [&resultData](Http::Response resp) { - PS_LOG_DEBUG_ARGS("Http::Response %d", resp.code()); - - std::cout << "Response code is " << resp.code() << std::endl; - if (resp.code() == Http::Code::Ok) - { - resultData = resp.body(); - } - }, - Async::Throw); - PS_LOG_DEBUG("response.then() returned"); + Http::Experimental::Client client; + client.init(); + auto rb = client.get(server_address); + PS_LOG_DEBUG("Calling send"); - const int WAIT_TIME = 2; - Async::Barrier barrier(response); - barrier.wait_for(std::chrono::seconds(WAIT_TIME)); + auto response = rb.send(); + std::string resultData; + PS_LOG_DEBUG("About to wait for response"); + response.then( + [&resultData](Http::Response resp) { + PS_LOG_DEBUG_ARGS("Http::Response %d", resp.code()); - client.shutdown(); - server.shutdown(); + std::cout << "Response code is " << resp.code() << std::endl; + if (resp.code() == Http::Code::Ok) + { + resultData = resp.body(); + } + }, + Async::Throw); + PS_LOG_DEBUG("response.then() returned"); - LOGGER("test", "Deleting file " << fileName); - std::remove(fileName); + const int WAIT_TIME = 2; + Async::Barrier barrier(response); + barrier.wait_for(std::chrono::seconds(WAIT_TIME)); - ASSERT_EQ(data, resultData); + client.shutdown(); + server.shutdown(); + + LOGGER("test", "Deleting file " << fileName); + std::remove(fileName); + + ASSERT_EQ(data, resultData); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -667,55 +669,55 @@ TEST(http_server_test, server_request_copies_address) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - Http::Experimental::Client client; - client.init(); - auto rb = client.get(server_address); - auto response = rb.send(); - std::string resultData; - response.then( - [&resultData](Http::Response resp) { - LOGGER("client", " Response code is " << resp.code()); - if (resp.code() == Http::Code::Ok) - { - resultData = resp.body(); - } - }, - Async::Throw); + Http::Experimental::Client client; + client.init(); + auto rb = client.get(server_address); + auto response = rb.send(); + std::string resultData; + response.then( + [&resultData](Http::Response resp) { + LOGGER("client", " Response code is " << resp.code()); + if (resp.code() == Http::Code::Ok) + { + resultData = resp.body(); + } + }, + Async::Throw); - const int WAIT_TIME = 2; - Async::Barrier barrier(response); - barrier.wait_for(std::chrono::seconds(WAIT_TIME)); + const int WAIT_TIME = 2; + Async::Barrier barrier(response); + barrier.wait_for(std::chrono::seconds(WAIT_TIME)); - client.shutdown(); - server.shutdown(); + client.shutdown(); + server.shutdown(); - if (address.family() == AF_INET) - { - ASSERT_EQ("127.0.0.1", resultData); - } - else if (address.family() == AF_INET6) - { - ASSERT_EQ("::1", resultData); - } - else - { - ASSERT_TRUE(false); - } + if (address.family() == AF_INET) + { + ASSERT_EQ("127.0.0.1", resultData); + } + else if (address.family() == AF_INET6) + { + ASSERT_EQ("::1", resultData); + } + else + { + ASSERT_TRUE(false); + } } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -764,67 +766,67 @@ TEST(http_server_test, response_size_captured) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - size_t rsize = 0; - Http::Code rcode; + size_t rsize = 0; + Http::Code rcode; - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); - server.setHandler(Http::make_handler(rsize, rcode)); - server.serveThreaded(); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); + server.setHandler(Http::make_handler(rsize, rcode)); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - // Use the built-in http client, but this test is interested in testing - // that the ResponseWriter in the server stashed the correct size and code - // values. - Http::Experimental::Client client; - client.init(); - auto rb = client.get(server_address); - auto response = rb.send(); - std::string resultData; - response.then( - [&resultData](Http::Response resp) { - LOGGER("client", "Response code is " << resp.code()); - if (resp.code() == Http::Code::Ok) - { - resultData = resp.body(); - } - }, - Async::Throw); + // Use the built-in http client, but this test is interested in testing + // that the ResponseWriter in the server stashed the correct size and code + // values. + Http::Experimental::Client client; + client.init(); + auto rb = client.get(server_address); + auto response = rb.send(); + std::string resultData; + response.then( + [&resultData](Http::Response resp) { + LOGGER("client", "Response code is " << resp.code()); + if (resp.code() == Http::Code::Ok) + { + resultData = resp.body(); + } + }, + Async::Throw); - const int WAIT_TIME = 2; - Async::Barrier barrier(response); - barrier.wait_for(std::chrono::seconds(WAIT_TIME)); + const int WAIT_TIME = 2; + Async::Barrier barrier(response); + barrier.wait_for(std::chrono::seconds(WAIT_TIME)); - client.shutdown(); - server.shutdown(); + client.shutdown(); + server.shutdown(); - // Sanity check (stolen from AddressEchoHandler test). - if (address.family() == AF_INET) - { - ASSERT_EQ("127.0.0.1", resultData); - } - else if (address.family() == AF_INET6) - { - ASSERT_EQ("::1", resultData); - } - else - { - ASSERT_TRUE(false); - } + // Sanity check (stolen from AddressEchoHandler test). + if (address.family() == AF_INET) + { + ASSERT_EQ("127.0.0.1", resultData); + } + else if (address.family() == AF_INET6) + { + ASSERT_EQ("::1", resultData); + } + else + { + ASSERT_TRUE(false); + } - LOGGER("test", "Response size is " << rsize); - ASSERT_GT(rsize, 1u); - ASSERT_LT(rsize, 300u); - ASSERT_EQ(rcode, Http::Code::Ok); + LOGGER("test", "Response size is " << rsize); + ASSERT_GT(rsize, 1u); + ASSERT_LT(rsize, 300u); + ASSERT_EQ(rcode, Http::Code::Ok); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -848,38 +850,38 @@ TEST(http_server_test, client_request_timeout_on_only_connect_raises_http_408) { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(2); + const auto headerTimeout = std::chrono::seconds(2); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr) + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr) - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - server.shutdown(); + server.shutdown(); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -903,45 +905,45 @@ TEST(http_server_test, client_request_timeout_on_delay_in_header_send_raises_htt { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(1); + const auto headerTimeout = std::chrono::seconds(1); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr); + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr); - const std::string reqStr = "GET /ping HTTP/1.1\r\n"; - const std::string headerStr = "Host: localhost\r\nUser-Agent: test\r\n"; + const std::string reqStr = "GET /ping HTTP/1.1\r\n"; + const std::string headerStr = "Host: localhost\r\nUser-Agent: test\r\n"; - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - EXPECT_TRUE(client.send(reqStr)) << client.lastError(); + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + EXPECT_TRUE(client.send(reqStr)) << client.lastError(); - std::this_thread::sleep_for(headerTimeout / 2); - EXPECT_TRUE(client.send(headerStr)) << client.lastError(); + std::this_thread::sleep_for(headerTimeout / 2); + EXPECT_TRUE(client.send(headerStr)) << client.lastError(); - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - server.shutdown(); + server.shutdown(); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -965,56 +967,56 @@ TEST(http_server_test, client_request_timeout_on_delay_in_request_line_send_rais { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(2); + const auto headerTimeout = std::chrono::seconds(2); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr); + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr); - const std::string reqStr { "GET /ping HTTP/1.1\r\n" }; - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - bool send_failed = false; - for (size_t i = 0; i < reqStr.size(); ++i) - { - if (!client.send(reqStr.substr(i, 1))) + const std::string reqStr { "GET /ping HTTP/1.1\r\n" }; + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + bool send_failed = false; + for (size_t i = 0; i < reqStr.size(); ++i) { - send_failed = true; - break; + if (!client.send(reqStr.substr(i, 1))) + { + send_failed = true; + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(300)); } - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - } - - if (send_failed) - { // Usually, send does fail; but on macOS occasionally it does not fail - // We workaround that here, since of course we can only check for an - // error code when there is an actual error - EXPECT_EQ(client.lastErrno(), EPIPE) << "Errno: " << client.lastErrno(); - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - } + if (send_failed) + { // Usually, send does fail; but on macOS occasionally it does not fail + // We workaround that here, since of course we can only check for an + // error code when there is an actual error + EXPECT_EQ(client.lastErrno(), EPIPE) << "Errno: " << client.lastErrno(); + + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + } - server.shutdown(); + server.shutdown(); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1038,43 +1040,43 @@ TEST(http_server_test, client_request_timeout_on_delay_in_body_send_raises_http_ { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(1); - const auto bodyTimeout = std::chrono::seconds(2); + const auto headerTimeout = std::chrono::seconds(1); + const auto bodyTimeout = std::chrono::seconds(2); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout) - .bodyTimeout(bodyTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout) + .bodyTimeout(bodyTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr); + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr); - const std::string reqStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 32\r\n\r\nabc"; + const std::string reqStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 32\r\n\r\nabc"; - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - EXPECT_TRUE(client.send(reqStr)) << client.lastError(); + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + EXPECT_TRUE(client.send(reqStr)) << client.lastError(); - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - server.shutdown(); + server.shutdown(); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1098,49 +1100,49 @@ TEST(http_server_test, client_request_no_timeout) { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(2); - const auto bodyTimeout = std::chrono::seconds(4); + const auto headerTimeout = std::chrono::seconds(2); + const auto bodyTimeout = std::chrono::seconds(4); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout) - .bodyTimeout(bodyTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout) + .bodyTimeout(bodyTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr); + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr); - const std::string headerStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 8\r\n\r\n"; - const std::string bodyStr = "abcdefgh\r\n\r\n"; + const std::string headerStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 8\r\n\r\n"; + const std::string bodyStr = "abcdefgh\r\n\r\n"; - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - std::this_thread::sleep_for(headerTimeout / 2); - EXPECT_TRUE(client.send(headerStr)) << client.lastError(); + std::this_thread::sleep_for(headerTimeout / 2); + EXPECT_TRUE(client.send(headerStr)) << client.lastError(); - std::this_thread::sleep_for(bodyTimeout / 2); - EXPECT_TRUE(client.send(bodyStr)) << client.lastError(); + std::this_thread::sleep_for(bodyTimeout / 2); + EXPECT_TRUE(client.send(bodyStr)) << client.lastError(); - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_NE(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_NE(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - server.shutdown(); + server.shutdown(); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1232,32 +1234,32 @@ TEST(http_server_test, client_multiple_requests_disconnects_handled) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); - std::cout << "Trying to run server...\n"; - auto waitHelper = std::make_shared(); - auto handler = Http::make_handler(waitHelper); - server.setHandler(handler); - server.serveThreaded(); + std::cout << "Trying to run server...\n"; + auto waitHelper = std::make_shared(); + auto handler = Http::make_handler(waitHelper); + server.setHandler(handler); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - std::cout << "Server address: " << server_address << "\n"; + const std::string server_address = "localhost:" + server.getPort().toString(); + std::cout << "Server address: " << server_address << "\n"; - const size_t CLIENT_REQUEST_SIZE = 3; - clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, 1, 6); + const size_t CLIENT_REQUEST_SIZE = 3; + clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, 1, 6); - const bool result = waitHelper->wait(CLIENT_REQUEST_SIZE, std::chrono::seconds(2)); - server.shutdown(); + const bool result = waitHelper->wait(CLIENT_REQUEST_SIZE, std::chrono::seconds(2)); + server.shutdown(); - ASSERT_EQ(result, true); + ASSERT_EQ(result, true); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1297,6 +1299,12 @@ struct ContentEncodingHandler : public Http::Handler switch (encoding) { +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD + case Http::Header::Encoding::Zstd: + std::cout << "Hello"; + break; +#endif + #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI // Set maximum compression if using Brotli case Http::Header::Encoding::Br: @@ -1320,6 +1328,14 @@ struct ContentEncodingHandler : public Http::Handler } }; +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD +TEST(http_server_test, server_with_content_encoding_zstd) +{ + std::cout << "Hey"; + ASSERT_TRUE(false); +} +#endif + #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI TEST(http_server_test, server_with_content_encoding_brotli) { @@ -1333,147 +1349,147 @@ TEST(http_server_test, server_with_content_encoding_brotli) { // encapsulate - // Data to send to server to expect it to return compressed... + // Data to send to server to expect it to return compressed... - // Allocate storage... - std::vector originalUncompressedData(1024); + // Allocate storage... + std::vector originalUncompressedData(1024); - // Random bytes engine... - using random_bytes_engine_type = std::independent_bits_engine< - std::default_random_engine, CHAR_BIT, unsigned char>; - random_bytes_engine_type randomEngine; + // Random bytes engine... + using random_bytes_engine_type = std::independent_bits_engine< + std::default_random_engine, CHAR_BIT, unsigned char>; + random_bytes_engine_type randomEngine; - // Fill with random bytes... - std::generate( - std::begin(originalUncompressedData), - std::end(originalUncompressedData), - [&randomEngine]() { return static_cast(randomEngine()); }); + // Fill with random bytes... + std::generate( + std::begin(originalUncompressedData), + std::end(originalUncompressedData), + [&randomEngine]() { return static_cast(randomEngine()); }); - // Bind server to localhost on a random port... - const Pistache::Address address("localhost", Pistache::Port(0)); + // Bind server to localhost on a random port... + const Pistache::Address address("localhost", Pistache::Port(0)); - // Initialize server... - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server_opts.maxRequestSize(1024 * 1024 * 20); - server_opts.maxResponseSize(1024 * 1024 * 20); - server.init(server_opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + // Initialize server... + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server_opts.maxRequestSize(1024 * 1024 * 20); + server_opts.maxResponseSize(1024 * 1024 * 20); + server.init(server_opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - // Verify server is running... - ASSERT_TRUE(server.isBound()); + // Verify server is running... + ASSERT_TRUE(server.isBound()); - // Log server coordinates... - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + // Log server coordinates... + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - // Initialize client... + // Initialize client... - // Construct and initialize... - Http::Experimental::Client client; - client.init(); + // Construct and initialize... + Http::Experimental::Client client; + client.init(); - // Set server to connect to and get request builder object... - auto rb = client.get(server_address); + // Set server to connect to and get request builder object... + auto rb = client.get(server_address); - // Set data to send as body... - rb.body( - std::string( - reinterpret_cast(originalUncompressedData.data()), - originalUncompressedData.size())); + // Set data to send as body... + rb.body( + std::string( + reinterpret_cast(originalUncompressedData.data()), + originalUncompressedData.size())); - // Request server send back response Brotli compressed... - rb.header(Http::Header::Encoding::Br); + // Request server send back response Brotli compressed... + rb.header(Http::Header::Encoding::Br); - // Send client request. Note that Transport::asyncSendRequestImpl() is - // buggy, or at least with Pistache::Client, when the amount of data being - // sent is large. When that happens send() breaks in asyncSendRequestImpl() - // receiving an errno=EAGAIN... - auto response = rb.send(); + // Send client request. Note that Transport::asyncSendRequestImpl() is + // buggy, or at least with Pistache::Client, when the amount of data being + // sent is large. When that happens send() breaks in asyncSendRequestImpl() + // receiving an errno=EAGAIN... + auto response = rb.send(); - // Storage for server response body... - std::string resultStringData; + // Storage for server response body... + std::string resultStringData; - // Verify response code, expected header, and store its body... - response.then( - [&resultStringData](Http::Response resp) { - // Log response code... - LOGGER("client", "Response code: " << resp.code()); + // Verify response code, expected header, and store its body... + response.then( + [&resultStringData](Http::Response resp) { + // Log response code... + LOGGER("client", "Response code: " << resp.code()); - // Log Content-Encoding header value, if present... - if (resp.headers().tryGetRaw("Content-Encoding").has_value()) - { - LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); - } + // Log Content-Encoding header value, if present... + if (resp.headers().tryGetRaw("Content-Encoding").has_value()) + { + LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); + } - // Preserve body only if response code as expected... - if (resp.code() == Http::Code::Ok) - resultStringData = resp.body(); + // Preserve body only if response code as expected... + if (resp.code() == Http::Code::Ok) + resultStringData = resp.body(); - // Get response headers... - const auto& headers = resp.headers(); + // Get response headers... + const auto& headers = resp.headers(); - // Verify Content-Encoding header was present... - ASSERT_TRUE(headers.has()); + // Verify Content-Encoding header was present... + ASSERT_TRUE(headers.has()); - // Verify Content-Encoding was set to Brotli... - const auto ce = headers.get().get(); - ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Br); - }, - Async::Throw); + // Verify Content-Encoding was set to Brotli... + const auto ce = headers.get().get(); + ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Br); + }, + Async::Throw); - // Wait for response to complete... - Async::Barrier barrier(response); - barrier.wait(); + // Wait for response to complete... + Async::Barrier barrier(response); + barrier.wait(); - // Cleanup client and server... - client.shutdown(); - server.shutdown(); + // Cleanup client and server... + client.shutdown(); + server.shutdown(); - // Get server response body in vector... - std::vector newlyCompressedResponse(resultStringData.size()); - std::transform( - std::cbegin(resultStringData), - std::cend(resultStringData), - std::begin(newlyCompressedResponse), - [](const char character) { return static_cast(character); }); + // Get server response body in vector... + std::vector newlyCompressedResponse(resultStringData.size()); + std::transform( + std::cbegin(resultStringData), + std::cend(resultStringData), + std::begin(newlyCompressedResponse), + [](const char character) { return static_cast(character); }); - // The data the server responded with should be compressed, and therefore - // different from the original uncompressed sent during the request... - ASSERT_NE(originalUncompressedData, newlyCompressedResponse); + // The data the server responded with should be compressed, and therefore + // different from the original uncompressed sent during the request... + ASSERT_NE(originalUncompressedData, newlyCompressedResponse); - // Decompress response body... + // Decompress response body... - // Storage for decompressed data... - std::vector newlyDecompressedData( - originalUncompressedData.size()); + // Storage for decompressed data... + std::vector newlyDecompressedData( + originalUncompressedData.size()); - // Size of destination buffer, but will be updated by uncompress() to - // actual size used... - size_t destinationLength = originalUncompressedData.size(); + // Size of destination buffer, but will be updated by uncompress() to + // actual size used... + size_t destinationLength = originalUncompressedData.size(); - // Decompress... - const auto compressionStatus = ::BrotliDecoderDecompress( - resultStringData.size(), - reinterpret_cast(resultStringData.data()), - &destinationLength, - reinterpret_cast(newlyDecompressedData.data())); + // Decompress... + const auto compressionStatus = ::BrotliDecoderDecompress( + resultStringData.size(), + reinterpret_cast(resultStringData.data()), + &destinationLength, + reinterpret_cast(newlyDecompressedData.data())); - // Check for failure... - ASSERT_EQ(compressionStatus, BROTLI_DECODER_RESULT_SUCCESS); + // Check for failure... + ASSERT_EQ(compressionStatus, BROTLI_DECODER_RESULT_SUCCESS); - // The sizes of both the original uncompressed data we sent the server - // and the result of decompressing what it sent back should match... - ASSERT_EQ(originalUncompressedData.size(), destinationLength); + // The sizes of both the original uncompressed data we sent the server + // and the result of decompressing what it sent back should match... + ASSERT_EQ(originalUncompressedData.size(), destinationLength); - // Check to ensure the compressed data received back from server after - // decompression matches exactly what we originally sent it... - ASSERT_EQ(originalUncompressedData, newlyDecompressedData); + // Check to ensure the compressed data received back from server after + // decompression matches exactly what we originally sent it... + ASSERT_EQ(originalUncompressedData, newlyDecompressedData); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1499,147 +1515,147 @@ TEST(http_server_test, server_with_content_encoding_deflate) { // encapsulate - // Data to send to server to expect it to return compressed... + // Data to send to server to expect it to return compressed... - // Allocate storage... - std::vector originalUncompressedData(1024); + // Allocate storage... + std::vector originalUncompressedData(1024); - // Random bytes engine... - using random_bytes_engine_type = std::independent_bits_engine< - std::default_random_engine, CHAR_BIT, unsigned char>; - random_bytes_engine_type randomEngine; + // Random bytes engine... + using random_bytes_engine_type = std::independent_bits_engine< + std::default_random_engine, CHAR_BIT, unsigned char>; + random_bytes_engine_type randomEngine; - // Fill with random bytes... - std::generate( - std::begin(originalUncompressedData), - std::end(originalUncompressedData), - [&randomEngine]() { return static_cast(randomEngine()); }); + // Fill with random bytes... + std::generate( + std::begin(originalUncompressedData), + std::end(originalUncompressedData), + [&randomEngine]() { return static_cast(randomEngine()); }); - // Bind server to localhost on a random port... - const Pistache::Address address("localhost", Pistache::Port(0)); + // Bind server to localhost on a random port... + const Pistache::Address address("localhost", Pistache::Port(0)); - // Initialize server... - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server_opts.maxRequestSize(1024 * 1024 * 20); - server_opts.maxResponseSize(1024 * 1024 * 20); - server.init(server_opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + // Initialize server... + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server_opts.maxRequestSize(1024 * 1024 * 20); + server_opts.maxResponseSize(1024 * 1024 * 20); + server.init(server_opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - // Verify server is running... - ASSERT_TRUE(server.isBound()); + // Verify server is running... + ASSERT_TRUE(server.isBound()); - // Log server coordinates... - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + // Log server coordinates... + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - // Initialize client... + // Initialize client... - // Construct and initialize... - Http::Experimental::Client client; - client.init(); + // Construct and initialize... + Http::Experimental::Client client; + client.init(); - // Set server to connect to and get request builder object... - auto rb = client.get(server_address); + // Set server to connect to and get request builder object... + auto rb = client.get(server_address); - // Set data to send as body... - rb.body( - std::string( - reinterpret_cast(originalUncompressedData.data()), - originalUncompressedData.size())); + // Set data to send as body... + rb.body( + std::string( + reinterpret_cast(originalUncompressedData.data()), + originalUncompressedData.size())); - // Request server send back response deflate compressed... - rb.header(Http::Header::Encoding::Deflate); + // Request server send back response deflate compressed... + rb.header(Http::Header::Encoding::Deflate); - // Send client request. Note that Transport::asyncSendRequestImpl() is - // buggy, or at least with Pistache::Client, when the amount of data being - // sent is large. When that happens send() breaks in asyncSendRequestImpl() - // receiving an errno=EAGAIN... - auto response = rb.send(); + // Send client request. Note that Transport::asyncSendRequestImpl() is + // buggy, or at least with Pistache::Client, when the amount of data being + // sent is large. When that happens send() breaks in asyncSendRequestImpl() + // receiving an errno=EAGAIN... + auto response = rb.send(); - // Storage for server response body... - std::string resultStringData; + // Storage for server response body... + std::string resultStringData; - // Verify response code, expected header, and store its body... - response.then( - [&resultStringData](Http::Response resp) { - // Log response code... - LOGGER("client", "Response code: " << resp.code()); + // Verify response code, expected header, and store its body... + response.then( + [&resultStringData](Http::Response resp) { + // Log response code... + LOGGER("client", "Response code: " << resp.code()); - // Log Content-Encoding header value, if present... - if (resp.headers().tryGetRaw("Content-Encoding").has_value()) - { - LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); - } + // Log Content-Encoding header value, if present... + if (resp.headers().tryGetRaw("Content-Encoding").has_value()) + { + LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); + } - // Preserve body only if response code as expected... - if (resp.code() == Http::Code::Ok) - resultStringData = resp.body(); + // Preserve body only if response code as expected... + if (resp.code() == Http::Code::Ok) + resultStringData = resp.body(); - // Get response headers... - const auto& headers = resp.headers(); + // Get response headers... + const auto& headers = resp.headers(); - // Verify Content-Encoding header was present... - ASSERT_TRUE(headers.has()); + // Verify Content-Encoding header was present... + ASSERT_TRUE(headers.has()); - // Verify Content-Encoding was set to deflate... - const auto ce = headers.get().get(); - ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Deflate); - }, - Async::Throw); + // Verify Content-Encoding was set to deflate... + const auto ce = headers.get().get(); + ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Deflate); + }, + Async::Throw); - // Wait for response to complete... - Async::Barrier barrier(response); - barrier.wait(); + // Wait for response to complete... + Async::Barrier barrier(response); + barrier.wait(); - // Cleanup client and server... - client.shutdown(); - server.shutdown(); + // Cleanup client and server... + client.shutdown(); + server.shutdown(); - // Get server response body in vector... - std::vector newlyCompressedResponse(resultStringData.size()); - std::transform( - std::cbegin(resultStringData), - std::cend(resultStringData), - std::begin(newlyCompressedResponse), - [](const char character) { return static_cast(character); }); + // Get server response body in vector... + std::vector newlyCompressedResponse(resultStringData.size()); + std::transform( + std::cbegin(resultStringData), + std::cend(resultStringData), + std::begin(newlyCompressedResponse), + [](const char character) { return static_cast(character); }); - // The data the server responded with should be compressed, and therefore - // different from the original uncompressed sent during the request... - ASSERT_NE(originalUncompressedData, newlyCompressedResponse); + // The data the server responded with should be compressed, and therefore + // different from the original uncompressed sent during the request... + ASSERT_NE(originalUncompressedData, newlyCompressedResponse); - // Decompress response body... + // Decompress response body... - // Storage for decompressed data... - std::vector newlyDecompressedData( - originalUncompressedData.size()); + // Storage for decompressed data... + std::vector newlyDecompressedData( + originalUncompressedData.size()); - // Size of destination buffer, but will be updated by uncompress() to - // actual size used... - unsigned long destinationLength = originalUncompressedData.size(); + // Size of destination buffer, but will be updated by uncompress() to + // actual size used... + unsigned long destinationLength = originalUncompressedData.size(); - // Decompress... - const auto compressionStatus = ::uncompress( - reinterpret_cast(newlyDecompressedData.data()), - &destinationLength, - reinterpret_cast(resultStringData.data()), - resultStringData.size()); + // Decompress... + const auto compressionStatus = ::uncompress( + reinterpret_cast(newlyDecompressedData.data()), + &destinationLength, + reinterpret_cast(resultStringData.data()), + resultStringData.size()); - // Check for failure... - ASSERT_EQ(compressionStatus, Z_OK); + // Check for failure... + ASSERT_EQ(compressionStatus, Z_OK); - // The sizes of both the original uncompressed data we sent the server - // and the result of decompressing what it sent back should match... - ASSERT_EQ(originalUncompressedData.size(), destinationLength); + // The sizes of both the original uncompressed data we sent the server + // and the result of decompressing what it sent back should match... + ASSERT_EQ(originalUncompressedData.size(), destinationLength); - // Check to ensure the compressed data received back from server after - // decompression matches exactly what we originally sent it... - ASSERT_EQ(originalUncompressedData, newlyDecompressedData); + // Check to ensure the compressed data received back from server after + // decompression matches exactly what we originally sent it... + ASSERT_EQ(originalUncompressedData, newlyDecompressedData); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1660,76 +1676,76 @@ TEST(http_server_test, http_server_is_not_leaked) #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG - const int em_event_count_before = EventMethFns::getEmEventCount(); - const int libevent_event_count_before = EventMethFns::getLibeventEventCount(); - const int event_meth_epoll_equiv_count_before = EventMethFns::getEventMethEpollEquivCount(); - const int event_meth_base_count_before = EventMethFns::getEventMethBaseCount(); - const int wait_then_get_count_before = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); + const int em_event_count_before = EventMethFns::getEmEventCount(); + const int libevent_event_count_before = EventMethFns::getLibeventEventCount(); + const int event_meth_epoll_equiv_count_before = EventMethFns::getEventMethEpollEquivCount(); + const int event_meth_base_count_before = EventMethFns::getEventMethBaseCount(); + const int wait_then_get_count_before = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); #endif #endif - const auto fds_before = get_open_fds_count(); - const Pistache::Address address("localhost", Pistache::Port(0)); + const auto fds_before = get_open_fds_count(); + const Pistache::Address address("localhost", Pistache::Port(0)); - auto server = std::make_unique(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags).threads(4); - server->init(server_opts); - server->setHandler(Http::make_handler()); - server->serveThreaded(); + auto server = std::make_unique(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags).threads(4); + server->init(server_opts); + server->setHandler(Http::make_handler()); + server->serveThreaded(); #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG - const int em_event_count_during = EventMethFns::getEmEventCount(); - const int libevent_event_count_during = EventMethFns::getLibeventEventCount(); - const int event_meth_epoll_equiv_count_during = EventMethFns::getEventMethEpollEquivCount(); - const int event_meth_base_count_during = EventMethFns::getEventMethBaseCount(); - const int wait_then_get_count_during = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); + const int em_event_count_during = EventMethFns::getEmEventCount(); + const int libevent_event_count_during = EventMethFns::getLibeventEventCount(); + const int event_meth_epoll_equiv_count_during = EventMethFns::getEventMethEpollEquivCount(); + const int event_meth_base_count_during = EventMethFns::getEventMethBaseCount(); + const int wait_then_get_count_during = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); #endif #endif - server->shutdown(); - server.reset(); + server->shutdown(); + server.reset(); - const auto fds_after = get_open_fds_count(); - ASSERT_EQ(fds_before, fds_after); + const auto fds_after = get_open_fds_count(); + ASSERT_EQ(fds_before, fds_after); #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG - const int em_event_count_after = EventMethFns::getEmEventCount(); - const int libevent_event_count_after = EventMethFns::getLibeventEventCount(); - const int event_meth_epoll_equiv_count_after = EventMethFns::getEventMethEpollEquivCount(); - const int event_meth_base_count_after = EventMethFns::getEventMethBaseCount(); - const int wait_then_get_count_after = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); - - PS_LOG_DEBUG_ARGS( - "em_event_count_before %d, em_event_count_during %d, " - "em_event_count_after %d; " - "libevent_event_count_before %d, libevent_event_count_during %d, " - "libevent_event_count_after %d; " - "event_meth_epoll_equiv_count_before %d, " - "event_meth_epoll_equiv_count_during %d, " - "event_meth_epoll_equiv_count_after %d; " - "event_meth_base_count_before %d, event_meth_base_count_during %d, " - "event_meth_base_count_after %d; " - "wait_then_get_count_before %d, wait_then_get_count_during %d, " - "wait_then_get_count_after %d; ", - em_event_count_before, em_event_count_during, em_event_count_after, - libevent_event_count_before, libevent_event_count_during, - libevent_event_count_after, - event_meth_epoll_equiv_count_before, - event_meth_epoll_equiv_count_during, event_meth_epoll_equiv_count_after, - event_meth_base_count_before, event_meth_base_count_during, - event_meth_base_count_after, - wait_then_get_count_before, wait_then_get_count_during, - wait_then_get_count_after); - - ASSERT_EQ(em_event_count_before, em_event_count_after); - ASSERT_EQ(libevent_event_count_before, libevent_event_count_after); - ASSERT_EQ(event_meth_epoll_equiv_count_before, - event_meth_epoll_equiv_count_after); - ASSERT_EQ(event_meth_base_count_before, event_meth_base_count_after); - ASSERT_EQ(wait_then_get_count_before, wait_then_get_count_after); + const int em_event_count_after = EventMethFns::getEmEventCount(); + const int libevent_event_count_after = EventMethFns::getLibeventEventCount(); + const int event_meth_epoll_equiv_count_after = EventMethFns::getEventMethEpollEquivCount(); + const int event_meth_base_count_after = EventMethFns::getEventMethBaseCount(); + const int wait_then_get_count_after = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); + + PS_LOG_DEBUG_ARGS( + "em_event_count_before %d, em_event_count_during %d, " + "em_event_count_after %d; " + "libevent_event_count_before %d, libevent_event_count_during %d, " + "libevent_event_count_after %d; " + "event_meth_epoll_equiv_count_before %d, " + "event_meth_epoll_equiv_count_during %d, " + "event_meth_epoll_equiv_count_after %d; " + "event_meth_base_count_before %d, event_meth_base_count_during %d, " + "event_meth_base_count_after %d; " + "wait_then_get_count_before %d, wait_then_get_count_during %d, " + "wait_then_get_count_after %d; ", + em_event_count_before, em_event_count_during, em_event_count_after, + libevent_event_count_before, libevent_event_count_during, + libevent_event_count_after, + event_meth_epoll_equiv_count_before, + event_meth_epoll_equiv_count_during, event_meth_epoll_equiv_count_after, + event_meth_base_count_before, event_meth_base_count_during, + event_meth_base_count_after, + wait_then_get_count_before, wait_then_get_count_during, + wait_then_get_count_after); + + ASSERT_EQ(em_event_count_before, em_event_count_after); + ASSERT_EQ(libevent_event_count_before, libevent_event_count_after); + ASSERT_EQ(event_meth_epoll_equiv_count_before, + event_meth_epoll_equiv_count_after); + ASSERT_EQ(event_meth_base_count_before, event_meth_base_count_after); + ASSERT_EQ(wait_then_get_count_before, wait_then_get_count_after); #endif #endif diff --git a/tests/meson.build b/tests/meson.build index 3f72f81a..c859a1d6 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -56,6 +56,10 @@ if get_option('PISTACHE_USE_CONTENT_ENCODING_BROTLI') brotli_dep = dependency('libbrotlidec') endif +if get_option('PISTACHE_USE_CONTENT_ENCODING_ZSTD') + zstd_dep = dependency('libzstd') +endif + foreach test_name : pistache_test_files suite = {} if test_name in network_tests @@ -77,7 +81,8 @@ foreach test_name : pistache_test_files gmock_dep, curl_dep, cpp_httplib_dep, - brotli_dep + brotli_dep, + zstd_dep ] ), timeout: 600, diff --git a/version.txt b/version.txt index 44c12a6c..b7f23394 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.4.7.20240928 +0.4.7.20240930 From f046d8f9f4c1939bf636d6feed9b1fb3138048c4 Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Tue, 1 Oct 2024 18:56:57 -0700 Subject: [PATCH 03/12] added zstd on http tests and http.cc --- bldscripts/mesbuild.sh | 1 + include/pistache/http.h | 4 ++ meson.build | 3 + src/common/http.cc | 24 +++++++ tests/http_server_test.cc | 146 +++++++++++++++++++++++++++++++++++++- version.txt | 2 +- 6 files changed, 177 insertions(+), 3 deletions(-) diff --git a/bldscripts/mesbuild.sh b/bldscripts/mesbuild.sh index 648beb97..6bc19a9b 100755 --- a/bldscripts/mesbuild.sh +++ b/bldscripts/mesbuild.sh @@ -22,6 +22,7 @@ else -DPISTACHE_BUILD_TESTS=true \ -DPISTACHE_BUILD_DOCS=false \ -DPISTACHE_USE_CONTENT_ENCODING_DEFLATE=true \ + -DPISTACHE_USE_CONTENT_ENCODING_ZSTD=true \ --prefix="${MESON_PREFIX_DIR}" \ # -DPISTACHE_FORCE_LIBEVENT=true diff --git a/include/pistache/http.h b/include/pistache/http.h index 4fd8d316..2475fd8c 100644 --- a/include/pistache/http.h +++ b/include/pistache/http.h @@ -48,6 +48,10 @@ #include #endif +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD +#include +#endif + namespace Pistache { namespace Tcp diff --git a/meson.build b/meson.build index b3ef6fd4..1ca66e4f 100644 --- a/meson.build +++ b/meson.build @@ -62,7 +62,10 @@ if get_option('PISTACHE_USE_RAPIDJSON') public_deps += rapidjson_dep endif +# Support Zstd compressed Content-Encoding responses... if get_option('PISTACHE_USE_CONTENT_ENCODING_ZSTD') + + #Need Zstd encoder for library... zstd_dep = dependency('libzstd') deps_libpistache += zstd_dep public_deps += zstd_dep diff --git a/src/common/http.cc b/src/common/http.cc index b48f0bea..222cfaeb 100644 --- a/src/common/http.cc +++ b/src/common/http.cc @@ -10,6 +10,7 @@ Http layer implementation */ +#include "pistache/http_header.h" #include #include #include @@ -924,6 +925,29 @@ namespace Pistache::Http #ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD + case Http::Header::Encoding::Zstd: { + // Get max compressed size + size_t estimated_size = ZSTD_compressBound(size); + // Allocate a smart buffer to contain compressed data... + std::unique_ptr compressedData = std::make_unique(estimated_size); + + // Compress data using compresion_level = 5: https://facebook.github.io/zstd/zstd_manual.html#Chapter5 + auto compress_size = ZSTD_compress((void*)compressedData.get(), estimated_size, + data, size, ZSTD_lazy2); + if (ZSTD_isError(compress_size)) + { + throw std::runtime_error( + std::string("failed to compress data to ZSTD on ZSTD_compress(), returning: ") + std::to_string(compress_size)); + } + headers().add( + Http::Header::Encoding::Zstd); + + // Send compressed data back to client... + return putOnWire( + reinterpret_cast(compressedData.get()), + compress_size); + } + #endif #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE diff --git a/tests/http_server_test.cc b/tests/http_server_test.cc index 459e1c90..28f72a21 100644 --- a/tests/http_server_test.cc +++ b/tests/http_server_test.cc @@ -27,6 +27,10 @@ #include #endif +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD +#include +#endif + #include #include #include @@ -1331,8 +1335,146 @@ struct ContentEncodingHandler : public Http::Handler #ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD TEST(http_server_test, server_with_content_encoding_zstd) { - std::cout << "Hey"; - ASSERT_TRUE(false); + { // encapsulate + + // Data to send to server to expect it to return compressed... + + // Allocate storage... + std::vector originalUncompressedData(1024); + + // Random bytes engine... + using random_bytes_engine_type = std::independent_bits_engine< + std::default_random_engine, CHAR_BIT, unsigned char>; + random_bytes_engine_type randomEngine; + + // Fill with random bytes... + std::generate( + std::begin(originalUncompressedData), + std::end(originalUncompressedData), + [&randomEngine]() { return static_cast(randomEngine()); }); + + // Bind server to localhost on a random port... + const Pistache::Address address("localhost", Pistache::Port(0)); + + // Initialize server... + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server_opts.maxRequestSize(1024 * 1024 * 20); + server_opts.maxResponseSize(1024 * 1024 * 20); + server.init(server_opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); + + // Verify server is running... + ASSERT_TRUE(server.isBound()); + + // Log server coordinates... + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); + + // Initialize client... + + // Construct and initialize... + Http::Experimental::Client client; + client.init(); + + // Set server to connect to and get request builder object... + auto rb = client.get(server_address); + + // Set data to send as body... + rb.body( + std::string( + reinterpret_cast(originalUncompressedData.data()), + originalUncompressedData.size())); + + // Request server send back response Brotli compressed... + rb.header(Http::Header::Encoding::Zstd); + + // Send client request. Note that Transport::asyncSendRequestImpl() is + // buggy, or at least with Pistache::Client, when the amount of data being + // sent is large. When that happens send() breaks in asyncSendRequestImpl() + // receiving an errno=EAGAIN... + auto response = rb.send(); + + // Storage for server response body... + std::string resultStringData; + + // Verify response code, expected header, and store its body... + response.then( + [&resultStringData](Http::Response resp) { + // Log response code... + LOGGER("client", "Response code: " << resp.code()); + + // Log Content-Encoding header value, if present... + if (resp.headers().tryGetRaw("Content-Encoding").has_value()) + { + LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); + } + + // Preserve body only if response code as expected... + if (resp.code() == Http::Code::Ok) + resultStringData = resp.body(); + + // Get response headers... + const auto& headers = resp.headers(); + + // Verify Content-Encoding header was present... + ASSERT_TRUE(headers.has()); + + // Verify Content-Encoding was set to Brotli... + const auto ce = headers.get().get(); + ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Zstd); + }, + Async::Throw); + + // Wait for response to complete... + Async::Barrier barrier(response); + barrier.wait(); + + // Cleanup client and server... + client.shutdown(); + server.shutdown(); + + // Get server response body in vector... + std::vector newlyCompressedResponse(resultStringData.size()); + std::transform( + std::cbegin(resultStringData), + std::cend(resultStringData), + std::begin(newlyCompressedResponse), + [](const char character) { return static_cast(character); }); + + // The data the server responded with should be compressed, and therefore + // different from the original uncompressed sent during the request... + ASSERT_NE(originalUncompressedData, newlyCompressedResponse); + + // Decompress response body... + + // Storage for decompressed data... + std::vector newlyDecompressedData( + originalUncompressedData.size()); + + // Size of destination buffer, but will be updated by uncompress() to + // actual size used... + size_t destinationLength = originalUncompressedData.size(); + + // Decompress... + const auto compressionStatus = ZSTD_getFrameContentSize(newlyDecompressedData.data(), newlyDecompressedData.size()); + + const auto decompressed_size = ZSTD_decompress((void*)newlyDecompressedData.data(), compressionStatus, newlyCompressedResponse.data(), newlyCompressedResponse.size()); + + ASSERT_EQ(ZSTD_isError(decompressed_size), 0); + + ASSERT_TRUE(compressionStatus != ZSTD_CONTENTSIZE_ERROR); + + // The sizes of both the original uncompressed data we sent the server + // and the result of decompressing what it sent back should match... + ASSERT_EQ(originalUncompressedData.size(), destinationLength); + + // Check to ensure the compressed data received back from server after + // decompression matches exactly what we originally sent it... + ASSERT_EQ(originalUncompressedData, newlyDecompressedData); + } } #endif diff --git a/version.txt b/version.txt index b7f23394..a6c9b311 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.4.7.20240930 +0.4.7.20241001 From 255d16dc9865103a7ec7eb6be0cefee94763ca01 Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Sat, 5 Oct 2024 18:10:06 -0700 Subject: [PATCH 04/12] added support for zstd compression --- README.md | 32 +++++++++++++++++--------------- bldscripts/mesbuilddebug.sh | 1 + include/pistache/http.h | 12 ++++++++++++ src/common/http.cc | 3 +++ src/common/http_header.cc | 12 ++++++++++++ tests/http_server_test.cc | 7 +++---- version.txt | 2 +- 7 files changed, 49 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 04d1ab2a..883e88e5 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ SPDX-License-Identifier: Apache-2.0 Pistache is a modern and elegant HTTP and REST framework for C++. It is entirely written in pure-C++17[\*](#linux-only) and provides a clear and pleasant API. -Pistache supports Linux and macOS. To use in macOS, see the file: *Building on macOS.txt* +Pistache supports Linux and macOS. To use in macOS, see the file: _Building on macOS.txt_ ## Documentation @@ -25,9 +25,9 @@ A benchmark comparison of Pistache to other C++ RESTful APIs was created by gute ## Articles, Tutorials & Videos -* [Building an API in C++ With Pistache](https://levelup.gitconnected.com/building-an-api-in-c-with-pistache-413247535fd3) -* [Adding a REST API with Pistache](https://www.youtube.com/watch?v=9BCO5W_Kw3Q) -* [Slim Microservices with Pistache](https://www.dev-insider.de/schlanke-microservices-mit-pistache-a-87155e2f183e637103e19708200f8931/) (German) +- [Building an API in C++ With Pistache](https://levelup.gitconnected.com/building-an-api-in-c-with-pistache-413247535fd3) +- [Adding a REST API with Pistache](https://www.youtube.com/watch?v=9BCO5W_Kw3Q) +- [Slim Microservices with Pistache](https://www.dev-insider.de/schlanke-microservices-mit-pistache-a-87155e2f183e637103e19708200f8931/) (German) ## Dependencies @@ -44,7 +44,7 @@ Pistache has the following third party dependencies Pistache is released under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). Contributors are welcome! -Pistache was originally created by Mathieu Stefani (`@octal`). He continues to contribute to the maintainence and development of Pistache, supported by a team of volunteers. The maintainers can be reached in `#pistache` on [Libera.Chat](https://libera.chat/) (ircs://irc.libera.chat:6697). Please come and join us! +Pistache was originally created by Mathieu Stefani (`@octal`). He continues to contribute to the maintainence and development of Pistache, supported by a team of volunteers. The maintainers can be reached in `#pistache` on [Libera.Chat](https://libera.chat/) (ircs://irc.libera.chat:6697). Please come and join us! The [Launchpad Team](https://launchpad.net/~pistache+team) administers the daily and stable Ubuntu pre-compiled packages. @@ -201,7 +201,7 @@ $ git clone https://github.com/pistacheio/pistache.git ``` To build for macOS, you can follow the instructions in: - *Building on macOS.txt* +_Building on macOS.txt_ Continuing the Linux instructions: @@ -217,6 +217,7 @@ $ meson setup build \ -DPISTACHE_BUILD_DOCS=false \ -DPISTACHE_USE_CONTENT_ENCODING_BROTLI=true \ -DPISTACHE_USE_CONTENT_ENCODING_DEFLATE=true \ + -DPISTACHE_USE_CONTENT_ENCODING_ZSTD=true \ --prefix="$PWD/prefix" $ meson compile -C build $ meson install -C build @@ -232,14 +233,15 @@ Be patient, async_test can take some time before completing. And that's it, now Some other Meson options: -| Option | Default | Description | -| ------------------------------------- | ------- | ---------------------------------------------- | -| PISTACHE_USE_SSL | False | Build server with SSL support | -| PISTACHE_BUILD_TESTS | False | Build all of the unit tests | -| PISTACHE_BUILD_EXAMPLES | False | Build all of the example apps | -| PISTACHE_BUILD_DOCS | False | Build Doxygen docs | -| PISTACHE_USE_CONTENT_ENCODING_BROTLI | False | Build with Brotli content encoding support | -| PISTACHE_USE_CONTENT_ENCODING_DEFLATE | False | Build with deflate content encoding support | +| Option | Default | Description | +| ------------------------------------- | ------- | ------------------------------------------- | +| PISTACHE_USE_SSL | False | Build server with SSL support | +| PISTACHE_BUILD_TESTS | False | Build all of the unit tests | +| PISTACHE_BUILD_EXAMPLES | False | Build all of the example apps | +| PISTACHE_BUILD_DOCS | False | Build Doxygen docs | +| PISTACHE_USE_CONTENT_ENCODING_BROTLI | False | Build with Brotli content encoding support | +| PISTACHE_USE_CONTENT_ENCODING_DEFLATE | False | Build with deflate content encoding support | +| PISTACHE_USE_CONTENT_ENCODING_ZSTD | False | Build with Zstd content encoding support | ## Example @@ -264,7 +266,7 @@ int main() { ## Tutorials -* [Adding a REST API with Pistache](https://www.youtube.com/watch?v=9BCO5W_Kw3Q), Utah Cpp Programmers, 20 July 2022. +- [Adding a REST API with Pistache](https://www.youtube.com/watch?v=9BCO5W_Kw3Q), Utah Cpp Programmers, 20 July 2022. ## Project status diff --git a/bldscripts/mesbuilddebug.sh b/bldscripts/mesbuilddebug.sh index 3fa72d3e..9843b97c 100755 --- a/bldscripts/mesbuilddebug.sh +++ b/bldscripts/mesbuilddebug.sh @@ -22,6 +22,7 @@ else -DPISTACHE_BUILD_TESTS=true \ -DPISTACHE_BUILD_DOCS=false \ -DPISTACHE_USE_CONTENT_ENCODING_DEFLATE=true \ + -DPISTACHE_USE_CONTENT_ENCODING_ZSTD=true \ -DPISTACHE_DEBUG=true \ --prefix="${MESON_PREFIX_DIR}" \ # -DPISTACHE_FORCE_LIBEVENT=true diff --git a/include/pistache/http.h b/include/pistache/http.h index 2475fd8c..0173263d 100644 --- a/include/pistache/http.h +++ b/include/pistache/http.h @@ -534,6 +534,14 @@ namespace Pistache contentEncodingBrotliLevel_ = _contentEncodingBrotliLevel; } #endif +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD + + void setCompressionZstdLevel(const int _contentEncodingZstdLevel) + { + contentEncodingZstdLevel = _contentEncodingZstdLevel; + } + +#endif #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE // Set the compression level for deflate algorithm. Defaults to @@ -566,6 +574,10 @@ namespace Pistache int contentEncodingBrotliLevel_ = BROTLI_DEFAULT_QUALITY; #endif +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD + int contentEncodingZstdLevel = ZSTD_lazy2; +#endif + #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE int contentEncodingDeflateLevel_ = Z_DEFAULT_COMPRESSION; #endif diff --git a/src/common/http.cc b/src/common/http.cc index 222cfaeb..cabab341 100644 --- a/src/common/http.cc +++ b/src/common/http.cc @@ -1113,6 +1113,9 @@ namespace Pistache::Http #endif #ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD + case Http::Header::Encoding::Zstd: + contentEncoding_ = Http::Header::Encoding::Zstd; + break; #endif #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE diff --git a/src/common/http_header.cc b/src/common/http_header.cc index 6282eb62..1ecedb37 100644 --- a/src/common/http_header.cc +++ b/src/common/http_header.cc @@ -52,6 +52,8 @@ namespace Pistache::Http::Header return "gzip"; case Encoding::Br: return "br"; + case Encoding::Zstd: + return "zstd"; case Encoding::Compress: return "compress"; case Encoding::Deflate: @@ -73,6 +75,11 @@ namespace Pistache::Http::Header return Encoding::Unknown; } + if (!strncasecmp(str.data(), "zstd", str.length())) + { + return Encoding::Zstd; + } + if (!strncasecmp(str.data(), "gzip", str.length())) { return Encoding::Gzip; @@ -107,6 +114,11 @@ namespace Pistache::Http::Header { switch (encoding) { + +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD + case Encoding::Zstd: + /* @fallthrough@ */ +#endif #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI case Encoding::Br: /* @fallthrough@ */ diff --git a/tests/http_server_test.cc b/tests/http_server_test.cc index 28f72a21..1f4d5727 100644 --- a/tests/http_server_test.cc +++ b/tests/http_server_test.cc @@ -1305,7 +1305,7 @@ struct ContentEncodingHandler : public Http::Handler #ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD case Http::Header::Encoding::Zstd: - std::cout << "Hello"; + writer.setCompressionZstdLevel(ZSTD_btultra2); break; #endif @@ -1388,7 +1388,7 @@ TEST(http_server_test, server_with_content_encoding_zstd) reinterpret_cast(originalUncompressedData.data()), originalUncompressedData.size())); - // Request server send back response Brotli compressed... + // Request server send back response Zstd compressed... rb.header(Http::Header::Encoding::Zstd); // Send client request. Note that Transport::asyncSendRequestImpl() is @@ -1398,6 +1398,7 @@ TEST(http_server_test, server_with_content_encoding_zstd) auto response = rb.send(); // Storage for server response body... + std::string resultStringData; // Verify response code, expected header, and store its body... @@ -1465,8 +1466,6 @@ TEST(http_server_test, server_with_content_encoding_zstd) ASSERT_EQ(ZSTD_isError(decompressed_size), 0); - ASSERT_TRUE(compressionStatus != ZSTD_CONTENTSIZE_ERROR); - // The sizes of both the original uncompressed data we sent the server // and the result of decompressing what it sent back should match... ASSERT_EQ(originalUncompressedData.size(), destinationLength); diff --git a/version.txt b/version.txt index a6c9b311..3ca036e1 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.4.7.20241001 +0.4.7.20241005 From ba69362662cad7ca7f73740683cfc75e87ffc2a5 Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Sat, 5 Oct 2024 18:46:46 -0700 Subject: [PATCH 05/12] Included zstd documentation on README --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 883e88e5..35c20884 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ SPDX-License-Identifier: Apache-2.0 Pistache is a modern and elegant HTTP and REST framework for C++. It is entirely written in pure-C++17[\*](#linux-only) and provides a clear and pleasant API. -Pistache supports Linux and macOS. To use in macOS, see the file: _Building on macOS.txt_ +Pistache supports Linux and macOS. To use in macOS, see the file: *Building on macOS.txt* ## Documentation @@ -25,9 +25,9 @@ A benchmark comparison of Pistache to other C++ RESTful APIs was created by gute ## Articles, Tutorials & Videos -- [Building an API in C++ With Pistache](https://levelup.gitconnected.com/building-an-api-in-c-with-pistache-413247535fd3) -- [Adding a REST API with Pistache](https://www.youtube.com/watch?v=9BCO5W_Kw3Q) -- [Slim Microservices with Pistache](https://www.dev-insider.de/schlanke-microservices-mit-pistache-a-87155e2f183e637103e19708200f8931/) (German) +* [Building an API in C++ With Pistache](https://levelup.gitconnected.com/building-an-api-in-c-with-pistache-413247535fd3) +* [Adding a REST API with Pistache](https://www.youtube.com/watch?v=9BCO5W_Kw3Q) +* [Slim Microservices with Pistache](https://www.dev-insider.de/schlanke-microservices-mit-pistache-a-87155e2f183e637103e19708200f8931/) (German) ## Dependencies @@ -44,7 +44,7 @@ Pistache has the following third party dependencies Pistache is released under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). Contributors are welcome! -Pistache was originally created by Mathieu Stefani (`@octal`). He continues to contribute to the maintainence and development of Pistache, supported by a team of volunteers. The maintainers can be reached in `#pistache` on [Libera.Chat](https://libera.chat/) (ircs://irc.libera.chat:6697). Please come and join us! +Pistache was originally created by Mathieu Stefani (`@octal`). He continues to contribute to the maintainence and development of Pistache, supported by a team of volunteers. The maintainers can be reached in `#pistache` on [Libera.Chat](https://libera.chat/) (ircs://irc.libera.chat:6697). Please come and join us! The [Launchpad Team](https://launchpad.net/~pistache+team) administers the daily and stable Ubuntu pre-compiled packages. @@ -201,7 +201,7 @@ $ git clone https://github.com/pistacheio/pistache.git ``` To build for macOS, you can follow the instructions in: -_Building on macOS.txt_ + *Building on macOS.txt* Continuing the Linux instructions: @@ -233,15 +233,15 @@ Be patient, async_test can take some time before completing. And that's it, now Some other Meson options: -| Option | Default | Description | -| ------------------------------------- | ------- | ------------------------------------------- | -| PISTACHE_USE_SSL | False | Build server with SSL support | -| PISTACHE_BUILD_TESTS | False | Build all of the unit tests | -| PISTACHE_BUILD_EXAMPLES | False | Build all of the example apps | -| PISTACHE_BUILD_DOCS | False | Build Doxygen docs | -| PISTACHE_USE_CONTENT_ENCODING_BROTLI | False | Build with Brotli content encoding support | -| PISTACHE_USE_CONTENT_ENCODING_DEFLATE | False | Build with deflate content encoding support | -| PISTACHE_USE_CONTENT_ENCODING_ZSTD | False | Build with Zstd content encoding support | +| Option | Default | Description | +| ------------------------------------- | ------- | ---------------------------------------------- | +| PISTACHE_USE_SSL | False | Build server with SSL support | +| PISTACHE_BUILD_TESTS | False | Build all of the unit tests | +| PISTACHE_BUILD_EXAMPLES | False | Build all of the example apps | +| PISTACHE_BUILD_DOCS | False | Build Doxygen docs | +| PISTACHE_USE_CONTENT_ENCODING_BROTLI | False | Build with Brotli content encoding support | +| PISTACHE_USE_CONTENT_ENCODING_DEFLATE | False | Build with deflate content encoding support | +| PISTACHE_USE_CONTENT_ENCODING_ZSTD | False | Build with zstd content encoding support | ## Example @@ -266,7 +266,7 @@ int main() { ## Tutorials -- [Adding a REST API with Pistache](https://www.youtube.com/watch?v=9BCO5W_Kw3Q), Utah Cpp Programmers, 20 July 2022. +* [Adding a REST API with Pistache](https://www.youtube.com/watch?v=9BCO5W_Kw3Q), Utah Cpp Programmers, 20 July 2022. ## Project status From dff96d1c6cd87e6df712b7cb3b8eaf953da07f0e Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Fri, 11 Oct 2024 21:08:28 -0700 Subject: [PATCH 06/12] bumped version number --- tests/http_server_test.cc | 1561 +++++++++++++++++-------------------- version.txt | 2 +- 2 files changed, 703 insertions(+), 860 deletions(-) diff --git a/tests/http_server_test.cc b/tests/http_server_test.cc index 1f4d5727..b57771d2 100644 --- a/tests/http_server_test.cc +++ b/tests/http_server_test.cc @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "pistache/http_header.h" -#include #include #include #include @@ -27,10 +25,6 @@ #include #endif -#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD -#include -#endif - #include #include #include @@ -308,31 +302,31 @@ TEST(http_server_test, #endif { // encapsulate + + const Pistache::Address address("localhost", Pistache::Port(0)); - const Pistache::Address address("localhost", Pistache::Port(0)); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); + LOGGER("test", "Trying to run server..."); + const int ONE_SECOND_TIMEOUT = 1; + const int SIX_SECONDS_DELAY = 6; + server.setHandler( + Http::make_handler(SIX_SECONDS_DELAY)); + server.serveThreaded(); - LOGGER("test", "Trying to run server..."); - const int ONE_SECOND_TIMEOUT = 1; - const int SIX_SECONDS_DELAY = 6; - server.setHandler( - Http::make_handler(SIX_SECONDS_DELAY)); - server.serveThreaded(); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const int CLIENT_REQUEST_SIZE = 1; + int counter = clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, + ONE_SECOND_TIMEOUT, SIX_SECONDS_DELAY); - const int CLIENT_REQUEST_SIZE = 1; - int counter = clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, - ONE_SECOND_TIMEOUT, SIX_SECONDS_DELAY); + server.shutdown(); - server.shutdown(); - - ASSERT_EQ(counter, 0); + ASSERT_EQ(counter, 0); } // end encapsulate @@ -361,33 +355,33 @@ TEST( { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); - LOGGER("test", "Trying to run server..."); - const int ONE_SECOND_TIMEOUT = 1; - const int SIX_SECONDS_DELAY = 6; - server.setHandler( - Http::make_handler(SIX_SECONDS_DELAY)); - server.serveThreaded(); + LOGGER("test", "Trying to run server..."); + const int ONE_SECOND_TIMEOUT = 1; + const int SIX_SECONDS_DELAY = 6; + server.setHandler( + Http::make_handler(SIX_SECONDS_DELAY)); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - const int CLIENT_REQUEST_SIZE = 3; - int counter = clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, - ONE_SECOND_TIMEOUT, SIX_SECONDS_DELAY); + const int CLIENT_REQUEST_SIZE = 3; + int counter = clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, + ONE_SECOND_TIMEOUT, SIX_SECONDS_DELAY); - server.shutdown(); + server.shutdown(); - ASSERT_EQ(counter, 0); + ASSERT_EQ(counter, 0); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -411,40 +405,40 @@ TEST(http_server_test, multiple_client_with_requests_to_multithreaded_server) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags).threads(3); - server.init(server_opts); - LOGGER("test", "Trying to run server..."); - server.setHandler(Http::make_handler()); - ASSERT_NO_THROW(server.serveThreaded()); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags).threads(3); + server.init(server_opts); + LOGGER("test", "Trying to run server..."); + server.setHandler(Http::make_handler()); + ASSERT_NO_THROW(server.serveThreaded()); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - const int NO_TIMEOUT = 0; - const int SIX_SECONDS_TIMOUT = 6; - const int FIRST_CLIENT_REQUEST_SIZE = 4; - std::future result1(std::async(clientLogicFunc, - FIRST_CLIENT_REQUEST_SIZE, server_address, - NO_TIMEOUT, SIX_SECONDS_TIMOUT)); - const int SECOND_CLIENT_REQUEST_SIZE = 5; - std::future result2( - std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, - NO_TIMEOUT, SIX_SECONDS_TIMOUT)); + const int NO_TIMEOUT = 0; + const int SIX_SECONDS_TIMOUT = 6; + const int FIRST_CLIENT_REQUEST_SIZE = 4; + std::future result1(std::async(clientLogicFunc, + FIRST_CLIENT_REQUEST_SIZE, server_address, + NO_TIMEOUT, SIX_SECONDS_TIMOUT)); + const int SECOND_CLIENT_REQUEST_SIZE = 5; + std::future result2( + std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, + NO_TIMEOUT, SIX_SECONDS_TIMOUT)); - int res1 = result1.get(); - int res2 = result2.get(); + int res1 = result1.get(); + int res2 = result2.get(); - server.shutdown(); + server.shutdown(); - ASSERT_EQ(res1, FIRST_CLIENT_REQUEST_SIZE); - ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); + ASSERT_EQ(res1, FIRST_CLIENT_REQUEST_SIZE); + ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -468,40 +462,40 @@ TEST(http_server_test, many_client_with_requests_to_multithreaded_server) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags).threads(6); - server.init(server_opts); - LOGGER("test", "Trying to run server..."); - server.setHandler(Http::make_handler()); - ASSERT_NO_THROW(server.serveThreaded()); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags).threads(6); + server.init(server_opts); + LOGGER("test", "Trying to run server..."); + server.setHandler(Http::make_handler()); + ASSERT_NO_THROW(server.serveThreaded()); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - const int NO_TIMEOUT = 0; - const int SECONDS_TIMOUT = 20; - const int FIRST_CLIENT_REQUEST_SIZE = 128; - std::future result1(std::async(clientLogicFunc, - FIRST_CLIENT_REQUEST_SIZE, server_address, - NO_TIMEOUT, SECONDS_TIMOUT)); - const int SECOND_CLIENT_REQUEST_SIZE = 192; - std::future result2( - std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, - NO_TIMEOUT, 3 * SECONDS_TIMOUT)); + const int NO_TIMEOUT = 0; + const int SECONDS_TIMOUT = 20; + const int FIRST_CLIENT_REQUEST_SIZE = 128; + std::future result1(std::async(clientLogicFunc, + FIRST_CLIENT_REQUEST_SIZE, server_address, + NO_TIMEOUT, SECONDS_TIMOUT)); + const int SECOND_CLIENT_REQUEST_SIZE = 192; + std::future result2( + std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, + NO_TIMEOUT, 3 * SECONDS_TIMOUT)); - int res1 = result1.get(); - int res2 = result2.get(); + int res1 = result1.get(); + int res2 = result2.get(); - server.shutdown(); + server.shutdown(); - ASSERT_EQ(res1, FIRST_CLIENT_REQUEST_SIZE); - ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); + ASSERT_EQ(res1, FIRST_CLIENT_REQUEST_SIZE); + ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -526,47 +520,47 @@ TEST(http_server_test, { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags).threads(4); - server.init(server_opts); - const int SIX_SECONDS_DELAY = 6; - server.setHandler(Http::make_handler(SIX_SECONDS_DELAY)); - server.serveThreaded(); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags).threads(4); + server.init(server_opts); + const int SIX_SECONDS_DELAY = 6; + server.setHandler(Http::make_handler(SIX_SECONDS_DELAY)); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - const int FIRST_CLIENT_REQUEST_SIZE = 1; - const int FIRST_CLIENT_TIMEOUT = SIX_SECONDS_DELAY / 2; - std::future result1(std::async( - clientLogicFunc, FIRST_CLIENT_REQUEST_SIZE, server_address + SLOW_PAGE, - FIRST_CLIENT_TIMEOUT, SIX_SECONDS_DELAY)); - const int SECOND_CLIENT_REQUEST_SIZE = 2; - const int SECOND_CLIENT_TIMEOUT = SIX_SECONDS_DELAY * 2; - std::future result2( - std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, - SECOND_CLIENT_TIMEOUT, 2 * SIX_SECONDS_DELAY)); + const int FIRST_CLIENT_REQUEST_SIZE = 1; + const int FIRST_CLIENT_TIMEOUT = SIX_SECONDS_DELAY / 2; + std::future result1(std::async( + clientLogicFunc, FIRST_CLIENT_REQUEST_SIZE, server_address + SLOW_PAGE, + FIRST_CLIENT_TIMEOUT, SIX_SECONDS_DELAY)); + const int SECOND_CLIENT_REQUEST_SIZE = 2; + const int SECOND_CLIENT_TIMEOUT = SIX_SECONDS_DELAY * 2; + std::future result2( + std::async(clientLogicFunc, SECOND_CLIENT_REQUEST_SIZE, server_address, + SECOND_CLIENT_TIMEOUT, 2 * SIX_SECONDS_DELAY)); - int res1 = result1.get(); - int res2 = result2.get(); + int res1 = result1.get(); + int res2 = result2.get(); - server.shutdown(); + server.shutdown(); - if (hardware_concurrency() > 1) - { - ASSERT_EQ(res1, 0); - ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); - } - else - { - ASSERT_TRUE(true); - } + if (hardware_concurrency() > 1) + { + ASSERT_EQ(res1, 0); + ASSERT_EQ(res2, SECOND_CLIENT_REQUEST_SIZE); + } + else + { + ASSERT_TRUE(true); + } } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -590,66 +584,66 @@ TEST(http_server_test, server_with_static_file) { // encapsulate - const std::string data("Hello, World!"); - char fileName[PATH_MAX] = "/tmp/pistacheioXXXXXX"; - if (!mkstemp(fileName)) - { - std::cerr << "No suitable filename can be generated!" << std::endl; - } - LOGGER("test", "Creating temporary file: " << fileName); - - std::ofstream tmpFile; - tmpFile.open(fileName); - tmpFile << data; - tmpFile.close(); - - const Pistache::Address address("localhost", Pistache::Port(0)); + const std::string data("Hello, World!"); + char fileName[PATH_MAX] = "/tmp/pistacheioXXXXXX"; + if (!mkstemp(fileName)) + { + std::cerr << "No suitable filename can be generated!" << std::endl; + } + LOGGER("test", "Creating temporary file: " << fileName); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); - server.setHandler(Http::make_handler(fileName)); - server.serveThreaded(); + std::ofstream tmpFile; + tmpFile.open(fileName); + tmpFile << data; + tmpFile.close(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Experimental::Client client; - client.init(); - auto rb = client.get(server_address); - PS_LOG_DEBUG("Calling send"); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); + server.setHandler(Http::make_handler(fileName)); + server.serveThreaded(); - auto response = rb.send(); - std::string resultData; - PS_LOG_DEBUG("About to wait for response"); - response.then( - [&resultData](Http::Response resp) { - PS_LOG_DEBUG_ARGS("Http::Response %d", resp.code()); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - std::cout << "Response code is " << resp.code() << std::endl; - if (resp.code() == Http::Code::Ok) - { - resultData = resp.body(); - } - }, - Async::Throw); - PS_LOG_DEBUG("response.then() returned"); + Http::Experimental::Client client; + client.init(); + auto rb = client.get(server_address); + PS_LOG_DEBUG("Calling send"); + + auto response = rb.send(); + std::string resultData; + PS_LOG_DEBUG("About to wait for response"); + response.then( + [&resultData](Http::Response resp) { + PS_LOG_DEBUG_ARGS("Http::Response %d", resp.code()); + + std::cout << "Response code is " << resp.code() << std::endl; + if (resp.code() == Http::Code::Ok) + { + resultData = resp.body(); + } + }, + Async::Throw); + PS_LOG_DEBUG("response.then() returned"); - const int WAIT_TIME = 2; - Async::Barrier barrier(response); - barrier.wait_for(std::chrono::seconds(WAIT_TIME)); + const int WAIT_TIME = 2; + Async::Barrier barrier(response); + barrier.wait_for(std::chrono::seconds(WAIT_TIME)); - client.shutdown(); - server.shutdown(); + client.shutdown(); + server.shutdown(); - LOGGER("test", "Deleting file " << fileName); - std::remove(fileName); + LOGGER("test", "Deleting file " << fileName); + std::remove(fileName); - ASSERT_EQ(data, resultData); + ASSERT_EQ(data, resultData); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -673,55 +667,55 @@ TEST(http_server_test, server_request_copies_address) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - Http::Experimental::Client client; - client.init(); - auto rb = client.get(server_address); - auto response = rb.send(); - std::string resultData; - response.then( - [&resultData](Http::Response resp) { - LOGGER("client", " Response code is " << resp.code()); - if (resp.code() == Http::Code::Ok) - { - resultData = resp.body(); - } - }, - Async::Throw); + Http::Experimental::Client client; + client.init(); + auto rb = client.get(server_address); + auto response = rb.send(); + std::string resultData; + response.then( + [&resultData](Http::Response resp) { + LOGGER("client", " Response code is " << resp.code()); + if (resp.code() == Http::Code::Ok) + { + resultData = resp.body(); + } + }, + Async::Throw); - const int WAIT_TIME = 2; - Async::Barrier barrier(response); - barrier.wait_for(std::chrono::seconds(WAIT_TIME)); + const int WAIT_TIME = 2; + Async::Barrier barrier(response); + barrier.wait_for(std::chrono::seconds(WAIT_TIME)); - client.shutdown(); - server.shutdown(); + client.shutdown(); + server.shutdown(); - if (address.family() == AF_INET) - { - ASSERT_EQ("127.0.0.1", resultData); - } - else if (address.family() == AF_INET6) - { - ASSERT_EQ("::1", resultData); - } - else - { - ASSERT_TRUE(false); - } + if (address.family() == AF_INET) + { + ASSERT_EQ("127.0.0.1", resultData); + } + else if (address.family() == AF_INET6) + { + ASSERT_EQ("::1", resultData); + } + else + { + ASSERT_TRUE(false); + } } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -770,67 +764,67 @@ TEST(http_server_test, response_size_captured) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - size_t rsize = 0; - Http::Code rcode; + size_t rsize = 0; + Http::Code rcode; - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); - server.setHandler(Http::make_handler(rsize, rcode)); - server.serveThreaded(); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); + server.setHandler(Http::make_handler(rsize, rcode)); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - // Use the built-in http client, but this test is interested in testing - // that the ResponseWriter in the server stashed the correct size and code - // values. - Http::Experimental::Client client; - client.init(); - auto rb = client.get(server_address); - auto response = rb.send(); - std::string resultData; - response.then( - [&resultData](Http::Response resp) { - LOGGER("client", "Response code is " << resp.code()); - if (resp.code() == Http::Code::Ok) - { - resultData = resp.body(); - } - }, - Async::Throw); + // Use the built-in http client, but this test is interested in testing + // that the ResponseWriter in the server stashed the correct size and code + // values. + Http::Experimental::Client client; + client.init(); + auto rb = client.get(server_address); + auto response = rb.send(); + std::string resultData; + response.then( + [&resultData](Http::Response resp) { + LOGGER("client", "Response code is " << resp.code()); + if (resp.code() == Http::Code::Ok) + { + resultData = resp.body(); + } + }, + Async::Throw); - const int WAIT_TIME = 2; - Async::Barrier barrier(response); - barrier.wait_for(std::chrono::seconds(WAIT_TIME)); + const int WAIT_TIME = 2; + Async::Barrier barrier(response); + barrier.wait_for(std::chrono::seconds(WAIT_TIME)); - client.shutdown(); - server.shutdown(); + client.shutdown(); + server.shutdown(); - // Sanity check (stolen from AddressEchoHandler test). - if (address.family() == AF_INET) - { - ASSERT_EQ("127.0.0.1", resultData); - } - else if (address.family() == AF_INET6) - { - ASSERT_EQ("::1", resultData); - } - else - { - ASSERT_TRUE(false); - } + // Sanity check (stolen from AddressEchoHandler test). + if (address.family() == AF_INET) + { + ASSERT_EQ("127.0.0.1", resultData); + } + else if (address.family() == AF_INET6) + { + ASSERT_EQ("::1", resultData); + } + else + { + ASSERT_TRUE(false); + } - LOGGER("test", "Response size is " << rsize); - ASSERT_GT(rsize, 1u); - ASSERT_LT(rsize, 300u); - ASSERT_EQ(rcode, Http::Code::Ok); + LOGGER("test", "Response size is " << rsize); + ASSERT_GT(rsize, 1u); + ASSERT_LT(rsize, 300u); + ASSERT_EQ(rcode, Http::Code::Ok); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -854,38 +848,38 @@ TEST(http_server_test, client_request_timeout_on_only_connect_raises_http_408) { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(2); + const auto headerTimeout = std::chrono::seconds(2); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr) + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr) - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - server.shutdown(); + server.shutdown(); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -909,45 +903,45 @@ TEST(http_server_test, client_request_timeout_on_delay_in_header_send_raises_htt { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(1); + const auto headerTimeout = std::chrono::seconds(1); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr); + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr); - const std::string reqStr = "GET /ping HTTP/1.1\r\n"; - const std::string headerStr = "Host: localhost\r\nUser-Agent: test\r\n"; + const std::string reqStr = "GET /ping HTTP/1.1\r\n"; + const std::string headerStr = "Host: localhost\r\nUser-Agent: test\r\n"; - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - EXPECT_TRUE(client.send(reqStr)) << client.lastError(); + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + EXPECT_TRUE(client.send(reqStr)) << client.lastError(); - std::this_thread::sleep_for(headerTimeout / 2); - EXPECT_TRUE(client.send(headerStr)) << client.lastError(); + std::this_thread::sleep_for(headerTimeout / 2); + EXPECT_TRUE(client.send(headerStr)) << client.lastError(); - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - server.shutdown(); + server.shutdown(); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -971,56 +965,56 @@ TEST(http_server_test, client_request_timeout_on_delay_in_request_line_send_rais { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(2); + const auto headerTimeout = std::chrono::seconds(2); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr); + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr); - const std::string reqStr { "GET /ping HTTP/1.1\r\n" }; - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - bool send_failed = false; - for (size_t i = 0; i < reqStr.size(); ++i) + const std::string reqStr { "GET /ping HTTP/1.1\r\n" }; + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + bool send_failed = false; + for (size_t i = 0; i < reqStr.size(); ++i) + { + if (!client.send(reqStr.substr(i, 1))) { - if (!client.send(reqStr.substr(i, 1))) - { - send_failed = true; - break; - } - std::this_thread::sleep_for(std::chrono::milliseconds(300)); + send_failed = true; + break; } + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } - if (send_failed) - { // Usually, send does fail; but on macOS occasionally it does not fail - // We workaround that here, since of course we can only check for an - // error code when there is an actual error - EXPECT_EQ(client.lastErrno(), EPIPE) << "Errno: " << client.lastErrno(); - - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - } + if (send_failed) + { // Usually, send does fail; but on macOS occasionally it does not fail + // We workaround that here, since of course we can only check for an + // error code when there is an actual error + EXPECT_EQ(client.lastErrno(), EPIPE) << "Errno: " << client.lastErrno(); - server.shutdown(); + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + } - } // end encapsulate + server.shutdown(); + } // end encapsulate + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1044,43 +1038,43 @@ TEST(http_server_test, client_request_timeout_on_delay_in_body_send_raises_http_ { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(1); - const auto bodyTimeout = std::chrono::seconds(2); + const auto headerTimeout = std::chrono::seconds(1); + const auto bodyTimeout = std::chrono::seconds(2); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout) - .bodyTimeout(bodyTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout) + .bodyTimeout(bodyTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr); + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr); - const std::string reqStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 32\r\n\r\nabc"; + const std::string reqStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 32\r\n\r\nabc"; - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - EXPECT_TRUE(client.send(reqStr)) << client.lastError(); + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + EXPECT_TRUE(client.send(reqStr)) << client.lastError(); - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - server.shutdown(); + server.shutdown(); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1104,49 +1098,49 @@ TEST(http_server_test, client_request_no_timeout) { // encapsulate - Pistache::Address address("localhost", Pistache::Port(0)); + Pistache::Address address("localhost", Pistache::Port(0)); - const auto headerTimeout = std::chrono::seconds(2); - const auto bodyTimeout = std::chrono::seconds(4); + const auto headerTimeout = std::chrono::seconds(2); + const auto bodyTimeout = std::chrono::seconds(4); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto opts = Http::Endpoint::options() - .flags(flags) - .headerTimeout(headerTimeout) - .bodyTimeout(bodyTimeout); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto opts = Http::Endpoint::options() + .flags(flags) + .headerTimeout(headerTimeout) + .bodyTimeout(bodyTimeout); - server.init(opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + server.init(opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - auto port = server.getPort(); - auto addr = "localhost:" + port.toString(); - LOGGER("test", "Server address: " << addr); + auto port = server.getPort(); + auto addr = "localhost:" + port.toString(); + LOGGER("test", "Server address: " << addr); - const std::string headerStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 8\r\n\r\n"; - const std::string bodyStr = "abcdefgh\r\n\r\n"; + const std::string headerStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 8\r\n\r\n"; + const std::string bodyStr = "abcdefgh\r\n\r\n"; - TcpClient client; - EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); + TcpClient client; + EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError(); - std::this_thread::sleep_for(headerTimeout / 2); - EXPECT_TRUE(client.send(headerStr)) << client.lastError(); + std::this_thread::sleep_for(headerTimeout / 2); + EXPECT_TRUE(client.send(headerStr)) << client.lastError(); - std::this_thread::sleep_for(bodyTimeout / 2); - EXPECT_TRUE(client.send(bodyStr)) << client.lastError(); + std::this_thread::sleep_for(bodyTimeout / 2); + EXPECT_TRUE(client.send(bodyStr)) << client.lastError(); - char recvBuf[1024] = { - 0, - }; - size_t bytes; - EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); - EXPECT_NE(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); + char recvBuf[1024] = { + 0, + }; + size_t bytes; + EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError(); + EXPECT_NE(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine))); - server.shutdown(); + server.shutdown(); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1238,32 +1232,32 @@ TEST(http_server_test, client_multiple_requests_disconnects_handled) { // encapsulate - const Pistache::Address address("localhost", Pistache::Port(0)); + const Pistache::Address address("localhost", Pistache::Port(0)); - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server.init(server_opts); + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server.init(server_opts); - std::cout << "Trying to run server...\n"; - auto waitHelper = std::make_shared(); - auto handler = Http::make_handler(waitHelper); - server.setHandler(handler); - server.serveThreaded(); + std::cout << "Trying to run server...\n"; + auto waitHelper = std::make_shared(); + auto handler = Http::make_handler(waitHelper); + server.setHandler(handler); + server.serveThreaded(); - const std::string server_address = "localhost:" + server.getPort().toString(); - std::cout << "Server address: " << server_address << "\n"; + const std::string server_address = "localhost:" + server.getPort().toString(); + std::cout << "Server address: " << server_address << "\n"; - const size_t CLIENT_REQUEST_SIZE = 3; - clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, 1, 6); + const size_t CLIENT_REQUEST_SIZE = 3; + clientLogicFunc(CLIENT_REQUEST_SIZE, server_address, 1, 6); - const bool result = waitHelper->wait(CLIENT_REQUEST_SIZE, std::chrono::seconds(2)); - server.shutdown(); + const bool result = waitHelper->wait(CLIENT_REQUEST_SIZE, std::chrono::seconds(2)); + server.shutdown(); - ASSERT_EQ(result, true); + ASSERT_EQ(result, true); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1303,12 +1297,6 @@ struct ContentEncodingHandler : public Http::Handler switch (encoding) { -#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD - case Http::Header::Encoding::Zstd: - writer.setCompressionZstdLevel(ZSTD_btultra2); - break; -#endif - #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI // Set maximum compression if using Brotli case Http::Header::Encoding::Br: @@ -1332,151 +1320,6 @@ struct ContentEncodingHandler : public Http::Handler } }; -#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD -TEST(http_server_test, server_with_content_encoding_zstd) -{ - { // encapsulate - - // Data to send to server to expect it to return compressed... - - // Allocate storage... - std::vector originalUncompressedData(1024); - - // Random bytes engine... - using random_bytes_engine_type = std::independent_bits_engine< - std::default_random_engine, CHAR_BIT, unsigned char>; - random_bytes_engine_type randomEngine; - - // Fill with random bytes... - std::generate( - std::begin(originalUncompressedData), - std::end(originalUncompressedData), - [&randomEngine]() { return static_cast(randomEngine()); }); - - // Bind server to localhost on a random port... - const Pistache::Address address("localhost", Pistache::Port(0)); - - // Initialize server... - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server_opts.maxRequestSize(1024 * 1024 * 20); - server_opts.maxResponseSize(1024 * 1024 * 20); - server.init(server_opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); - - // Verify server is running... - ASSERT_TRUE(server.isBound()); - - // Log server coordinates... - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); - - // Initialize client... - - // Construct and initialize... - Http::Experimental::Client client; - client.init(); - - // Set server to connect to and get request builder object... - auto rb = client.get(server_address); - - // Set data to send as body... - rb.body( - std::string( - reinterpret_cast(originalUncompressedData.data()), - originalUncompressedData.size())); - - // Request server send back response Zstd compressed... - rb.header(Http::Header::Encoding::Zstd); - - // Send client request. Note that Transport::asyncSendRequestImpl() is - // buggy, or at least with Pistache::Client, when the amount of data being - // sent is large. When that happens send() breaks in asyncSendRequestImpl() - // receiving an errno=EAGAIN... - auto response = rb.send(); - - // Storage for server response body... - - std::string resultStringData; - - // Verify response code, expected header, and store its body... - response.then( - [&resultStringData](Http::Response resp) { - // Log response code... - LOGGER("client", "Response code: " << resp.code()); - - // Log Content-Encoding header value, if present... - if (resp.headers().tryGetRaw("Content-Encoding").has_value()) - { - LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); - } - - // Preserve body only if response code as expected... - if (resp.code() == Http::Code::Ok) - resultStringData = resp.body(); - - // Get response headers... - const auto& headers = resp.headers(); - - // Verify Content-Encoding header was present... - ASSERT_TRUE(headers.has()); - - // Verify Content-Encoding was set to Brotli... - const auto ce = headers.get().get(); - ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Zstd); - }, - Async::Throw); - - // Wait for response to complete... - Async::Barrier barrier(response); - barrier.wait(); - - // Cleanup client and server... - client.shutdown(); - server.shutdown(); - - // Get server response body in vector... - std::vector newlyCompressedResponse(resultStringData.size()); - std::transform( - std::cbegin(resultStringData), - std::cend(resultStringData), - std::begin(newlyCompressedResponse), - [](const char character) { return static_cast(character); }); - - // The data the server responded with should be compressed, and therefore - // different from the original uncompressed sent during the request... - ASSERT_NE(originalUncompressedData, newlyCompressedResponse); - - // Decompress response body... - - // Storage for decompressed data... - std::vector newlyDecompressedData( - originalUncompressedData.size()); - - // Size of destination buffer, but will be updated by uncompress() to - // actual size used... - size_t destinationLength = originalUncompressedData.size(); - - // Decompress... - const auto compressionStatus = ZSTD_getFrameContentSize(newlyDecompressedData.data(), newlyDecompressedData.size()); - - const auto decompressed_size = ZSTD_decompress((void*)newlyDecompressedData.data(), compressionStatus, newlyCompressedResponse.data(), newlyCompressedResponse.size()); - - ASSERT_EQ(ZSTD_isError(decompressed_size), 0); - - // The sizes of both the original uncompressed data we sent the server - // and the result of decompressing what it sent back should match... - ASSERT_EQ(originalUncompressedData.size(), destinationLength); - - // Check to ensure the compressed data received back from server after - // decompression matches exactly what we originally sent it... - ASSERT_EQ(originalUncompressedData, newlyDecompressedData); - } -} -#endif - #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI TEST(http_server_test, server_with_content_encoding_brotli) { @@ -1490,147 +1333,147 @@ TEST(http_server_test, server_with_content_encoding_brotli) { // encapsulate - // Data to send to server to expect it to return compressed... + // Data to send to server to expect it to return compressed... - // Allocate storage... - std::vector originalUncompressedData(1024); + // Allocate storage... + std::vector originalUncompressedData(1024); - // Random bytes engine... - using random_bytes_engine_type = std::independent_bits_engine< - std::default_random_engine, CHAR_BIT, unsigned char>; - random_bytes_engine_type randomEngine; + // Random bytes engine... + using random_bytes_engine_type = std::independent_bits_engine< + std::default_random_engine, CHAR_BIT, unsigned char>; + random_bytes_engine_type randomEngine; - // Fill with random bytes... - std::generate( - std::begin(originalUncompressedData), - std::end(originalUncompressedData), - [&randomEngine]() { return static_cast(randomEngine()); }); + // Fill with random bytes... + std::generate( + std::begin(originalUncompressedData), + std::end(originalUncompressedData), + [&randomEngine]() { return static_cast(randomEngine()); }); - // Bind server to localhost on a random port... - const Pistache::Address address("localhost", Pistache::Port(0)); + // Bind server to localhost on a random port... + const Pistache::Address address("localhost", Pistache::Port(0)); - // Initialize server... - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server_opts.maxRequestSize(1024 * 1024 * 20); - server_opts.maxResponseSize(1024 * 1024 * 20); - server.init(server_opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + // Initialize server... + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server_opts.maxRequestSize(1024 * 1024 * 20); + server_opts.maxResponseSize(1024 * 1024 * 20); + server.init(server_opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - // Verify server is running... - ASSERT_TRUE(server.isBound()); + // Verify server is running... + ASSERT_TRUE(server.isBound()); - // Log server coordinates... - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + // Log server coordinates... + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - // Initialize client... + // Initialize client... - // Construct and initialize... - Http::Experimental::Client client; - client.init(); + // Construct and initialize... + Http::Experimental::Client client; + client.init(); - // Set server to connect to and get request builder object... - auto rb = client.get(server_address); + // Set server to connect to and get request builder object... + auto rb = client.get(server_address); - // Set data to send as body... - rb.body( - std::string( - reinterpret_cast(originalUncompressedData.data()), - originalUncompressedData.size())); + // Set data to send as body... + rb.body( + std::string( + reinterpret_cast(originalUncompressedData.data()), + originalUncompressedData.size())); - // Request server send back response Brotli compressed... - rb.header(Http::Header::Encoding::Br); + // Request server send back response Brotli compressed... + rb.header(Http::Header::Encoding::Br); - // Send client request. Note that Transport::asyncSendRequestImpl() is - // buggy, or at least with Pistache::Client, when the amount of data being - // sent is large. When that happens send() breaks in asyncSendRequestImpl() - // receiving an errno=EAGAIN... - auto response = rb.send(); + // Send client request. Note that Transport::asyncSendRequestImpl() is + // buggy, or at least with Pistache::Client, when the amount of data being + // sent is large. When that happens send() breaks in asyncSendRequestImpl() + // receiving an errno=EAGAIN... + auto response = rb.send(); - // Storage for server response body... - std::string resultStringData; + // Storage for server response body... + std::string resultStringData; - // Verify response code, expected header, and store its body... - response.then( - [&resultStringData](Http::Response resp) { - // Log response code... - LOGGER("client", "Response code: " << resp.code()); + // Verify response code, expected header, and store its body... + response.then( + [&resultStringData](Http::Response resp) { + // Log response code... + LOGGER("client", "Response code: " << resp.code()); - // Log Content-Encoding header value, if present... - if (resp.headers().tryGetRaw("Content-Encoding").has_value()) - { - LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); - } + // Log Content-Encoding header value, if present... + if (resp.headers().tryGetRaw("Content-Encoding").has_value()) + { + LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); + } - // Preserve body only if response code as expected... - if (resp.code() == Http::Code::Ok) - resultStringData = resp.body(); + // Preserve body only if response code as expected... + if (resp.code() == Http::Code::Ok) + resultStringData = resp.body(); - // Get response headers... - const auto& headers = resp.headers(); + // Get response headers... + const auto& headers = resp.headers(); - // Verify Content-Encoding header was present... - ASSERT_TRUE(headers.has()); + // Verify Content-Encoding header was present... + ASSERT_TRUE(headers.has()); - // Verify Content-Encoding was set to Brotli... - const auto ce = headers.get().get(); - ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Br); - }, - Async::Throw); + // Verify Content-Encoding was set to Brotli... + const auto ce = headers.get().get(); + ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Br); + }, + Async::Throw); - // Wait for response to complete... - Async::Barrier barrier(response); - barrier.wait(); + // Wait for response to complete... + Async::Barrier barrier(response); + barrier.wait(); - // Cleanup client and server... - client.shutdown(); - server.shutdown(); + // Cleanup client and server... + client.shutdown(); + server.shutdown(); - // Get server response body in vector... - std::vector newlyCompressedResponse(resultStringData.size()); - std::transform( - std::cbegin(resultStringData), - std::cend(resultStringData), - std::begin(newlyCompressedResponse), - [](const char character) { return static_cast(character); }); + // Get server response body in vector... + std::vector newlyCompressedResponse(resultStringData.size()); + std::transform( + std::cbegin(resultStringData), + std::cend(resultStringData), + std::begin(newlyCompressedResponse), + [](const char character) { return static_cast(character); }); - // The data the server responded with should be compressed, and therefore - // different from the original uncompressed sent during the request... - ASSERT_NE(originalUncompressedData, newlyCompressedResponse); + // The data the server responded with should be compressed, and therefore + // different from the original uncompressed sent during the request... + ASSERT_NE(originalUncompressedData, newlyCompressedResponse); - // Decompress response body... + // Decompress response body... - // Storage for decompressed data... - std::vector newlyDecompressedData( - originalUncompressedData.size()); + // Storage for decompressed data... + std::vector newlyDecompressedData( + originalUncompressedData.size()); - // Size of destination buffer, but will be updated by uncompress() to - // actual size used... - size_t destinationLength = originalUncompressedData.size(); + // Size of destination buffer, but will be updated by uncompress() to + // actual size used... + size_t destinationLength = originalUncompressedData.size(); - // Decompress... - const auto compressionStatus = ::BrotliDecoderDecompress( - resultStringData.size(), - reinterpret_cast(resultStringData.data()), - &destinationLength, - reinterpret_cast(newlyDecompressedData.data())); + // Decompress... + const auto compressionStatus = ::BrotliDecoderDecompress( + resultStringData.size(), + reinterpret_cast(resultStringData.data()), + &destinationLength, + reinterpret_cast(newlyDecompressedData.data())); - // Check for failure... - ASSERT_EQ(compressionStatus, BROTLI_DECODER_RESULT_SUCCESS); + // Check for failure... + ASSERT_EQ(compressionStatus, BROTLI_DECODER_RESULT_SUCCESS); - // The sizes of both the original uncompressed data we sent the server - // and the result of decompressing what it sent back should match... - ASSERT_EQ(originalUncompressedData.size(), destinationLength); + // The sizes of both the original uncompressed data we sent the server + // and the result of decompressing what it sent back should match... + ASSERT_EQ(originalUncompressedData.size(), destinationLength); - // Check to ensure the compressed data received back from server after - // decompression matches exactly what we originally sent it... - ASSERT_EQ(originalUncompressedData, newlyDecompressedData); + // Check to ensure the compressed data received back from server after + // decompression matches exactly what we originally sent it... + ASSERT_EQ(originalUncompressedData, newlyDecompressedData); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1656,147 +1499,147 @@ TEST(http_server_test, server_with_content_encoding_deflate) { // encapsulate - // Data to send to server to expect it to return compressed... + // Data to send to server to expect it to return compressed... - // Allocate storage... - std::vector originalUncompressedData(1024); + // Allocate storage... + std::vector originalUncompressedData(1024); - // Random bytes engine... - using random_bytes_engine_type = std::independent_bits_engine< - std::default_random_engine, CHAR_BIT, unsigned char>; - random_bytes_engine_type randomEngine; + // Random bytes engine... + using random_bytes_engine_type = std::independent_bits_engine< + std::default_random_engine, CHAR_BIT, unsigned char>; + random_bytes_engine_type randomEngine; - // Fill with random bytes... - std::generate( - std::begin(originalUncompressedData), - std::end(originalUncompressedData), - [&randomEngine]() { return static_cast(randomEngine()); }); + // Fill with random bytes... + std::generate( + std::begin(originalUncompressedData), + std::end(originalUncompressedData), + [&randomEngine]() { return static_cast(randomEngine()); }); - // Bind server to localhost on a random port... - const Pistache::Address address("localhost", Pistache::Port(0)); + // Bind server to localhost on a random port... + const Pistache::Address address("localhost", Pistache::Port(0)); - // Initialize server... - Http::Endpoint server(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags); - server_opts.maxRequestSize(1024 * 1024 * 20); - server_opts.maxResponseSize(1024 * 1024 * 20); - server.init(server_opts); - server.setHandler(Http::make_handler()); - server.serveThreaded(); + // Initialize server... + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server_opts.maxRequestSize(1024 * 1024 * 20); + server_opts.maxResponseSize(1024 * 1024 * 20); + server.init(server_opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); - // Verify server is running... - ASSERT_TRUE(server.isBound()); + // Verify server is running... + ASSERT_TRUE(server.isBound()); - // Log server coordinates... - const std::string server_address = "localhost:" + server.getPort().toString(); - LOGGER("test", "Server address: " << server_address); + // Log server coordinates... + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); - // Initialize client... + // Initialize client... - // Construct and initialize... - Http::Experimental::Client client; - client.init(); + // Construct and initialize... + Http::Experimental::Client client; + client.init(); - // Set server to connect to and get request builder object... - auto rb = client.get(server_address); + // Set server to connect to and get request builder object... + auto rb = client.get(server_address); - // Set data to send as body... - rb.body( - std::string( - reinterpret_cast(originalUncompressedData.data()), - originalUncompressedData.size())); + // Set data to send as body... + rb.body( + std::string( + reinterpret_cast(originalUncompressedData.data()), + originalUncompressedData.size())); - // Request server send back response deflate compressed... - rb.header(Http::Header::Encoding::Deflate); + // Request server send back response deflate compressed... + rb.header(Http::Header::Encoding::Deflate); - // Send client request. Note that Transport::asyncSendRequestImpl() is - // buggy, or at least with Pistache::Client, when the amount of data being - // sent is large. When that happens send() breaks in asyncSendRequestImpl() - // receiving an errno=EAGAIN... - auto response = rb.send(); + // Send client request. Note that Transport::asyncSendRequestImpl() is + // buggy, or at least with Pistache::Client, when the amount of data being + // sent is large. When that happens send() breaks in asyncSendRequestImpl() + // receiving an errno=EAGAIN... + auto response = rb.send(); - // Storage for server response body... - std::string resultStringData; + // Storage for server response body... + std::string resultStringData; - // Verify response code, expected header, and store its body... - response.then( - [&resultStringData](Http::Response resp) { - // Log response code... - LOGGER("client", "Response code: " << resp.code()); + // Verify response code, expected header, and store its body... + response.then( + [&resultStringData](Http::Response resp) { + // Log response code... + LOGGER("client", "Response code: " << resp.code()); - // Log Content-Encoding header value, if present... - if (resp.headers().tryGetRaw("Content-Encoding").has_value()) - { - LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); - } + // Log Content-Encoding header value, if present... + if (resp.headers().tryGetRaw("Content-Encoding").has_value()) + { + LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); + } - // Preserve body only if response code as expected... - if (resp.code() == Http::Code::Ok) - resultStringData = resp.body(); + // Preserve body only if response code as expected... + if (resp.code() == Http::Code::Ok) + resultStringData = resp.body(); - // Get response headers... - const auto& headers = resp.headers(); + // Get response headers... + const auto& headers = resp.headers(); - // Verify Content-Encoding header was present... - ASSERT_TRUE(headers.has()); + // Verify Content-Encoding header was present... + ASSERT_TRUE(headers.has()); - // Verify Content-Encoding was set to deflate... - const auto ce = headers.get().get(); - ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Deflate); - }, - Async::Throw); + // Verify Content-Encoding was set to deflate... + const auto ce = headers.get().get(); + ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Deflate); + }, + Async::Throw); - // Wait for response to complete... - Async::Barrier barrier(response); - barrier.wait(); + // Wait for response to complete... + Async::Barrier barrier(response); + barrier.wait(); - // Cleanup client and server... - client.shutdown(); - server.shutdown(); + // Cleanup client and server... + client.shutdown(); + server.shutdown(); - // Get server response body in vector... - std::vector newlyCompressedResponse(resultStringData.size()); - std::transform( - std::cbegin(resultStringData), - std::cend(resultStringData), - std::begin(newlyCompressedResponse), - [](const char character) { return static_cast(character); }); + // Get server response body in vector... + std::vector newlyCompressedResponse(resultStringData.size()); + std::transform( + std::cbegin(resultStringData), + std::cend(resultStringData), + std::begin(newlyCompressedResponse), + [](const char character) { return static_cast(character); }); - // The data the server responded with should be compressed, and therefore - // different from the original uncompressed sent during the request... - ASSERT_NE(originalUncompressedData, newlyCompressedResponse); + // The data the server responded with should be compressed, and therefore + // different from the original uncompressed sent during the request... + ASSERT_NE(originalUncompressedData, newlyCompressedResponse); - // Decompress response body... + // Decompress response body... - // Storage for decompressed data... - std::vector newlyDecompressedData( - originalUncompressedData.size()); + // Storage for decompressed data... + std::vector newlyDecompressedData( + originalUncompressedData.size()); - // Size of destination buffer, but will be updated by uncompress() to - // actual size used... - unsigned long destinationLength = originalUncompressedData.size(); + // Size of destination buffer, but will be updated by uncompress() to + // actual size used... + unsigned long destinationLength = originalUncompressedData.size(); - // Decompress... - const auto compressionStatus = ::uncompress( - reinterpret_cast(newlyDecompressedData.data()), - &destinationLength, - reinterpret_cast(resultStringData.data()), - resultStringData.size()); + // Decompress... + const auto compressionStatus = ::uncompress( + reinterpret_cast(newlyDecompressedData.data()), + &destinationLength, + reinterpret_cast(resultStringData.data()), + resultStringData.size()); - // Check for failure... - ASSERT_EQ(compressionStatus, Z_OK); + // Check for failure... + ASSERT_EQ(compressionStatus, Z_OK); - // The sizes of both the original uncompressed data we sent the server - // and the result of decompressing what it sent back should match... - ASSERT_EQ(originalUncompressedData.size(), destinationLength); + // The sizes of both the original uncompressed data we sent the server + // and the result of decompressing what it sent back should match... + ASSERT_EQ(originalUncompressedData.size(), destinationLength); - // Check to ensure the compressed data received back from server after - // decompression matches exactly what we originally sent it... - ASSERT_EQ(originalUncompressedData, newlyDecompressedData); + // Check to ensure the compressed data received back from server after + // decompression matches exactly what we originally sent it... + ASSERT_EQ(originalUncompressedData, newlyDecompressedData); } // end encapsulate - + #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG const int em_event_count_after = EventMethFns::getEmEventCount(); @@ -1817,76 +1660,76 @@ TEST(http_server_test, http_server_is_not_leaked) #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG - const int em_event_count_before = EventMethFns::getEmEventCount(); - const int libevent_event_count_before = EventMethFns::getLibeventEventCount(); - const int event_meth_epoll_equiv_count_before = EventMethFns::getEventMethEpollEquivCount(); - const int event_meth_base_count_before = EventMethFns::getEventMethBaseCount(); - const int wait_then_get_count_before = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); + const int em_event_count_before = EventMethFns::getEmEventCount(); + const int libevent_event_count_before = EventMethFns::getLibeventEventCount(); + const int event_meth_epoll_equiv_count_before = EventMethFns::getEventMethEpollEquivCount(); + const int event_meth_base_count_before = EventMethFns::getEventMethBaseCount(); + const int wait_then_get_count_before = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); #endif #endif - const auto fds_before = get_open_fds_count(); - const Pistache::Address address("localhost", Pistache::Port(0)); + const auto fds_before = get_open_fds_count(); + const Pistache::Address address("localhost", Pistache::Port(0)); - auto server = std::make_unique(address); - auto flags = Tcp::Options::ReuseAddr; - auto server_opts = Http::Endpoint::options().flags(flags).threads(4); - server->init(server_opts); - server->setHandler(Http::make_handler()); - server->serveThreaded(); + auto server = std::make_unique(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags).threads(4); + server->init(server_opts); + server->setHandler(Http::make_handler()); + server->serveThreaded(); #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG - const int em_event_count_during = EventMethFns::getEmEventCount(); - const int libevent_event_count_during = EventMethFns::getLibeventEventCount(); - const int event_meth_epoll_equiv_count_during = EventMethFns::getEventMethEpollEquivCount(); - const int event_meth_base_count_during = EventMethFns::getEventMethBaseCount(); - const int wait_then_get_count_during = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); + const int em_event_count_during = EventMethFns::getEmEventCount(); + const int libevent_event_count_during = EventMethFns::getLibeventEventCount(); + const int event_meth_epoll_equiv_count_during = EventMethFns::getEventMethEpollEquivCount(); + const int event_meth_base_count_during = EventMethFns::getEventMethBaseCount(); + const int wait_then_get_count_during = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); #endif #endif - server->shutdown(); - server.reset(); + server->shutdown(); + server.reset(); - const auto fds_after = get_open_fds_count(); - ASSERT_EQ(fds_before, fds_after); + const auto fds_after = get_open_fds_count(); + ASSERT_EQ(fds_before, fds_after); #ifdef _USE_LIBEVENT_LIKE_APPLE #ifdef DEBUG - const int em_event_count_after = EventMethFns::getEmEventCount(); - const int libevent_event_count_after = EventMethFns::getLibeventEventCount(); - const int event_meth_epoll_equiv_count_after = EventMethFns::getEventMethEpollEquivCount(); - const int event_meth_base_count_after = EventMethFns::getEventMethBaseCount(); - const int wait_then_get_count_after = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); - - PS_LOG_DEBUG_ARGS( - "em_event_count_before %d, em_event_count_during %d, " - "em_event_count_after %d; " - "libevent_event_count_before %d, libevent_event_count_during %d, " - "libevent_event_count_after %d; " - "event_meth_epoll_equiv_count_before %d, " - "event_meth_epoll_equiv_count_during %d, " - "event_meth_epoll_equiv_count_after %d; " - "event_meth_base_count_before %d, event_meth_base_count_during %d, " - "event_meth_base_count_after %d; " - "wait_then_get_count_before %d, wait_then_get_count_during %d, " - "wait_then_get_count_after %d; ", - em_event_count_before, em_event_count_during, em_event_count_after, - libevent_event_count_before, libevent_event_count_during, - libevent_event_count_after, - event_meth_epoll_equiv_count_before, - event_meth_epoll_equiv_count_during, event_meth_epoll_equiv_count_after, - event_meth_base_count_before, event_meth_base_count_during, - event_meth_base_count_after, - wait_then_get_count_before, wait_then_get_count_during, - wait_then_get_count_after); - - ASSERT_EQ(em_event_count_before, em_event_count_after); - ASSERT_EQ(libevent_event_count_before, libevent_event_count_after); - ASSERT_EQ(event_meth_epoll_equiv_count_before, - event_meth_epoll_equiv_count_after); - ASSERT_EQ(event_meth_base_count_before, event_meth_base_count_after); - ASSERT_EQ(wait_then_get_count_before, wait_then_get_count_after); + const int em_event_count_after = EventMethFns::getEmEventCount(); + const int libevent_event_count_after = EventMethFns::getLibeventEventCount(); + const int event_meth_epoll_equiv_count_after = EventMethFns::getEventMethEpollEquivCount(); + const int event_meth_base_count_after = EventMethFns::getEventMethBaseCount(); + const int wait_then_get_count_after = EventMethFns::getWaitThenGetAndEmptyReadyEvsCount(); + + PS_LOG_DEBUG_ARGS( + "em_event_count_before %d, em_event_count_during %d, " + "em_event_count_after %d; " + "libevent_event_count_before %d, libevent_event_count_during %d, " + "libevent_event_count_after %d; " + "event_meth_epoll_equiv_count_before %d, " + "event_meth_epoll_equiv_count_during %d, " + "event_meth_epoll_equiv_count_after %d; " + "event_meth_base_count_before %d, event_meth_base_count_during %d, " + "event_meth_base_count_after %d; " + "wait_then_get_count_before %d, wait_then_get_count_during %d, " + "wait_then_get_count_after %d; ", + em_event_count_before, em_event_count_during, em_event_count_after, + libevent_event_count_before, libevent_event_count_during, + libevent_event_count_after, + event_meth_epoll_equiv_count_before, + event_meth_epoll_equiv_count_during, event_meth_epoll_equiv_count_after, + event_meth_base_count_before, event_meth_base_count_during, + event_meth_base_count_after, + wait_then_get_count_before, wait_then_get_count_during, + wait_then_get_count_after); + + ASSERT_EQ(em_event_count_before, em_event_count_after); + ASSERT_EQ(libevent_event_count_before, libevent_event_count_after); + ASSERT_EQ(event_meth_epoll_equiv_count_before, + event_meth_epoll_equiv_count_after); + ASSERT_EQ(event_meth_base_count_before, event_meth_base_count_after); + ASSERT_EQ(wait_then_get_count_before, wait_then_get_count_after); #endif #endif diff --git a/version.txt b/version.txt index 3ca036e1..0a496db8 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.4.7.20241005 +0.4.8.20241011 From 6f237fbd31de7826e0abc904fc9718ecb55e3db0 Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Fri, 11 Oct 2024 21:15:12 -0700 Subject: [PATCH 07/12] removed indentation on http server test file --- tests/http_server_test.cc | 155 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/tests/http_server_test.cc b/tests/http_server_test.cc index b57771d2..fbba6513 100644 --- a/tests/http_server_test.cc +++ b/tests/http_server_test.cc @@ -25,6 +25,10 @@ #include #endif +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD +#include +#endif + #include #include #include @@ -1297,6 +1301,12 @@ struct ContentEncodingHandler : public Http::Handler switch (encoding) { +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD + case Http::Header::Encoding::Zstd: + writer.setCompressionZstdLevel(ZSTD_btultra2); + break; +#endif + #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI // Set maximum compression if using Brotli case Http::Header::Encoding::Br: @@ -1320,6 +1330,151 @@ struct ContentEncodingHandler : public Http::Handler } }; +#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD +TEST(http_server_test, server_with_content_encoding_zstd) +{ + { // encapsulate + + // Data to send to server to expect it to return compressed... + + // Allocate storage... + std::vector originalUncompressedData(1024); + + // Random bytes engine... + using random_bytes_engine_type = std::independent_bits_engine< + std::default_random_engine, CHAR_BIT, unsigned char>; + random_bytes_engine_type randomEngine; + + // Fill with random bytes... + std::generate( + std::begin(originalUncompressedData), + std::end(originalUncompressedData), + [&randomEngine]() { return static_cast(randomEngine()); }); + + // Bind server to localhost on a random port... + const Pistache::Address address("localhost", Pistache::Port(0)); + + // Initialize server... + Http::Endpoint server(address); + auto flags = Tcp::Options::ReuseAddr; + auto server_opts = Http::Endpoint::options().flags(flags); + server_opts.maxRequestSize(1024 * 1024 * 20); + server_opts.maxResponseSize(1024 * 1024 * 20); + server.init(server_opts); + server.setHandler(Http::make_handler()); + server.serveThreaded(); + + // Verify server is running... + ASSERT_TRUE(server.isBound()); + + // Log server coordinates... + const std::string server_address = "localhost:" + server.getPort().toString(); + LOGGER("test", "Server address: " << server_address); + + // Initialize client... + + // Construct and initialize... + Http::Experimental::Client client; + client.init(); + + // Set server to connect to and get request builder object... + auto rb = client.get(server_address); + + // Set data to send as body... + rb.body( + std::string( + reinterpret_cast(originalUncompressedData.data()), + originalUncompressedData.size())); + + // Request server send back response Zstd compressed... + rb.header(Http::Header::Encoding::Zstd); + + // Send client request. Note that Transport::asyncSendRequestImpl() is + // buggy, or at least with Pistache::Client, when the amount of data being + // sent is large. When that happens send() breaks in asyncSendRequestImpl() + // receiving an errno=EAGAIN... + auto response = rb.send(); + + // Storage for server response body... + + std::string resultStringData; + + // Verify response code, expected header, and store its body... + response.then( + [&resultStringData](Http::Response resp) { + // Log response code... + LOGGER("client", "Response code: " << resp.code()); + + // Log Content-Encoding header value, if present... + if (resp.headers().tryGetRaw("Content-Encoding").has_value()) + { + LOGGER("client", "Content-Encoding: " << resp.headers().tryGetRaw("Content-Encoding").value().value()); + } + + // Preserve body only if response code as expected... + if (resp.code() == Http::Code::Ok) + resultStringData = resp.body(); + + // Get response headers... + const auto& headers = resp.headers(); + + // Verify Content-Encoding header was present... + ASSERT_TRUE(headers.has()); + + // Verify Content-Encoding was set to Brotli... + const auto ce = headers.get().get(); + ASSERT_EQ(ce->encoding(), Http::Header::Encoding::Zstd); + }, + Async::Throw); + + // Wait for response to complete... + Async::Barrier barrier(response); + barrier.wait(); + + // Cleanup client and server... + client.shutdown(); + server.shutdown(); + + // Get server response body in vector... + std::vector newlyCompressedResponse(resultStringData.size()); + std::transform( + std::cbegin(resultStringData), + std::cend(resultStringData), + std::begin(newlyCompressedResponse), + [](const char character) { return static_cast(character); }); + + // The data the server responded with should be compressed, and therefore + // different from the original uncompressed sent during the request... + ASSERT_NE(originalUncompressedData, newlyCompressedResponse); + + // Decompress response body... + + // Storage for decompressed data... + std::vector newlyDecompressedData( + originalUncompressedData.size()); + + // Size of destination buffer, but will be updated by uncompress() to + // actual size used... + size_t destinationLength = originalUncompressedData.size(); + + // Decompress... + const auto compressionStatus = ZSTD_getFrameContentSize(newlyDecompressedData.data(), newlyDecompressedData.size()); + + const auto decompressed_size = ZSTD_decompress((void*)newlyDecompressedData.data(), compressionStatus, newlyCompressedResponse.data(), newlyCompressedResponse.size()); + + ASSERT_EQ(ZSTD_isError(decompressed_size), 0); + + // The sizes of both the original uncompressed data we sent the server + // and the result of decompressing what it sent back should match... + ASSERT_EQ(originalUncompressedData.size(), destinationLength); + + // Check to ensure the compressed data received back from server after + // decompression matches exactly what we originally sent it... + ASSERT_EQ(originalUncompressedData, newlyDecompressedData); + } +} +#endif + #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI TEST(http_server_test, server_with_content_encoding_brotli) { From acb30d4870852928e0d20d9a370eb90206e0ec9e Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Fri, 11 Oct 2024 22:35:30 -0700 Subject: [PATCH 08/12] fixed meson build, use mid value on zstd compression --- src/common/http.cc | 2 +- tests/meson.build | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/http.cc b/src/common/http.cc index cabab341..4e2b2c8a 100644 --- a/src/common/http.cc +++ b/src/common/http.cc @@ -933,7 +933,7 @@ namespace Pistache::Http // Compress data using compresion_level = 5: https://facebook.github.io/zstd/zstd_manual.html#Chapter5 auto compress_size = ZSTD_compress((void*)compressedData.get(), estimated_size, - data, size, ZSTD_lazy2); + data, size, 11); if (ZSTD_isError(compress_size)) { throw std::runtime_error( diff --git a/tests/meson.build b/tests/meson.build index c859a1d6..a3ebc114 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -7,6 +7,7 @@ gtest_main_dep = dependency('gtest', main: true, fallback: ['gtest', 'gtest_main gmock_dep = dependency('gmock', version: '>=1.11.0', fallback: ['gtest', 'gmock_dep']) cpp_httplib_dep = dependency('cpp-httplib', fallback: ['cpp-httplib', 'cpp_httplib_dep']) brotli_dep = dependency('', required: false) +zstd_dep = dependency('', required: false) subdir('helpers') From 2b8803824468fd0e96347b161fcd7a900f02bcee Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Sun, 20 Oct 2024 09:23:25 -0700 Subject: [PATCH 09/12] fixed default value for zstd encoding, removed leading underscore --- include/pistache/http.h | 6 +++--- version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/pistache/http.h b/include/pistache/http.h index 0173263d..1511800d 100644 --- a/include/pistache/http.h +++ b/include/pistache/http.h @@ -536,9 +536,9 @@ namespace Pistache #endif #ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD - void setCompressionZstdLevel(const int _contentEncodingZstdLevel) + void setCompressionZstdLevel(const int contentEncodingZstdLevel) { - contentEncodingZstdLevel = _contentEncodingZstdLevel; + contentEncodingZstdLevel_ = contentEncodingZstdLevel; } #endif @@ -575,7 +575,7 @@ namespace Pistache #endif #ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD - int contentEncodingZstdLevel = ZSTD_lazy2; + int contentEncodingZstdLevel_ = ZSTD_defaultCLevel(); #endif #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE diff --git a/version.txt b/version.txt index 0a496db8..269ca44c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.4.8.20241011 +0.4.8.20241020 From 6ff23bbeb53aae6fd60113aa9c39a41bb8214d87 Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Sun, 20 Oct 2024 11:05:49 -0700 Subject: [PATCH 10/12] updated description of zstd meson option --- meson_options.txt | 2 +- src/common/http.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meson_options.txt b/meson_options.txt index 4be0675f..88ed0472 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -9,7 +9,7 @@ option('PISTACHE_INSTALL', type: 'boolean', value: true, description: 'add pista option('PISTACHE_USE_SSL', type: 'boolean', value: false, description: 'add support for SSL server') option('PISTACHE_USE_RAPIDJSON', type: 'boolean', value: true, description: 'add support for rapidjson') option('PISTACHE_USE_CONTENT_ENCODING_BROTLI', type: 'boolean', value: false, description: 'add support for Brotli compressed content encoding') -option('PISTACHE_USE_CONTENT_ENCODING_ZSTD', type: 'boolean', value: false, description: 'add support for ZSTD compressed content encoding') +option('PISTACHE_USE_CONTENT_ENCODING_ZSTD', type: 'boolean', value: false, description: 'add support for Zstandard compressed content encoding') option('PISTACHE_USE_CONTENT_ENCODING_DEFLATE', type: 'boolean', value: false, description: 'add support for deflate compressed content encoding') option('PISTACHE_DEBUG', type: 'boolean', value: false, description: 'with debugging code') option('PISTACHE_LOG_AND_STDOUT', type: 'boolean', value: false, description: 'send log msgs to stdout too') diff --git a/src/common/http.cc b/src/common/http.cc index 3e8633be..8f5b3df3 100644 --- a/src/common/http.cc +++ b/src/common/http.cc @@ -10,7 +10,7 @@ Http layer implementation */ -#include "pistache/http_header.h" +#include #include #include #include From 241a76f8d0c57c94823f9caba02af09a90424e11 Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Sun, 20 Oct 2024 11:19:06 -0700 Subject: [PATCH 11/12] use reinterpret_cast in zstd compress func --- src/common/http.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/http.cc b/src/common/http.cc index 8f5b3df3..b4a198e9 100644 --- a/src/common/http.cc +++ b/src/common/http.cc @@ -938,9 +938,9 @@ namespace Pistache::Http // Allocate a smart buffer to contain compressed data... std::unique_ptr compressedData = std::make_unique(estimated_size); - // Compress data using compresion_level = 5: https://facebook.github.io/zstd/zstd_manual.html#Chapter5 - auto compress_size = ZSTD_compress((void*)compressedData.get(), estimated_size, - data, size, 11); + // Compress data using default compression level: https://raw.githack.com/facebook/zstd/release/doc/zstd_manual.html#Chapter3 + auto compress_size = ZSTD_compress(reinterpret_cast(compressedData.get()), estimated_size, + data, size, ZSTD_defaultCLevel()); if (ZSTD_isError(compress_size)) { throw std::runtime_error( From 342ceee13de9cc8641301bd9dd37a564a68224e3 Mon Sep 17 00:00:00 2001 From: Edgar Modesto Date: Sun, 20 Oct 2024 16:11:00 -0700 Subject: [PATCH 12/12] fix: bumped version to 12 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 269ca44c..22366db5 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.4.8.20241020 +0.4.12.20241020