Tls troubles with std http Client

I recently noticed that the std lib HTTP client returns TLS related errors when sending requests to some addresses.
I haven’t seen anyone else having this exact issue, so I’m wondering if it’s related to my machine’s setup.

const std = @import("std");

pub fn main() !void {
    var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
    defer _ = gpa.deinit();

    var client = std.http.Client{ .allocator = gpa.allocator() };
    defer client.deinit();

    const uri = try std.Uri.parse("https://medium.com");

    var server_header_buffer: [1024 * 1024]u8 = undefined;
    var req = try client.open(.GET, uri, .{
        .server_header_buffer = &server_header_buffer,
    });
    defer req.deinit();

    try req.send();
    try req.wait();

    var buf: [1024 * 1024]u8 = undefined;
    const len = try req.readAll(&buf);

    std.debug.print("{s}", .{buf[0..len]});
}

❯ zig version
0.14.0-dev.1924+bdd3bc056
❯ zig build run
error: TlsInitializationFailed
/home/robert/Projects/zigs/master/lib/std/crypto/aes_gcm.zig:102:17: 0x1283375 in decrypt (zig_http_tls_aes)
                return error.AuthenticationFailed;
                ^
/home/robert/Projects/zigs/master/lib/std/crypto/tls/Client.zig:470:29: 0x121b838 in init__anon_12944 (zig_http_tls_aes)
                            return error.TlsBadRecordMac;
                            ^
/home/robert/Projects/zigs/master/lib/std/http/Client.zig:1357:99: 0x11534df in connectTcp (zig_http_tls_aes)
        conn.data.tls_client.* = std.crypto.tls.Client.init(stream, client.ca_bundle, host) catch return error.TlsInitializationFailed;
                                                                                                  ^
/home/robert/Projects/zigs/master/lib/std/http/Client.zig:1492:14: 0x11298a0 in connect (zig_http_tls_aes)
    } orelse return client.connectTcp(host, port, protocol);
             ^
/home/robert/Projects/zigs/master/lib/std/http/Client.zig:1640:9: 0x111f0be in open (zig_http_tls_aes)
        try client.connect(valid_uri.host.?.raw, uriPort(valid_uri, protocol), protocol);
        ^
/home/robert/Projects/toy/zig_http_tls_aes/src/main.zig:15:15: 0x111e033 in main (zig_http_tls_aes)
    var req = try client.open(.GET, uri, .{
              ^

If I change the uri to https://google.com the error becomes:

error: TlsInitializationFailed
/home/robert/Projects/zigs/master/lib/std/crypto/tls.zig:201:9: 0x127c0f2 in toError (zig_http_tls_aes)
        return switch (alert) {
        ^
/home/robert/Projects/zigs/master/lib/std/crypto/tls/Client.zig:252:17: 0x1217353 in init__anon_12944 (zig_http_tls_aes)
                try desc.toError();
                ^
/home/robert/Projects/zigs/master/lib/std/http/Client.zig:1357:99: 0x11534df in connectTcp (zig_http_tls_aes)
        conn.data.tls_client.* = std.crypto.tls.Client.init(stream, client.ca_bundle, host) catch return error.TlsInitializationFailed;
                                                                                                  ^
/home/robert/Projects/zigs/master/lib/std/http/Client.zig:1492:14: 0x11298a0 in connect (zig_http_tls_aes)
    } orelse return client.connectTcp(host, port, protocol);
             ^
/home/robert/Projects/zigs/master/lib/std/http/Client.zig:1640:9: 0x111f0be in open (zig_http_tls_aes)
        try client.connect(valid_uri.host.?.raw, uriPort(valid_uri, protocol), protocol);
        ^
/home/robert/Projects/toy/zig_http_tls_aes/src/main.zig:15:15: 0x111e033 in main (zig_http_tls_aes)
    var req = try client.open(.GET, uri, .{
              ^

curl, Rust and Golang equivalents work as expected.
Any clue what is going wrong? Could it be a bug in the std lib?

Zig’s std still only supports TLS 1.1. Try this:

1 Like

Thank you. I wasn’t aware of the TLS version limitation.
The tls.zig solution seems like a bit of a hassle to maintain. I will look into using a proxy instead.

1 Like

Proxy support is also broken :sweat:

IIRC HTTP client has been added to std pretty recently with the sole purpose of enabling the Zig package manager, so its powers are limited at the moment.

1 Like

The Zig cookbook says:

Note: Since HTTP support is in early stage, it’s recommended to use libcurl for any complex task.

But that libcurl link points to the actual C bindings.

GitHub - jiacai2050/zig-curl: Zig bindings for libcurl looks relatively up to date with its most recent commit being two months old.

GitHub - mattnite/zig-libcurl: compile libcurl in your build.zig shows up earlier in search results, but its most recent commit is a year old, referencing Zig 0.11.

Thank you for pointing that out.
Should the first link be https://github.com/jiacai2050/zig-curl?

In my case I was also using the std lib TLS for websockets (TCP+TLS), so will need to find another solution.

1 Like

Yes, I copied and pasted the wrong link – thanks for pointing that out, let me edit the post.

For those interested I ended up using curl for HTTPS starting from this example and WolfSSL taking inspiration from this example and a little workaround for websockets over TCP.
tls.zig didn’t work, probably because I’m using Zig master and the lib hasn’t been updated in a few months. I didn’t look further into it because I don’t like the way it requires overriding std lib to work with the std HTTP Client.

1 Like