srctree

Gregory Mullen parent e161bd22 49339404
new iovec abstraction

src/cookies.zig added: 122, removed: 63, total 59
@@ -25,24 +25,62 @@ pub const Attributes = struct {
none,
};
 
/// vec must be large enough for the largest cookie (10)
pub fn writeVec(a: Attributes, vec: []std.posix.iovec_const) !usize {
var used: usize = 0;
if (a.domain) |d| {
vec[used] = .{ .base = "; Domain=", .len = 9 };
used += 1;
vec[used] = .{ .base = d.ptr, .len = d.len };
used += 1;
}
if (a.path) |p| {
vec[used] = .{ .base = "; Path=", .len = 7 };
used += 1;
vec[used] = .{ .base = p.ptr, .len = p.len };
used += 1;
}
if (a.max_age) |_| {
@panic("not implemented");
//vec[used] = .[ .base = "; Max-Age={}", .{m});
//vec[used] = .{ .base = m.ptr, .len = m.len};
}
if (a.same_site) |s| {
vec[used] = switch (s) {
.strict => .{ .base = "; SameSite=Strict", .len = 17 },
.lax => .{ .base = "; SameSite=Lax", .len = 14 },
.none => .{ .base = "; SameSite=None", .len = 15 },
};
used += 1;
}
if (a.partitioned) {
vec[used] = .{ .base = "; Partitioned", .len = 13 };
used += 1;
}
if (a.secure) {
vec[used] = .{ .base = "; Secure", .len = 8 };
used += 1;
}
if (a.httponly) {
vec[used] = .{ .base = "; HttpOnly", .len = 10 };
used += 1;
}
 
return used;
}
 
pub fn format(a: Attributes, comptime _: []const u8, _: fmt.FormatOptions, w: anytype) !void {
if (a.domain) |d|
try w.print("; Domain={s}", .{d});
if (a.path) |p|
try w.print("; Path={s}", .{p});
if (a.max_age) |m|
try w.print("; Max-Age={}", .{m});
if (a.domain) |d| try w.print("; Domain={s}", .{d});
if (a.path) |p| try w.print("; Path={s}", .{p});
if (a.max_age) |m| try w.print("; Max-Age={}", .{m});
if (a.same_site) |s| try switch (s) {
.strict => w.writeAll("; SameSite=Strict"),
.lax => w.writeAll("; SameSite=Lax"),
.none => w.writeAll("; SameSite=None"),
};
if (a.partitioned)
try w.writeAll("; Partitioned");
if (a.secure)
try w.writeAll("; Secure");
if (a.httponly)
try w.writeAll("; HttpOnly");
if (a.partitioned) try w.writeAll("; Partitioned");
if (a.secure) try w.writeAll("; Secure");
if (a.httponly) try w.writeAll("; HttpOnly");
}
};
 
@@ -63,6 +101,23 @@ pub const Cookie = struct {
};
}
 
/// vec must be large enough for the largest cookie (4 + attributes)
pub fn writeVec(c: Cookie, vec: []std.posix.iovec_const) !usize {
if (vec.len < 12) return error.NoSpaceLeft;
var used: usize = 0;
vec[used] = .{ .base = "Set-Cookie: ".ptr, .len = 12 };
used += 1;
vec[used] = .{ .base = c.name.ptr, .len = c.name.len };
used += 1;
vec[used] = .{ .base = "=".ptr, .len = 1 };
used += 1;
vec[used] = .{ .base = c.value.ptr, .len = c.value.len };
used += 1;
used += try c.attr.writeVec(vec[4..]);
 
return used;
}
 
