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
|
const std = @import("std");
const log = std.log;
const net = std.Io.net;
const Io = std.Io;
const Request = std.http.Server.Request;
const MAX_BUF = 1024;
const Endpoint = enum {
root_get,
login_get,
create_account_get,
};
pub fn serve(io: std.Io, address: []const u8, port: u16) !void {
const addr = try net.IpAddress.parse(address, port);
var server = try addr.listen(io, .{ .reuse_address = true });
defer server.deinit(io);
log.info("Start HTTP server at {f}", .{addr});
while (true) {
const stream = server.accept(io) catch |err| {
log.err("failed to accept connection: {s}", .{@errorName(err)});
continue;
};
const thread = std.Thread.spawn(.{}, accept, .{ stream, io }) catch |err| {
log.err("unable to spawn connection thread: {s}", .{@errorName(err)});
stream.close(io);
continue;
};
thread.detach();
}
}
fn accept(stream: net.Stream, io: Io) !void {
defer stream.close(io);
log.info("Got new client!", .{});
var recv_buffer: [1024]u8 = undefined;
var send_buffer: [100]u8 = undefined;
var stream_reader = stream.reader(io, &recv_buffer);
var stream_writer = stream.writer(io, &send_buffer);
var server = std.http.Server.init(&stream_reader.interface, &stream_writer.interface);
while (server.reader.state == .ready) {
var request = server.receiveHead() catch |err| switch (err) {
error.HttpConnectionClosing => return,
else => return err,
};
try serveHTTP(&request);
}
}
fn endpointFromRequest(request: *Request) !Endpoint {
const head = request.head;
if (head.method == .GET and std.mem.eql(u8, head.target, "/")) {
return .root_get;
} else if (head.method == .GET and std.mem.eql(u8, head.target, "/login")) {
return .login_get;
} else if (head.method == .GET and std.mem.eql(u8, head.target, "/create-account")) {
return .create_account_get;
}
return error.InvalidEndpoint;
}
fn serveHTTP(request: *Request) !void {
log.info("Received request: {s}", .{request.head.target});
const endpoint = endpointFromRequest(request) catch {
try respondNotFound(request);
return;
};
switch (endpoint) {
.root_get => try handleRoot(request),
.login_get => try handleLogin(request),
.create_account_get => try handleCreateAccountGet(request),
}
}
fn respondNotFound(request: *Request) !void {
try request.respond("", .{ .status = .not_found });
}
fn handleRoot(request: *Request) !void {
try request.respond("Welcome to Coinvane!\n", .{});
}
fn handleLogin(request: *Request) !void {
try request.respond("Login page\n", .{});
}
fn handleCreateAccountGet(request: *Request) !void {
try request.respond(
\\<!DOCTYPE html>
\\<html>
\\ <head>
\\ <meta charset="utf-8">
\\ <meta name="viewport" content="width=device-width, inital-scale=1">
\\ <title>Coinvane</title>
\\ <style type="text/css">
\\ body{
\\ margin: 40px auto;
\\ max-width: 650px;
\\ line-height: 1.6;
\\ font-size: 18px;
\\ color: #444;
\\ padding: 0 10px
\\ }
\\ h1,h2,h3{
\\ line-height: 1.2
\\ }
\\ </style>
\\ </head>
\\ <body>
\\ <header>
\\ <h1>Create Account</h1>
\\ </header>
\\ <form action="/create-account" method="POST">
\\ <label for="email">Email:</label>
\\ <input type="email" id="email" name="email" required>
\\ <label for="password">Password:</label>
\\ <input type="password" id="password" name="password required>
\\ <input type="submit" value="Submit">
\\ </form>
\\ </body>
\\</html>
, .{});
}
|