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