ref: 70f97fe9898b4852257a9268e6ea0592ee7e3a88
dir: /lib/http/client.myr/
use std
use bio
use "types"
use "session"
use "parse"
pkg http =
/* simple versions */
const get : (s : session#, r : url# -> std.result(resp#, err))
const head : (s : session#, r : url# -> std.result(resp#, err))
const put : (s : session#, r : url#, data : byte[:] -> std.result(resp#, err))
const post : (s : session#, r : url#, data : byte[:] -> std.result(resp#, err))
const delete : (s : session#, r : url# -> std.result(resp#, err))
const options : (s : session#, r : url# -> std.result(resp#, err))
const trace : (s : session#, r : url# -> std.result(resp#, err))
/* request based versions */
const getreq : (s : session#, r : req# -> std.result(resp#, err))
const headreq : (s : session#, r : req# -> std.result(resp#, err))
const putreq : (s : session#, r : req#, data : byte[:] -> std.result(resp#, err))
const postreq : (s : session#, r : req#, data : byte[:] -> std.result(resp#, err))
const deletereq : (s : session#, r : req# -> std.result(resp#, err))
const optionsreq : (s : session#, r : req# -> std.result(resp#, err))
const tracereq : (s : session#, r : req# -> std.result(resp#, err))
const freeresp : (r : resp# -> void)
;;
const get = {s, path; -> getreq(s, &[.url=path])}
const head = {s, path; -> headreq(s, &[.url=path])}
const put = {s, path, data; -> putreq(s, &[.url=path], data)}
const post = {s, path, data; -> postreq(s, &[.url=path], data)}
const delete = {s, path; -> deletereq(s, &[.url=path])}
const options = {s, path; -> optionsreq(s, &[.url=path])}
const trace = {s, path; -> tracereq(s, &[.url=path])}
const getreq = {s, r
match request(s, `Get, r, `std.None)
| `std.Ok _: /* nothing */
| `std.Err e: -> `std.Err e
;;
-> response(s, true)
}
const headreq = {s, r
match request(s, `Head, r, `std.None)
| `std.Ok _: /* nothing */
| `std.Err e: -> `std.Err e
;;
-> response(s, false)
}
const putreq = {s, r, data
match request(s, `Put, r, `std.Some data)
| `std.Ok _: /* nothing */
| `std.Err e: -> `std.Err e
;;
-> response(s, true)
}
const postreq = {s, r, data
match request(s, `Post, r, `std.Some data)
| `std.Ok _: /* nothing */
| `std.Err e: -> `std.Err e
;;
-> response(s, true)
}
const deletereq = {s, r
match request(s, `Delete, r, `std.None)
| `std.Ok _: /* nothing */
| `std.Err e: -> `std.Err e
;;
-> response(s, true)
}
const optionsreq = {s, r
match request(s, `Options, r, `std.None)
| `std.Ok _: /* nothing */
| `std.Err e: -> `std.Err e
;;
-> response(s, true)
}
const tracereq = {s, r
match request(s, `Trace, r, `std.None)
| `std.Ok _: /* nothing */
| `std.Err e: -> `std.Err e
;;
-> response(s, true)
}
const response = {s, body
var resp
resp = std.mk([
.hdrs = [][:],
.len = 0,
.err = `std.None,
.reason = "",
.status = 0,
.enc = `Length,
])
if parseresp(s, resp)
if !body
-> `std.Ok resp
else
match readbody(s, resp)
| `std.Ok buf: resp.body = buf
| `std.Err e: -> `std.Err e
;;
;;
else
match resp.err
| `std.Some e: -> `std.Err e
| `std.None: -> `std.Err `Ewat
;;
;;
-> `std.Ok resp
}
const request = {s, method, r, data
/* status */
ioput(s, "{} {p} HTTP/1.1\r\n", method, r.url)
/* headers */
ioput(s, "Host: {}\r\n", s.host)
ioput(s, "User-Agent: {}\r\n", s.ua)
match data
| `std.Some d: ioput(s, "Content-Length: {}\r\n", d.len)
| `std.None: /* nothing to do */
;;
for (k, v) : r.hdrs
ioput(s, "{}: {}\r\n", k, v)
;;
ioput(s, "\r\n")
/* body */
match data
| `std.None: /* nothing to do */
| `std.Some d:
iowrite(s, d)
ioput(s, "\r\n")
;;
ioflush(s)
if s.err
-> `std.Err `Econn
else
-> `std.Ok void
;;
}
const readbody = {s, r -> std.result(byte[:], err)
match r.enc
| `Length: -> readlenbody(s, r)
| `Chunked: -> readchunkedbody(s, r)
| badenc: std.fatal("unsupported encoding {}\n", badenc)
;;
}
const readlenbody = {s, r
var buf
buf = ""
if r.len == 0
-> `std.Ok buf
;;
buf = std.slalloc(r.len)
match bio.read(s.f, buf)
| `std.Err e: goto shortread
| `std.Ok rd:
if rd.len != r.len
goto shortread
;;
;;
-> `std.Ok buf
:shortread
std.slfree(buf)
-> `std.Err `Eshort
}
const __init__ = {
var m
m = `Get
std.fmtinstall(std.typeof(m), fmtmethod)
}
const readchunkedbody = {s, r
var buf, len
buf = ""
len = 0
while true
match parsechunksz(s)
| `std.Err e:
std.slfree(buf)
-> `std.Err e
| `std.Ok 0:
break
| `std.Ok sz:
std.slgrow(&buf, buf.len + sz)
match bio.read(s.f, buf[len:len + sz])
| `std.Err e:
std.slfree(buf)
-> `std.Err `Econn
| `std.Ok str:
if str.len != sz
std.slfree(buf)
-> `std.Err `Eshort
;;
len += sz
match checkendln(s)
| `std.Ok _: /* nothing */
| `std.Err e:
std.slfree(buf)
-> `std.Err e
;;
;;
;;
;;
-> `std.Ok buf
}
const checkendln = {s
var r
match bio.readln(s.f)
| `std.Err e: r = `std.Err `Econn
| `std.Ok crlf:
if std.strstrip(crlf).len == 0
r = `std.Ok void
else
r = `std.Err `Eproto
;;
std.slfree(crlf)
;;
-> r
}
const fmtmethod = {sb, ap, opt
var r
r = std.vanext(ap)
match r
| `Get: std.sbputs(sb, "GET")
| `Head: std.sbputs(sb, "HEAD")
| `Put: std.sbputs(sb, "PUT")
| `Post: std.sbputs(sb, "POST")
| `Delete: std.sbputs(sb, "DELETE")
| `Trace: std.sbputs(sb, "TRACE")
| `Options: std.sbputs(sb, "OPTIONS")
;;
}
const freeresp = {r
for (k, v) : r.hdrs
std.slfree(k)
std.slfree(v)
;;
std.slfree(r.reason)
std.free(r)
}