about summary refs log tree commit diff stats
path: root/src/loader
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-03-12 19:51:03 +0100
committerbptato <nincsnevem662@gmail.com>2024-03-12 20:20:24 +0100
commit22480a38c618aea42285b20868231f247932f2ab (patch)
treeba9b562124c1c30782ed87cd94122e962b80f637 /src/loader
parent232d861836993d81f7828a2917e8d242a23194e0 (diff)
downloadchawan-22480a38c618aea42285b20868231f247932f2ab.tar.gz
loader: remove applyHeaders
Better compute the values we need on-demand at the call sites; this way,
we can pass through content type attributes to mailcap too.

(Also, remove a bug where applyResponse was called twice.)
Diffstat (limited to 'src/loader')
-rw-r--r--src/loader/loader.nim42
-rw-r--r--src/loader/response.nim38
2 files changed, 31 insertions, 49 deletions
diff --git a/src/loader/loader.nim b/src/loader/loader.nim
index 70f84043..904069a6 100644
--- a/src/loader/loader.nim
+++ b/src/loader/loader.nim
@@ -43,11 +43,8 @@ import types/cookie
 import types/referrer
 import types/urimethodmap
 import types/url
-import utils/mimeguess
 import utils/twtstr
 
-import chagashi/charset
-
 export request
 export response
 
@@ -760,38 +757,7 @@ proc runFileLoader*(fd: cint; config: LoaderConfig) =
     ctx.finishCycle(unregRead, unregWrite)
   ctx.exitLoader()
 
-proc getAttribute(contentType, attrname: string): string =
-  let kvs = contentType.after(';')
-  var i = kvs.find(attrname)
-  var s = ""
-  if i != -1 and kvs.len > i + attrname.len and
-      kvs[i + attrname.len] == '=':
-    i += attrname.len + 1
-    while i < kvs.len and kvs[i] in AsciiWhitespace:
-      inc i
-    var q = false
-    for j, c in kvs.toOpenArray(i, kvs.high):
-      if q:
-        s &= c
-      elif c == '\\':
-        q = true
-      elif c == ';' or c in AsciiWhitespace:
-        break
-      else:
-        s &= c
-  return s
-
-proc applyHeaders(response: Response; request: Request) =
-  if "Content-Type" in response.headers.table:
-    #TODO this is inefficient and broken on several levels. (In particular,
-    # it breaks mailcap named attributes other than charset.)
-    # Ideally, contentType would be a separate object type.
-    let header = response.headers.table["Content-Type"][0].toLowerAscii()
-    response.contentType = header.until(';').strip().toLowerAscii()
-    response.charset = getCharset(header.getAttribute("charset"))
-  else:
-    response.contentType = guessContentType($response.url.path,
-      "application/octet-stream", DefaultGuess)
+proc getRedirect*(response: Response; request: Request): Request =
   if "Location" in response.headers.table:
     if response.status in 301u16..303u16 or response.status in 307u16..308u16:
       let location = response.headers.table["Location"][0]
@@ -801,14 +767,15 @@ proc applyHeaders(response: Response; request: Request) =
             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,
+          return newRequest(url.get, HTTP_GET,
             mode = request.mode, credentialsMode = request.credentialsMode,
             destination = request.destination)
         else:
