ref: 1d4f6a7925c05705dda39c9fe799c64138d6d02a
dir: /lib/http/parse.myr/
use std use bio use "types" pkg http = pkglocal const parsereq : (s : session#, r : req# -> bool) 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 /* match bio.readln(s.f) | `bio.Err e: r.err = `std.Some `Econn | `bio.Eof: r.err = `std.Some `Econn | `bio.Ok ln: if !parsereqstatus(s, r, ln) std.slfree(ln) -> false ;; std.slfree(ln) ;; while true match bio.readln(s.f) | `bio.Err e: r.err = `std.Some `Econn | `bio.Eof: r.err = `std.Some `Econn | `bio.Ok ln: if std.strstrip(ln).len == 0 std.slfree(ln) break ;; if !parsehdr(s, r, ln) std.slfree(ln) -> false ;; std.slfree(ln) ;; ;; */ } const parseresp = {s, r 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) std.slfree(ln) -> false ;; std.slfree(ln) ;; while true match bio.readln(s.f) | `bio.Err e: r.err = `std.Some `Econn | `bio.Eof: r.err = `std.Some `Econn | `bio.Ok ln: if std.strstrip(ln).len == 0 std.slfree(ln) break ;; if !parsehdr(s, r, ln) std.slfree(ln) -> false ;; std.slfree(ln) ;; ;; match getenc(r) | `std.Ok `Length: r.enc = `Length match getlen(r) | `std.Some n: r.len = n | `std.None: r.err = `std.Some `Eproto -> false ;; | `std.Ok enc: r.enc = enc | `std.Fail e: r.err = `std.Some e -> false ;; -> true } const parsechunksz = {s var ret, str match bio.readln(s.f) | `bio.Eof: ret = `std.Fail `Econn | `bio.Err e: ret = `std.Fail `Econn | `bio.Ok ln: str = ln match parsenumber(&str, 16) | `std.Some n: ret = `std.Ok (n : std.size) | `std.None: ret = `std.Fail `Eproto ;; std.slfree(ln) ;; -> ret } const parsestatus = {s, r, ln /* HTTP/1.1 */ ln = std.strfstrip(ln) if !std.chomp(&ln, "HTTP") r.err = `std.Some `Eproto -> false ;; if !std.chomp(&ln, "/1.1") r.err = `std.Some `Eproto -> false ;; ln = std.strfstrip(ln) match parsenumber(&ln, 10) | `std.Some n: r.status = n | `std.None: r.err = `std.Some `Eproto -> false ;; ln = std.strfstrip(ln) r.reason = std.sldup(ln) -> true } const parsehdr = {s, r, 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.None: r.err = `std.Some `Ehdr -> false ;; } const getlen = {r match findhdr(r, "Content-Length") | `std.Some v: match std.intparsebase(v, 10) | `std.Some n: -> `std.Some n | `std.None: -> `std.None ;; | `std.None: -> `std.None ;; } const getenc = {r match findhdr(r, "Transfer-Encoding") | `std.None: -> `std.Ok `Length | `std.Some "chunked": -> `std.Ok `Chunked | `std.Some "compress": -> `std.Ok `Compress | `std.Some "deflate": -> `std.Ok `Deflate | `std.Some "gzip": -> `std.Ok `Gzip | `std.Some unknown: -> `std.Fail `Eenc ;; } const findhdr = {r, name for (k, v) in r.hdrs if std.strcaseeq(k, name) -> `std.Some v ;; ;; -> `std.None } const parsenumber = {ln, base var n, ok var c, s, dig n = 0 s = ln# ok = false while true (c, s) = std.strstep(s) dig = std.charval(c, base) if dig >= 0 && dig < base ok = true n *= base n += dig else break ;; ;; ln# = s if ok -> `std.Some n else -> `std.None ;; }