diff options
author | bptato <nincsnevem662@gmail.com> | 2023-04-30 12:19:24 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-04-30 12:19:24 +0200 |
commit | a02c408f933aea6f405ed3c64ab151b01b33ae9e (patch) | |
tree | 4946f6b13da782a92a1ecc0f03402abacf2614b3 /src/io | |
parent | 05b64a1d8fa95381d756231f665c0b8c79787b67 (diff) | |
download | chawan-a02c408f933aea6f405ed3c64ab151b01b33ae9e.tar.gz |
WIP fetch
Diffstat (limited to 'src/io')
-rw-r--r-- | src/io/loader.nim | 99 | ||||
-rw-r--r-- | src/io/request.nim | 27 |
2 files changed, 101 insertions, 25 deletions
diff --git a/src/io/loader.nim b/src/io/loader.nim index bced0298..7c55b2bc 100644 --- a/src/io/loader.nim +++ b/src/io/loader.nim @@ -26,6 +26,7 @@ import io/http import io/promise import io/request import io/urlfilter +import js/javascript import ips/serialize import ips/serversocket import ips/socketstream @@ -36,8 +37,17 @@ import types/url import utils/twtstr type - FileLoader* = object + FileLoader* = ref object process*: Pid + connecting*: Table[int, ConnectData] + ongoing*: Table[int, Response] + registerFun*: proc(fd: int) + unregisterFun*: proc(fd: int) + + ConnectData = object + promise: Promise[Response] + stream: Stream + request: Request LoaderCommand = enum LOAD, QUIT @@ -192,9 +202,72 @@ proc runFileLoader*(fd: cint, config: LoaderConfig) = ctx.handleList.del(idx) ctx.exitLoader() -#TODO async requests... +proc applyHeaders(request: Request, response: Response) = + if "Content-Type" in response.headers.table: + response.contenttype = response.headers.table["Content-Type"][0].until(';') + else: + response.contenttype = guessContentType($response.url.path) + if "Location" in response.headers.table: + if response.status in 301..303 or response.status in 307..308: + let location = response.headers.table["Location"][0] + let url = parseUrl(location, option(request.url)) + if url.isSome: + if (response.status == 303 and + request.httpmethod notin {HTTP_GET, HTTP_HEAD}) or + (response.status == 301 or response.status == 302 and + request.httpmethod == HTTP_POST): + response.redirect = newRequest(url.get, HTTP_GET, + mode = request.mode, credentialsMode = request.credentialsMode, + destination = request.destination) + else: + response.redirect = newRequest(url.get, request.httpmethod, + body = request.body, multipart = request.multipart, + mode = request.mode, credentialsMode = request.credentialsMode, + destination = request.destination) + +#TODO: add init +proc fetch*(loader: FileLoader, input: Request): Promise[Response] = + let stream = connectSocketStream(loader.process, false, blocking = true) + stream.swrite(LOAD) + stream.swrite(input) + stream.flush() + let fd = int(stream.source.getFd()) + loader.registerFun(fd) + let promise = Promise[Response]() + loader.connecting[fd] = ConnectData(promise: promise, request: input) + +proc newResponse(res: int, request: Request, stream: Stream = nil): Response = + return Response( + res: res, + url: request.url, + body: stream + ) + +proc onConnected*(loader: FileLoader, fd: int) = + let connectData = loader.connecting[fd] + let stream = connectData.stream + let promise = connectData.promise + let request = connectData.request + var res: int + stream.sread(res) + if res == 0: + let response = newResponse(res, request, stream) + response.unregisterFun = proc() = loader.unregisterFun(fd) + stream.sread(response.status) + stream.sread(response.headers) + applyHeaders(request, response) + response.body = stream + loader.ongoing[fd] = response + promise.resolve(response) + else: + #TODO: reject promise instead. + let response = newResponse(res, request) + promise.resolve(response) + loader.connecting.del(fd) + proc doRequest*(loader: FileLoader, request: Request, blocking = true): Response = new(result) + result.url = request.url let stream = connectSocketStream(loader.process, false, blocking = true) stream.swrite(LOAD) stream.swrite(request) @@ -203,27 +276,7 @@ proc doRequest*(loader: FileLoader, request: Request, blocking = true): Response if result.res == 0: stream.sread(result.status) stream.sread(result.headers) - if "Content-Type" in result.headers.table: - result.contenttype = result.headers.table["Content-Type"][0].until(';') - else: - result.contenttype = guessContentType($request.url.path) - if "Location" in result.headers.table: - if result.status in 301..303 or result.status in 307..308: - let location = result.headers.table["Location"][0] - let url = parseUrl(location, some(request.url)) - if url.isSome: - if (result.status == 303 and - request.httpmethod notin {HTTP_GET, HTTP_HEAD}) or - (result.status == 301 or result.status == 302 and - request.httpmethod == HTTP_POST): - result.redirect = newRequest(url.get, HTTP_GET, - mode = request.mode, credentialsMode = request.credentialsMode, - destination = request.destination) - else: - result.redirect = newRequest(url.get, request.httpmethod, - body = request.body, multipart = request.multipart, - mode = request.mode, credentialsMode = request.credentialsMode, - destination = request.destination) + applyHeaders(request, result) # Only a stream of the response body may arrive after this point. result.body = stream if not blocking: diff --git a/src/io/request.nim b/src/io/request.nim index 8837a1f9..c499ca8e 100644 --- a/src/io/request.nim +++ b/src/io/request.nim @@ -2,6 +2,7 @@ import options import streams import tables +import bindings/quickjs import types/url import js/javascript import utils/twtstr @@ -47,11 +48,14 @@ type Response* = ref object body*: Stream + bodyUsed* {.jsget.}: bool res* {.jsget.}: int contenttype* {.jsget.}: string status* {.jsget.}: int headers* {.jsget.}: HeaderList redirect*: Request + url*: URL #TODO should be urllist? + unregisterFun*: proc() ReadableStream* = ref object of Stream isource*: Stream @@ -203,12 +207,31 @@ func getOrDefault*(headers: HeaderList, k: string, default = ""): string = else: default -proc readAll*(response: Response): string {.jsfunc.} = +proc text*(response: Response): string {.jsfunc.} = + #TODO: this looks pretty unsafe. result = response.body.readAll() response.body.close() + response.bodyUsed = true + response.unregisterFun() + +#TODO: get rid of this +proc readAll*(response: Response): string {.jsfunc.} = + return response.text() + +proc Response_json*(ctx: JSContext, this: JSValue, argc: cint, argv: ptr JSValue): JSValue {.cdecl.} = + let op = getOpaque0(this) + if unlikely(not ctx.isInstanceOf(this, "Response") or op == nil): + return JS_ThrowTypeError(ctx, "Value is not an instance of %s", "Response") + let response = cast[Response](op) + var s = response.text() + return JS_ParseJSON(ctx, addr s[0], cast[csize_t](s.len), cstring"<input>") +#TODO: this should be a property of body proc close*(response: Response) {.jsfunc.} = + #TODO: this looks pretty unsafe response.body.close() + response.bodyUsed = true + response.unregisterFun() func credentialsMode*(attribute: CORSAttribute): CredentialsMode = case attribute @@ -219,5 +242,5 @@ func credentialsMode*(attribute: CORSAttribute): CredentialsMode = proc addRequestModule*(ctx: JSContext) = ctx.registerType(Request) - ctx.registerType(Response) + ctx.registerType(Response, extra_funcs = [TabFunc(name: "json", fun: Response_json)]) ctx.registerType(HeaderList) |