Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
lib/
zig-out/
.zig-cache/
book_titles.txt
tests/db*
zig-pkg
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ zig ?= zig
.PHONY: t
t:
TEST_FILTER='${F}' '${zig}' build test -Dtsan=true -Dforce_blocking=false -freference-trace --summary all
TEST_FILTER='${F}' '${zig}' build test -Dtsan=true -Dforce_blocking=true -freference-trace --summary all
TEST_FILTER='${F}' '${zig}' build test -Dforce_blocking=true -freference-trace --summary all

.PHONY: tn
tn:
Expand Down
22 changes: 10 additions & 12 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ pub fn build(b: *std.Build) !void {

const dep_opts = .{ .target = target, .optimize = optimize };
const metrics_module = b.dependency("metrics", dep_opts).module("metrics");
const websocket_module = b.dependency("websocket", dep_opts).module("websocket");
// const websocket_module = b.dependency("websocket", dep_opts).module("websocket");

const enable_tsan = b.option(bool, "tsan", "Enable ThreadSanitizer");

const httpz_module = b.addModule("httpz", .{
.link_libc = true,
.root_source_file = b.path("src/httpz.zig"),
.target = target,
.optimize = optimize,
.sanitize_thread = enable_tsan,
.imports = &.{
.{ .name = "metrics", .module = metrics_module },
.{ .name = "websocket", .module = websocket_module },
// .{ .name = "websocket", .module = websocket_module },
},
});
{
Expand All @@ -33,21 +34,20 @@ pub fn build(b: *std.Build) !void {
.filters = test_filter orelse &.{},
.test_runner = .{ .path = b.path("test_runner.zig"), .mode = .simple },
});
tests.linkLibC();
const force_blocking = b.option(bool, "force_blocking", "Force blocking mode") orelse false;
{
const options = b.addOptions();
options.addOption(bool, "httpz_blocking", force_blocking);
tests.root_module.addOptions("build", options);
}
{
const options = b.addOptions();
options.addOption(bool, "websocket_blocking", force_blocking);
websocket_module.addOptions("build", options);
// const options = b.addOptions();
// options.addOption(bool, "websocket_blocking", force_blocking);
// websocket_module.addOptions("build", options);
}

tests.root_module.addImport("metrics", metrics_module);
tests.root_module.addImport("websocket", websocket_module);
// tests.root_module.addImport("websocket", websocket_module);
const run_test = b.addRunArtifact(tests);
run_test.has_side_effects = true;

Expand All @@ -67,8 +67,9 @@ pub fn build(b: *std.Build) !void {
.{ .file = "examples/05_request_takeover.zig", .name = "example_5" },
.{ .file = "examples/06_middleware.zig", .name = "example_6" },
.{ .file = "examples/07_advanced_routing.zig", .name = "example_7" },
.{ .file = "examples/08_websocket.zig", .name = "example_8" },
.{ .file = "examples/09_shutdown.zig", .name = "example_9", .libc = true },
// @ZIG016
// .{ .file = "examples/08_websocket.zig", .name = "example_8" },
// .{ .file = "examples/09_shutdown.zig", .name = "example_9", .libc = true },
.{ .file = "examples/10_file_upload.zig", .name = "example_10" },
.{ .file = "examples/11_html_streaming.zig", .name = "example_11" },
};
Expand All @@ -86,9 +87,6 @@ pub fn build(b: *std.Build) !void {
},
}),
});
if (ex.libc) {
exe.linkLibC();
}
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
Expand Down
12 changes: 6 additions & 6 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
.fingerprint = 0x472add02ac73d53c,
.dependencies = .{
.metrics = .{
.url = "https://github.com/karlseguin/metrics.zig/archive/603954879849c331a26529b88254770089acac8b.tar.gz",
.hash = "metrics-0.0.0-W7G4eP2_AQAdJGKMonHeZFaY4oU4ZXPFFTqFCFXItX3O",
},
.websocket = .{
.url = "https://github.com/karlseguin/websocket.zig/archive/4deaaef2b4475a63f19c5e2f43e38fd55464b118.tar.gz",
.hash = "websocket-0.1.0-ZPISdZJxAwAt6Ys_JpoHQQV3NpWCof_N9Jg-Ul2g7OoV",
.url = "https://github.com/karlseguin/metrics.zig/archive/6de29b83a750a06c438d268543e0e3c3c1b309da.tar.gz",
.hash = "metrics-0.0.0-W7G4eIegAQD4XxA9Co7Atbw59u_2zvxYf406AZuoAHPM",
},
// .websocket = .{
// .url = "https://github.com/karlseguin/websocket.zig/archive/4deaaef2b4475a63f19c5e2f43e38fd55464b118.tar.gz",
// .hash = "websocket-0.1.0-ZPISdZJxAwAt6Ys_JpoHQQV3NpWCof_N9Jg-Ul2g7OoV",
// },
// .websocket = .{ .path = "../websocket.zig" },
},
}
7 changes: 3 additions & 4 deletions examples/01_basic.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ const PORT = 8801;
// This example demonstrates basic httpz usage, with focus on using the
// httpz.Request and httpz.Response objects.

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

