diff options
Diffstat (limited to 'src/transmission.zig')
-rw-r--r-- | src/transmission.zig | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/src/transmission.zig b/src/transmission.zig new file mode 100644 index 0000000..fec1b12 --- /dev/null +++ b/src/transmission.zig @@ -0,0 +1,106 @@ +const std = @import("std"); + +const Request = @import("request.zig").Request; +const SessionGetFields = @import("request.zig").SessionGetFields; + +pub const ClientOptions = extern struct { + host: [*:0]const u8, + port: u16, + https: bool, + user: ?[*:0]const u8 = null, + password: ?[*:0]const u8 = null, +}; + +pub const Client = struct { + uri: std.Uri, + allocator: std.mem.Allocator, + http_client: std.http.Client, + http_headers: std.http.Headers, + current_session_id: []u8 = "", + + pub fn init(allocator: std.mem.Allocator, opts: ClientOptions) Client { + const base_url = std.Uri{ + .path = "/transmission/rpc", + .scheme = blk: { + if (opts.https) { + break :blk "https"; + } else { + break :blk "http"; + } + }, + .host = std.mem.span(opts.host), + .port = opts.port, + .user = null, + .password = null, + .query = null, + .fragment = null, + }; + + return Client{ + .uri = base_url, + .allocator = allocator, + .http_client = std.http.Client{ .allocator = allocator }, + .http_headers = std.http.Headers{ .allocator = allocator }, + }; + } + + pub fn deinit(self: *Client) void { + self.http_headers.deinit(); + self.http_client.deinit(); + self.allocator.free(self.current_session_id); + } + + fn setSessionId(self: *Client, r: std.http.Client.Response) !void { + self.allocator.free(self.current_session_id); + const session_id = r.headers.getFirstValue("X-Transmission-Session-Id") orelse return error.NoSessionId; + self.current_session_id = try std.fmt.allocPrint(self.allocator, "{s}", .{session_id}); + } + + fn newRequest(self: *Client) !std.http.Client.Request { + try self.http_headers.append("X-Transmission-Session-Id", self.current_session_id); + return try self.http_client.request(.POST, self.uri, self.http_headers, .{}); + } + + pub fn do(self: *Client, req: Request) ![]u8 { + var real_req = try self.newRequest(); + defer real_req.deinit(); + + var payload = std.ArrayList(u8).init(self.allocator); + defer payload.deinit(); + + try std.json.stringify(req, .{}, payload.writer()); + + real_req.transfer_encoding = std.http.Client.RequestTransfer{ + .content_length = payload.items.len, + }; + + try real_req.start(); + if (try real_req.write(payload.items) != payload.items.len) { + return error.InvalidSize; + } + + try real_req.finish(); + try real_req.wait(); + + if (real_req.response.status == std.http.Status.conflict) { + try self.setSessionId(real_req.response); + return self.do(req); + } + + const body = try real_req.reader().readAllAlloc(self.allocator, 9000); + return body; + } +}; + +pub fn sessionGet(client: *Client) ![]u8 { + const r = Request{ + .method = .session_get, + .arguments = .{ + .session_get = .{ + .fields = &[_]SessionGetFields{ .version, .utp_enabled }, + }, + }, + }; + const body = try client.do(r); + return body; +} |