srctree

Gregory Mullen parent b2c3d5ee a45cf299
add content-type

yes I hate it, no I don't know what I'm actually gonna do... but I've already burned way too much time on this
filename was Deleted added: 206, removed: 69, total 137
@@ -0,0 +1,151 @@
base: ContentBase,
parameter: ?CharSet = null,
 
pub const default: ContentType = .{ .base = .{ .text = .html } };
 
pub const ContentType = @This();
 
pub const ContentBase = union(Base) {
application: Application,
audio: Audio,
font: Font,
image: Image,
text: Text,
video: Video,
/// Multipart types
multipart: MultiPart,
message: MultiPart,
};
 
pub const Base = enum {
// Basic types
application,
audio,
font,
image,
text,
video,
/// Multipart types
multipart,
message,
 
pub fn isMultipart(b: Base) bool {
return switch (b) {
.multipart, .message => true,
else => false,
};
}
};
 
pub const Application = enum {
@"x-www-form-urlencoded",
@"x-git-upload-pack-request",
@"octet-stream",
 
pub fn toSlice(comptime app: Application) [:0]const u8 {
return switch (app) {
inline else => |r| @typeName(@This())[13..] ++ "/" ++ @tagName(r),
};
}
 
test "ApplicationtoSlice" {
// This should be a lowercase A, but I don't know how much time to
// invest into this yet.
try std.testing.expectEqualStrings(
"Application/octet-stream",
Application.@"octet-stream".toSlice(),
);
}
};
 
pub const Audio = enum {
ogg,
};
 
pub const Font = enum {
otf,
ttf,
woff,
};
 
pub const Image = enum {
png,
jpeg,
};
 
pub const Text = enum {
plain,
css,
html,
javascript,
};
 
pub const Video = enum {
mp4,
};
 
pub const MultiPart = enum {
mixed,
@"form-data",
};
 
pub const CharSet = enum {
@"utf-8",
};
 
fn a(comptime b: ContentBase) [:0]const u8 {
return switch (b) {
inline else => |t| @tagName(t),
};
}
 
pub fn toSlice(comptime ct: ContentType) []const u8 {
return switch (ct.base) {
inline else => |tag| @tagName(ct.base) ++ "/" ++ @tagName(tag),
};
}
 
test toSlice {
try std.testing.expectEqualStrings("text/html", default.toSlice());
try std.testing.expectEqualStrings("image/png", (ContentType{ .base = .{ .image = .png } }).toSlice());
}
 
pub fn fromStr(str: []const u8) !ContentType {
inline for (std.meta.fields(ContentBase)) |field| {
if (startsWith(u8, str, field.name)) {
return wrap(field.type, str[field.name.len + 1 ..]);
}
}
return error.UnknownContentType;
}
 
fn subWrap(comptime Kind: type, str: []const u8) !Kind {
inline for (std.meta.fields(Kind)) |field| {
if (startsWith(u8, str, field.name)) {
return @enumFromInt(field.value);
}
}
return error.UnknownContentType;
}
 
fn wrap(comptime kind: type, val: anytype) !ContentType {
return .{
.base = switch (kind) {
MultiPart => .{ .multipart = try subWrap(kind, val) },
Application => .{ .application = try subWrap(kind, val) },
Audio => .{ .audio = try subWrap(kind, val) },
Font => .{ .font = try subWrap(kind, val) },
Image => .{ .image = try subWrap(kind, val) },
Text => .{ .text = try subWrap(kind, val) },
Video => .{ .video = try subWrap(kind, val) },
else => @compileError("not implemented type " ++ @typeName(kind)),
},
};
}
 
test ContentType {
std.testing.refAllDecls(@This());
}
 
const std = @import("std");
pub const startsWith = std.mem.startsWith;
 
src/request_data.zig added: 206, removed: 69, total 137
@@ -1,15 +1,9 @@
const std = @import("std");
const Type = @import("builtin").Type;
const Allocator = std.mem.Allocator;
const eql = std.mem.eql;
const splitScalar = std.mem.splitScalar;
const splitSequence = std.mem.splitSequence;
 
const Data = @This();
 
post: ?PostData,
query: QueryData,
 
const Data = @This();
pub const ContentType = @import("content-type.zig");
 
pub fn validate(data: Data, comptime T: type) !T {
return RequestData(T).init(data);
}
@@ -163,45 +157,6 @@ pub const QueryData = struct {
}
};
 
