fn create(
client: *Client,
remote_host: HostName,
port: u16,
stream: Io.net.Stream,
) !*Tls {
const io = client.io;
const gpa = client.allocator;
const alloc_len = allocLen(client, remote_host.bytes.len);
const base = try gpa.alignedAlloc(u8, .of(Tls), alloc_len);
errdefer gpa.free(base);
const host_buffer = base[@sizeOf(Tls)..][0..remote_host.bytes.len];
// The TLS client wants enough buffer for the max encrypted frame
// size, and the HTTP body reader wants enough buffer for the
// entire HTTP header. This means we need a combined upper bound.
const tls_read_buffer_len = client.tls_buffer_size + client.read_buffer_size;
const tls_read_buffer = host_buffer.ptr[host_buffer.len..][0..tls_read_buffer_len];
const tls_write_buffer = tls_read_buffer.ptr[tls_read_buffer.len..][0..client.tls_buffer_size];
const socket_write_buffer = tls_write_buffer.ptr[tls_write_buffer.len..][0..client.write_buffer_size];
const socket_read_buffer = socket_write_buffer.ptr[socket_write_buffer.len..][0..client.tls_buffer_size];
assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len);
@memcpy(host_buffer, remote_host.bytes);
const tls: *Tls = @ptrCast(base);
var random_buffer: [std.crypto.tls.Client.Options.entropy_len]u8 = undefined;
io.random(&random_buffer);
tls.* = .{
.connection = .{
.client = client,
.stream_writer = stream.writer(io, tls_write_buffer),
.stream_reader = stream.reader(io, socket_read_buffer),
.pool_node = .{},
.port = port,
.host_len = @intCast(remote_host.bytes.len),
.proxied = false,
.closing = false,
.protocol = .tls,
},
// TODO data race here on ca_bundle if the user sets `now` to null
.client = std.crypto.tls.Client.init(
&tls.connection.stream_reader.interface,
&tls.connection.stream_writer.interface,
.{
.host = .{ .explicit = remote_host.bytes },
.ca = .{ .bundle = .{
.gpa = client.allocator,
.io = client.io,
.lock = &client.ca_bundle_lock,
.bundle = &client.ca_bundle,
} },
.ssl_key_log = client.ssl_key_log,
.read_buffer = tls_read_buffer,
.write_buffer = socket_write_buffer,
.entropy = &random_buffer,
.realtime_now = client.now.?,
// This is appropriate for HTTPS because the HTTP headers contain
// the content length which is used to detect truncation attacks.
.allow_truncation_attacks = true,
},
) catch |err| switch (err) {
error.WriteFailed => return tls.connection.stream_writer.err.?,
error.ReadFailed => return tls.connection.stream_reader.err.?,
else => |e| return e,
},
};
return tls;
}
Asserts that
client.nowis non-null.