Simple HTTP fetch request (with new writer interface)?

Hiya, I’m trying to replicate this HTTP request example but having trouble with the new writer interface. Here’s my minimal not-working example adapted from the link above.

const std = @import("std");

pub fn main() !void {
    var stdout_buffer: [1024]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
    const stdout = &stdout_writer.interface;

    var gpa = std.heap.DebugAllocator(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    var body = std.ArrayList(u8){};
    const bodywriter: *std.Io.Writer = body.writer(allocator);
    defer body.deinit(allocator);

    const uri = try std.Uri.parse("https://ziglang.org/download/index.json");
    var client = std.http.Client{ .allocator = allocator };

    // zig fmt: off
    const response = try client.fetch(.{ 
        .method = .GET,
        .location = .{ .uri = uri },
        .response_writer = bodywriter 
    });
    // zig fmt: on

    if (response.status == .ok) {
        @panic("oh no...");
    }

    try stdout.print("{s}\n", .{body.items});

    stdout.flush();
}

The error I get when compiling is pasted below.

install
+- install zig_http_json_req
   +- compile exe zig_http_json_req Debug native 1 errors
src/main.zig:13:51: error: expected type '*Io.Writer', found 'Io.GenericWriter(array_list.Aligned(u8,null).WriterContext,error{OutOfMemory},(function 'appendWrite'))'
    const bodywriter: *std.Io.Writer = body.writer(allocator);
                                       ~~~~~~~~~~~^~~~~~~~~~~
/home/coco/.local/bin/zig-x86_64-linux-0.15.2/lib/std/Io.zig:335:12: note: struct declared here
    return struct {
           ^~~~~~
referenced by:
    callMain [inlined]: /home/coco/.local/bin/zig-x86_64-linux-0.15.2/lib/std/start.zig:627:37
    callMainWithArgs [inlined]: /home/coco/.local/bin/zig-x86_64-linux-0.15.2/lib/std/start.zig:587:20
    posixCallMainAndExit: /home/coco/.local/bin/zig-x86_64-linux-0.15.2/lib/std/start.zig:542:36
    2 reference(s) hidden; use '-freference-trace=5' to see all references
error: the following command failed with 1 compilation errors:
/home/coco/.local/bin/zig-x86_64-linux-0.15.2/zig build-exe -ODebug --dep zig_http_json_req -Mroot=/home/coco/workspace/scratchspace/zig-http-json-req/src/main.zig -Mzig_http_json_req=/home/coco/workspace/scratchspace/zig-http-json-req/src/root.zig --cache-dir .zig-cache --global-cache-dir /home/coco/.cache/zig --name zig_http_json_req --zig-lib-dir /home/coco/.local/bin/zig-x86_64-linux-0.15.2/lib/ --listen=-

Build Summary: 0/3 steps succeeded; 1 failed
install transitive failure
+- install zig_http_json_req transitive failure
   +- compile exe zig_http_json_req Debug native 1 errors

error: the following build command failed with exit code 1:
.zig-cache/o/3e83670fa5c76d1508a25408257f43bb/build /home/coco/.local/bin/zig-x86_64-linux-0.15.2/zig /home/coco/.local/bin/zig-x86_64-linux-0.15.2/lib /home/coco/workspace/scratchspace/zig-http-json-req .zig-cache /home/coco/.cache/zig --seed 0x7470d52a -Z38a9259d47b4598b

Any idea how to fix this? My end goal is to download the JSON file at ziglang.org/download/index.json, and parse it into something? Maybe std.json.Value like serde_json does for loosely typed JSON or something? I haven’t gotten to that part yet. But to parse I need to body of the GET request.

It looks like ArrayList(u8).writer() is returnin the old Generic writer.

You probably want to use std.Io.Writer.Allocating instead.


    var body = std.Io.Writer.Allocating.init(allocator);
    defer body.deinit();

    const uri = try std.Uri.parse("https://ziglang.org/download/index.json");
    var client = std.http.Client{ .allocator = allocator };

    // zig fmt: off
    const response = try client.fetch(.{ 
        .method = .GET,
        .location = .{ .uri = uri },
        .response_writer = &body.writer, 
    });
    // zig fmt: on

    if (response.status != .ok) {
        @panic("oh no...");
    }

    try stdout.print("{s}\n", .{body.written()});

One note though. You will get a memory leak with this. Make sure to deinit the http Client.

1 Like

Yep, that works, tysm! I had to get the writer interface by accessing .writer on it.

const std = @import("std");

pub fn main() !void {
    var stdout_buffer: [1024]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
    const stdout = &stdout_writer.interface;

    var gpa = std.heap.DebugAllocator(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    var body = std.Io.Writer.Allocating.init(allocator);
    const bodywriter: *std.Io.Writer = &body.writer;
    defer body.deinit();

    const uri = try std.Uri.parse("https://ziglang.org/download/index.json");
    var client = std.http.Client{ .allocator = allocator };
    defer client.deinit();

    // zig fmt: off
    const response = try client.fetch(.{ 
        .method = .GET,
        .location = .{ .uri = uri },
        .response_writer = bodywriter 
    });
    // zig fmt: on

    if (response.status != .ok) {
        try stdout.print("got code {}\n", .{response.status});
        try stdout.flush();
        @panic("oh no...");
    }

    try stdout.print("{s}\n", .{body.written()});

    try stdout.flush();
}
3 Likes

If you use a trailing comma for the last field then there is no reason to use zig fmt: off here.

3 Likes

OH MY GOD THAT IS SO LIFESAVING YOU DON’T EVEN KNOW HOW ANNOYED I WAS WITH ZIG FMT COLLAPSING MULTIPLE LINES INTO ONE 200 CHAR LINE THANK YOUUUUU :heart:

3 Likes

Btw, this trailing comma trick also works for functions with many parameters.

4 Likes