pub fn format(c: Cookie, comptime fstr: []const u8, _: fmt.FormatOptions, w: anytype) !void {
if (comptime eql(u8, fstr, "header")) {
try w.writeAll("Set-Cookie: ");
@@ -92,6 +147,18 @@ test Cookie {
const res = try fmt.bufPrint(&buffer, "{header}", .{cookie});
try std.testing.expectEqualStrings(expect, res);
}
 
const v_expct = [4]std.posix.iovec_const{
.{ .base = "Set-Cookie: ", .len = 12 },
.{ .base = "name", .len = 4 },
.{ .base = "=", .len = 1 },
.{ .base = "value", .len = 5 },
};
 
var v_buf: [14]std.posix.iovec_const = undefined;
const used = try cookies[0].writeVec(v_buf[0..]);
try std.testing.expectEqual(4, used);
try std.testing.expectEqualDeep(v_expct[0..4], v_buf[0..4]);
}
 
pub const Jar = struct {
 
src/errors.zig added: 122, removed: 63, total 59
@@ -24,6 +24,7 @@ pub const NetworkError = error{
/// full response is delivered.
BrokenPipe,
IOWriteFailure,
NoSpaceLeft,
};
 
pub const Error = ServerError || ClientError || NetworkError;
 
src/frame.zig added: 122, removed: 63, total 59
@@ -197,6 +197,27 @@ pub fn init(a: Allocator, req: *const Request, auth: Auth.Provider) !Frame {
};
}
 
fn VecList(comptime SIZE: usize) type {
return struct {
pub const capacity = SIZE;
vect: [SIZE]iovec_c = undefined,
length: usize = 0,
 
pub fn init() @This() {
return .{};
}
 
pub fn append(self: *@This(), str: []const u8) !void {
if (self.length >= capacity) return error.NoSpaceLeft;
self.vect[self.length] = .{
.base = str.ptr,
.len = str.len,
};
self.length += 1;
}
};
}
 
pub fn sendHeaders(vrs: *Frame) SendError!void {
if (vrs.wrote_headers) {
return SendError.HeadersFinished;
@@ -204,79 +225,49 @@ pub fn sendHeaders(vrs: *Frame) SendError!void {
 
switch (vrs.downstream) {
.http, .zwsgi => |stream| {
var vect: [HEADER_VEC_COUNT]iovec_c = undefined;
var count: usize = 0;
var vect = VecList(HEADER_VEC_COUNT).init();
 
const h_resp = vrs.HTTPHeader();
vect[count] = .{ .base = h_resp.ptr, .len = h_resp.len };
count += 1;
try vect.append(h_resp);
 
// Default headers
const s_name = "Server: verse/" ++ build_version ++ "\r\n";
vect[count] = .{ .base = s_name.ptr, .len = s_name.len };
count += 1;
try vect.append(s_name);
 
if (vrs.content_type) |ct| {
vect[count] = .{ .base = "Content-Type: ".ptr, .len = "Content-Type: ".len };
count += 1;
try vect.append("Content-Type: ");
switch (ct.base) {
inline else => |tag, name| {
vect[count] = .{
.base = @tagName(name).ptr,
.len = @tagName(name).len,
};
count += 1;
vect[count] = .{ .base = "/".ptr, .len = "/".len };
count += 1;
vect[count] = .{
.base = @tagName(tag).ptr,
.len = @tagName(tag).len,
};
count += 1;
try vect.append(@tagName(name));
try vect.append("/");
try vect.append(@tagName(tag));
},
}
if (ct.parameter) |param| {
const pre = "; charset=";
vect[count] = .{ .base = pre.ptr, .len = pre.len };
count += 1;
try vect.append(pre);
const tag = @tagName(param);
vect[count] = .{ .base = tag.ptr, .len = tag.len };
count += 1;
try vect.append(tag);
}
 
vect[count] = .{ .base = "\r\n".ptr, .len = "\r\n".len };
count += 1;
 
try vect.append("\r\n");
//"text/html; charset=utf-8"); // Firefox is trash
}
 
var itr = vrs.headers.iterator();
while (itr.next()) |header| {
vect[count] = .{ .base = header.name.ptr, .len = header.name.len };
count += 1;
vect[count] = .{ .base = ": ".ptr, .len = ": ".len };
count += 1;
vect[count] = .{ .base = header.value.ptr, .len = header.value.len };
count += 1;
vect[count] = .{ .base = "\r\n".ptr, .len = "\r\n".len };
count += 1;
try vect.append(header.name);
try vect.append(": ");
try vect.append(header.value);
try vect.append("\r\n");
}
 
for (vrs.cookie_jar.cookies.items) |cookie| {
vect[count] = .{ .base = "Set-Cookie: ".ptr, .len = "Set-Cookie: ".len };
count += 1;
// TODO remove this alloc
const cookie_str = allocPrint(vrs.alloc, "{}", .{cookie}) catch @panic("OOM");
vect[count] = .{
.base = cookie_str.ptr,
.len = cookie_str.len,
};
count += 1;
vect[count] = .{ .base = "\r\n".ptr, .len = "\r\n".len };
count += 1;
const used = try cookie.writeVec(vect.vect[vect.length..]);
vect.length += used;
try vect.append("\r\n");
}
 
stream.writevAll(vect[0..count]) catch return error.IOWriteFailure;
stream.writevAll(vect.vect[0..vect.length]) catch return error.IOWriteFailure;
},
.buffer => @panic("not implemented"),
}