-          response.redirect = newRequest(url.get, request.httpMethod,
+          return newRequest(url.get, request.httpMethod,
             body = request.body, multipart = request.multipart,
             mode = request.mode, credentialsMode = request.credentialsMode,
             destination = request.destination)
+  return nil
 
 proc connect(loader: FileLoader; buffered = true): SocketStream =
   let stream = connectSocketStream(loader.process, buffered, blocking = true)
@@ -899,7 +866,6 @@ proc handleHeaders(response: Response; request: Request; stream: SocketStream) =
   stream.sread(response.outputId)
   stream.sread(response.status)
   stream.sread(response.headers)
-  response.applyHeaders(request)
   # Only a stream of the response body may arrive after this point.
   response.body = stream
 
diff --git a/src/loader/response.nim b/src/loader/response.nim
index 6add4928..f419c432 100644
--- a/src/loader/response.nim
+++ b/src/loader/response.nim
@@ -1,4 +1,6 @@
 import std/streams
+import std/strutils
+import std/tables
 
 import bindings/quickjs
 import io/promise
@@ -10,6 +12,8 @@ import loader/headers
 import loader/request
 import types/blob
 import types/url
+import utils/mimeguess
+import utils/twtstr
 
 import chagashi/charset
 import chagashi/decoder
@@ -37,15 +41,12 @@ type
     res*: int
     body*: SocketStream
     bodyUsed* {.jsget.}: bool
-    contentType*: string
     status* {.jsget.}: uint16
     headers* {.jsget.}: Headers
     headersGuard: HeadersGuard
-    redirect*: Request
     url*: URL #TODO should be urllist?
     unregisterFun*: proc()
     bodyRead*: Promise[string]
-    charset*: Charset
     internalMessage*: string # should NOT be exposed to JS!
     outputId*: int
 
@@ -87,6 +88,23 @@ proc close*(response: Response) {.jsfunc.} =
   if response.body != nil:
     response.body.close()
 
+func getCharset*(this: Response; fallback: Charset): Charset =
+  if "Content-Type" notin this.headers.table:
+    return fallback
+  let header = this.headers.table["Content-Type"][0].toLowerAscii()
+  let cs = header.getContentTypeAttr("charset").getCharset()
+  if cs == CHARSET_UNKNOWN:
+    return fallback
+  return cs
+
+func getContentType*(this: Response): string =
+  if "Content-Type" in this.headers.table:
+    let header = this.headers.table["Content-Type"][0].toLowerAscii()
+    return header.until(';').strip()
+  # also use DefaultGuess for container, so that local mime.types cannot
+  # override buffer mime.types
+  return DefaultGuess.guessContentType(this.url.pathname)
+
 proc text*(response: Response): Promise[JSResult[string]] {.jsfunc.} =
   if response.body == nil:
     let p = newPromise[JSResult[string]]()
@@ -101,16 +119,13 @@ proc text*(response: Response): Promise[JSResult[string]] {.jsfunc.} =
   let bodyRead = response.bodyRead
   response.bodyRead = nil
   return bodyRead.then(proc(s: string): JSResult[string] =
-    let cs = if response.charset == CHARSET_UNKNOWN:
-      CHARSET_UTF_8
-    else:
-      response.charset
+    let charset = response.getCharset(CHARSET_UTF_8)
     #TODO this is inefficient
     # maybe add a JS type that turns a seq[char] into JS strings
-    if cs in {CHARSET_UTF_8, CHARSET_UNKNOWN}:
+    if charset == CHARSET_UTF_8:
       ok(s.toValidUTF8())
     else:
-      ok(newTextDecoder(cs).decodeAll(s))
+      ok(newTextDecoder(charset).decodeAll(s))
   )
 
 proc blob*(response: Response): Promise[JSResult[Blob]] {.jsfunc.} =
@@ -122,13 +137,14 @@ proc blob*(response: Response): Promise[JSResult[Blob]] {.jsfunc.} =
     return p
   let bodyRead = response.bodyRead
   response.bodyRead = nil
+  let contentType = response.getContentType()
   return bodyRead.then(proc(s: string): JSResult[Blob] =
     if s.len == 0:
-      return ok(newBlob(nil, 0, response.contentType, nil))
+      return ok(newBlob(nil, 0, contentType, nil))
     GC_ref(s)
     let deallocFun = proc() =
       GC_unref(s)
-    let blob = newBlob(unsafeAddr s[0], s.len, response.contentType, deallocFun)
+    let blob = newBlob(unsafeAddr s[0], s.len, contentType, deallocFun)
     ok(blob))
 
 proc json(ctx: JSContext, this: Response): Promise[JSResult[JSValue]]