ref: 5a6878701b5066d0143b0a2e21be35ce7f3d8976
dir: /lib/http/parse.myr/
use std
use bio
use "types"
pkg http =
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 strcaseeq = {a : byte[:], b : byte[:] -> bool
var ca, cb
while a.len == 0 || b.len == 0
(ca, a) = std.charstep(a)
(cb, b) = std.charstep(b)
if std.tolower(ca) != std.tolower(cb)
-> false
;;
;;
-> a.len == b.len
}
const parsereq = {s
var r, err
r.hdrs = [][:]
r.url = std.mk([
.schema=`Http,
.host=std.sldup(s.host),
.port=s.port,
.path="",
.params=[][:],
])
match bio.readln(s.f)
| `std.Err e:
err = `Econn
goto error
| `std.Ok ln:
match parsereqstatus(s, r, ln)
| `std.Ok void:
| `std.Err e:
std.slfree(ln)
err = e
-> `std.Err e
;;
std.slfree(ln)
;;
while true
match bio.readln(s.f)
| `std.Err e:
err = `Econn
goto error
| `std.Ok ln:
if std.strstrip(ln).len == 0
std.slfree(ln)
break
;;
match parsehdr(s, ln)
| `std.Ok kvp:
std.slpush(&r.hdrs, kvp)
| `std.Err e:
std.slfree(ln)
-> `std.Err e
;;
std.slfree(ln)
;;
;;
-> `std.Ok std.mk(r)
:error
-> `std.Err err
}
const parseresp = {s, r : resp#
match bio.readln(s.f)
| `std.Err _: r.err = `std.Some `Econn
| `std.Ok ln:
if !parserespstatus(s, r, ln)
std.slfree(ln)
-> false
;;
std.slfree(ln)
;;
while true
match bio.readln(s.f)
| `std.Err e: r.err = `std.Some `Econn
| `std.Ok ln:
if std.strstrip(ln).len == 0
std.slfree(ln)
break
;;
match parsehdr(s, ln)
| `std.Ok kvp:
std.slpush(&r.hdrs, kvp)
| `std.Err e:
r.err = `std.Some e
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.Err e:
r.err = `std.Some e
-> false
;;
-> true
}
const parsereqstatus = {s, r, ln
match parseword(&ln)
| `std.Some "GET": r.method = `Get
| `std.Some "HEAD": r.method = `Head
| `std.Some "POST": r.method = `Post
| `std.Some "DELETE": r.method = `Delete
| `std.Some "TRACE": r.method = `Trace
| `std.Some "OPTIONS": r.method = `Options
| `std.Some _: -> `std.Err `Eproto
| `std.None: -> `std.Err `Eproto
;;
match parseword(&ln)
| `std.Some w: r.url.path = std.sldup(w)
| `std.None: -> `std.Err `Eproto
;;
match parseword(&ln)
| `std.Some "HTTP/1.1": /* ok */
| `std.Some w: std.put("warn: http version '{}'\n", w)
| `std.None: -> `std.Err `Eproto
;;
ln = std.strfstrip(ln)
-> `std.Ok void
}
const parserespstatus = {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, 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.Ok (key, val)
| `std.None:
-> `std.Err `Ehdr
;;
}
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 parsechunksz = {s
var ret, str
match bio.readln(s.f)
| `std.Err e: ret = `std.Err `Econn
| `std.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
| `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.Err `Eenc
;;
}
const findhdr = {r, name
for (k, v) : r.hdrs
if strcaseeq(k, name)
-> `std.Some v
;;
;;
-> `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
end = i + 1
elif 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
n = 0
s = ln#
ok = false
while true
(c, s) = std.charstep(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
;;
}