diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml new file mode 100644 index 0000000..80fe397 --- /dev/null +++ b/.github/workflows/mirror.yml @@ -0,0 +1,18 @@ +name: Mirror + +on: + push: + branches: [main, master] + workflow_dispatch: + +jobs: + codeberg: + if: github.repository_owner == 'jiacai2050' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: pixta-dev/repository-mirroring-action@v1 + with: + target_repo_url: https://${{ secrets.CBTOKEN }}@codeberg.org/${{ github.repository }}.git diff --git a/Makefile b/Makefile index c944244..4e7257e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ run-examples: - zig build run-basic - zig build run-advanced + zig build run-basic -freference-trace + zig build run-advanced -freference-trace diff --git a/examples/advanced.zig b/examples/advanced.zig index a80f2ed..02aa9f1 100644 --- a/examples/advanced.zig +++ b/examples/advanced.zig @@ -1,9 +1,25 @@ const std = @import("std"); +const println = @import("util.zig").println; const mem = std.mem; const Allocator = mem.Allocator; const curl = @import("curl"); const Easy = curl.Easy; +const UA = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0"; + +const Resposne = struct { + headers: struct { + @"User-Agent": []const u8, + Authorization: []const u8, + }, + json: struct { + name: []const u8, + age: usize, + }, + method: []const u8, + url: []const u8, +}; + fn put_with_custom_header(allocator: Allocator, easy: Easy) !void { var payload = std.io.fixedBufferStream( \\{"name": "John", "age": 15} @@ -13,11 +29,12 @@ fn put_with_custom_header(allocator: Allocator, easy: Easy) !void { var h = curl.RequestHeader.init(allocator); errdefer h.deinit(); try h.add(curl.HEADER_CONTENT_TYPE, "application/json"); - try h.add("User-Agent", "zig-curl/0.1.0"); + try h.add("user-agent", UA); + try h.add("Authorization", "Basic YWxhZGRpbjpvcGVuc2VzYW1l"); break :blk h; }; var req = curl.request( - "http://httpbin.org/anything", + "http://httpbin.org/anything/zig-curl", payload.reader(), ); req.method = .PUT; @@ -32,7 +49,28 @@ fn put_with_custom_header(allocator: Allocator, easy: Easy) !void { resp.body.items, }); - if (!curl.has_curl_header_support()) { + const parsed = try std.json.parseFromSlice(Resposne, allocator, resp.body.items, .{ + .ignore_unknown_fields = true, + }); + defer parsed.deinit(); + + try std.testing.expectEqualDeep( + parsed.value, + .{ + .headers = .{ + .@"User-Agent" = UA, + .Authorization = "Basic YWxhZGRpbjpvcGVuc2VzYW1l", + }, + .json = .{ + .name = "John", + .age = 15, + }, + .method = "PUT", + .url = "http://httpbin.org/anything/zig-curl", + }, + ); + + if (!curl.has_parse_header_support()) { return; } // Get response header `date`. @@ -45,16 +83,15 @@ fn put_with_custom_header(allocator: Allocator, easy: Easy) !void { } pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - const allocator = arena.allocator(); + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer if (gpa.deinit() != .ok) @panic("leak"); + var allocator = gpa.allocator(); const easy = try Easy.init(allocator); defer easy.deinit(); curl.print_libcurl_version(); - const sep = "-" ** 20; - std.debug.print("{s}PUT with custom header demo{s}\n", .{ sep, sep }); + println("PUT with custom header demo"); try put_with_custom_header(allocator, easy); } diff --git a/examples/basic.zig b/examples/basic.zig index 76391f5..7037cab 100644 --- a/examples/basic.zig +++ b/examples/basic.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const println = @import("util.zig").println; const mem = std.mem; const Allocator = mem.Allocator; const curl = @import("curl"); @@ -28,17 +29,16 @@ fn post(easy: Easy) !void { } pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - const allocator = arena.allocator(); + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer if (gpa.deinit() != .ok) @panic("leak"); + var allocator = gpa.allocator(); const easy = try Easy.init(allocator); defer easy.deinit(); - const sep = "-" ** 20; - std.debug.print("{s}GET demo{s}\n", .{ sep, sep }); + println("GET demo"); try get(easy); - std.debug.print("{s}POST demo{s}\n", .{ sep, sep }); + println("POST demo"); try post(easy); } diff --git a/examples/util.zig b/examples/util.zig new file mode 100644 index 0000000..5ae6f23 --- /dev/null +++ b/examples/util.zig @@ -0,0 +1,7 @@ +const std = @import("std"); + +const SEP = "-" ** 20; + +pub fn println(msg: []const u8) void { + std.debug.print("{s}{s}{s}\n", .{ SEP, msg, SEP }); +} diff --git a/src/c.zig b/src/c.zig index 0b5ff0c..7ee7249 100644 --- a/src/c.zig +++ b/src/c.zig @@ -44,7 +44,7 @@ pub fn print_libcurl_version() void { } pub fn polyfill_struct_curl_header() type { - if (has_curl_header_support()) { + if (has_parse_header_support()) { return *c.struct_curl_header; } else { // return a dummy struct to make it compile on old version. @@ -54,7 +54,7 @@ pub fn polyfill_struct_curl_header() type { } } -pub fn has_curl_header_support() bool { +pub fn has_parse_header_support() bool { // `curl_header` is officially supported since 7.84.0. // https://curl.se/libcurl/c/curl_easy_header.html return c.CURL_AT_LEAST_VERSION(7, 84, 0); diff --git a/src/easy.zig b/src/easy.zig index 5018f8a..41130e4 100644 --- a/src/easy.zig +++ b/src/easy.zig @@ -4,7 +4,7 @@ const std = @import("std"); const mem = std.mem; const fmt = std.fmt; -const has_curl_header = @import("c.zig").has_curl_header_support; +const has_curl_header = @import("c.zig").has_parse_header_support; const polyfill_struct_curl_header = @import("c.zig").polyfill_struct_curl_header; const Allocator = mem.Allocator; @@ -14,8 +14,10 @@ allocator: Allocator, handle: *c.CURL, /// The maximum time in milliseconds that the entire transfer operation to take. timeout_ms: usize = 30_000, +default_user_agent: []const u8 = "zig-curl/0.1.0", pub const HEADER_CONTENT_TYPE: []const u8 = "Content-Type"; +pub const HEADER_USER_AGENT: []const u8 = "User-Agent"; pub const Method = enum { GET, @@ -50,19 +52,30 @@ pub const RequestHeader = struct { } // Note: Caller should free returned list (after usage) with `freeCHeader`. - fn asCHeader(self: @This()) !?*c.struct_curl_slist { + fn asCHeader(self: @This(), ua: []const u8) !?*c.struct_curl_slist { if (self.entries.count() == 0) { return null; } var lst: ?*c.struct_curl_slist = null; var it = self.entries.iterator(); + var has_ua = false; while (it.next()) |entry| { + if (!has_ua and std.ascii.eqlIgnoreCase(entry.key_ptr.*, HEADER_USER_AGENT)) { + has_ua = true; + } + const kv = try fmt.allocPrintZ(self.allocator, "{s}: {s}", .{ entry.key_ptr.*, entry.value_ptr.* }); defer self.allocator.free(kv); lst = c.curl_slist_append(lst, kv); } + if (!has_ua) { + const kv = try fmt.allocPrintZ(self.allocator, "{s}: {s}", .{ HEADER_USER_AGENT, ua }); + defer self.allocator.free(kv); + + lst = c.curl_slist_append(lst, kv); + } return lst; } @@ -201,7 +214,7 @@ pub fn do(self: Self, req: anytype) !Response { var header: ?*c.struct_curl_slist = null; if (req.header) |h| { - header = try h.asCHeader(); + header = try h.asCHeader(self.default_user_agent); } defer if (header) |h| RequestHeader.freeCHeader(h); diff --git a/src/main.zig b/src/main.zig index 4c3b17b..4d3910b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,8 +1,5 @@ pub const Easy = @import("easy.zig"); -// pub const request = Easy.request; -// pub const Request = Easy.Request; -// pub const Response = Easy.Response; - pub usingnamespace Easy; + pub const print_libcurl_version = @import("c.zig").print_libcurl_version; -pub const has_curl_header_support = @import("c.zig").has_curl_header_support; +pub const has_parse_header_support = @import("c.zig").has_parse_header_support;