diff options
Diffstat (limited to 'lib/http/url.myr')
-rw-r--r-- | lib/http/url.myr | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/lib/http/url.myr b/lib/http/url.myr new file mode 100644 index 0000000..be17d20 --- /dev/null +++ b/lib/http/url.myr @@ -0,0 +1,238 @@ +use std + +use "types" +use "parse" + +pkg http = + const parseurl : (url : byte[:] -> std.result(url#, err)) + const urlfree : (url : url# -> void) +;; + +const __init__ = { + var u : url# + + u = u + std.fmtinstall(std.typeof(u), urlfmt, [ + ("p", false) + ][:]) +} + +const urlfmt = {sb, ap, opts + var url : url# + var defaultport + var sep + var showhost + + showhost = true + url = std.vanext(ap) + for o : opts + match o + | ("p", ""): showhost = false + | _: std.fatal("unknown param\n") + ;; + ;; + + if showhost + match url.schema + | `Http: + std.sbputs(sb, "http://") + defaultport = 80 + | `Https: + std.sbputs(sb, "https://") + defaultport = 443 + ;; + + std.sbputs(sb, url.host) + if url.port != defaultport + std.sbfmt(sb, ":{}", url.port) + ;; + ;; + + std.sbfmt(sb, url.path) + + if url.params.len > 0 + sep = '?' + for (k, v) : url.params + std.sbfmt(sb, "{}{}={}", sep, k, v) + sep = '&' + ;; + ;; +} + +const parseurl = {url + var schema, host, port, path, params + + match parseschema(&url) + | `std.Ok s: schema = s + | `std.Err e: -> `std.Err e + ;; + + match parsehostname(&url) + | `std.Ok h: host = h + | `std.Err e: -> `std.Err e + ;; + + match parseport(&url) + | `std.Ok p: port = p + | `std.Err e: -> `std.Err e + ;; + + match parsepath(&url) + | `std.Ok p: path = p + | `std.Err e: -> `std.Err e + ;; + + match parseparams(&url) + | `std.Ok p: params = p + | `std.Err e: -> `std.Err e + ;; + + /* todo: params */ + -> `std.Ok std.mk([ + .schema=schema, + .host=std.sldup(host), + .port=port, + .path=std.sldup(path), + .params=params, + ]) +} + +const urlfree = {url + std.slfree(url.host) + std.slfree(url.path) + std.free(url) +} + +const parseschema = {url + if std.chomp(url, "http://") + -> `std.Ok `Http + elif std.chomp(url, "https://") + -> `std.Err `Eunsupp + else + -> `std.Err `Eproto + ;; +} + +const parsehostname = {url + if std.chomp(url, "[") + -> ipv6hostname(url) + else + -> hostname(url) + ;; +} + +const parsepath = {url + var len, p + + if url#.len == 0 + -> `std.Ok "/" + elif std.decode(url#) == '?' + -> `std.Ok "/" + elif std.decode(url#) == '/' + match std.strfind(url#, "?") + | `std.Some i: len = i + | `std.None: len = url#.len + ;; + p = url#[:len] + url# = url#[len:] + -> `std.Ok p + else + -> `std.Err `Egarbled + ;; +} + +const parseparams = {url + var kvp : byte[:][2] + var params + + if url#.len == 0 + -> `std.Ok [][:] + ;; + + match std.decode(url#) + | '?': (_, url#) = std.strstep(url#) + | _: -> `std.Err `Egarbled + ;; + + params = [][:] + for sp : std.bysplit(url#, "&") + if std.bstrsplit(kvp[:], sp, "=").len != 2 + -> `std.Err `Egarbled + ;; + std.slpush(¶ms, (std.sldup(kvp[0]), std.sldup(kvp[1]))) + ;; + + -> `std.Ok params +} + +const hostname = {url + var len, host + + len = 0 + for c : std.bychar(url#) + match c + | ':': break + | '/': break + | '?': break + | chr: + if ishostchar(chr) + len += std.charlen(chr) + else + -> `std.Err `Egarbled + ;; + ;; + ;; + + host = url#[:len] + url# = url#[len:] + -> `std.Ok host +} + +const ipv6hostname = {url -> std.result(byte[:], err) + var ip + + match std.strfind(url#, "]") + | `std.None: -> `std.Err `Egarbled + | `std.Some idx: + ip = url#[:idx] + url# = url#[idx+1:] + match std.ip6parse(url#[:idx]) + | `std.Some _: -> `std.Ok ip + | `std.None: -> `std.Err `Egarbled + ;; + ;; +} + +const parseport = {url + if std.chomp(url, ":") + match parsenumber(url, 10) + | `std.None: -> `std.Err `Egarbled + | `std.Some n: + if n > 65535 + -> `std.Err `Egarbled + else + -> `std.Ok (n : uint16) + ;; + ;; + else + -> `std.Ok 80 + ;; +} + +const ishostchar = {c + if !std.isascii(c) + -> false + else + -> std.isalnum(c) || unreserved(c) || subdelim(c) + ;; +} + +const unreserved = {c + -> c == '.' || c == '-' || c == '_' || c == '~' +} + +const subdelim = {c + -> c == '.' || c == '-' || c == '_' || c == '~' || c == '!' || \ + c == '$' || c == '&' || c == '\'' || c == '(' || c == ')' || \ + c == '*' || c == '+' || c == ',' || c == ';' || c == '=' +} |