Skip to content

Commit

Permalink
chore(docs): prepare for v0.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
mookums committed Nov 10, 2024
1 parent cbc8bf7 commit 2e6327b
Show file tree
Hide file tree
Showing 24 changed files with 137 additions and 111 deletions.
26 changes: 12 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
## Installing
Latest Zig Stable: `0.13.0`

Latest zzz release: `0.1.0`
Latest zzz release: `0.2.0`
```
zig fetch --save git+https://github.com/mookums/zzz#v0.1.0
zig fetch --save git+https://github.com/mookums/zzz#v0.2.0
```

You can then add the dependency in your `build.zig` file:
Expand All @@ -21,14 +21,14 @@ exe.root_module.addImport(zzz);
```

## zzz?
zzz is a framework for writing performant and reliable networked services in Zig. It currently only supports TCP as the underlying transport layer and allows for any arbitrary protocol to run on top. It also natively supports TLS for securing connections.
zzz is a framework for writing performant and reliable networked services in Zig. It supports both HTTP and HTTPS (using BearSSL for TLS).

zzz currently supports Linux, Mac and Windows. Linux is currently the only target supported for deployments.
zzz currently supports Linux, Mac and Windows. Linux is currently the reccomended target for deployments.

> [!IMPORTANT]
> zzz is currently **alpha** software and there is still a lot changing at a fairly quick pace and certain places where things are less polished.
It focuses on modularity and portability, allowing you to swap in your own implementations for various things. Consumers can provide both a protocol and an async implementation, allowing for maximum flexibility. This allows for use in standard servers as well as embedded/bare metal domains.
It focuses on modularity and portability, allowing you to swap in your own implementations for various things. Consumers can provide an async implementation, allowing for maximum flexibility. This allows for use in standard servers as well as embedded/bare metal domains.

For more information, look here:
1. [Getting Started](./docs/getting_started.md)
Expand All @@ -51,20 +51,18 @@ With the recent migration to [tardy](https://github.com/mookums/tardy), zzz is a

On the CCX63 instance on Hetzner with 2000 max connections, we are 70.9% faster than [zap](https://github.com/zigzap/zap) and 83.8% faster than [http.zig](https://github.com/karlseguin/http.zig). We also utilize less memory, using only ~3% of the memory used by zap and ~1.6% of the memory used by http.zig.

zzz can be configured to utilize minimal memory while remaining performant. The provided `minram` example only uses 256 kB (using `io_uring` and musl)!
zzz can be configured to utilize minimal memory while remaining performant. The provided `minram` example only uses 256 kB!

## Features
- Built on top of [Tardy](https://github.com/mookums/tardy), an asynchronous runtime.
- [Modular Asynchronous Implementation](https://muki.gg/post/modular-async)
- `io_uring` for Linux (>= 5.1.0).
- `epoll` for Linux (>= 2.5.45).
- `kqueue` for BSD & Mac.
- `busy_loop` for Linux, Mac and Windows.
- Modular Protocol Implementation
- Allows for defining your own Protocol on top of TCP.
- Comes with:
- [HTTP/1.1](https://github.com/mookums/zzz/blob/main/src/http)
- HTTP/2 (planned)
- MQTT (planned)
- Single and Multi-threaded Support
- Single and Multithreaded Support
- TLS using BearSSL
- (Almost) all memory allocated at startup
- Memory Pooling for minimal allocations

## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in zzz by you, shall be licensed as MPL2.0, without any additional terms or conditions.
46 changes: 20 additions & 26 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ pub fn build(b: *std.Build) void {

zzz.linkLibrary(bearssl);

add_example(b, "basic", .http, false, target, optimize, zzz, tardy);
add_example(b, "sse", .http, false, target, optimize, zzz, tardy);
add_example(b, "custom", .http, false, target, optimize, zzz, tardy);
add_example(b, "tls", .http, true, target, optimize, zzz, tardy);
add_example(b, "minram", .http, false, target, optimize, zzz, tardy);
add_example(b, "fs", .http, false, target, optimize, zzz, tardy);
add_example(b, "multithread", .http, false, target, optimize, zzz, tardy);
add_example(b, "benchmark", .http, false, target, optimize, zzz, tardy);
add_example(b, "valgrind", .http, true, target, optimize, zzz, tardy);
add_example(b, "basic", false, target, optimize, zzz, tardy);
add_example(b, "sse", false, target, optimize, zzz, tardy);
add_example(b, "tls", true, target, optimize, zzz, tardy);
add_example(b, "minram", false, target, optimize, zzz, tardy);
add_example(b, "fs", false, target, optimize, zzz, tardy);
add_example(b, "multithread", false, target, optimize, zzz, tardy);
add_example(b, "benchmark", false, target, optimize, zzz, tardy);
add_example(b, "valgrind", true, target, optimize, zzz, tardy);

const tests = b.addTest(.{
.name = "tests",
Expand All @@ -51,23 +50,18 @@ pub fn build(b: *std.Build) void {
test_step.dependOn(&run_test.step);
}

const Protocol = enum {
http,
};

fn add_example(
b: *std.Build,
name: []const u8,
protocol: Protocol,
link_libc: bool,
target: std.Build.ResolvedTarget,
optimize: std.builtin.Mode,
zzz_module: *std.Build.Module,
tardy_module: *std.Build.Module,
) void {
const example = b.addExecutable(.{
.name = b.fmt("{s}_{s}", .{ @tagName(protocol), name }),
.root_source_file = b.path(b.fmt("./examples/{s}/{s}/main.zig", .{ @tagName(protocol), name })),
.name = name,
.root_source_file = b.path(b.fmt("./examples/{s}/main.zig", .{name})),
.target = target,
.optimize = optimize,
.strip = false,
Expand All @@ -79,17 +73,17 @@ fn add_example(

example.root_module.addImport("zzz", zzz_module);
example.root_module.addImport("tardy", tardy_module);

const install_artifact = b.addInstallArtifact(example, .{});
b.getInstallStep().dependOn(&install_artifact.step);

const run_cmd = b.addRunArtifact(example);
run_cmd.step.dependOn(&install_artifact.step);
if (b.args) |args| {
run_cmd.addArgs(args);
}
const build_step = b.step(b.fmt("{s}", .{name}), b.fmt("Build zzz example ({s})", .{name}));
build_step.dependOn(&install_artifact.step);

const run_artifact = b.addRunArtifact(example);
run_artifact.step.dependOn(&install_artifact.step);

const run_step = b.step(
b.fmt("run_{s}_{s}", .{ @tagName(protocol), name }),
b.fmt("Run {s} {s}", .{ @tagName(protocol), name }),
);
run_step.dependOn(&run_cmd.step);
const run_step = b.step(b.fmt("run_{s}", .{name}), b.fmt("Run zzz example ({s})", .{name}));
run_step.dependOn(&install_artifact.step);
run_step.dependOn(&run_artifact.step);
}
6 changes: 3 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
.{
.name = "zzz",
.version = "0.0.0",
.version = "0.2.0",
.minimum_zig_version = "0.13.0",
.dependencies = .{
.tardy = .{
.url = "git+https://github.com/mookums/tardy?ref=main#feae50e9bf60ac13f1d5d14a7d3346fcfe442fa8",
.hash = "122093168263d66adc14bbee5de6aa0d4a2600e7299cad2b66175feeb6ce8aaef173",
.url = "git+https://github.com/mookums/tardy?ref=v0.1.0#ae0970d6b3fa5b03625b14e142c664efe1fd7789",
.hash = "12207f5afee3b8933c1c32737e8feedc80a2e4feebe058739509094c812e4a8d2cc8",
},
.bearssl = .{
.url = "https://github.com/mookums/bearssl-zig/archive/37a96eee56fe2543579bbc6da148ca886f3dd32b.tar.gz",
Expand Down
65 changes: 49 additions & 16 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
# Getting Started
zzz is a networking framework that allows for modularity and flexibility in design. For most use cases, this flexibility is not a requirement and so various defaults are provided.

For this guide, we will assume that you are running on a modern Linux platform and looking to design a service that utilizes HTTP.
For this guide, we will assume that you are running on a modern Linux platform and looking to design a service that utilizes HTTP. We will need both `zzz` and `tardy` for this to work.
You will need to match the version of Tardy that zzz is currently using to the version of Tardy you currently use within your program. This will eventually be standardized.

`zig fetch --save git+https://github.com/mookums/zzz#main`
These are the current latest releases and are compatible.
`zig fetch --save git+https://github.com/mookums/zzz#v0.2.0`
`zig fetch --save git+https://github.com/mookums/tardy#v0.1.0`

