238 lines
7.7 KiB
Zig
238 lines
7.7 KiB
Zig
const std = @import("std");
|
|
const cNet = @cImport({
|
|
@cInclude("pico/cyw43_arch.h");
|
|
@cInclude("lwip/init.h");
|
|
@cInclude("lwip/tcp.h");
|
|
@cInclude("lwip/dns.h");
|
|
});
|
|
|
|
fn print(text: []const u8) void {
|
|
var buffer: [4096]u8 = undefined;
|
|
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
|
const allocator = fba.allocator();
|
|
const c_text = allocator.alloc(u8, text.len + 1) catch unreachable;
|
|
allocator.free(c_text);
|
|
|
|
for (0..text.len) |i| {
|
|
c_text[i] = text[i];
|
|
}
|
|
c_text[c_text.len - 1] = 0;
|
|
|
|
_ = cNet.printf(c_text.ptr);
|
|
_ = cNet.printf("\r\n");
|
|
}
|
|
|
|
fn print_usize(num: usize) void {
|
|
_ = cNet.printf("%d", num);
|
|
_ = cNet.printf("\r\n");
|
|
}
|
|
|
|
pub const HTTP_METHOD = enum {
|
|
GET,
|
|
POST,
|
|
PUT,
|
|
PATCH,
|
|
DELETE,
|
|
};
|
|
pub const HttpRequest = struct {
|
|
url: std.Uri,
|
|
method: HTTP_METHOD,
|
|
body: []const u8,
|
|
headers: []const HttpHeader,
|
|
};
|
|
|
|
pub const HttpResponse = struct {};
|
|
|
|
pub const HttpHeader = struct {
|
|
name: []const u8,
|
|
value: []const u8,
|
|
};
|
|
|
|
pub const HttpContext = struct { client: *const Client, request: *const HttpRequest, response: ?*const HttpResponse = null, finished: bool = false };
|
|
|
|
fn tcp_recv_callback(context: ?*anyopaque, pcb: ?*cNet.tcp_pcb, p1: ?*cNet.pbuf, _: cNet.err_t) callconv(.C) cNet.err_t {
|
|
print("asd2");
|
|
if (p1 == null) {
|
|
print("asd3");
|
|
_ = cNet.tcp_close(pcb);
|
|
return cNet.ERR_OK;
|
|
}
|
|
defer {
|
|
_ = cNet.pbuf_free(p1);
|
|
}
|
|
print("asd4");
|
|
const http_context: *HttpContext = @ptrCast(@alignCast(context));
|
|
const response = http_context.client.allocator.create(HttpResponse) catch unreachable;
|
|
http_context.response = response;
|
|
http_context.finished = true;
|
|
// std.debug.print("Received data: {s}\n", .{@ptrCast([*]const u8, p1.?.payload)});
|
|
return cNet.ERR_OK;
|
|
}
|
|
|
|
fn send_http_request(pcb: ?*cNet.tcp_pcb, context: *HttpContext) !void {
|
|
print("Preparing request");
|
|
print_usize(@intFromPtr(context));
|
|
// const request = "POST /todo-channel-name HTTP/1.1\r\nHost: ntfy.sh\r\nTitle: Csengo\r\nContent-Length: 6\r\n\r\nCsengo";
|
|
print("Getting request");
|
|
if (context.request.url.host != null) {
|
|
print("HOST");
|
|
} else {
|
|
print("NOT HOST");
|
|
}
|
|
const host = if (context.request.url.host) |host| host else unreachable;
|
|
const host_string = switch (host) {
|
|
.raw => |v| v,
|
|
.percent_encoded => |v| v,
|
|
};
|
|
print("Got host");
|
|
print(host_string);
|
|
print("Getting path");
|
|
const path_string = switch (context.request.url.path) {
|
|
.raw => |v| v,
|
|
.percent_encoded => |v| v,
|
|
};
|
|
print("Got path");
|
|
print(path_string);
|
|
|
|
const request_data = .{ @tagName(context.request.method), path_string, host_string, context.request.body.len, context.request.body };
|
|
|
|
// const raw_request = try std.fmt.allocPrint(context.client.allocator, "{} {} HTTP/1.1\r\nHost: {}\r\nContent-Length: {}\r\n\r\n{}", request_data);
|
|
const raw_request = try std.fmt.allocPrint(context.client.allocator, "{s} {s} HTTP/1.1\r\nHost: {s}\r\nTitle: Csengo\r\nContent-Length: {}\r\n\r\n{s}", request_data);
|
|
print("Sending request");
|
|
print(raw_request);
|
|
|
|
defer context.client.allocator.free(raw_request);
|
|
|
|
_ = cNet.tcp_write(pcb, raw_request.ptr, @intCast(raw_request.len), cNet.TCP_WRITE_FLAG_COPY);
|
|
_ = cNet.tcp_output(pcb);
|
|
}
|
|
fn tcp_connected_callback(context: ?*anyopaque, pcb: ?*cNet.tcp_pcb, err: cNet.err_t) callconv(.C) cNet.err_t {
|
|
print("tcp_connected_callback");
|
|
print_usize(@intFromPtr(context.?));
|
|
print(if (err != cNet.ERR_OK) "not ok" else "ok");
|
|
if (err != cNet.ERR_OK) {
|
|
return err;
|
|
}
|
|
// std.debug.print("Connected to server\n", .{});
|
|
print("Connected to server");
|
|
_ = cNet.tcp_recv(pcb, tcp_recv_callback);
|
|
// TODO: handle this error
|
|
print("asd1");
|
|
const http_context: *HttpContext = @ptrCast(@alignCast(context));
|
|
print_usize(@intFromPtr(http_context));
|
|
send_http_request(pcb, http_context) catch unreachable;
|
|
print("asd2");
|
|
return cNet.ERR_OK;
|
|
}
|
|
|
|
fn open_tcp_connection(ipaddr: [*c]const cNet.struct_ip4_addr, context: *HttpContext, context2: ?*anyopaque) void {
|
|
print("open_tcp_connection");
|
|
if (context.request.url.host != null) {
|
|
print("HOST");
|
|
} else {
|
|
print("NOT HOST");
|
|
}
|
|
print_usize(@intFromPtr(context));
|
|
print_usize(@intFromPtr(context2.?));
|
|
const pcb = cNet.tcp_new();
|
|
if (pcb == null) {
|
|
return;
|
|
}
|
|
if (context.request.url.host != null) {
|
|
print("HOST");
|
|
} else {
|
|
print("NOT HOST");
|
|
}
|
|
cNet.tcp_arg(pcb, context2);
|
|
_ = cNet.tcp_connect(pcb, ipaddr, context.request.url.port orelse 80, tcp_connected_callback);
|
|
}
|
|
|
|
fn dns_callback(_: [*c]const u8, ipaddr: [*c]const cNet.struct_ip4_addr, context: ?*anyopaque) callconv(.C) void {
|
|
print("DNS resolution returned!");
|
|
if (ipaddr) |addr| {
|
|
print("DNS resolution successful!");
|
|
const http_context: *HttpContext = @ptrCast(@alignCast(context));
|
|
print_usize(@intFromPtr(http_context));
|
|
print(http_context.request.url.scheme);
|
|
if (http_context.request.url.host != null) {
|
|
print("HOST");
|
|
} else {
|
|
print("NOT HOST");
|
|
}
|
|
open_tcp_connection(addr, http_context, context);
|
|
}
|
|
}
|
|
|
|
pub const Client = struct {
|
|
allocator: std.mem.Allocator,
|
|
pub fn sendRequest(client: *const Client, request: *const HttpRequest) !?*const HttpResponse {
|
|
print("Client.sendRequest");
|
|
const context: HttpContext = .{ .client = client, .request = request };
|
|
|
|
cNet.cyw43_arch_lwip_begin();
|
|
var cached_address = cNet.ip_addr_t{};
|
|
|
|
if (context.request.url.host != null) {
|
|
print("HOST");
|
|
} else {
|
|
print("NOT HOST");
|
|
}
|
|
|
|
const host = if (request.url.host) |host| host else unreachable;
|
|
const host_string = switch (host) {
|
|
.raw => |v| v,
|
|
.percent_encoded => |v| v,
|
|
};
|
|
if (context.request.url.host != null) {
|
|
print("HOST");
|
|
} else {
|
|
print("NOT HOST");
|
|
}
|
|
|
|
const host_c_string = try client.allocator.alloc(u8, host_string.len + 1);
|
|
defer client.allocator.free(host_c_string);
|
|
print_usize(host_string.len);
|
|
print_usize(host_c_string.len);
|
|
host_c_string[host_c_string.len - 1] = 0;
|
|
// @memcpy(host_c_string, host_string);
|
|
for (0..host_string.len) |i| {
|
|
host_c_string[i] = host_string[i];
|
|
}
|
|
if (context.request.url.host != null) {
|
|
print("HOST");
|
|
} else {
|
|
print("NOT HOST");
|
|
}
|
|
|
|
print("Request host is");
|
|
print(host_c_string);
|
|
print_usize(host_string.len);
|
|
print_usize(host_c_string.len);
|
|
if (context.request.url.host != null) {
|
|
print("HOST");
|
|
} else {
|
|
print("NOT HOST");
|
|
}
|
|
print(context.request.url.scheme);
|
|
const result = cNet.dns_gethostbyname(@ptrCast(host_c_string), &cached_address, dns_callback, @ptrCast(@alignCast(@constCast(&context))));
|
|
|
|
if (result == cNet.ERR_OK) {
|
|
print("DNS resolution returned with OK");
|
|
} else if (result == cNet.ERR_INPROGRESS) {
|
|
print("DNS resolution returned with INPROGRESS");
|
|
} else if (result == cNet.ERR_ARG) {
|
|
print("DNS resolution returned with ERR_ARG");
|
|
}
|
|
print("Calling cyw43_arch_lwip_end");
|
|
cNet.cyw43_arch_lwip_end();
|
|
print("Ending sendRequest...");
|
|
while (!context.finished) {
|
|
cNet.sleep_ms(2000);
|
|
print("loop...");
|
|
}
|
|
|
|
print("Request finished");
|
|
return context.response;
|
|
}
|
|
};
|