about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-09-09 12:18:45 +0200
committerbptato <nincsnevem662@gmail.com>2023-09-09 12:18:45 +0200
commitecaf2a633425bd6f02f7857e68410594ae0547a0 (patch)
tree46aeb17b7e0f55f90f2e10b69e7b63a9a2b5a928
parent07245ef4826298fffc3e34e34a74fdf77c7a4fac (diff)
downloadchawan-ecaf2a633425bd6f02f7857e68410594ae0547a0.tar.gz
fetch: use JSDict
-rw-r--r--src/display/client.nim4
-rw-r--r--src/html/env.nim2
-rw-r--r--src/io/headers.nim72
-rw-r--r--src/io/request.nim107
4 files changed, 139 insertions, 46 deletions
diff --git a/src/display/client.nim b/src/display/client.nim
index c8a4ab1f..c621580a 100644
--- a/src/display/client.nim
+++ b/src/display/client.nim
@@ -109,8 +109,8 @@ proc finalize(client: Client) {.jsfin.} =
 proc doRequest(client: Client, req: Request): Response {.jsfunc.} =
   return client.loader.doRequest(req)
 
-proc fetch[T: Request|string](client: Client, req: T, init = none(JSValue)):
-    JSResult[FetchPromise] {.jsfunc.} =
+proc fetch[T: Request|string](client: Client, req: T,
+    init = none(RequestInit)): JSResult[FetchPromise] {.jsfunc.} =
   let req = ?newRequest(client.jsctx, req, init)
   return ok(client.loader.fetch(req))
 
diff --git a/src/html/env.nim b/src/html/env.nim
index 144a6a05..94bef626 100644
--- a/src/html/env.nim
+++ b/src/html/env.nim
@@ -66,7 +66,7 @@ proc addNavigatorModule(ctx: JSContext) =
   ctx.registerType(PluginArray)
   ctx.registerType(MimeTypeArray)
 
-proc fetch[T: Request|string](window: Window, req: T, init = none(JSValue)):
+proc fetch[T: Request|string](window: Window, req: T, init = none(RequestInit)):
     JSResult[FetchPromise] {.jsfunc.} =
   if window.loader.isSome:
     let req = ?newRequest(window.jsctx, req, init)
diff --git a/src/io/headers.nim b/src/io/headers.nim
index e0dacf33..b02f30df 100644
--- a/src/io/headers.nim
+++ b/src/io/headers.nim
@@ -1,48 +1,78 @@
 import tables
 
+import bindings/quickjs
+import js/error
 import js/fromjs
 import js/javascript
 import utils/twtstr
 
-type Headers* = ref object
-  table* {.jsget.}: Table[string, seq[string]]
+type
+  Headers* = ref object
+    table* {.jsget.}: Table[string, seq[string]]
+
+  HeadersInitType = enum
+    HEADERS_INIT_SEQUENCE, HEADERS_INIT_TABLE
+
+  HeadersInit* = object
+    case t: HeadersInitType
+    of HEADERS_INIT_SEQUENCE:
+      s: seq[(string, string)]
+    of HEADERS_INIT_TABLE:
+      tab: Table[string, string]
 
 jsDestructor(Headers)
 
-proc fill*(headers: Headers, ctx: JSContext, val: JSValue) =
+proc fromJS2*(ctx: JSContext, val: JSValue, res: var JSResult[HeadersInit]) =
+  if JS_IsUndefined(val) or JS_IsNull(val):
+    res.err(nil)
+    return
   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]
+      res.ok(HeadersInit(t: HEADERS_INIT_SEQUENCE, s: x.get))
   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]
+      res.ok(HeadersInit(t: HEADERS_INIT_TABLE, tab: x.get))
+
+proc fill*(headers: Headers, s: seq[(string, string)]) =
+  for (k, v) in s:
+    if k in headers.table:
+      headers.table[k].add(v)
+    else:
+      headers.table[k] = @[v]
+
+proc fill*(headers: Headers, tab: Table[string, string]) =
+  for k, v in tab:
+    if k in headers.table:
+      headers.table[k].add(v)
+    else:
+      headers.table[k] = @[v]
+
+proc fill*(headers: Headers, init: HeadersInit) =
+  if init.t == HEADERS_INIT_SEQUENCE:
+    headers.fill(init.s)
+  else: # table
+    headers.fill(init.tab)
 
 func newHeaders*(): Headers =
-  new(result)
+  return Headers()
 
-func newHeaders*(ctx: JSContext, obj = none(JSValue)): Headers {.jsctor.} =
-  new(result)
+func newHeaders(obj = none(HeadersInit)): Headers {.jsctor.} =
+  let headers = Headers()
   if obj.isSome:
-    result.fill(ctx, obj.get)
+    headers.fill(obj.get)
+  return headers
 
 func newHeaders*(table: Table[string, string]): Headers =
-  new(result)
+  let headers = Headers()
   for k, v in table:
     let k = k.toHeaderCase()
-    if k in result.table:
-      result.table[k].add(v)
+    if k in headers.table:
+      headers.table[k].add(v)
     else:
-      result.table[k] = @[v]
+      headers.table[k] = @[v]
+  return headers
 
 func clone*(headers: Headers): Headers =
   return Headers(
diff --git a/src/io/request.nim b/src/io/request.nim
index 211246f1..8e1df806 100644
--- a/src/io/request.nim
+++ b/src/io/request.nim
@@ -5,17 +5,20 @@ import tables
 
 import bindings/quickjs
 import io/headers
+import js/dict
 import js/error
 import js/fromjs
 import js/javascript
+import types/blob
 import types/formdata
+import types/referer
 import types/url
 
 type
   HttpMethod* = enum
+    HTTP_GET = "GET"
     HTTP_CONNECT = "CONNECT"
     HTTP_DELETE = "DELETE"
-    HTTP_GET = "GET"
     HTTP_HEAD = "HEAD"
     HTTP_OPTIONS = "OPTIONS"
     HTTP_PATCH = "PATCH"
@@ -194,9 +197,66 @@ func createPotentialCORSRequest*(url: URL, destination: RequestDestination, cors
   else: CredentialsMode.INCLUDE
   return newRequest(url, destination = destination, mode = mode, credentialsMode = credentialsMode)
 
+type
+  BodyInitType = enum
+    BODY_INIT_BLOB, BODY_INIT_FORM_DATA, BODY_INIT_URL_SEARCH_PARAMS,
+    BODY_INIT_STRING
+
+  BodyInit = object
+    #TODO ReadableStream, BufferSource
+    case t: BodyInitType
+    of BODY_INIT_BLOB:
+      blob: Blob
+    of BODY_INIT_FORM_DATA:
+      formData: FormData
+    of BODY_INIT_URL_SEARCH_PARAMS:
+      searchParams: URLSearchParams
+    of BODY_INIT_STRING:
+      str: string
+
+  RequestInit* = object of JSDict
+    #TODO aliasing in dicts
+    `method`: HttpMethod # default: GET
+    headers: Opt[HeadersInit]
+    body: Opt[BodyInit]
+    referrer: Opt[string]
+    referrerPolicy: Opt[ReferrerPolicy]
+    credentials: Opt[CredentialsMode]
+    proxyUrl: URL
+    mode: Opt[RequestMode]
+
+proc fromJS2*(ctx: JSContext, val: JSValue, res: var JSResult[BodyInit]) =
+  if JS_IsUndefined(val) or JS_IsNull(val):
+    res.err(nil)
+    return
+  if not JS_IsObject(val):
+    res.err(newTypeError("Not an object"))
+    return
+  block formData:
+    let x = fromJS[FormData](ctx, val)
+    if x.isSome:
+      res.ok(BodyInit(t: BODY_INIT_FORM_DATA, formData: x.get))
+      return
+  block blob:
+    let x = fromJS[Blob](ctx, val)
+    if x.isSome:
+      res.ok(BodyInit(t: BODY_INIT_BLOB, blob: x.get))
+      return
+  block searchParams:
+    let x = fromJS[URLSearchParams](ctx, val)
+    if x.isSome:
+      res.ok(BodyInit(t: BODY_INIT_URL_SEARCH_PARAMS, searchParams: x.get))
+      return
+  block str:
+    let x = fromJS[string](ctx, val)
+    if x.isSome:
+      res.ok(BodyInit(t: BODY_INIT_STRING, str: x.get))
+      return
+  res.err(newTypeError("Invalid body init type"))
+
 #TODO init as an actual dictionary
 func newRequest*[T: string|Request](ctx: JSContext, resource: T,
-    init = none(JSValue)): JSResult[Request] {.jsctor.} =
+    init = none(RequestInit)): JSResult[Request] {.jsctor.} =
   when T is string:
     let url = ?newURL(resource)
     if url.username != "" or url.password != "":
@@ -224,28 +284,31 @@ func newRequest*[T: string|Request](ctx: JSContext, resource: T,
   let destination = NO_DESTINATION
   #TODO origin, window
   if init.isSome:
+    if mode == RequestMode.NAVIGATE:
+      mode = RequestMode.SAME_ORIGIN
+    #TODO flags?
+    #TODO referrer
     let init = init.get
-    httpMethod = fromJS[HttpMethod](ctx,
-      JS_GetPropertyStr(ctx, init, "method")).get(HTTP_GET)
-    let bodyProp = JS_GetPropertyStr(ctx, init, "body")
-    if not JS_IsNull(bodyProp) and not JS_IsUndefined(bodyProp):
-      # ????
-      multipart = opt(fromJS[FormData](ctx, bodyProp))
-      if multipart.isNone:
-        body = opt(fromJS[string](ctx, bodyProp))
-    #TODO inputbody
-    if (multipart.isSome or body.isSome) and
-        httpMethod in {HTTP_GET, HTTP_HEAD}:
-      return err(newTypeError("HEAD or GET Request cannot have a body."))
-    let jheaders = JS_GetPropertyStr(ctx, init, "headers")
-    headers.fill(ctx, jheaders)
-    credentials = fromJS[CredentialsMode](ctx, JS_GetPropertyStr(ctx, init,
-      "credentials")).get(credentials)
-    mode = fromJS[RequestMode](ctx, JS_GetPropertyStr(ctx, init, "mode"))
-      .get(mode)
+    httpMethod = init.`method`
+    if init.body.isSome:
+      let ibody = init.body.get
+      case ibody.t
+      of BODY_INIT_FORM_DATA:
+        multipart = opt(ibody.formData)
+      of BODY_INIT_STRING:
+        body = opt(ibody.str)
+      else:
+        discard #TODO
+      if httpMethod in {HTTP_GET, HTTP_HEAD}:
+        return err(newTypeError("HEAD or GET Request cannot have a body."))
+    if init.headers.isSome:
+      headers.fill(init.headers.get)
+    if init.credentials.isSome:
+      credentials = init.credentials.get
+    if init.mode.isSome:
+      mode = init.mode.get
     #TODO find a standard compatible way to implement this
-    let proxyUrlProp = JS_GetPropertyStr(ctx, init, "proxyUrl")
-    proxyUrl = fromJS[URL](ctx, proxyUrlProp).get(nil)
+    proxyUrl = init.proxyUrl
   return ok(Request(
     url: url,
     httpmethod: httpmethod,