## Hello, World!
We can write a quick example that serves out "Hello, World" responses to any client that connects to the server. This example is the same as the one that is provided within the `examples/basic` directory.

```zig
const std = @import("std");
const zzz = @import("zzz");
const http = zzz.HTTP;
const log = std.log.scoped(.@"examples/basic");
const tardy = @import("tardy");
const Tardy = tardy.Tardy(.auto);
const Runtime = tardy.Runtime;
const Server = http.Server(.plain);
const Router = Server.Router;
const Context = Server.Context;
const Route = Server.Route;
pub fn main() !void {
const host: []const u8 = "0.0.0.0";
Expand All @@ -22,36 +34,57 @@ pub fn main() !void {
const allocator = gpa.allocator();
defer _ = gpa.deinit();
var router = http.Router.init(allocator);
// Creating our Tardy instance that will spawn our runtimes.
var t = try Tardy.init(.{
.allocator = allocator,
.threading = .single,
});
defer t.deinit();
var router = Router.init(allocator);
defer router.deinit();
try router.serve_route("/", http.Route.init().get(struct {
pub fn handler_fn(_: http.Request, response: *http.Response, _: http.Context) void {
const body =
const num: i8 = 12;
try router.serve_route("/", Route.init().get(&num, struct {
pub fn handler_fn(ctx: *Context, id: *const i8) !void {
const body_fmt =
\\ <!DOCTYPE html>
\\ <html>
\\ <body>
\\ <h1>Hello, World!</h1>
\\ <p>id: {d}</p>
\\ </body>
\\ </html>
;
response.set(.{
const body = try std.fmt.allocPrint(ctx.allocator, body_fmt, .{id.*});
try ctx.respond(.{
.status = .OK,
.mime = http.Mime.HTML,
.body = body[0..],
});
}
}.handler_fn));
var server = http.Server(.plain, .auto).init(.{
.allocator = allocator,
.threading = .single,
});
defer server.deinit();
try server.bind(host, port);
try server.listen(.{ .router = &router });
// This provides the entry function into every Tardy runtime.
try t.entry(
&router,
struct {
fn entry(rt: *Runtime, r: *const Router) !void {
var server = Server.init(.{ .allocator = rt.allocator });
try server.bind(host, port);
try server.serve(r, rt);
}
}.entry,
{},
struct {
fn exit(rt: *Runtime, _: void) !void {
try Server.clean(rt);
}
}.exit,
);
}
```

