From a3939d9a6699d6124f84654cf9148df5ac5c7fc8 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 6 Oct 2025 12:30:06 +0800 Subject: [PATCH] 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). --- .gitmodules | 3 +++ build.zig | 26 ++++++++++++++++++++++++++ src/http/Http.zig | 19 +++++++++++++++++++ vendor/brotli | 1 + 4 files changed, 49 insertions(+) create mode 160000 vendor/brotli diff --git a/.gitmodules b/.gitmodules index 0bf6654b..717d079b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "vendor/curl"] path = vendor/curl url = https://github.com/curl/curl.git +[submodule "vendor/brotli"] + path = vendor/brotli + url = https://github.com/google/brotli diff --git a/build.zig b/build.zig index 1c938581..6235cb4c 100644 --- a/build.zig +++ b/build.zig @@ -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_BASENAME", "1"); mod.addCMacro("HAVE_BOOL_T", "1"); + mod.addCMacro("HAVE_BROTLI", "1"); mod.addCMacro("HAVE_BUILTIN_AVAILABLE", "1"); mod.addCMacro("HAVE_CLOCK_GETTIME_MONOTONIC", "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 buildBrotli(b, mod); try buildMbedtls(b, mod); try buildNghttp2(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 { const mbedtls = b.addLibrary(.{ .name = "mbedtls", diff --git a/src/http/Http.zig b/src/http/Http.zig index 74d61290..270bbd17 100644 --- a/src/http/Http.zig +++ b/src/http/Http.zig @@ -168,6 +168,13 @@ pub const Connection = struct { // debug if (comptime Http.ENABLE_DEBUG) { 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 .{ @@ -435,3 +442,15 @@ const LineWriter = struct { 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}), + } +} diff --git a/vendor/brotli b/vendor/brotli new file mode 160000 index 00000000..f1c80224 --- /dev/null +++ b/vendor/brotli @@ -0,0 +1 @@ +Subproject commit f1c80224e824f8c0e935b994a776b0d7455282f8