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"); const std = @import("std");
pub fn build(b: *std.build.Builder) void { // Although this function looks imperative, note that its job is to
// Standard release options allow the person running `zig build` to select // declaratively construct a build graph that will be executed by an external
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. // runner.
const mode = b.standardReleaseOptions(); 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"); // Standard optimization options allow the person running `zig build` to select
lib.setBuildMode(mode); // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
lib.install(); // 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"); const exe = b.addExecutable(.{
main_tests.setBuildMode(mode); .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"); // This declares intent for the executable to be installed into the
test_step.dependOn(&main_tests.step); // 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 std = @import("std");
const allocator = std.heap.page_allocator; const zrv = @import("zerve.zig");
pub fn main() !void { pub fn main() !void {
var rt = [_]zrv.Route{.{ "/", index }};
// Init server try zrv.Server.listen("0.0.0.0", 8080, &rt);
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;
} }
// Handling connections fn index(req: zrv.Request) zrv.Response {
while (true) { _ = req;
const conn = if (server.accept()) |conn| conn else |_| continue; return zrv.Response.write("hello!");
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>");
}
} }

View file

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const tuple = std.meta.Tuple; const tuple = std.meta.Tuple;
const allocator = std.heap.page_allocator;
const stat = @import("./status.zig"); const stat = @import("./status.zig");
/// Route is a touple that consists of the path and the function that shall handle it. /// 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`. /// A header of a `Request` or a `Response`.
/// It is usual that more than one is sent, so you can declare an array. /// 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. /// The HTTP Version.
pub const HTTP_Version = enum { HTTP1_1, HTTP2 }; pub const HTTP_Version = enum { HTTP1_1, HTTP2 };
@ -28,6 +32,27 @@ pub const Request = struct {
headers: []Header, headers: []Header,
/// Represents the request body sent by the client /// Represents the request body sent by the client
body: []u8, 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). /// Represents a standard http-Response sent by the webapp (server).
@ -36,7 +61,74 @@ pub const Response = struct {
/// Response status, default is "200 OK" /// Response status, default is "200 OK"
status: stat.Status = stat.Status.OK, status: stat.Status = stat.Status.OK,
/// Response eaders sent by the server /// 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 /// 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;