-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathroot.zig
More file actions
148 lines (123 loc) · 4.66 KB
/
Copy pathroot.zig
File metadata and controls
148 lines (123 loc) · 4.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// DanzigGain Web Server - Proper HTTP server in Zig
// Single executable that serves UI and processes audio
const std = @import("std");
const danzig = @import("danzig");
const PORT = 3000;
pub fn main() !void {
var gpa = std.heap.DebugAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Load HTML at startup
var cwd = std.fs.cwd();
const html_content = cwd.readFileAlloc(allocator, "ui/index.html", 1024 * 1024) catch |err| {
std.debug.print("Error loading UI: {}\n", .{err});
std.debug.print("Make sure to run from danzig root directory\n", .{});
return err;
};
defer allocator.free(html_content);
const address = try std.net.Address.parseIp("127.0.0.1", PORT);
var tcp_server = try address.listen(.{
.reuse_address = true,
});
defer tcp_server.deinit();
std.debug.print("\n🎵 DanzigGain Web Server\n", .{});
std.debug.print("========================\n", .{});
std.debug.print("🌐 Open http://localhost:{d}\n", .{PORT});
std.debug.print("⏹️ Press Ctrl+C to stop\n\n", .{});
while (tcp_server.accept()) |connection| {
defer connection.stream.close();
handleConnection(connection.stream, html_content) catch {};
} else |err| {
std.debug.print("Server error: {}\n", .{err});
}
}
fn handleConnection(stream: std.net.Stream, html_content: []const u8) !void {
var buffer: [8192]u8 = undefined;
// A single read rather than readAll: readAll blocks until the buffer is
// full or the peer closes, which for a keep-alive HTTP client means
// hanging. It was also removed from net.Stream in Zig 0.15.
const bytes_read = try stream.read(&buffer);
if (bytes_read == 0) return;
const request = buffer[0..bytes_read];
if (std.mem.startsWith(u8, request, "GET")) {
var lines = std.mem.splitSequence(u8, request, "\r\n");
const first_line = lines.next() orelse return;
var parts = std.mem.splitSequence(u8, first_line, " ");
_ = parts.next(); // GET
const path = parts.next() orelse "/";
if (std.mem.eql(u8, path, "/") or std.mem.eql(u8, path, "")) {
try sendHtml(stream, html_content);
} else if (std.mem.startsWith(u8, path, "/api/")) {
try sendJson(stream);
} else {
try sendNotFound(stream);
}
} else if (std.mem.startsWith(u8, request, "POST")) {
var lines = std.mem.splitSequence(u8, request, "\r\n");
const first_line = lines.next() orelse return;
var parts = std.mem.splitSequence(u8, first_line, " ");
_ = parts.next(); // POST
const path = parts.next() orelse "/";
if (std.mem.eql(u8, path, "/api/process")) {
try handleAudioProcess(stream);
} else {
try sendNotFound(stream);
}
} else if (std.mem.startsWith(u8, request, "OPTIONS")) {
try sendCorsHeaders(stream);
} else {
try sendNotFound(stream);
}
}
fn handleAudioProcess(stream: std.net.Stream) !void {
const response =
"HTTP/1.1 200 OK\r\n" ++
"Content-Type: application/json\r\n" ++
"Content-Length: 33\r\n" ++
"Access-Control-Allow-Origin: *\r\n" ++
"\r\n" ++
"{\"status\":\"ok\",\"processed\":true}";
try stream.writeAll(response);
}
fn sendHtml(stream: std.net.Stream, html: []const u8) !void {
var buffer: [512]u8 = undefined;
const header = try std.fmt.bufPrint(&buffer,
"HTTP/1.1 200 OK\r\n" ++
"Content-Type: text/html; charset=utf-8\r\n" ++
"Content-Length: {d}\r\n" ++
"Access-Control-Allow-Origin: *\r\n" ++
"\r\n",
.{html.len}
);
try stream.writeAll(header);
try stream.writeAll(html);
}
fn sendJson(stream: std.net.Stream) !void {
const response =
"HTTP/1.1 200 OK\r\n" ++
"Content-Type: application/json\r\n" ++
"Content-Length: 20\r\n" ++
"Access-Control-Allow-Origin: *\r\n" ++
"\r\n" ++
"{\"status\":\"ok\"}";
try stream.writeAll(response);
}
fn sendCorsHeaders(stream: std.net.Stream) !void {
const response =
"HTTP/1.1 200 OK\r\n" ++
"Access-Control-Allow-Origin: *\r\n" ++
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n" ++
"Access-Control-Allow-Headers: Content-Type\r\n" ++
"Content-Length: 0\r\n" ++
"\r\n";
try stream.writeAll(response);
}
fn sendNotFound(stream: std.net.Stream) !void {
const response =
"HTTP/1.1 404 Not Found\r\n" ++
"Content-Type: text/plain\r\n" ++
"Content-Length: 9\r\n" ++
"\r\n" ++
"Not Found";
try stream.writeAll(response);
}