Rely on zig 0.10.1
This commit is contained in:
parent
a3a1718341
commit
32c1f95938
3 changed files with 73 additions and 70 deletions
|
@ -1,6 +1,8 @@
|
|||
# zerve
|
||||
A simple framework for writing web services in zig.
|
||||
|
||||
## Relies on zig 0.10.1 !
|
||||
|
||||
|
||||
## Create a simple web app
|
||||
|
||||
|
@ -76,15 +78,10 @@ pub const Request = struct {
|
|||
httpVersion: HTTP_Version,
|
||||
/// Represents the request headers sent by the client
|
||||
headers: []const Header,
|
||||
/// Teh Request URI
|
||||
/// The Request URI
|
||||
uri: []const u8,
|
||||
/// Represents the request body sent by the client
|
||||
body: []const u8,
|
||||
|
||||
/// Builds a `Request` struct from `[]u8`
|
||||
pub fn build(bytes: []const u8) !Request {...}
|
||||
|
||||
test "build a Request" {...}
|
||||
};
|
||||
```
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@ const types = @import("types.zig");
|
|||
const Route = types.Route;
|
||||
const Request = types.Request;
|
||||
const Response = types.Response;
|
||||
const Header = types.Header;
|
||||
const Method = types.Method;
|
||||
const HTTP_Version = types.HTTP_Version;
|
||||
|
||||
/// Server is a namespace to configure IP and Port the app will listen to, as well as
|
||||
/// the routing paths (`[]Route`) it shall handle.
|
||||
|
@ -43,8 +46,8 @@ pub const Server = struct {
|
|||
if (std.mem.containsAtLeast(u8, buffer.items, 2, "\r\n")) break;
|
||||
}
|
||||
// Build the Request
|
||||
const req_stream = try buffer.toOwnedSlice();
|
||||
var req = try Request.build(req_stream);
|
||||
const req_stream = buffer.toOwnedSlice();
|
||||
var req = try buildRequest(req_stream, allocator);
|
||||
|
||||
// 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, "/");
|
||||
|
@ -67,14 +70,58 @@ pub const Server = struct {
|
|||
}
|
||||
}
|
||||
// Stringify the Response and send it to the client.
|
||||
const response_string = try stringify(res, allocator);
|
||||
_ = try conn.stream.writeAll(response_string);
|
||||
const response_string = try stringifyResponse(res, allocator);
|
||||
_ = try conn.stream.write(response_string);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Function that build the request from stream
|
||||
fn buildRequest(bytes: []const u8, allocator: std.mem.Allocator) !Request {
|
||||
var req: Request = undefined;
|
||||
var parts = std.mem.split(u8, bytes, "\r\n");
|
||||
const header = parts.first();
|
||||
var header_lines = std.mem.split(u8, header, "\n");
|
||||
var header_buffer = std.ArrayList(Header).init(allocator);
|
||||
|
||||
var header_items = std.mem.split(u8, header_lines.first(), " ");
|
||||
req.method = Method.parse(header_items.first());
|
||||
req.uri = if (header_items.next()) |value| value else "";
|
||||
|
||||
if (header_items.next()) |value| {
|
||||
req.httpVersion = HTTP_Version.parse(value);
|
||||
} else {
|
||||
req.httpVersion = HTTP_Version.HTTP1_1;
|
||||
}
|
||||
|
||||
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;
|
||||
const header_pair = Header{ .key = item1, .value = item2 };
|
||||
try header_buffer.append(header_pair);
|
||||
}
|
||||
req.headers = header_buffer.toOwnedSlice();
|
||||
req.body = if (parts.next()) |value| value else "";
|
||||
return req;
|
||||
}
|
||||
|
||||
// Test the Request build function
|
||||
test "build a Request" {
|
||||
const bytes = "GET /test HTTP/1.1\nHost: localhost:8080\nUser-Agent: Testbot\r\nThis is the test body!";
|
||||
const req = try buildRequest(bytes);
|
||||
try std.testing.expect(req.method == Method.GET);
|
||||
try std.testing.expect(req.httpVersion == HTTP_Version.HTTP1_1);
|
||||
try std.testing.expect(std.mem.eql(u8, req.uri, "/test"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.headers[1].key, "User-Agent"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.headers[1].value, "Testbot"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.headers[0].key, "Host"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.headers[0].value, "localhost:8080"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.body, "This is the test body!"));
|
||||
}
|
||||
|
||||
// Function that turns Response into a string
|
||||
fn stringify(r: Response, allocator: std.mem.Allocator) ![]const u8 {
|
||||
fn stringifyResponse(r: Response, allocator: std.mem.Allocator) ![]const u8 {
|
||||
var res = std.ArrayList(u8).init(allocator);
|
||||
try res.appendSlice(r.httpVersion.stringify());
|
||||
try res.append(' ');
|
||||
|
@ -88,7 +135,7 @@ fn stringify(r: Response, allocator: std.mem.Allocator) ![]const u8 {
|
|||
try res.appendSlice("\r\n\r\n");
|
||||
try res.appendSlice(r.body);
|
||||
|
||||
return try res.toOwnedSlice();
|
||||
return res.toOwnedSlice();
|
||||
}
|
||||
|
||||
test "stringify Response" {
|
||||
|
@ -96,5 +143,5 @@ test "stringify Response" {
|
|||
const headers = [_]types.Header{.{ .key = "User-Agent", .value = "Testbot" }};
|
||||
const res = Response{ .headers = &headers, .body = "This is the body!" };
|
||||
|
||||
try std.testing.expect(eql(u8, try stringify(res, allocator), "HTTP/1.1 200 OK\r\nUser-Agent: Testbot\n\r\n\r\nThis is the body!"));
|
||||
try std.testing.expect(eql(u8, try stringifyResponse(res, allocator), "HTTP/1.1 200 OK\r\nUser-Agent: Testbot\n\r\n\r\nThis is the body!"));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const std = @import("std");
|
||||
const tuple = std.meta.Tuple;
|
||||
const allocator = std.heap.page_allocator;
|
||||
const eql = std.mem.eql;
|
||||
const stat = @import("./status.zig");
|
||||
|
||||
/// Route is a touple that consists of the path and the function that shall handle it.
|
||||
|
@ -21,7 +22,7 @@ pub const Header = struct {
|
|||
string.appendSlice(header.key) catch unreachable;
|
||||
string.appendSlice(": ") catch unreachable;
|
||||
string.appendSlice(header.value) catch unreachable;
|
||||
const out = string.toOwnedSlice() catch "";
|
||||
const out = string.toOwnedSlice();
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -63,16 +64,17 @@ pub const Method = enum {
|
|||
PATCH,
|
||||
UNKNOWN,
|
||||
|
||||
fn parse(value: []const u8) Method {
|
||||
if (std.mem.containsAtLeast(u8, value, 1, "GET")) return Method.GET;
|
||||
if (std.mem.containsAtLeast(u8, value, 1, "POST")) return Method.POST;
|
||||
if (std.mem.containsAtLeast(u8, value, 1, "PUT")) return Method.PUT;
|
||||
if (std.mem.containsAtLeast(u8, value, 1, "HEAD")) return Method.HEAD;
|
||||
if (std.mem.containsAtLeast(u8, value, 1, "DELETE")) return Method.DELETE;
|
||||
if (std.mem.containsAtLeast(u8, value, 1, "CONNECT")) return Method.CONNECT;
|
||||
if (std.mem.containsAtLeast(u8, value, 1, "OPTIONS")) return Method.OPTIONS;
|
||||
if (std.mem.containsAtLeast(u8, value, 1, "TRACE")) return Method.TRACE;
|
||||
if (std.mem.containsAtLeast(u8, value, 1, "PATCH")) return Method.PATCH;
|
||||
/// Parses the Method from a string
|
||||
pub fn parse(value: []const u8) Method {
|
||||
if (eql(u8, value, "GET") or eql(u8, value, "get")) return Method.GET;
|
||||
if (eql(u8, value, "POST") or eql(u8, value, "post")) return Method.POST;
|
||||
if (eql(u8, value, "PUT") or eql(u8, value, "put")) return Method.PUT;
|
||||
if (eql(u8, value, "HEAD") or eql(u8, value, "head")) return Method.HEAD;
|
||||
if (eql(u8, value, "DELETE") or eql(u8, value, "delete")) return Method.DELETE;
|
||||
if (eql(u8, value, "CONNECT") or eql(u8, value, "connect")) return Method.CONNECT;
|
||||
if (eql(u8, value, "OPTIONS") or eql(u8, value, "options")) return Method.OPTIONS;
|
||||
if (eql(u8, value, "TRACE") or eql(u8, value, "trace")) return Method.TRACE;
|
||||
if (eql(u8, value, "PATCH") or eql(u8, value, "patch")) return Method.PATCH;
|
||||
return Method.UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -101,53 +103,10 @@ pub const Request = struct {
|
|||
httpVersion: HTTP_Version,
|
||||
/// Represents the request headers sent by the client
|
||||
headers: []const Header,
|
||||
/// Teh Request URI
|
||||
/// The Request URI
|
||||
uri: []const u8,
|
||||
/// Represents the request body sent by the client
|
||||
body: []const u8,
|
||||
|
||||
pub fn build(bytes: []const u8) !Request {
|
||||
var req: Request = undefined;
|
||||
var parts = std.mem.split(u8, bytes, "\r\n");
|
||||
const header = parts.first();
|
||||
var header_lines = std.mem.split(u8, header, "\n");
|
||||
var header_buffer = std.ArrayList(Header).init(allocator);
|
||||
|
||||
var header_items = std.mem.split(u8, header_lines.first(), " ");
|
||||
req.method = Method.parse(header_items.first());
|
||||
req.uri = if (header_items.next()) |value| value else "";
|
||||
|
||||
if (header_items.next()) |value| {
|
||||
req.httpVersion = HTTP_Version.parse(value);
|
||||
} else {
|
||||
req.httpVersion = HTTP_Version.HTTP1_1;
|
||||
}
|
||||
|
||||
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;
|
||||
const header_pair = Header{ .key = item1, .value = item2 };
|
||||
try header_buffer.append(header_pair);
|
||||
}
|
||||
req.headers = header_buffer.toOwnedSlice() catch &[_]Header{Header{ .key = "", .value = "" }};
|
||||
req.body = if (parts.next()) |value| value else "";
|
||||
return req;
|
||||
}
|
||||
|
||||
// Test the Request build function
|
||||
test "build a Request" {
|
||||
const bytes = "GET /test HTTP/1.1\nHost: localhost:8080\nUser-Agent: Testbot\r\nThis is the test body!";
|
||||
const req = try Request.build(bytes);
|
||||
try std.testing.expect(req.method == Method.GET);
|
||||
try std.testing.expect(req.httpVersion == HTTP_Version.HTTP1_1);
|
||||
try std.testing.expect(std.mem.eql(u8, req.uri, "/test"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.headers[1].key, "User-Agent"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.headers[1].value, "Testbot"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.headers[0].key, "Host"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.headers[0].value, "localhost:8080"));
|
||||
try std.testing.expect(std.mem.eql(u8, req.body, "This is the test body!"));
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents a standard http-Response sent by the webapp (server).
|
||||
|
@ -162,7 +121,7 @@ pub const Response = struct {
|
|||
body: []const u8 = "",
|
||||
|
||||
/// Write a simple response.
|
||||
pub fn new(s: []const u8) Response {
|
||||
pub fn write(s: []const u8) Response {
|
||||
return Response{ .body = s };
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue