const std = @import("std"); const net = std.net; const heap = std.heap; const Thread = std.Thread; const log = std.log; const Address = net.Address; const Allocator = std.mem.Allocator; const testing = std.testing; const expect = testing.expect; const FakeStream = @import("helpers").FakeStream; const is_test = @import("builtin").is_test; const ExchangeConnectionStatus = enum { waiting, trying, active, dead }; pub const ExchangeConnection = struct { in_address: Address, out_address: Address, in: if (is_test) *FakeStream else net.Stream = undefined, out: if (is_test) *FakeStream else net.Stream = undefined, server_status: ExchangeConnectionStatus = .waiting, out_status: ExchangeConnectionStatus = .trying, listen_thread: Thread = undefined, pub fn init(host: []const u8, port: u16) !ExchangeConnection { var buf: [1024]u8 = undefined; var pba = heap.FixedBufferAllocator.init(&buf); const addrList = try net.getAddressList(pba.allocator(), host, port); defer addrList.deinit(); var final_addr: ?Address = null; for (addrList.addrs) |addr| { final_addr = addr; } const in_address = Address.initIp4(.{ 0, 0, 0, 0 }, port); return ExchangeConnection{ .out_address = final_addr.?, .in_address = in_address, }; } pub fn start(self: *ExchangeConnection) void { self.listen_thread = Thread.spawn(.{ .stack_size = 1024 * 16 }, ExchangeConnection.listen, .{self}) catch |err| { log.err("Was not possible thread secret connection listen {any}", .{err}); unreachable; }; _ = Thread.spawn(.{ .stack_size = 1024 * 16 }, ExchangeConnection.tryConnectOut, .{self}) catch |err| { log.err("Was not possible create thread tryConenctOut {any}", .{err}); unreachable; }; } pub fn tryConnectOut(self: *ExchangeConnection) void { if (is_test) { return; } for (0..10) |_| { Thread.sleep(1_000_000 * 1000); self.out = net.tcpConnectToAddress(self.out_address) catch |err| { log.err("Error connect to secret server {any}", .{err}); continue; }; self.out_status = .active; return; } } fn listen(self: *ExchangeConnection) void { if (is_test) { return; } std.debug.print("Listening ...\n", .{}); for (0..10) |_| { var server = self.in_address.listen(.{ .kernel_backlog = 1 }) catch |err| { log.err("Error starting secret server connection {any}", .{err}); continue; }; while (true) { const server_conn = server.accept() catch |err| { log.err("Error when accepting connection secret server {any}", .{err}); self.server_status = .waiting; break; }; self.server_status = .active; self.in = server_conn.stream; } server.deinit(); Thread.sleep(1_000_000 * 1000); } self.server_status = .dead; } }; test "expect create a secret connection" { const conn = try ExchangeConnection.init("uchoamp.dev", 80); try expect(conn.in_address.getPort() == 80); } //test "expect start secret connection" { // var conn1 = try ExchangeConnection.init("localhost", 5002); // conn1.out_address.setPort(2323); // // conn1.start(); // // //_ = try conn1.out.write("hello!\r\n"); // //Thread.sleep(1_000_000 * 1000 * 10); // conn1.listen_thread.join(); // // var buf: [100]u8 = undefined; // try expect(conn1.server_status == .active); // const size = try conn1.in.read(&buf); // std.debug.print("Secret conn response: {s}", .{buf[0..size]}); //}