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");
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
42
src/main.zig
42
src/main.zig
|
@ -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!");
|
||||
}
|
||||
|
|
|
@ -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
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