pub const ContentType = union(enum) {
const Application = enum {
@"x-www-form-urlencoded",
@"x-git-upload-pack-request",
};
const MultiPart = enum {
mixed,
@"form-data",
};
multipart: MultiPart,
application: Application,
 
fn subWrap(comptime Kind: type, str: []const u8) !Kind {
inline for (std.meta.fields(Kind)) |field| {
if (std.mem.startsWith(u8, str, field.name)) {
return @enumFromInt(field.value);
}
}
return error.UnknownContentType;
}
 
fn wrap(comptime kind: type, val: anytype) !ContentType {
return switch (kind) {
MultiPart => .{ .multipart = try subWrap(kind, val) },
Application => .{ .application = try subWrap(kind, val) },
else => @compileError("not implemented type"),
};
}
 
pub fn fromStr(str: []const u8) !ContentType {
inline for (std.meta.fields(ContentType)) |field| {
if (std.mem.startsWith(u8, str, field.name)) {
return wrap(field.type, str[field.name.len + 1 ..]);
}
}
return error.UnknownContentType;
}
};
 
pub fn RequestData(comptime T: type) type {
return struct {
req: T,
@@ -345,6 +300,9 @@ fn parseApplication(a: Allocator, ap: ContentType.Application, data: []u8, htype
// Git just uses the raw data instead, no need to preprocess
return &[0]DataItem{};
},
.@"octet-stream" => {
unreachable; // Not implemented
},
}
}
 
@@ -457,9 +415,10 @@ pub fn readBody(
const read_size = try reader.read(post_buf);
if (read_size != size) return error.UnexpectedHttpBodySize;
 
const items = switch (try ContentType.fromStr(htype)) {
const items = switch ((try ContentType.fromStr(htype)).base) {
.application => |ap| try parseApplication(a, ap, post_buf, htype),
.multipart => |mp| try parseMulti(a, mp, post_buf, htype),
.multipart, .message => |mp| try parseMulti(a, mp, post_buf, htype),
.audio, .font, .image, .text, .video => @panic("content-type not implemented"),
};
 
return .{
@@ -472,17 +431,8 @@ pub fn readQuery(a: Allocator, query: []const u8) !QueryData {
return QueryData.init(a, query);
}
 
pub fn parseRequestData(
a: Allocator,
query: []const u8,
acpt: std.net.StreamServer.Connection,
size: usize,
htype: []const u8,
) !RequestData {
return RequestData{
.post_data = try readBody(a, acpt, size, htype),
.query_data = try readQuery(a, query),
};
test {
std.testing.refAllDecls(@This());
}
 
test "multipart/mixed" {}
@@ -492,3 +442,10 @@ test "multipart/form-data" {}
test "multipart/multipart" {}
 
test "application/x-www-form-urlencoded" {}
 
const std = @import("std");
const Type = @import("builtin").Type;
const Allocator = std.mem.Allocator;
const eql = std.mem.eql;
const splitScalar = std.mem.splitScalar;
const splitSequence = std.mem.splitSequence;
 
src/verse.zig added: 206, removed: 69, total 137
@@ -9,6 +9,7 @@ pub const UriIter = Router.UriIter;
pub const Headers = @import("headers.zig");
pub const Auth = @import("auth.zig");
pub const Cookies = @import("cookies.zig");
pub const ContentType = @import("content-type.zig");
 
const Error = @import("errors.zig").Error;
const NetworkError = @import("errors.zig").NetworkError;
@@ -28,8 +29,8 @@ auth: Auth,
route_ctx: ?*const anyopaque = null,
 
// Raw move from response.zig
 
headers: Headers,
content_type: ?ContentType = ContentType.default,
cookie_jar: Cookies.Jar,
status: ?std.http.Status = null,
 
@@ -56,7 +57,7 @@ const VarPair = struct {
 
pub fn init(a: Allocator, req: *const Request, reqdata: RequestData) !Verse {
std.debug.assert(req.uri[0] == '/');
var self = Verse{
return .{
.alloc = a,
.request = req,
.reqdata = reqdata,
@@ -71,9 +72,6 @@ pub fn init(a: Allocator, req: *const Request, reqdata: RequestData) !Verse {
.headers = Headers.init(a),
.cookie_jar = try Cookies.Jar.init(a),
};
try self.headersAdd("Server", "zwsgi/0.0.0");
try self.headersAdd("Content-Type", "text/html; charset=utf-8"); // Firefox is trash
return self;
}
 
pub fn headersAdd(vrs: *Verse, comptime name: []const u8, value: []const u8) !void {
@@ -149,6 +147,37 @@ pub fn sendHeaders(vrs: *Verse) !void {
vect[count] = .{ .base = h_resp.ptr, .len = h_resp.len };
count += 1;
 
// Default headers
const s_name = "Server: verse/0.0.0-dev\r\n";
vect[count] = .{ .base = s_name.ptr, .len = s_name.len };
count += 1;
 
if (vrs.content_type) |ct| {
vect[count] = .{ .base = "Content-Type: ".ptr, .len = "Content-Type: ".len };
count += 1;
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;
},
}
 
vect[count] = .{ .base = "\r\n".ptr, .len = "\r\n".len };
count += 1;
 
//"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 };