diff options
author | Ori Bernstein <ori@eigenstate.org> | 2017-04-13 00:17:34 -0700 |
---|---|---|
committer | Ori Bernstein <ori@eigenstate.org> | 2017-04-13 00:17:57 -0700 |
commit | cdb2435ea25e9752a0b06f72fd699c46185bb7d1 (patch) | |
tree | e1e5fe2cfbd3e563ead0501fad300ddfd6e0abe0 | |
parent | c492d277f96911cc6eecb0e9de72dbbfdf9f45e7 (diff) | |
download | mc-cdb2435ea25e9752a0b06f72fd699c46185bb7d1.tar.gz |
start of server.
-rw-r--r-- | lib/http/parse.myr | 149 | ||||
-rw-r--r-- | lib/http/serve.myr | 14 | ||||
-rw-r--r-- | lib/http/server.myr | 78 | ||||
-rw-r--r-- | lib/http/session.myr | 11 | ||||
-rw-r--r-- | lib/http/srvdot.myr | 49 | ||||
-rw-r--r-- | lib/http/types.myr | 7 | ||||
-rw-r--r-- | lib/http/url.myr | 7 |
7 files changed, 270 insertions, 45 deletions
diff --git a/lib/http/parse.myr b/lib/http/parse.myr index 8f6348c..dc1e58e 100644 --- a/lib/http/parse.myr +++ b/lib/http/parse.myr @@ -4,51 +4,81 @@ use bio 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 @@ const parseresp = {s, r 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 parseresp = {s, r } -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,18 +177,16 @@ const parsestatus = {s, r, ln -> true } -const parsehdr = {s, r, ln +const parsehdr = {s, ln var key, val match std.strfind(ln, ":") | `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 getlen = {r ;; } +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 @@ -183,6 +240,26 @@ const findhdr = {r, name -> `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 var n, ok var c, s, dig diff --git a/lib/http/serve.myr b/lib/http/serve.myr new file mode 100644 index 0000000..359e308 --- /dev/null +++ b/lib/http/serve.myr @@ -0,0 +1,14 @@ +use std +use bio + +use "types" +use "parse" + +pkg http = + const readreq : (srv : server# -> req#) +;; + + +const readreq = {srv : server# + -> req +} diff --git a/lib/http/server.myr b/lib/http/server.myr index 6e1aa4c..220f56d 100644 --- 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 announce = {ds ;; } +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 shutdown = {srv 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 ;; } diff --git a/lib/http/session.myr b/lib/http/session.myr index 17833ea..32ec8eb 100644 --- a/lib/http/session.myr +++ b/lib/http/session.myr @@ -4,7 +4,8 @@ use bio 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) @@ -37,6 +38,14 @@ const mksession = {schema, host, port -> sess } +const mksrvsession = {fd + -> std.mk([ + .err = false, + .srvname = std.sldup("Myrfoo HTTP Server"), + .f = bio.mkfile(fd, bio.Rw) + ]) +} + const freesession = {s bio.close(s.f) std.slfree(s.host) diff --git a/lib/http/srvdot.myr b/lib/http/srvdot.myr new file mode 100644 index 0000000..384b310 --- /dev/null +++ b/lib/http/srvdot.myr @@ -0,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) +// } diff --git a/lib/http/types.myr b/lib/http/types.myr index cd095fb..d103ac1 100644 --- a/lib/http/types.myr +++ b/lib/http/types.myr @@ -9,12 +9,16 @@ pkg http = 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 @@ pkg http = type url = struct schema : schema - port : int + port : uint16 host : byte[:] path : byte[:] params : (byte[:], byte[:])[:] @@ -77,6 +81,7 @@ pkg http = type req = struct url : url# hdrs : (byte[:], byte[:])[:] + err : std.option(err) ;; type resp = struct diff --git a/lib/http/url.myr b/lib/http/url.myr index deaee5f..b4ffd2f 100644 --- a/lib/http/url.myr +++ b/lib/http/url.myr @@ -206,8 +206,13 @@ const ipv6hostname = {url -> std.result(byte[:], err) 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 |