change many things an continue work
This commit is contained in:
parent
86a989220b
commit
2c83ea2a38
4 changed files with 174 additions and 52 deletions
72
build.zig
72
build.zig
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
42
src/main.zig
42
src/main.zig
|
@ -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>");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
10
src/zerve.zig
Normal 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;
|
Loading…
Add table
Reference in a new issue