Skip to content

Commit 7ee1ed1

Browse files
feat: add router (#42)
* feat: add router * lint: ignore server.zig * add router to readme * fix
1 parent 6947682 commit 7ee1ed1

8 files changed

Lines changed: 792 additions & 153 deletions

File tree

.github/workflows/ci.yml

Lines changed: 4 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,70 +16,23 @@ concurrency:
1616

1717
env:
1818
ZIG_VERSION: 0.16.0-dev.2905+5d71e3051
19-
ZLINT_VERSION: v0.7.9
2019
BORINGSSL_CACHE_VERSION: v1
2120

2221
defaults:
2322
run:
2423
shell: bash
2524

2625
jobs:
27-
fmt:
28-
name: Format
26+
lint:
2927
runs-on: ubuntu-latest
30-
timeout-minutes: 10
31-
32-
steps:
33-
- name: Checkout source
34-
uses: actions/checkout@v6
35-
with:
36-
persist-credentials: false
37-
38-
- name: Setup Zig
39-
uses: mlugg/setup-zig@v2
40-
with:
41-
version: ${{ env.ZIG_VERSION }}
42-
43-
- name: Check formatting
44-
run: zig build fmt-check
45-
46-
zlint:
47-
name: ZLint
48-
runs-on: ubuntu-latest
49-
timeout-minutes: 10
50-
5128
steps:
52-
- name: Checkout source
53-
uses: actions/checkout@v6
54-
with:
55-
persist-credentials: false
56-
29+
- uses: actions/checkout@v6
5730
- name: Setup Zig
5831
uses: mlugg/setup-zig@v2
5932
with:
6033
version: ${{ env.ZIG_VERSION }}
61-
62-
- name: Install zlint
63-
run: |
64-
case "$(uname -m)" in
65-
x86_64) target="linux-x86_64" ;;
66-
aarch64 | arm64) target="linux-aarch64" ;;
67-
*)
68-
echo "Unsupported zlint architecture: $(uname -m)" >&2
69-
exit 1
70-
;;
71-
esac
72-
73-
install_dir="${RUNNER_TEMP}/zlint-bin"
74-
mkdir -p "${install_dir}"
75-
curl --fail --location --silent --show-error --retry 3 --retry-delay 2 \
76-
--output "${install_dir}/zlint" \
77-
"https://github.com/DonIsaac/zlint/releases/download/${ZLINT_VERSION}/zlint-${target}"
78-
chmod +x "${install_dir}/zlint"
79-
echo "${install_dir}" >> "${GITHUB_PATH}"
80-
81-
- name: Run zlint
82-
run: zlint . --deny-warnings --format github
34+
- run: zig fmt --check src/*.zig
35+
- uses: DonIsaac/zlint-action@v0
8336

8437
boringssl_linux:
8538
name: Prepare BoringSSL (Linux)

README.md

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,34 @@ exe.root_module.addImport("http2", http2_dep.module("http2"));
6363
const std = @import("std");
6464
const http2 = @import("http2");
6565
66+
fn indexHandler(ctx: *const http2.Context) !http2.Response {
67+
return ctx.response.text(.ok, "hello from http2.zig\n");
68+
}
69+
70+
fn notFoundHandler(ctx: *const http2.Context) !http2.Response {
71+
return ctx.response.text(.not_found, "not found\n");
72+
}
73+
6674
pub fn main() !void {
67-
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
75+
var gpa: std.heap.DebugAllocator(.{}) = .init;
6876
defer _ = gpa.deinit();
6977
const allocator = gpa.allocator();
7078
7179
// Initialize the HTTP/2 system
7280
try http2.init(allocator);
7381
defer http2.deinit();
7482
83+
// Configure the request router
84+
var router = http2.Router.init(allocator);
85+
defer router.deinit();
86+
87+
try router.get("/", indexHandler);
88+
router.setFallback(notFoundHandler);
89+
7590
// Configure and create server
7691
const config = http2.Server.Config{
77-
.address = try std.net.Address.resolveIp("127.0.0.1", 3000),
78-
.max_connections = 1000,
92+
.address = try std.Io.net.IpAddress.parse("127.0.0.1", 3000),
93+
.router = &router,
7994
};
8095
8196
var server = try http2.Server.init(allocator, config);
@@ -99,7 +114,10 @@ TBD
99114
```zig
100115
pub const Server.Config = struct {
101116
/// Address to bind to
102-
address: std.net.Address,
117+
address: std.Io.net.IpAddress,
118+
119+
/// Request router for handling HTTP requests
120+
router: *Router,
103121
104122
/// Maximum concurrent connections (default: 1000)
105123
max_connections: u32 = 1000,
@@ -109,6 +127,35 @@ pub const Server.Config = struct {
109127
};
110128
```
111129

130+
### Router
131+
132+
The server expects a router in `Server.Config`, and requests are dispatched through it.
133+
134+
```zig
135+
try router.get("/", indexHandler);
136+
try router.post("/api/messages", createMessageHandler);
137+
try router.getPrefix("/assets", staticAssetsHandler);
138+
router.setFallback(notFoundHandler);
139+
```
140+
141+
Current routing behavior:
142+
143+
- `get`, `post`, `put`, `delete`, `head`, `options`, and `patch` register exact routes.
144+
- `getPrefix` and `postPrefix` register prefix routes.
145+
- Prefix routes are ordered by longest path first.
146+
- Prefix matching is segment-aware: `/api` matches `/api` and `/api/users`, but not `/apix`.
147+
- A matching path with the wrong method returns `405 Method Not Allowed`.
148+
- A missing path falls through to the fallback handler when configured; otherwise it returns `404 Not Found`.
149+
150+
The request context passed to handlers exposes:
151+
152+
- `ctx.method`
153+
- `ctx.path`
154+
- `ctx.query`
155+
- `ctx.headers`
156+
- `ctx.body`
157+
- `ctx.response`
158+
112159
### Server Methods
113160

114161
```zig

0 commit comments

Comments
 (0)