this repo has no description
1const std = @import("std");
2const xev = @import("xev");
3
4const log = @import("log.zig");
5
6const HeapArena = @import("HeapArena.zig");
7const Server = @import("Server.zig");
8const WorkerQueue = Server.WorkerQueue;
9
10/// Wrapper which authenticates with github
11pub fn authenticate(
12 arena: HeapArena,
13 client: *std.http.Client,
14 queue: *WorkerQueue,
15 fd: xev.TCP,
16 auth_header: []const u8,
17) !void {
18 errdefer {
19 queue.push(.{ .auth_failure = .{
20 .arena = arena,
21 .fd = fd,
22 .msg = "github authentication failed",
23 } });
24 }
25
26 log.debug("authenticating with github", .{});
27 const endpoint = "https://api.github.com/user";
28
29 var storage = std.ArrayList(u8).init(arena.allocator());
30
31 const req: std.http.Client.FetchOptions = .{
32 .response_storage = .{ .dynamic = &storage },
33 .location = .{ .url = endpoint },
34 .method = .GET,
35 .headers = .{
36 .authorization = .{ .override = auth_header },
37 },
38 };
39 const result = try fetch(client, req);
40
41 switch (result.status) {
42 .ok => {
43 const value = try std.json.parseFromSliceLeaky(std.json.Value, arena.allocator(), storage.items, .{});
44 const resp = value.object;
45 const login = resp.get("login").?.string;
46 const avatar_url = resp.get("avatar_url").?.string;
47 const realname = resp.get("name").?.string;
48 const id = resp.get("id").?.integer;
49 queue.push(.{
50 .auth_success = .{
51 .arena = arena,
52 .fd = fd,
53 .nick = login,
54 .user = try std.fmt.allocPrint(arena.allocator(), "{d}", .{id}),
55 .realname = realname,
56 .avatar_url = avatar_url,
57 },
58 });
59 },
60 .unauthorized, .forbidden => {
61 queue.push(.{
62 .auth_failure = .{
63 .arena = arena,
64 .fd = fd,
65 .msg = storage.items,
66 },
67 });
68 },
69 else => {
70 log.warn("unexpected github response: {s}", .{storage.items});
71 return error.UnexpectedResponse;
72 },
73 }
74}
75
76/// Performs a fetch with retries
77fn fetch(
78 client: *std.http.Client,
79 request: std.http.Client.FetchOptions,
80) !std.http.Client.FetchResult {
81 const max_attempts: u2 = 3;
82 var attempts: u2 = 0;
83 while (true) {
84 const result = client.fetch(request) catch |err| {
85 if (attempts == max_attempts) return err;
86 defer attempts += 1;
87 const delay: u64 = @as(u64, 500 * std.time.ns_per_ms) << (attempts + 1);
88 log.warn("request failed, retrying in {d} ms", .{delay / std.time.ns_per_ms});
89 std.time.sleep(delay);
90 continue;
91 };
92 return result;
93 }
94}