Expand Down
76 changes: 41 additions & 35 deletions docs/https.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,40 @@ zzz utilizes [BearSSL](https://bearssl.org/) to provide a safe and performant TL
This is derived from the example at `examples/tls` and utilizes some certificates that are present within the repository.
```zig
const std = @import("std");
const zzz = @import("zzz");
const http = zzz.HTTP;
const log = std.log.scoped(.@"examples/tls");
const tardy = @import("tardy");
const Tardy = tardy.Tardy(.auto);
const Runtime = tardy.Runtime;
const Server = http.Server(.{ .tls = .{
.cert = .{ .file = .{ .path = "./examples/http/tls/certs/cert.pem" } },
.key = .{ .file = .{ .path = "./examples/http/tls/certs/key.pem" } },
.cert_name = "CERTIFICATE",
.key_name = "EC PRIVATE KEY",
} });
const Context = Server.Context;
const Route = Server.Route;
const Router = Server.Router;
pub fn main() !void {
const host: []const u8 = "0.0.0.0";
const port: u16 = 9862;
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var gpa = std.heap.GeneralPurposeAllocator(
.{ .thread_safe = true },
){ .backing_allocator = std.heap.c_allocator };
const allocator = gpa.allocator();
defer _ = gpa.deinit();
var router = http.Router.init(allocator);
var router = Router.init(allocator);
defer router.deinit();
try router.serve_route("/", http.Route.init().get(struct {
pub fn handler_fn(_: http.Request, response: *http.Response, _: http.Context) void {
try router.serve_route("/", Route.init().get({}, struct {
pub fn handler_fn(ctx: *Context, _: void) !void {
const body =
\\ <!DOCTYPE html>
\\ <html>
Expand All @@ -33,47 +50,36 @@ pub fn main() !void {
\\ </html>
;
response.set(.{
try ctx.respond(.{
.status = .OK,
.mime = http.Mime.HTML,
.body = body[0..],
});
}
}.handler_fn));
try router.serve_route("/kill", http.Route.init().get(struct {
pub fn handler_fn(_: http.Request, response: *http.Response, _: http.Context) void {
response.set(.{
.status = .Kill,
.mime = http.Mime.HTML,
.body = "",
});
}
}.handler_fn));
var server = http.Server(
.{
.tls = .{
.cert = .{
.file = .{ .path = "./examples/http/tls/certs/cert.pem" },
},
.key = .{
.file = .{ .path = "./examples/http/tls/certs/key.pem" },
},
.cert_name = "CERTIFICATE",
.key_name = "EC PRIVATE KEY",
},
},
.auto,
).init(.{
var t = try Tardy.init(.{
.allocator = allocator,
.threading = .single,
});
defer server.deinit();
defer t.deinit();
try server.bind(host, port);
try server.listen(.{ .router = &router });
}
try t.entry(
&router,
struct {
fn entry(rt: *Runtime, r: *const Router) !void {
var server = Server.init(.{ .allocator = rt.allocator });
try server.bind(host, port);
try server.serve(r, rt);
}
}.entry,
{},
struct {
fn exit(rt: *Runtime, _: void) !void {
try Server.clean(rt);
}
}.exit,
);
}
```
This example above passes the `.tls` variant of the enum to the HTTP Server and provides the location of the certificate and key to be used. It also has the functionality to pass in a buffer containing the cert and key data if that is preferable. You must also provide the certificate and key name as the PEM format allows for multiple items to be placed within the same file.
Expand Down
12 changes: 4 additions & 8 deletions docs/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@ zzz's design philosophy results in a lot of knobs that the consumer of the libra
These performance tips are general and can apply to any protocol implementation. HTTP is used as the general example because it is currently the only completed protocol.

## Performance Hunting
zzz now officially runs multithreaded by default. By default, it will utilize `@min(cpu_count / 2 - 1, 1)` threads. This can be tuned by using the `.threading` flag.
zzz now officially runs multithreaded by default. By default, it will utilize `@min(cpu_count / 2 - 1, 1)` threads. This can be tuned by changing the `.threading` option of the Tardy runtime.

```zig
var server = http.Server(.plain, .auto).init(.{
var t = try Tardy.init(.{
.allocator = allocator,
.threading = .{ .multi = COUNT },
.size_backlog = 32,
.size_connections_max = 16,
.size_connection_arena_retain = 64,
.size_socket_buffer = 512,
.threading = .{ .multi = COUNT},
});
```

Expand All @@ -30,7 +26,7 @@ Other settings of note include:
When using zzz in certain environments, your goal may be to reduce memory usage. zzz provides a variety of controls for handling how much memory is allocated at start up.

```zig
var server = http.Server(.plain, .auto).init(.{
var server = Server.init(.{
.allocator = allocator,
.size_backlog = 32,
.size_connections_max = 16,
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 2e6327b

Please sign in to comment.