summaryrefslogtreecommitdiff
path: root/lib/http/server.myr
blob: 8b221bdd61bfcab1e5398c8c67d722dfaf34d2e6 (plain)
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
use bio
use std
use thread

use "types"
use "session"
use "parse"

pkg http =
	const announce	: (ds : byte[:] -> std.result(server#, err))
	const shutdown	: (srv : server# -> void)
	const serve	: (srv : server#, fn : (srv : server#, s : session#, req : req# -> void) -> void)
	const respond	: (srv : server#, sess : session#, resp : resp# -> void)
;;

const announce = {ds
	var afd

	match std.announce(ds)
	| `std.Ok f:	afd = f
	| `std.Err e:	-> `std.Err `Econn
	;;

	match std.listen(afd)
	| `std.Err e:	-> `std.Err `Econn
	| `std.Ok lfd:
		std.close(afd)
		-> `std.Ok std.mk([
			.refs=1,
			.lfd=lfd,
			.quit=false
		])
	;;
}

const serve = {srv, fn
	while !srv.quit
		match waitconn(srv)
		| `std.Ok fd:
			ref(srv)
			thread.spawn({;communicate(srv, fd, fn)})
		| `std.Err e:	/* eh? */
		;;
	;;
	unref(srv)
}

const communicate = {srv, fd, fn
	var s

	s = mksrvsession(fd)
	while !srv.quit
		match parsereq(s)
		| `std.Ok req:	fn(srv, s, req)
		| `std.Err e:	break
		;;
	;;
	std.close(fd)
	unref(srv)
}

const respond = {srv, s, resp
	var sb

	sb = std.mksb()
	bio.put(s.f, "HTTP/1.1 {} {}\r\n", resp.status, statusstr(resp.status))
	bio.put(s.f, "Content-Length: {}\r\n", resp.body.len)
	bio.put(s.f, "Encoding: {}\r\n", resp.enc)
	for (k, v) : resp.hdrs
		bio.put(s.f, "{}: {}\r\n", k, v)
	;;
	bio.put(s.f, "\r\n")
	bio.write(s.f, resp.body)
	bio.flush(s.f)
}

const statusstr = {st
	match st
	| 200:	-> "OK"
	| 404:	-> "Not Found"
	| 503:	-> "Internal Error"
	| _:	-> "Bad State"
	;;
}

const shutdown = {srv
	std.close(srv.lfd)
	srv.quit = true

}


const waitconn = {srv
	match std.accept(srv.lfd)
	| `std.Ok fd:	-> `std.Ok fd
	| `std.Err e:	-> `std.Err `Econn
	;;
}


const ref = {srv
	thread.xadd(&srv.refs, 1)
}

const unref = {srv
	thread.xadd(&srv.refs, -1)
	if thread.xget(&srv.refs) == 0
		std.free(srv)
	;;
}