Supports brotli compression

Adds bortli as a submodules, and compiles the decoder, making it available to
libcurl.

Some websites appear to sent brotli encoded responses even though we don't
advertise support for it (e.g. https://movie.douban.com).
This commit is contained in:
Karl Seguin
2025-10-06 12:30:06 +08:00
parent 432e3c3a5e
commit a3939d9a66
4 changed files with 49 additions and 0 deletions

3
.gitmodules vendored
View File

@@ -31,3 +31,6 @@
[submodule "vendor/curl"] [submodule "vendor/curl"]
path = vendor/curl path = vendor/curl
url = https://github.com/curl/curl.git url = https://github.com/curl/curl.git
[submodule "vendor/brotli"]
path = vendor/brotli
url = https://github.com/google/brotli

View File

@@ -245,6 +245,7 @@ fn addDependencies(b: *Build, mod: *Build.Module, opts: *Build.Step.Options) !vo
mod.addCMacro("HAVE_ASSERT_H", "1"); mod.addCMacro("HAVE_ASSERT_H", "1");
mod.addCMacro("HAVE_BASENAME", "1"); mod.addCMacro("HAVE_BASENAME", "1");
mod.addCMacro("HAVE_BOOL_T", "1"); mod.addCMacro("HAVE_BOOL_T", "1");
mod.addCMacro("HAVE_BROTLI", "1");
mod.addCMacro("HAVE_BUILTIN_AVAILABLE", "1"); mod.addCMacro("HAVE_BUILTIN_AVAILABLE", "1");
mod.addCMacro("HAVE_CLOCK_GETTIME_MONOTONIC", "1"); mod.addCMacro("HAVE_CLOCK_GETTIME_MONOTONIC", "1");
mod.addCMacro("HAVE_DLFCN_H", "1"); mod.addCMacro("HAVE_DLFCN_H", "1");
@@ -379,6 +380,7 @@ fn addDependencies(b: *Build, mod: *Build.Module, opts: *Build.Step.Options) !vo
} }
try buildZlib(b, mod); try buildZlib(b, mod);
try buildBrotli(b, mod);
try buildMbedtls(b, mod); try buildMbedtls(b, mod);
try buildNghttp2(b, mod); try buildNghttp2(b, mod);
try buildCurl(b, mod); try buildCurl(b, mod);
@@ -484,6 +486,30 @@ fn buildZlib(b: *Build, m: *Build.Module) !void {
} }); } });
} }
fn buildBrotli(b: *Build, m: *Build.Module) !void {
const brotli = b.addLibrary(.{
.name = "brotli",
.root_module = m,
});
const root = "vendor/brotli/c/";
brotli.installHeader(b.path(root ++ "brotli.h"), "brotli.h");
brotli.addCSourceFiles(.{ .flags = &.{}, .files = &.{
root ++ "common/constants.c",
root ++ "common/context.c",
root ++ "common/dictionary.c",
root ++ "common/platform.c",
root ++ "common/shared_dictionary.c",
root ++ "common/transform.c",
root ++ "dec/bit_reader.c",
root ++ "dec/decode.c",
root ++ "dec/huffman.c",
root ++ "dec/prefix.c",
root ++ "dec/state.c",
root ++ "dec/static_init.c",
} });
}
fn buildMbedtls(b: *Build, m: *Build.Module) !void { fn buildMbedtls(b: *Build, m: *Build.Module) !void {
const mbedtls = b.addLibrary(.{ const mbedtls = b.addLibrary(.{
.name = "mbedtls", .name = "mbedtls",

View File

@@ -168,6 +168,13 @@ pub const Connection = struct {
// debug // debug
if (comptime Http.ENABLE_DEBUG) { if (comptime Http.ENABLE_DEBUG) {
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_VERBOSE, @as(c_long, 1))); try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_VERBOSE, @as(c_long, 1)));
// Sometimes the default debug output hides some useful data. You can
// uncomment the following line (BUT KEEP THE LIVE ABOVE AS-IS), to
// get more control over the data (specifically, the `CURLINFO_TEXT`
// can include useful data).
// try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_DEBUGFUNCTION, debugCallback));
} }
return .{ return .{
@@ -435,3 +442,15 @@ const LineWriter = struct {
self.col = col + remain.len; self.col = col + remain.len;
} }
}; };
pub fn debugCallback(_: *c.CURL, msg_type: c.curl_infotype, raw: [*c]u8, len: usize, _: *anyopaque) callconv(.c) void {
const data = raw[0..len];
switch (msg_type) {
c.CURLINFO_TEXT => std.debug.print("libcurl [text]: {s}\n", .{data}),
c.CURLINFO_HEADER_OUT => std.debug.print("libcurl [req-h]: {s}\n", .{data}),
c.CURLINFO_HEADER_IN => std.debug.print("libcurl [res-h]: {s}\n", .{data}),
// c.CURLINFO_DATA_IN => std.debug.print("libcurl [res-b]: {s}\n", .{data}),
else => std.debug.print("libcurl ?? {d}\n", .{msg_type}),
}
}

1
vendor/brotli vendored Submodule

Submodule vendor/brotli added at f1c80224e8