From 1ffe01dc7a12cc92aaa610e89613076ad1930690 Mon Sep 17 00:00:00 2001 From: Rene Schallner Date: Sun, 16 Apr 2023 20:06:07 +0200 Subject: [PATCH] made http_client more universal --- src/http_auth.zig | 15 ++++++-- src/http_client.zig | 91 ++++++++++++++++++++++++++++----------------- src/test_auth.zig | 26 ++++++++++--- 3 files changed, 88 insertions(+), 44 deletions(-) diff --git a/src/http_auth.zig b/src/http_auth.zig index 5dea028..23989f0 100644 --- a/src/http_auth.zig +++ b/src/http_auth.zig @@ -1,7 +1,7 @@ const std = @import("std"); const zap = @import("zap.zig"); -const AuthScheme = enum { +pub const AuthScheme = enum { Basic, Bearer, @@ -12,12 +12,19 @@ const AuthScheme = enum { }; } - pub fn headerFieldStr(self: AuthScheme) []const u8 { + pub fn headerFieldStrFio(self: AuthScheme) []const u8 { return switch (self) { .Basic => "authentication", .Bearer => "authorization", }; } + + pub fn headerFieldStrHeader(self: AuthScheme) [:0]const u8 { + return switch (self) { + .Basic => "Authentication", + .Bearer => "Authorization", + }; + } }; pub fn checkAuthHeader(scheme: AuthScheme, auth_header: []const u8) bool { @@ -29,8 +36,8 @@ pub fn checkAuthHeader(scheme: AuthScheme, auth_header: []const u8) bool { pub fn extractAuthHeader(scheme: AuthScheme, r: *const zap.SimpleRequest) ?[]const u8 { return switch (scheme) { - .Basic => |b| r.getHeader(b.headerFieldStr()), - .Bearer => |b| r.getHeader(b.headerFieldStr()), + .Basic => |b| r.getHeader(b.headerFieldStrFio()), + .Bearer => |b| r.getHeader(b.headerFieldStrFio()), }; } diff --git a/src/http_client.zig b/src/http_client.zig index f5ce37a..fe115e1 100644 --- a/src/http_client.zig +++ b/src/http_client.zig @@ -2,43 +2,32 @@ const std = @import("std"); const zap = @import("zap.zig"); const fio = @import("fio.zig"); const util = @import("util.zig"); +const auth = @import("http_auth.zig"); -pub fn setHeader(h: [*c]fio.http_s, name: []const u8, value: []const u8) !void { - const hname: fio.fio_str_info_s = .{ - .data = util.toCharPtr(name), - .len = name.len, - .capa = name.len, +var http_header_field: [:0]const u8 = undefined; +var http_header_value: [:0]const u8 = undefined; + +pub fn main() !void { + var allocator = std.heap.page_allocator; + var args_it = std.process.args(); + _ = args_it.skip(); // skip process name + // + const url = args_it.next() orelse "http://127.0.0.1:3000/test"; + const method = args_it.next() orelse "Bearer"; + const token = args_it.next() orelse "ABCDEFG"; + + const scheme: auth.AuthScheme = if (std.mem.eql(u8, method, "Bearer")) .Bearer else .Basic; + http_header_field = scheme.headerFieldStrHeader(); + + http_header_value = switch (scheme) { + .Basic => try std.fmt.allocPrintZ(allocator, "Basic {s}", .{token}), + .Bearer => try std.fmt.allocPrintZ(allocator, "Bearer {s}", .{token}), }; - const vname: fio.fio_str_info_s = .{ - .data = util.toCharPtr(value), - .len = value.len, - .capa = value.len, - }; - const ret = fio.http_set_header2(h, hname, vname); + std.debug.print("Connecting to: {s}\n", .{url}); + std.debug.print(" Header: '{s}:{s}'\n", .{ http_header_field, http_header_value }); - if (ret == 0) return; - return zap.HttpError.HttpSetHeader; -} - -fn on_response(r: [*c]fio.http_s) callconv(.C) void { - if (r.*.status_str == zap.FIOBJ_INVALID) { - setHeader(r, "Authorization", "Bearer ABCDEFG") catch return; - zap.http_finish(r); - return; - } - - const response = zap.http_req2str(r); - if (zap.fio2str(response)) |body| { - std.debug.print("{s}\n", .{body}); - } else { - std.debug.print("Oops\n", .{}); - } - zap.fio_stop(); -} - -pub fn main() void { - const ret = zap.http_connect("http://127.0.0.1:3000/test", null, .{ + const ret = zap.http_connect(url, null, .{ .on_response = on_response, .on_request = null, .on_upgrade = null, @@ -59,7 +48,41 @@ pub fn main() void { .log = 1, .is_client = 1, }); - // _ = ret; std.debug.print("\nHTTP CONNECT ret = {d}\n", .{ret}); zap.fio_start(.{ .threads = 1, .workers = 1 }); } + +pub fn setHeader(h: [*c]fio.http_s, name: [:0]const u8, value: [:0]const u8) !void { + const hname: fio.fio_str_info_s = .{ + .data = util.toCharPtr(name), + .len = name.len, + .capa = name.len, + }; + + const vname: fio.fio_str_info_s = .{ + .data = util.toCharPtr(value), + .len = value.len, + .capa = value.len, + }; + const ret = fio.http_set_header2(h, hname, vname); + + if (ret == 0) return; + return zap.HttpError.HttpSetHeader; +} + +fn on_response(r: [*c]fio.http_s) callconv(.C) void { + // the first time around, we need to complete the request. E.g. set headers. + if (r.*.status_str == zap.FIOBJ_INVALID) { + setHeader(r, http_header_field, http_header_value) catch return; + zap.http_finish(r); + return; + } + + const response = zap.http_req2str(r); + if (zap.fio2str(response)) |body| { + std.debug.print("{s}\n", .{body}); + } else { + std.debug.print("Oops\n", .{}); + } + zap.fio_stop(); +} diff --git a/src/test_auth.zig b/src/test_auth.zig index 3531eb9..696bea9 100644 --- a/src/test_auth.zig +++ b/src/test_auth.zig @@ -51,7 +51,7 @@ fn endpoint_http_get(e: *Endpoints.SimpleEndpoint, r: zap.SimpleRequest) void { } // -// http client code +// http client code for in-process sending of http request // fn setHeader(h: [*c]fio.http_s, name: []const u8, value: []const u8) !void { const hname: fio.fio_str_info_s = .{ @@ -99,6 +99,7 @@ fn sendRequest() void { } fn on_response(r: [*c]fio.http_s) callconv(.C) void { + // the first time around, we need to complete the request. E.g. set headers. if (r.*.status_str == zap.FIOBJ_INVALID) { setHeader(r, "Authorization", "Bearer ABCDEFG") catch return; zap.http_finish(r); @@ -120,17 +121,30 @@ test "BearerAuthSingle authenticateRequest" { const a = std.testing.allocator; const token = "ABCDEFG"; - // spawn curl process before we start facilio threads - // unfortunately, this doesn't work: facilio doesn't start up if we spawn a child process - // var p = std.ChildProcess.init(&.{ "bash", "-c", "sleep 10; curl -H \"Authorization: Bearer\"" ++ token ++ " http://localhost:3000/test -v" }, a); + // + // Unfortunately, spawning a child process confuses facilio: + // + // 1. attempt: spawn curl process before we start facilio threads + // this doesn't work: facilio doesn't start up if we spawn a child process + // var p = std.ChildProcess.init(&.{ + // "bash", + // "-c", + // "sleep 10; curl -H \"Authorization: Bearer\"" ++ token ++ " http://localhost:3000/test -v", + // }, a); // try p.spawn(); + // 2. attempt: // our custom client doesn't work either - // var p = std.ChildProcess.init(&.{ "bash", "-c", "sleep 3; ./zig-out/bin/http_client &" }, a); + // var p = std.ChildProcess.init(&.{ + // "bash", + // "-c", + // "sleep 3; ./zig-out/bin/http_client &", + // }, a); // try p.spawn(); // std.debug.print("done spawning\n", .{}); - // this doesn't work either because facilio wants to be either server or client, gets confused doing it this way + // 3. attempt: sending the request in-process + // this doesn't work either because facilio wants to be either server or client, gets confused, at least when we're doing it this way // sendRequest(); // setup listener