//! Native use of the public HTTPS transport, exercised by the browser E2E runner. const std = @import("std"); const c9 = @import("cloud9"); const Session = struct { tunnel: *c9.http.Client, client: c9.Client, fn ask(s: *Session, request: c9.Client.Request) !c9.Client.Result { const tag = try s.client.submit(request); try s.tunnel.send(s.client.output()); s.client.wrote(s.client.output().len); var buffer: [65536]u8 = undefined; const frame = try s.tunnel.receive(&buffer); if (s.client.push(frame) != frame.len) return error.InputFull; const done = s.client.take() orelse return error.MissingReply; if (done.tag != tag or done.result == .fail) return error.UnexpectedReply; return done.result; } }; pub fn main(init: std.process.Init) !void { const args = try init.minimal.args.toSlice(init.arena.allocator()); if (args.len < 3 or args.len > 4) return error.Arguments; var http: std.http.Client = .{ .allocator = init.gpa, .io = init.io }; defer http.deinit(); if (args.len == 4) { http.now = std.Io.Clock.real.now(init.io); try http.ca_bundle.addCertsFromFilePathAbsolute(init.gpa, init.io, http.now.?, args[3]); } var tunnel = try c9.http.Client.connect(&http, args[1], args[2]); defer tunnel.deinit(); var input: [65536]u8 = undefined; var output: [65536]u8 = undefined; var session: Session = .{ .tunnel = &tunnel, .client = .init(.{ .in = &input, .out = &output }) }; _ = try session.ask(.{ .version = .{} }); _ = try session.ask(.{ .attach = .{ .fid = 0, .uname = "user" } }); _ = try session.ask(.{ .walk = .{ .fid = 0, .newfid = 1, .names = &.{"hello.txt"} } }); _ = try session.ask(.{ .open = .{ .fid = 1, .mode = c9.oread } }); const result = try session.ask(.{ .read = .{ .fid = 1, .offset = 0, .count = 1024 } }); if (!std.mem.eql(u8, result.read, "Hello from 9P.\n")) return error.WrongContents; // Two outstanding tags exercise the full-duplex bridge independently of // the browser UI's serialized file-operation queue. const read_tag = try session.client.submit(.{ .read = .{ .fid = 1, .offset = 0, .count = 1024 } }); try tunnel.send(session.client.output()); session.client.wrote(session.client.output().len); const stat_tag = try session.client.submit(.{ .stat = .{ .fid = 0 } }); try tunnel.send(session.client.output()); session.client.wrote(session.client.output().len); var seen_read = false; var seen_stat = false; var reply_buffer: [65536]u8 = undefined; for (0..2) |_| { const frame = try tunnel.receive(&reply_buffer); if (session.client.push(frame) != frame.len) return error.InputFull; const done = session.client.take() orelse return error.MissingReply; if (done.tag == read_tag and !seen_read and done.result == .read) { if (!std.mem.eql(u8, done.result.read, "Hello from 9P.\n")) return error.WrongContents; seen_read = true; } else if (done.tag == stat_tag and !seen_stat and done.result == .stat) { if (done.result.stat.qid.type & c9.qtdir == 0) return error.WrongStat; seen_stat = true; } else return error.UnexpectedReply; } if (!seen_read or !seen_stat) return error.MissingReply; _ = try session.ask(.{ .clunk = .{ .fid = 1 } }); _ = try session.ask(.{ .clunk = .{ .fid = 0 } }); std.debug.print("native HTTP transport passed\n", .{}); }