Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ members = [
"tests/examples/magic_enum",
"tests/examples/mimalloc",
"tests/examples/harfbuzz",
"tests/examples/httplib",
"tests/examples/httplib-brotli",
"tests/examples/httplib-no-exceptions",
"tests/examples/httplib-tls",
"tests/examples/httplib-zlib",
"tests/examples/httplib-zstd",
"tests/examples/msdfgen",
"tests/examples/gtl",
"tests/examples/miniaudio",
Expand Down
89 changes: 89 additions & 0 deletions pkgs/c/compat.httplib.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
-- Form B header-only descriptor for cpp-httplib. Optional backends stay off
-- unless a consumer requests the corresponding mcpp feature.
package = {
spec = "1",
namespace = "compat",
name = "httplib",
description = "C++ single-header HTTP/HTTPS server and client library",
licenses = {"MIT"},
repo = "https://github.com/yhirose/cpp-httplib",
type = "package",

xpm = {
-- No mcpp-res credentials are available in this environment. Keep the
-- GLOBAL URL as a plain string until a byte-identical CN asset exists.
linux = {
["0.53.1"] = {
url = "https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.53.1.tar.gz",
sha256 = "185af9587e270de9a3bfee234c6740f02e82265da33c7a41f97e02ee42f979d2",
},
},
macosx = {
["0.53.1"] = {
url = "https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.53.1.tar.gz",
sha256 = "185af9587e270de9a3bfee234c6740f02e82265da33c7a41f97e02ee42f979d2",
},
},
windows = {
["0.53.1"] = {
url = "https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.53.1.tar.gz",
sha256 = "185af9587e270de9a3bfee234c6740f02e82265da33c7a41f97e02ee42f979d2",
},
},
},

mcpp = {
language = "c++23",
import_std = false,
include_dirs = { "*" },
generated_files = {
["mcpp_generated/compat_httplib_anchor.cpp"] =
"int mcpp_compat_httplib_anchor(void) { return 0; }\n",
},
sources = { "mcpp_generated/compat_httplib_anchor.cpp" },
targets = { ["httplib"] = { kind = "lib" } },

-- Feature defines are interface requirements for this header-only
-- package: cpp-httplib's implementation is compiled in consumer TUs.
features = {
["tls"] = {
defines = { "CPPHTTPLIB_OPENSSL_SUPPORT" },
deps = { ["compat.openssl"] = "3.5.1" },
},
["zlib"] = {
defines = { "CPPHTTPLIB_ZLIB_SUPPORT" },
deps = { ["compat.zlib"] = "1.3.2" },
},
["brotli"] = {
defines = { "CPPHTTPLIB_BROTLI_SUPPORT" },
deps = { ["compat.brotli"] = "1.2.0" },
},
["zstd"] = {
defines = { "CPPHTTPLIB_ZSTD_SUPPORT" },
deps = { ["compat.zstd"] = "1.5.7" },
},
["no-exceptions"] = {
defines = { "CPPHTTPLIB_NO_EXCEPTIONS" },
},
},
deps = {},

-- Feature entries cannot carry platform-local ldflags. These are the
-- upstream target's system link requirements; unused system libraries
-- and frameworks are harmless for feature-free consumers.
linux = {
ldflags = { "-lpthread" },
},
macosx = {
ldflags = {
"-lpthread",
"-framework", "CFNetwork",
"-framework", "CoreFoundation",
"-framework", "Security",
},
},
windows = {
ldflags = { "-lws2_32", "-lcrypt32" },
},
},
}
6 changes: 6 additions & 0 deletions tests/examples/httplib-brotli/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "httplib-brotli-tests"
version = "0.1.0"

[dependencies.compat]
httplib = { version = "0.53.1", features = ["brotli"] }
7 changes: 7 additions & 0 deletions tests/examples/httplib-brotli/tests/brotli.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef CPPHTTPLIB_BROTLI_SUPPORT
#error "compat.httplib[brotli] must define CPPHTTPLIB_BROTLI_SUPPORT in consumers"
#endif

#define MCPP_HTTPLIB_FEATURE_NAME "brotli"
#define MCPP_HTTPLIB_ENCODING "br"
#include "../../../fixtures/httplib_compression_case.hpp"
6 changes: 6 additions & 0 deletions tests/examples/httplib-no-exceptions/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "httplib-no-exceptions-tests"
version = "0.1.0"

[dependencies.compat]
httplib = { version = "0.53.1", features = ["no-exceptions"] }
19 changes: 19 additions & 0 deletions tests/examples/httplib-no-exceptions/tests/no_exceptions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
#error "compat.httplib[no-exceptions] must define CPPHTTPLIB_NO_EXCEPTIONS in consumers"
#endif

#include <httplib.h>

#include <string>

int main() {
// Without CPPHTTPLIB_NO_EXCEPTIONS this constructor throws for an invalid
// scheme. The feature changes it into a normal invalid client result.
httplib::Client invalid("ftp://127.0.0.1:1");
if (invalid.is_valid()) return 1;

httplib::Headers headers;
const char *prohibited = httplib::detail::get_header_value(
headers, "REMOTE_ADDR", "missing", 0);
return prohibited != nullptr && prohibited[0] == '\0' ? 0 : 2;
}
6 changes: 6 additions & 0 deletions tests/examples/httplib-tls/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "httplib-tls-tests"
version = "0.1.0"

[dependencies.compat]
httplib = { version = "0.53.1", features = ["tls"] }
53 changes: 53 additions & 0 deletions tests/examples/httplib-tls/tests/tls.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#ifndef CPPHTTPLIB_OPENSSL_SUPPORT
#error "compat.httplib[tls] must define CPPHTTPLIB_OPENSSL_SUPPORT in consumers"
#endif

#include <httplib.h>

#include "../../../fixtures/httplib_localhost_pem.hpp"

#include <atomic>
#include <cstring>
#include <string>
#include <thread>

int main() {
httplib::SSLServer::PemMemory pem{
kHttplibCertificatePem,
std::strlen(kHttplibCertificatePem),
kHttplibPrivateKeyPem,
std::strlen(kHttplibPrivateKeyPem),
nullptr,
0,
nullptr,
};
httplib::SSLServer server(pem);
if (!server.is_valid()) return 1;

const std::string expected = "certificate-validated HTTPS response";
std::atomic<bool> handled{false};
server.Get("/secure", [&](const httplib::Request &req, httplib::Response &res) {
handled = req.method == "GET" && req.path == "/secure";
res.set_content(expected, "text/plain");
});

const int port = server.bind_to_any_port("127.0.0.1");
if (port <= 0) return 2;
std::thread server_thread([&] { server.listen_after_bind(); });

httplib::SSLClient client("localhost", port);
client.enable_system_ca(false);
client.load_ca_cert_store(kHttplibCertificatePem,
std::strlen(kHttplibCertificatePem));
client.set_connection_timeout(8, 0);
client.set_read_timeout(8, 0);
const auto result = client.Get("/secure");

const bool ok = result && result->status == httplib::StatusCode::OK_200 &&
result->get_header_value("Content-Type") == "text/plain" &&
result->body == expected && handled.load();

server.stop();
server_thread.join();
return ok ? 0 : 3;
}
6 changes: 6 additions & 0 deletions tests/examples/httplib-zlib/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "httplib-zlib-tests"
version = "0.1.0"

[dependencies.compat]
httplib = { version = "0.53.1", features = ["zlib"] }
7 changes: 7 additions & 0 deletions tests/examples/httplib-zlib/tests/zlib.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef CPPHTTPLIB_ZLIB_SUPPORT
#error "compat.httplib[zlib] must define CPPHTTPLIB_ZLIB_SUPPORT in consumers"
#endif

#define MCPP_HTTPLIB_FEATURE_NAME "zlib"
#define MCPP_HTTPLIB_ENCODING "gzip"
#include "../../../fixtures/httplib_compression_case.hpp"
6 changes: 6 additions & 0 deletions tests/examples/httplib-zstd/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "httplib-zstd-tests"
version = "0.1.0"

[dependencies.compat]
httplib = { version = "0.53.1", features = ["zstd"] }
7 changes: 7 additions & 0 deletions tests/examples/httplib-zstd/tests/zstd.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef CPPHTTPLIB_ZSTD_SUPPORT
#error "compat.httplib[zstd] must define CPPHTTPLIB_ZSTD_SUPPORT in consumers"
#endif

#define MCPP_HTTPLIB_FEATURE_NAME "zstd"
#define MCPP_HTTPLIB_ENCODING "zstd"
#include "../../../fixtures/httplib_compression_case.hpp"
6 changes: 6 additions & 0 deletions tests/examples/httplib/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "httplib-tests"
version = "0.1.0"

[dependencies.compat]
httplib = "0.53.1"
40 changes: 40 additions & 0 deletions tests/examples/httplib/tests/http.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <httplib.h>

#include <atomic>
#include <string>
#include <thread>

#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) || defined(CPPHTTPLIB_ZLIB_SUPPORT) || \
defined(CPPHTTPLIB_BROTLI_SUPPORT) || defined(CPPHTTPLIB_ZSTD_SUPPORT) || \
defined(CPPHTTPLIB_NO_EXCEPTIONS)
#error "compat.httplib enables no optional feature by default"
#endif

int main() {
const std::string expected = R"({"package":"compat.httplib","ok":true})";
std::atomic<bool> handled{false};

httplib::Server server;
server.Get("/status", [&](const httplib::Request &req, httplib::Response &res) {
handled = req.method == "GET" && req.path == "/status";
res.set_content(expected, "application/json");
});

const int port = server.bind_to_any_port("127.0.0.1");
if (port <= 0) return 1;

std::thread server_thread([&] { server.listen_after_bind(); });

httplib::Client client("127.0.0.1", port);
client.set_connection_timeout(5, 0);
client.set_read_timeout(5, 0);
const auto result = client.Get("/status");

const bool ok = result && result->status == httplib::StatusCode::OK_200 &&
result->get_header_value("Content-Type") == "application/json" &&
result->body == expected && handled.load();

server.stop();
server_thread.join();
return ok ? 0 : 2;
}
65 changes: 65 additions & 0 deletions tests/fixtures/httplib_compression_case.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#ifndef MCPP_INDEX_HTTPLIB_COMPRESSION_CASE_HPP
#define MCPP_INDEX_HTTPLIB_COMPRESSION_CASE_HPP

#if !defined(MCPP_HTTPLIB_FEATURE_NAME) || !defined(MCPP_HTTPLIB_ENCODING)
#error "compression test wrapper must name its feature and HTTP encoding"
#endif

#include <httplib.h>

#include <atomic>
#include <charconv>
#include <cstddef>
#include <string>
#include <thread>

int main() {
std::string payload;
payload.reserve(128 * 1024);
for (int i = 0; i < 2048; ++i) {
payload += "mcpp compat.httplib compression feature payload: ";
payload += MCPP_HTTPLIB_FEATURE_NAME;
payload += "\n";
}

std::atomic<bool> accepted_encoding{false};
httplib::Server server;
server.Get("/compressed", [&](const httplib::Request &req, httplib::Response &res) {
accepted_encoding =
req.get_header_value("Accept-Encoding").find(MCPP_HTTPLIB_ENCODING) !=
std::string::npos;
res.set_content(payload, "text/plain");
});

const int port = server.bind_to_any_port("127.0.0.1");
if (port <= 0) return 1;

std::thread server_thread([&] { server.listen_after_bind(); });

httplib::Client client("127.0.0.1", port);
client.set_connection_timeout(5, 0);
client.set_read_timeout(5, 0);
const httplib::Headers headers{{"Accept-Encoding", MCPP_HTTPLIB_ENCODING}};
const auto result = client.Get("/compressed", headers);

bool compressed_on_wire = false;
bool ok = result && result->status == httplib::StatusCode::OK_200 &&
result->get_header_value("Content-Type") == "text/plain" &&
result->get_header_value("Content-Encoding") == MCPP_HTTPLIB_ENCODING &&
result->body == payload && accepted_encoding.load();
if (result) {
const auto length_text = result->get_header_value("Content-Length");
std::size_t wire_length = 0;
const auto parsed = std::from_chars(
length_text.data(), length_text.data() + length_text.size(), wire_length);
compressed_on_wire = parsed.ec == std::errc{} &&
parsed.ptr == length_text.data() + length_text.size() &&
wire_length < payload.size() / 4;
}

server.stop();
server_thread.join();
return ok && compressed_on_wire ? 0 : 2;
}

#endif
Loading
Loading