diff options
author | bptato <nincsnevem662@gmail.com> | 2023-05-21 13:28:33 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-05-21 13:28:33 +0200 |
commit | d09319640c4b1e24b937d1fc37a3f8b82052e612 (patch) | |
tree | 45631e7ed2c23ce4a4217bfe2037cba2ba873231 /src/io | |
parent | 7c612d65be345fd1ef156f95bb033d9bbf8aece8 (diff) | |
download | chawan-d09319640c4b1e24b937d1fc37a3f8b82052e612.tar.gz |
Rewrite new Request binding
Still far from perfect, but now at least it has a bit more to do with what the standard mandates.
Diffstat (limited to 'src/io')
-rw-r--r-- | src/io/about.nim | 6 | ||||
-rw-r--r-- | src/io/file.nim | 6 | ||||
-rw-r--r-- | src/io/http.nim | 4 | ||||
-rw-r--r-- | src/io/loader.nim | 2 | ||||
-rw-r--r-- | src/io/request.nim | 131 |
5 files changed, 116 insertions, 33 deletions
diff --git a/src/io/about.nim b/src/io/about.nim index a3ecb1fe..b852c556 100644 --- a/src/io/about.nim +++ b/src/io/about.nim @@ -6,7 +6,7 @@ import ips/serialize import types/url const chawan = staticRead"res/chawan.html" -const Headers = { +const HeaderTable = { "Content-Type": "text/html" }.toTable() @@ -14,12 +14,12 @@ proc loadAbout*(request: Request, ostream: Stream) = if request.url.pathname == "blank": ostream.swrite(0) ostream.swrite(200) # ok - let headers = newHeaderList(Headers) + let headers = newHeaders(HeaderTable) ostream.swrite(headers) elif request.url.pathname == "chawan": ostream.swrite(0) ostream.swrite(200) # ok - let headers = newHeaderList(Headers) + let headers = newHeaders(HeaderTable) ostream.swrite(headers) ostream.write(chawan) else: diff --git a/src/io/file.nim b/src/io/file.nim index 092810e2..24dea51c 100644 --- a/src/io/file.nim +++ b/src/io/file.nim @@ -10,7 +10,7 @@ import types/url proc loadDir(url: URL, path: string, ostream: Stream) = ostream.swrite(0) ostream.swrite(200) # ok - ostream.swrite(newHeaderList({"Content-Type": "text/html"}.toTable())) + ostream.swrite(newHeaders({"Content-Type": "text/html"}.toTable())) ostream.write(""" <HTML> <HEAD> @@ -48,7 +48,7 @@ proc loadDir(url: URL, path: string, ostream: Stream) = proc loadSymlink(path: string, ostream: Stream) = ostream.swrite(0) ostream.swrite(200) # ok - ostream.swrite(newHeaderList({"Content-Type": "text/html"}.toTable())) + ostream.swrite(newHeaders({"Content-Type": "text/html"}.toTable())) let sl = expandSymlink(path) ostream.write(""" <HTML> @@ -79,7 +79,7 @@ proc loadFile*(url: URL, ostream: Stream) = else: ostream.swrite(0) ostream.swrite(200) # ok - ostream.swrite(newHeaderList()) + ostream.swrite(newHeaders()) while not istream.atEnd: const bufferSize = 4096 var buffer {.noinit.}: array[bufferSize, char] diff --git a/src/io/http.nim b/src/io/http.nim index 2eefb9ce..984cae7c 100644 --- a/src/io/http.nim +++ b/src/io/http.nim @@ -13,7 +13,7 @@ type HandleDataObj = object curl*: CURL statusline: bool - headers: HeaderList + headers: Headers request: Request ostream*: Stream mime: curl_mime @@ -21,7 +21,7 @@ type func newHandleData(curl: CURL, request: Request, ostream: Stream): HandleData = let handleData = HandleData( - headers: newHeaderList(), + headers: newHeaders(), curl: curl, ostream: ostream, request: request diff --git a/src/io/loader.nim b/src/io/loader.nim index 746c760d..1102b4b5 100644 --- a/src/io/loader.nim +++ b/src/io/loader.nim @@ -67,7 +67,7 @@ type handleList: seq[HandleData] LoaderConfig* = object - defaultheaders*: HeaderList + defaultheaders*: Headers filter*: URLFilter cookiejar*: CookieJar referrerpolicy*: ReferrerPolicy diff --git a/src/io/request.nim b/src/io/request.nim index f6fe5a73..531a2fc3 100644 --- a/src/io/request.nim +++ b/src/io/request.nim @@ -1,5 +1,6 @@ import options import streams +import strutils import tables import bindings/quickjs @@ -20,32 +21,57 @@ type HTTP_TRACE = "TRACE" RequestMode* = enum - NO_CORS, SAME_ORIGIN, CORS, NAVIGATE, WEBSOCKET + NO_CORS = "no-cors" + SAME_ORIGIN = "same-origin" + CORS = "cors" + NAVIGATE = "navigate" + WEBSOCKET = "websocket" RequestDestination* = enum - NO_DESTINATION, AUDIO, AUDIOWORKLET, DOCUMENT, EMBED, FONT, FRAME, IFRAME, - IMAGE, MANIFEST, OBJECT, PAINTWORKLET, REPORT, SCRIPT, SERVICEWORKER, - SHAREDWORKER, STYLE, TRACK, WORKER, XSLT + NO_DESTINATION = "" + AUDIO = "audio" + AUDIOWORKLET = "audioworklet" + DOCUMENT = "document" + EMBED = "embed" + FONT = "font" + FRAME = "frame" + IFRAME = "iframe" + IMAGE = "image" + MANIFEST = "manifest" + OBJECT = "object" + PAINTWORKLET = "paintworklet" + REPORT = "report" + SCRIPT = "script" + SERVICEWORKER = "serviceworker" + SHAREDWORKER = "sharedworker" + STYLE = "style" + TRACK = "track" + WORKER = "worker" + XSLT = "xslt" CredentialsMode* = enum - SAME_ORIGIN, OMIT, INCLUDE + SAME_ORIGIN = "same-origin" + OMIT = "omit" + INCLUDE = "include" CORSAttribute* = enum - NO_CORS, ANONYMOUS, USE_CREDENTIALS + NO_CORS = "no-cors" + ANONYMOUS = "anonymous" + USE_CREDENTIALS = "use-credentials" type Request* = ref RequestObj RequestObj* = object httpmethod*: HttpMethod url*: Url - headers*: HeaderList + headers* {.jsget.}: Headers body*: Option[string] multipart*: Option[MimeData] referer*: URL - mode*: RequestMode - destination*: RequestDestination - credentialsMode*: CredentialsMode - proxy*: URL #TODO: this should definitely be a different API. + mode* {.jsget.}: RequestMode + destination* {.jsget.}: RequestDestination + credentialsMode* {.jsget.}: CredentialsMode + proxy*: URL #TODO do something with this Response* = ref object body*: Stream @@ -53,7 +79,7 @@ type res* {.jsget.}: int contenttype* {.jsget.}: string status* {.jsget.}: int - headers* {.jsget.}: HeaderList + headers* {.jsget.}: Headers redirect*: Request url*: URL #TODO should be urllist? unregisterFun*: proc() @@ -63,7 +89,7 @@ type buf: string isend: bool - HeaderList* = ref object + Headers* = ref object table* {.jsget.}: Table[string, seq[string]] # Originally from the stdlib @@ -86,7 +112,14 @@ proc Request_url(ctx: JSContext, this: JSValue, magic: cint): JSValue {.cdecl.} let request = cast[Request](op) return toJS(ctx, $request.url) -iterator pairs*(headers: HeaderList): (string, string) = +proc Request_referrer(ctx: JSContext, this: JSValue, magic: cint): JSValue {.cdecl.} = + let op = getOpaque0(this) + if unlikely(not ctx.isInstanceOf(this, "Request") or op == nil): + return JS_ThrowTypeError(ctx, "Value is not an instance of %s", "Request") + let request = cast[Request](op) + return toJS(ctx, $request.referer) + +iterator pairs*(headers: Headers): (string, string) = for k, vs in headers.table: for v in vs: yield (k, v) @@ -147,10 +180,30 @@ proc newReadableStream*(isource: Stream): ReadableStream = else: result.isource.readStr(len, result.buf) -func newHeaderList*(): HeaderList = +proc fill(headers: Headers, ctx: JSContext, val: JSValue) = + if isSequence(ctx, val): + let x = fromJS[seq[(string, string)]](ctx, val) + if x.isSome: + for (k, v) in x.get: + if k in headers.table: + headers.table[k].add(v) + else: + headers.table[k] = @[v] + else: + let x = fromJS[Table[string, string]](ctx, val) + if x.isSome: + for k, v in x.get: + if k in headers.table: + headers.table[k].add(v) + else: + headers.table[k] = @[v] + +func newHeaders*(obj = none(JSObject)): Headers {.jsctor.} = new(result) + if obj.isSome: + result.fill(obj.get.ctx, obj.get.val) -func newHeaderList*(table: Table[string, string]): HeaderList = +func newHeaders*(table: Table[string, string]): Headers = new(result) for k, v in table: let k = k.toHeaderCase() @@ -159,7 +212,7 @@ func newHeaderList*(table: Table[string, string]): HeaderList = else: result.table[k] = @[v] -func newRequest*(url: URL, httpmethod = HTTP_GET, headers = newHeaderList(), +func newRequest*(url: URL, httpmethod = HTTP_GET, headers = newHeaders(), body = none(string), # multipart = none(MimeData), mode = RequestMode.NO_CORS, credentialsMode = CredentialsMode.SAME_ORIGIN, @@ -179,8 +232,8 @@ func newRequest*(url: URL, httpmethod = HTTP_GET, headers = newHeaderList(), func newRequest*(url: URL, httpmethod = HTTP_GET, headers: seq[(string, string)] = @[], body = none(string), # multipart = none(MimeData), TODO TODO TODO multipart - mode = RequestMode.NO_CORS, proxy: URL = nil): Request {.jsctor.} = - let hl = newHeaderList() + mode = RequestMode.NO_CORS, proxy: URL = nil): Request = + let hl = newHeaders() for pair in headers: let (k, v) = pair hl.table[k] = @[v] @@ -198,20 +251,47 @@ func createPotentialCORSRequest*(url: URL, destination: RequestDestination, cors else: CredentialsMode.INCLUDE return newRequest(url, destination = destination, mode = mode, credentialsMode = credentialsMode) +#TODO resource as Request +#TODO also, I'm not sure what to do with init. For now I've re-introduced +# JSObject to make this work, but I really wish we had a better solution. +func newRequest*(resource: string, init: JSObject): Request {.jserr, jsctor.} = + let x = parseURL(resource) + if x.isNone: + JS_ERR JS_TypeError, resource & " is not a valid URL" + if x.get.username != "" or x.get.password != "": + JS_ERR JS_TypeError, resource & " is not a valid URL" + let url = x.get + let ctx = init.ctx + let fallbackMode = some(RequestMode.CORS) #TODO none if resource is request + #TODO fallback mode, origin, window, request mode, ... + let httpMethod = fromJS[HttpMethod](ctx, + JS_GetPropertyStr(ctx, init.val, "method")).get(HTTP_GET) + let body = fromJS[string](ctx, JS_GetPropertyStr(ctx, init.val, "body")) + let jheaders = JS_GetPropertyStr(ctx, init.val, "headers") + let hl = newHeaders() + hl.fill(ctx, jheaders) + let credentials = fromJS[CredentialsMode](ctx, JS_GetPropertyStr(ctx, init.val, "credentials")) + .get(CredentialsMode.SAME_ORIGIN) + let mode = fromJS[RequestMode](ctx, JS_GetPropertyStr(ctx, init.val, "mode")) + .get(fallbackMode.get(RequestMode.NO_CORS)) + #TODO find a standard compatible way to implement this + let proxyUrl = fromJS[URL](ctx, JS_GetPropertyStr(ctx, init.val, "proxyUrl")) + return newRequest(url, httpMethod, hl, body, mode, credentials, proxy = proxyUrl.get(nil)) + proc `[]=`*(multipart: var MimeData, k, v: string) = multipart.content.add(MimePart(name: k, content: v)) -proc add*(headers: var HeaderList, k, v: string) = +proc add*(headers: var Headers, k, v: string) = let k = k.toHeaderCase() if k notin headers.table: headers.table[k] = @[v] else: headers.table[k].add(v) -proc `[]=`*(headers: var HeaderList, k, v: string) = +proc `[]=`*(headers: var Headers, k, v: string) = headers.table[k.toHeaderCase()] = @[v] -func getOrDefault*(headers: HeaderList, k: string, default = ""): string = +func getOrDefault*(headers: Headers, k: string, default = ""): string = let k = k.toHeaderCase() if k in headers.table: headers.table[k][0] @@ -252,6 +332,9 @@ func credentialsMode*(attribute: CORSAttribute): CredentialsMode = return INCLUDE proc addRequestModule*(ctx: JSContext) = - ctx.registerType(Request, extra_getset = [TabGetSet(name: "url", get: Request_url)]) + ctx.registerType(Request, extra_getset = [ + TabGetSet(name: "url", get: Request_url), + TabGetSet(name: "referrer", get: Request_referrer) + ]) ctx.registerType(Response, extra_funcs = [TabFunc(name: "json", fun: Response_json)]) - ctx.registerType(HeaderList) + ctx.registerType(Headers) |