// We pass a "void" handler. This is the simplest, but limits what we can do
// The last parameter is an instance of our handler. Since we have
// a void handler, we pass a void value: i.e. {}.
var server = try httpz.Server(void).init(allocator, .{
var server = try httpz.Server(void).init(init.io, allocator, .{
.address = .localhost(PORT),
.request = .{
// httpz has a number of tweakable configuration settings (see readme)
Expand Down
7 changes: 3 additions & 4 deletions examples/02_handler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ const PORT = 8802;
// including things such as a DB pool) and how to define not found and error
// handlers.

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

// We specify our "Handler" and, as the last parameter to init, pass an
// instance of it.
var handler = Handler{};
var server = try httpz.Server(*Handler).init(allocator, .{ .address = .localhost(PORT) }, &handler);
var server = try httpz.Server(*Handler).init(init.io, allocator, .{ .address = .localhost(PORT) }, &handler);

defer server.deinit();

Expand Down
20 changes: 13 additions & 7 deletions examples/03_dispatch.zig
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
const std = @import("std");
const httpz = @import("httpz");

const Io = std.Io;
const Allocator = std.mem.Allocator;

const PORT = 8803;

// This example uses a custom dispatch method on our handler for greater control
// in how actions are executed.

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

var handler = Handler{};
var server = try httpz.Server(*Handler).init(allocator, .{ .address = .localhost(PORT) }, &handler);
var handler = Handler{
.io = init.io,
};
var server = try httpz.Server(*Handler).init(init.io, allocator, .{ .address = .localhost(PORT) }, &handler);

defer server.deinit();

Expand All @@ -30,19 +33,22 @@ pub fn main() !void {
}

const Handler = struct {
io: Io,

// In addition to the special "notFound" and "uncaughtError" shown in example 2
// the special "dispatch" method can be used to gain more control over request handling.
pub fn dispatch(self: *Handler, action: httpz.Action(*Handler), req: *httpz.Request, res: *httpz.Response) !void {
// Our custom dispatch lets us add a log + timing for every request
// httpz supports middlewares, but in many cases, having a dispatch is good
// enough and is much more straightforward.

var start = try std.time.Timer.start();
var start = Io.Timestamp.now(self.io, .awake);
// We don't _have_ to call the action if we don't want to. For example
// we could do authentication and set the response directly on error.
try action(self, req, res);

std.debug.print("ts={d} us={d} path={s}\n", .{ std.time.timestamp(), start.lap() / 1000, req.url.path });
const elapsed = start.untilNow(self.io, .awake);
std.debug.print("ts={d} us={d} path={s}\n", .{ start.toSeconds(), elapsed.toMicroseconds(), req.url.path });
}
};

Expand Down
7 changes: 3 additions & 4 deletions examples/04_action_context.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ const PORT = 8804;
// This example is very similar to 03_dispatch.zig, but shows how the action
// state can be a different type than the handler.

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

var handler = Handler{};
var server = try httpz.Server(*Handler).init(allocator, .{ .address = .localhost(PORT) }, &handler);
var server = try httpz.Server(*Handler).init(init.io, allocator, .{ .address = .localhost(PORT) }, &handler);

defer server.deinit();

Expand Down
7 changes: 3 additions & 4 deletions examples/05_request_takeover.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ const PORT = 8805;
// This example uses the Handler's "handle" function to completely takeover
// request processing from httpz.

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

var handler = Handler{};
var server = try httpz.Server(*Handler).init(allocator, .{ .address = .localhost(PORT) }, &handler);
var server = try httpz.Server(*Handler).init(init.io, allocator, .{ .address = .localhost(PORT) }, &handler);

defer server.deinit();

Expand Down
9 changes: 4 additions & 5 deletions examples/06_middleware.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ const PORT = 8806;

// See middleware/Logger.zig for an example of how to write a middleware

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

var server = try httpz.Server(void).init(allocator, .{ .address = .localhost(PORT) }, {});
var server = try httpz.Server(void).init(init.io, allocator, .{ .address = .localhost(PORT) }, {});

defer server.deinit();

Expand All @@ -27,7 +26,7 @@ pub fn main() !void {

// creates an instance of the middleware with the given configuration
// see example/middleware/Logger.zig
const logger = try server.middleware(Logger, .{ .query = true });
const logger = try server.middleware(Logger, .{ .io = init.io, .query = true });

var router = try server.router(.{});

Expand Down
13 changes: 8 additions & 5 deletions examples/07_advanced_routing.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const std = @import("std");
const httpz = @import("httpz");

const Io = std.Io;
const Allocator = std.mem.Allocator;

const PORT = 8807;
Expand All @@ -9,19 +10,20 @@ const PORT = 8807;
// and route configuration. (The previous example, with middleware, also showed
// per-route configuration for middleware specifically).

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

var default_handler = Handler{
.io = init.io,
.log = true,
};

var nolog_handler = Handler{
.io = init.io,
.log = false,
};

var server = try httpz.Server(*Handler).init(allocator, .{ .address = .localhost(PORT) }, &default_handler);
var server = try httpz.Server(*Handler).init(init.io, allocator, .{ .address = .localhost(PORT) }, &default_handler);

defer server.deinit();

Expand All @@ -48,6 +50,7 @@ pub fn main() !void {
}

const Handler = struct {
io: Io,
log: bool,

// special dispatch set in the info route
Expand All @@ -58,7 +61,7 @@ const Handler = struct {
pub fn dispatch(h: *Handler, action: httpz.Action(*Handler), req: *httpz.Request, res: *httpz.Response) !void {
try action(h, req, res);
if (h.log) {
std.debug.print("ts={d} path={s} status={d}\n", .{ std.time.timestamp(), req.url.path, res.status });
std.debug.print("ts={d} path={s} status={d}\n", .{ Io.Timestamp.now(h.io, .real), req.url.path, res.status });
}
}
};
Expand Down
7 changes: 3 additions & 4 deletions examples/08_websocket.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ pub const std_options = std.Options{ .log_scope_levels = &[_]std.log.ScopeLevel{
} };

// This example show how to upgrade a request to websocket.
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

// For websocket support, you _must_ define a Handler, and your Handler _must_
// have a WebsocketHandler declaration
var server = try httpz.Server(Handler).init(allocator, .{ .address = .localhost(PORT) }, Handler{});
var server = try httpz.Server(Handler).init(init.io, allocator, .{ .address = .localhost(PORT) }, Handler{});

defer server.deinit();

Expand Down
8 changes: 3 additions & 5 deletions examples/09_shutdown.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ const PORT = 8809;

var server_instance: ?*httpz.Server(void) = null;

pub fn main() !void {
pub fn main(init: std.process.Init) !void {
if (comptime @import("builtin").os.tag == .windows) {
std.debug.print("This example does not run on Windows. Sorry\n", .{});
return error.PlatformNotSupported;
}

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const allocator = init.gpa;

// call our shutdown function (below) when
// SIGINT or SIGTERM are received
Expand All @@ -31,7 +29,7 @@ pub fn main() !void {
.flags = 0,
}, null);

var server = try httpz.Server(void).init(allocator, .{ .address = .localhost(PORT) }, {});
var server = try httpz.Server(void).init(init.io, allocator, .{ .address = .localhost(PORT) }, {});
defer server.deinit();

var router = try server.router(.{});
Expand Down
7 changes: 3 additions & 4 deletions examples/10_file_upload.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ const PORT = 8810;
// 3. Save uploaded files to disk
// 4. Handle both file and regular form fields

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

var server = try httpz.Server(void).init(allocator, .{
var server = try httpz.Server(void).init(init.io, allocator, .{
.address = .localhost(PORT),
.request = .{
// Configure the maximum number of multipart form fields
Expand Down
16 changes: 8 additions & 8 deletions examples/11_html_streaming.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ const Allocator = std.mem.Allocator;
const PORT = 8801;

/// This example demonstrates HTML streaming.
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

var server = try httpz.Server(void).init(allocator, .{
var server = try httpz.Server(void).init(init.io, allocator, .{
.address = .localhost(PORT),
}, {});
defer server.deinit();
Expand All @@ -29,7 +28,6 @@ pub fn main() !void {
}

fn index(_: *httpz.Request, res: *httpz.Response) !void {
const wait_time = 1_000_000_000; // 1 second

try res.chunk(
\\<!DOCTYPE html>
Expand All @@ -45,10 +43,12 @@ fn index(_: *httpz.Request, res: *httpz.Response) !void {
\\ </body>
\\</html>
);
std.Thread.sleep(wait_time);

const io = res.conn.io;
try io.sleep(.fromSeconds(1), .awake);
try res.chunk("\n<span slot='item-2'>Item 2</span>");
std.Thread.sleep(wait_time);
try io.sleep(.fromSeconds(1), .awake);
try res.chunk("\n<span slot='item-0'>Item 0</span>");
std.Thread.sleep(wait_time);
try io.sleep(.fromSeconds(1), .awake);
try res.chunk("\n<span slot='item-1'>Item 1</span>");
}
Loading