shithub: mc

Download patch

ref: cdb2435ea25e9752a0b06f72fd699c46185bb7d1
parent: c492d277f96911cc6eecb0e9de72dbbfdf9f45e7
author: Ori Bernstein <ori@eigenstate.org>
date: Wed Apr 12 20:17:34 EDT 2017

start of server.

--- a/lib/http/parse.myr
+++ b/lib/http/parse.myr
@@ -4,51 +4,81 @@
 use "types"
 
 pkg http =
-	pkglocal const parsereq		: (s : session#, r : req# -> bool)
+	pkglocal const parsereq		: (s : session# -> std.result(req#, err))
 	pkglocal const parseresp	: (s : session#, r : resp# -> bool)
 	pkglocal const parsechunksz	: (s : session# -> std.result(std.size, err))
 	pkglocal const parsenumber	: (str : byte[:]#, base : int -> std.option(int))
 ;;
 
-const parsereq = {s, r
-	-> false
-	/*
+const parsereq = {s
+	var r, err
+
+	r.hdrs = [][:]
+	r.url = std.mk([
+		.schema=`Http,
+		.host=std.sldup(s.host),
+		.port=s.port,
+		.path="",
+		.params=[][:],
+	])
+
+	std.put("parsing request\n")
 	match bio.readln(s.f)
-	| `bio.Err e:	r.err = `std.Some `Econn
-	| `bio.Eof:	r.err = `std.Some `Econn
+	| `bio.Err e:
+		err = `Econn
+		goto error
+	| `bio.Eof:	
+		err = `Econn
+		goto error
 	| `bio.Ok ln:
-		if !parsereqstatus(s, r, ln)
+		std.put("status line: {}\n", ln)
+		match parsereqstatus(s, r, ln)
+		| `std.Ok void:
+		| `std.Err e:
+			std.put("err: {}\n", e)
 			std.slfree(ln)
-			-> false
+			err = e
+			-> `std.Err e
 		;;
 		std.slfree(ln)
 	;;
 
 	while true
+		std.put("parsing headers\n")
 		match bio.readln(s.f)
-		| `bio.Err e:	r.err = `std.Some `Econn
-		| `bio.Eof:	r.err = `std.Some `Econn
+		| `bio.Err e:	
+			err = `Econn
+			goto error
+		| `bio.Eof:
+			err = `Econn
+			goto error
 		| `bio.Ok ln:
+			std.put("ln: {}\n", ln)
 			if std.strstrip(ln).len == 0
 				std.slfree(ln)
 				break
 			;;
-			if !parsehdr(s, r, ln)
+			match parsehdr(s, ln)
+			| `std.Ok kvp:
+				std.slpush(&r.hdrs, kvp)
+			| `std.Err e:
 				std.slfree(ln)
-				-> false
+				-> `std.Err e
 			;;
 			std.slfree(ln)
 		;;
 	;;
-	*/
+	-> `std.Ok std.mk(r)
+:error
+	-> `std.Err err
 }
 
-const parseresp = {s, r
+const parseresp = {s, r : resp#
 	match bio.readln(s.f)
 	| `bio.Err e:	r.err = `std.Some `Econn
 	| `bio.Eof:	r.err = `std.Some `Econn
 	| `bio.Ok ln:
-		if !parsestatus(s, r, ln)
+		if !parserespstatus(s, r, ln)
 			std.slfree(ln)
 			-> false
 		;;
@@ -64,7 +94,11 @@
 				std.slfree(ln)
 				break
 			;;
-			if !parsehdr(s, r, ln)
+			match parsehdr(s, ln)
+			| `std.Ok kvp:
+				std.slpush(&r.hdrs, kvp)
+			| `std.Err e:
+				r.err = `std.Some e
 				std.slfree(ln)
 				-> false
 			;;
@@ -92,25 +126,32 @@
 
 }
 
-const parsechunksz = {s
-	var ret, str
+const parsereqstatus = {s, r, ln
+	var m, p, v
 
-	match bio.readln(s.f)
-	| `bio.Eof:	ret = `std.Err `Econn
-	| `bio.Err e:	ret = `std.Err `Econn
-	| `bio.Ok ln:
-		str = ln
-		match parsenumber(&str, 16)
-		| `std.Some n:	ret = `std.Ok (n : std.size)
-		| `std.None:
-			ret = `std.Err `Eproto
-		;;
-		std.slfree(ln)
+	match parseword(&ln)
+	| `std.Some w:	m = w
+	| `std.None:	-> `std.Err `Eproto
 	;;
-	-> ret
+	std.put("got method: {}\n", m)
+
+	match parseword(&ln)
+	| `std.Some w:	p = w
+	| `std.None:	-> `std.Err `Eproto
+	;;
+	std.put("got path: {}\n", p)
+
+	match parseword(&ln)
+	| `std.Some w:	v = w
+	| `std.None:	-> `std.Err `Eproto
+	;;
+	std.put("got version: {}\n", v)
+
+	ln = std.strfstrip(ln)
+	-> `std.Ok void
 }
 
-const parsestatus = {s, r, ln
+const parserespstatus = {s, r, ln
 	/* HTTP/1.1 */
 	ln = std.strfstrip(ln)
 	if !std.chomp(&ln, "HTTP")
@@ -136,7 +177,7 @@
 	-> true
 }
 
-const parsehdr = {s, r, ln
+const parsehdr = {s, ln
 	var key, val
 
 	match std.strfind(ln, ":")
@@ -143,11 +184,9 @@
 	| `std.Some idx:
 		key = std.sldup(std.strstrip(ln[:idx]))
 		val = std.sldup(std.strstrip(ln[idx+1:]))
-		std.slpush(&r.hdrs, (key, val))
-		-> true
+		-> `std.Ok (key, val)
 	| `std.None:
-		r.err = `std.Some `Ehdr
-		-> false
+		-> `std.Err `Ehdr
 	;;
 }
 
@@ -163,6 +202,24 @@
 	;;
 }
 
+const parsechunksz = {s
+	var ret, str
+
+	match bio.readln(s.f)
+	| `bio.Eof:	ret = `std.Err `Econn
+	| `bio.Err e:	ret = `std.Err `Econn
+	| `bio.Ok ln:
+		str = ln
+		match parsenumber(&str, 16)
+		| `std.Some n:	ret = `std.Ok (n : std.size)
+		| `std.None:
+			ret = `std.Err `Eproto
+		;;
+		std.slfree(ln)
+	;;
+	-> ret
+}
+
 const getenc = {r
 	match findhdr(r, "Transfer-Encoding")
 	| `std.None:	-> `std.Ok `Length
@@ -181,6 +238,26 @@
 		;;
 	;;
 	-> `std.None
+}
+
+const parseword = {ln
+	var w, end
+
+	ln# = std.strfstrip(ln#)
+	end = 0
+	for var i = 0; i < ln#.len; i++
+		if i == ln#.len - 1 || std.isspace(std.decode(ln#[i:]))
+			end = i
+			break
+		;;
+	;;
+	if end == 0
+		-> `std.None
+	else
+		w = ln#[:end]
+		ln# = ln#[end:]
+		-> `std.Some w
+	;;
 }
 
 const parsenumber = {ln, base
--- /dev/null
+++ b/lib/http/serve.myr
@@ -1,0 +1,14 @@
+use std
+use bio
+
+use "types"
+use "parse"
+
+pkg http =
+	const readreq	: (srv	: server#	-> req#)
+;;
+
+
+const readreq = {srv : server#
+	-> req
+}
--- a/lib/http/server.myr
+++ b/lib/http/server.myr
@@ -1,15 +1,15 @@
+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 waitconn	: (srv : server# -> std.result(std.fd, err))
-//	const readmsg	: (srv : server# -> std.option(req#, err))
-//	const writemsg	: (srv : server# -> std.option(req#, err))
-//	const writehdr	: (srv : server# -> std.option(req#, err))
+	const serve	: (srv : server# -> void)
 ;;
 
 const announce = {ds
@@ -28,6 +28,72 @@
 	;;
 }
 
+const serve = {srv
+	std.put("waiting for connection\n")
+	while true
+		match waitconn(srv)
+		| `std.Ok fd:	communicate(srv, fd)
+		| `std.Err e:	/* eh? */
+		;;
+	;;
+}
+
+const communicate = {srv, fd
+	var s
+
+	s = mksrvsession(fd)
+	while !srv.quit
+		match parsereq(s)
+		| `std.Ok req:
+			dispatch(srv, s, req)
+		| `std.Err e:
+			std.put("failed to parse request: {}\n", e)
+			break
+		;;
+	;;
+	std.close(fd)
+}
+
+const dispatch = {srv, sess, req
+	var resp : resp#
+
+	std.put("got req: {}\n", req)
+	resp = std.mk([
+		.status=200,
+		.hdrs = [][:],
+		.len = 0,
+		.err = `std.None,
+		.reason = "",
+		.body = "heard you loud and clear\n",
+		.enc = `Length
+	])
+	respond(srv, sess, resp)
+}
+
+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) in 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)
 }
@@ -35,7 +101,7 @@
 
 const waitconn = {srv
 	match std.accept(srv.lfd)
-	| `std.Ok afd:	-> `std.Ok afd
+	| `std.Ok fd:	-> `std.Ok fd
 	| `std.Err e:	-> `std.Err `Econn
 	;;
 }
--- a/lib/http/session.myr
+++ b/lib/http/session.myr
@@ -4,7 +4,8 @@
 use "types"
 
 pkg http =
-	const mksession	: (schema : schema, host : byte[:], port : int -> std.result(session#, err))
+	const mksession		: (schema : schema, host : byte[:], port : int -> std.result(session#, err))
+	const mksrvsession	: (fd	: std.fd -> session#)
 	const freesession	: (s : session# -> void)
 
 	//const setcookie	: (s : session#, name : byte[:], val : byte[:] -> void)
@@ -35,6 +36,14 @@
 	;;
 	std.slfree(s)
 	-> sess
+}
+
+const mksrvsession = {fd
+	-> std.mk([
+		.err = false,
+		.srvname = std.sldup("Myrfoo HTTP Server"),
+		.f = bio.mkfile(fd, bio.Rw)
+	])
 }
 
 const freesession = {s
--- /dev/null
+++ b/lib/http/srvdot.myr
@@ -1,0 +1,49 @@
+use std
+use http
+
+const main = {
+	var srv //, router
+
+	match http.announce("tcp!localhost!8080")
+	| `std.Ok s:	srv = s
+	| `std.Err e:	std.fatal("unable to announce: {}\n", e)
+	;;
+
+	http.serve(srv)
+
+	//router = http.mkrouter(srv, [
+	//	[.path="*", .render=showfile],
+	//	[.path="/foo", .render={ctx; show(ctx, "foo")}],
+	//	[.path="/foo/bar", .render={ctx; show(ctx, "foobar")}],
+	//	[.path="/foo/baz", .render={ctx; show(ctx, "foobarbaz")}],
+	//][:])
+	//
+	//http.run(router)
+}
+
+// const showfile = {ctx
+// 	var buf : byte[32*std.KiB]
+// 	var fd
+// 
+// 	match std.open(ctx.path, std.Ordonly)
+// 	| `std.Ok f:
+// 		fd = f
+// 	| `std.Err e:
+// 		http.srvput(ctx, "unable to open path {}\n", ctx.path)
+// 		-> 404
+// 	;;
+// 
+// 	http.setchunk(ctx)
+// 	while true
+// 		match std.read(fd, buf[:])
+// 		| `std.Ok n:	http.writechunk(ctx, buf[:n])
+// 		| `std.Ok 0:	http.closechunk(ctx); break
+// 		| `std.Err e:	http.closechunk(ctx); break
+// 		;;
+// 	;;
+// 	-> 200
+// }
+// 
+// const show = {ctx, txt
+// 	http.srvput(ctx, "{}", txt)
+// }
--- a/lib/http/types.myr
+++ b/lib/http/types.myr
@@ -9,12 +9,16 @@
 	type session = struct
 		f	: bio.file#
 		host	: byte[:]
+		port	: uint16
+		srvname	: byte[:]
 		ua	: byte[:]
 		err	: bool
 	;;
 
+
 	type server = struct
 		lfd	: std.fd
+		quit	: bool
 	;;
 
 	type router = struct
@@ -33,7 +37,7 @@
 
 	type url = struct
 		schema	: schema
-		port	: int
+		port	: uint16
 		host	: byte[:]
 		path	: byte[:]
 		params	: (byte[:], byte[:])[:]
@@ -77,6 +81,7 @@
 	type req = struct
 		url	: url#
 		hdrs	: (byte[:], byte[:])[:]
+		err	: std.option(err)
 	;;
 
 	type resp = struct
--- a/lib/http/url.myr
+++ b/lib/http/url.myr
@@ -206,8 +206,13 @@
 const parseport = {url
 	if std.chomp(url, ":")
 		match parsenumber(url, 10)
-		| `std.Some n:	-> `std.Ok n
 		| `std.None:	-> `std.Err `Esyntax
+		| `std.Some n:
+			if n > 65535
+				-> `std.Err `Esyntax
+			else
+				-> `std.Ok (n : uint16)
+			;;
 		;;
 	else
 		-> `std.Ok 80