diff --git a/src/req_cookie.zig b/src/req_cookie.zig new file mode 100644 index 0000000..17267b8 --- /dev/null +++ b/src/req_cookie.zig @@ -0,0 +1,34 @@ +const std = @import("std"); +const trim = std.mem.trim; +const split = std.mem.split; +const olderVersion: bool = @import("builtin").zig_version.minor < 11; + +pub const Cookie = struct { + name: []const u8, + value: []const u8, + + pub fn parse(item2: []const u8, allocator: std.mem.Allocator) ![]const Cookie { + var items = split(u8, item2, ";"); + var cookie_buffer = std.ArrayList(Cookie).init(allocator); + var cookie_string = split(u8, items.first(), "="); + const first_cookie = Cookie{ .name = trim(u8, cookie_string.first(), " "), .value = trim(u8, cookie_string.next().?, " ") }; + try cookie_buffer.append(first_cookie); + + while (items.next()) |item| { + cookie_string = split(u8, item, "="); + const name = trim(u8, cookie_string.first(), " "); + const value = if (cookie_string.next()) |v| trim(u8, v, " ") else ""; + const cookie = Cookie{ .name = name, .value = value }; + try cookie_buffer.append(cookie); + } + return if (olderVersion) cookie_buffer.toOwnedSlice() else try cookie_buffer.toOwnedSlice(); + } +}; + +test "Parse Request Cookie(s)" { + const allocator = std.testing.allocator; + const cookie_string = "test_cookie=successful; second_cookie: also successful!"; + const cookie = try Cookie.parse(cookie_string, allocator); + defer allocator.free(cookie); + try std.testing.expect(std.mem.eql(u8, cookie[0].value, "successful")); +} diff --git a/src/server.zig b/src/server.zig index 3041a13..b00c607 100644 --- a/src/server.zig +++ b/src/server.zig @@ -50,6 +50,7 @@ pub const Server = struct { defer allocator.free(req_stream); var req = try buildRequest(client_ip, req_stream, allocator); defer allocator.free(req.headers); + defer allocator.free(req.cookies); // if there ist a path set in the uri trim the trailing slash in order to accept it later during the matching check. if (req.uri.len > 1) req.uri = std.mem.trimRight(u8, req.uri, "/"); @@ -89,6 +90,7 @@ fn buildRequest(client_ip: []const u8, bytes: []const u8, allocator: std.mem.All const header = parts.first(); var header_lines = std.mem.split(u8, header, "\n"); var header_buffer = std.ArrayList(Header).init(allocator); + var cookie_buffer = std.ArrayList(Request.Cookie).init(allocator); var header_items = std.mem.split(u8, header_lines.first(), " "); req.method = Method.parse(header_items.first()); @@ -103,10 +105,19 @@ fn buildRequest(client_ip: []const u8, bytes: []const u8, allocator: std.mem.All while (header_lines.next()) |line| { var headers = std.mem.split(u8, line, ": "); const item1 = headers.first(); - const item2 = if (headers.next()) |value| value else unreachable; + // Check if header is a cookie and parse it + if (eql(u8, item1, "Cookie") or eql(u8, item1, "cookie")) { + const item2 = if (headers.next()) |value| value else ""; + const cookies = try Request.Cookie.parse(item2, allocator); + defer allocator.free(cookies); + try cookie_buffer.appendSlice(cookies); + continue; + } + const item2 = if (headers.next()) |value| value else ""; const header_pair = Header{ .key = item1, .value = item2 }; try header_buffer.append(header_pair); } + req.cookies = if (olderVersion) cookie_buffer.toOwnedSlice() else try cookie_buffer.toOwnedSlice(); req.headers = if (olderVersion) header_buffer.toOwnedSlice() else try header_buffer.toOwnedSlice(); req.body = if (parts.next()) |value| value else ""; return req; @@ -172,7 +183,6 @@ test "Run server" { // Function for test "Run Server" fn handlefn(_: *types.Request) types.Response { // create Response and add cookie to test cookie setting - const rc = @import("./res_cookie.zig"); - var res = types.Response{ .body = "

Run Server Test OK!

", .cookies = &[_]rc.Cookie{.{ .name = "Test-Cookie", .value = "Test", .domain = "localhost:8080" }} }; + var res = types.Response{ .body = "

Run Server Test OK!

", .cookies = &[_]Response.Cookie{.{ .name = "Test-Cookie", .value = "Test", .domain = "localhost:8080" }} }; return res; } diff --git a/src/types.zig b/src/types.zig index 1f3da07..6c15b97 100644 --- a/src/types.zig +++ b/src/types.zig @@ -4,6 +4,7 @@ const allocator = std.heap.page_allocator; const eql = std.mem.eql; const stat = @import("./status.zig"); const rescookie = @import("./res_cookie.zig"); +const reqcookie = @import("./req_cookie.zig"); /// Route is a touple that consists of the path and the function that shall handle it. /// e.g. `const rt = Route{"/home", home};` @@ -90,10 +91,14 @@ pub const Request = struct { ip: []const u8, /// Represents the request headers sent by the client headers: []const Header, + /// Request Cookies + cookies: []const Cookie, /// The Request URI uri: []const u8, /// Represents the request body sent by the client body: []const u8, + + pub const Cookie = reqcookie.Cookie; }; /// Represents a standard http-Response sent by the webapp (server).