1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
//! 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", .{});
}
|