change many things an continue work

This commit is contained in:
flopetautschnig 2023-04-21 16:13:20 +02:00 committed by GitHub
parent 86a989220b
commit 2c83ea2a38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 174 additions and 52 deletions

View file

@ -1,17 +1,67 @@
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
const lib = b.addStaticLibrary("zeam", "src/main.zig");
lib.setBuildMode(mode);
lib.install();
// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});
const main_tests = b.addTest("src/main.zig");
main_tests.setBuildMode(mode);
const exe = b.addExecutable(.{
.name = "zerve",
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&main_tests.step);
// This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default
// step when running `zig build`).
exe.install();
// This *creates* a RunStep in the build graph, to be executed when another
// step is evaluated that depends on it. The next line below will establish
// such a dependency.
const run_cmd = exe.run();
// By making the run step depend on the install step, it will be run from the
// installation directory rather than directly from within the cache directory.
// This is not necessary, however, if the application depends on other installed
// files, this ensures they will be present and in the expected location.
run_cmd.step.dependOn(b.getInstallStep());
// This allows the user to pass arguments to the application in the build
// command itself, like this: `zig build run -- arg1 arg2 etc`
if (b.args) |args| {
run_cmd.addArgs(args);
}
// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build run`
// This will evaluate the `run` step rather than the default, which is "install".
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
// Creates a step for unit testing.
const exe_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&exe_tests.step);
}

View file

@ -1,43 +1,13 @@
const std = @import("std");
const allocator = std.heap.page_allocator;
const zrv = @import("zerve.zig");
pub fn main() !void {
var rt = [_]zrv.Route{.{ "/", index }};
// Init server
const server_options: std.net.StreamServer.Options = .{};
var server = std.net.StreamServer.init(server_options);
defer server.deinit();
const addr = try std.net.Address.parseIp("0.0.0.0", 8080);
while (true) {
server.listen(addr) catch {
server.close();
continue;
};
break;
try zrv.Server.listen("0.0.0.0", 8080, &rt);
}
// Handling connections
while (true) {
const conn = if (server.accept()) |conn| conn else |_| continue;
defer conn.stream.close();
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
var chunk_buf: [4096]u8 = undefined;
// Collect max 4096 bytes of data from the stream into the chunk_buf. Then add it
// to the ArrayList. Repeat this until request stream ends by counting the appearence
// of "\r\n"
while (true) {
_ = try conn.stream.read(chunk_buf[0..]);
try buffer.appendSlice(chunk_buf[0..]);
if (std.mem.containsAtLeast(u8, buffer.items, 2, "\r\n")) break;
}
std.debug.print("Data sent by the client:\n{s}\n", .{buffer.items});
// Creating Response
_ = try conn.stream.write("HTTP/1.1 200 OK\r\n");
_ = try conn.stream.write("Content-Type: text/html\r\n\r\n");
_ = try conn.stream.write("<h1>It works!</h1>");
}
fn index(req: zrv.Request) zrv.Response {
_ = req;
return zrv.Response.write("hello!");
}

View file

@ -1,5 +1,6 @@
const std = @import("std");
const tuple = std.meta.Tuple;
const allocator = std.heap.page_allocator;
const stat = @import("./status.zig");
/// Route is a touple that consists of the path and the function that shall handle it.
@ -10,7 +11,10 @@ pub const Route = tuple(&.{ []const u8, *const fn (Request) Response });
/// A header of a `Request` or a `Response`.
/// It is usual that more than one is sent, so you can declare an array.
pub const Header = tuple(&.{ []const u8, []const u8 });
pub const Header = struct {
key: []const u8,
value: []const u8,
};
/// The HTTP Version.
pub const HTTP_Version = enum { HTTP1_1, HTTP2 };
@ -28,6 +32,27 @@ pub const Request = struct {
headers: []Header,
/// Represents the request body sent by the client
body: []u8,
fn build(bytes: []u8) Request {
const lines = std.mem.split(u8, bytes, "\n");
var req: Request = undefined;
var header_buffer = std.ArrayList(Header).init(allocator);
defer header_buffer.deinit();
for (lines, 0..) |line, index| {
if (index == 0) {
const items = std.mem.split(u8, line, " ");
req.method = items[0];
req.uri = items[1];
continue;
}
var item = std.mem.split(u8, line, ":");
const header = Header{ .key = std.mem.trim(u8, item[0], " "), .value = std.mem.trim(u8, item[1], " ") };
try header_buffer.append(header);
}
req.headers = header_buffer.items;
return req;
}
};
/// Represents a standard http-Response sent by the webapp (server).
@ -36,7 +61,74 @@ pub const Response = struct {
/// Response status, default is "200 OK"
status: stat.Status = stat.Status.OK,
/// Response eaders sent by the server
headers: []Header,
headers: []const Header = &[_]Header{.{ .key = "Content-Type", .value = "text/html; charset=utf-8" }},
/// Response body sent by the server
body: []u8,
body: []const u8 = "",
/// Write a simple response.
pub fn write(s: []const u8) Response {
return Response{ .body = s };
}
/// Send a response with json content.
pub fn json(j: []const u8) Response {
return Response{ .headers = &[_]Header{.{ .key = "Content-Type", .value = "application/json" }}, .body = j };
}
/// Send a response with status not found.
pub fn notfound(s: []const u8) Response {
return Response{ .status = stat.Status.NOT_FOUND, .body = s };
}
/// Send a response with status forbidden.
pub fn forbidden(s: []u8) Response {
return Response{ .status = stat.Status.FORBIDDEN, .body = s };
}
};
pub const Server = struct {
pub fn listen(ip: []const u8, port: u16, rt: []const Route) !void {
_ = rt;
// Init server
const server_options: std.net.StreamServer.Options = .{};
var server = std.net.StreamServer.init(server_options);
defer server.deinit();
const addr = try std.net.Address.parseIp(ip, port);
while (true) {
server.listen(addr) catch {
server.close();
continue;
};
break;
}
// Handling connections
while (true) {
const conn = if (server.accept()) |conn| conn else |_| continue;
defer conn.stream.close();
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
var chunk_buf: [4096]u8 = undefined;
// Collect max 4096 bytes of data from the stream into the chunk_buf. Then add it
// to the ArrayList. Repeat this until request stream ends by counting the appearence
// of "\r\n"
while (true) {
_ = try conn.stream.read(chunk_buf[0..]);
try buffer.appendSlice(chunk_buf[0..]);
if (std.mem.containsAtLeast(u8, buffer.items, 2, "\r\n")) break;
}
// Building the Request
// TODO write Request building!
// TODO: Write the loop to handle the requests!
// TODO: Create Response!
_ = try conn.stream.write("HTTP/1.1 200 OK\r\n");
_ = try conn.stream.write("Content-Type: text/html\r\n\r\n");
_ = try conn.stream.write("<h1>It works!</h1>");
}
}
};

10
src/zerve.zig Normal file
View file

@ -0,0 +1,10 @@
const types = @import("types.zig");
const status = @import("status.zig");
pub const Server = types.Server;
pub const Route = types.Route;
pub const Header = types.Header;
pub const Request = types.Request;
pub const Response = types.Response;
pub const Method = types.Method;
pub const HTTP_Version = types.HTTP_Version;