about summary refs log tree commit diff stats
path: root/src/io
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-05-21 13:28:33 +0200
committerbptato <nincsnevem662@gmail.com>2023-05-21 13:28:33 +0200
commitd09319640c4b1e24b937d1fc37a3f8b82052e612 (patch)
tree45631e7ed2c23ce4a4217bfe2037cba2ba873231 /src/io
parent7c612d65be345fd1ef156f95bb033d9bbf8aece8 (diff)
downloadchawan-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.nim6
-rw-r--r--src/io/file.nim6
-rw-r--r--src/io/http.nim4
-rw-r--r--src/io/loader.nim2
-rw-r--r--src/io/request.nim131
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)