about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config/config.nim9
-rw-r--r--src/io/loader.nim132
2 files changed, 137 insertions, 4 deletions
diff --git a/src/config/config.nim b/src/config/config.nim
index 43e661c3..543236cf 100644
--- a/src/config/config.nim
+++ b/src/config/config.nim
@@ -41,6 +41,8 @@ type
     lemap*: ActionMap
     stylesheet*: string
     ambiguous_double*: bool
+    use_curl*: bool
+    curl_binary*: string
 
 func getRealKey(key: string): string =
   var realk: string
@@ -150,6 +152,13 @@ proc parseConfig(config: var Config, dir: string, t: TomlValue) =
       else: discard
     if "inline" in css:
       config.stylesheet &= css["inline"].s
+  if "network" in t:
+    let network = t["network"]
+    if "use-curl" in network:
+      let usecurl = network["use-curl"]
+      if usecurl.b:
+        config.use_curl = true
+        config.curl_binary = network["curl-binary"].s
 
 proc parseConfig(config: var Config, dir: string, stream: Stream) =
   config.parseConfig(dir, parseToml(stream))
diff --git a/src/io/loader.nim b/src/io/loader.nim
index 590ba697..c4627d56 100644
--- a/src/io/loader.nim
+++ b/src/io/loader.nim
@@ -1,7 +1,12 @@
 import httpclient
 import options
+import os
+import osproc
 import streams
+import streamwrapper
+import strutils
 
+import config/config
 import types/mime
 import types/url
 import utils/twtstr
@@ -15,6 +20,10 @@ type
     s*: Stream
     contenttype*: string
 
+  CurlStream = ref object of StreamObj
+    f: File
+    curl: Process
+
 const DefaultHeaders = {
   "User-Agent": "chawan",
   "Accept": "text/html,text/*;q=0.5",
@@ -34,6 +43,119 @@ proc newFileLoader*(): FileLoader =
     headers[header[0]] = header[1]
   newFileLoader(headers)
 
+# copy paste from stdlib for compatibility
+# ew.
+type
+  MultipartEntryClone* = object
+    name, content: string
+    case isFile: bool
+    of true:
+      filename, contentType: string
+      fileSize: int64
+      isStream: bool
+    else: discard
+
+  MultipartDataClone* = ref object
+    content: seq[MultipartEntryClone]
+
+proc fsAtEnd(s: Stream): bool = return endOfFile(CurlStream(s).f)
+proc fsSetPosition(s: Stream, pos: int) = setFilePos(CurlStream(s).f, pos)
+proc fsGetPosition(s: Stream): int = return int(getFilePos(CurlStream(s).f))
+
+proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
+  result = readBuffer(CurlStream(s).f, buffer, bufLen)
+
+proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
+  result = readBuffer(CurlStream(s).f, addr buffer[slice.a], slice.b + 1 - slice.a)
+
+proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
+  let pos = fsGetPosition(s)
+  defer: fsSetPosition(s, pos)
+  result = readBuffer(CurlStream(s).f, buffer, bufLen)
+
+proc fsReadLine(s: Stream, line: var string): bool =
+  result = readLine(CurlStream(s).f, line)
+
+proc getPageCurl(loader: FileLoader, url: Url, smethod: HttpMethod = HttpGet, mimetype = "", body: string = "", multipart: MultipartDataClone = nil): LoadResult =
+  var curl_args: seq[string]
+
+  # silent
+  curl_args.add("-s")
+
+  # follow
+  curl_args.add("-L")
+
+  # dump headers
+  curl_args.add("-D")
+  curl_args.add("/dev/stderr")
+
+  # headers
+  var requestHeaders = newHttpHeaders(true)
+  requestHeaders.table = loader.headers.table
+  if mimetype != "":
+    requestHeaders["Content-Type"] = mimetype
+  for k, v in requestHeaders:
+    curl_args.add("-H")
+    curl_args.add(k & ": " & v)
+
+  # method
+  curl_args.add("-X")
+  curl_args.add($smethod)
+
+  # body
+  if body != "":
+    curl_args.add("--data-binary")
+    curl_args.add(body)
+
+  # multipart
+  if multipart != nil:
+    for entry in multipart.content:
+      if entry.isFile:
+        #TODO TODO TODO
+        continue
+      curl_args.add("--form-string")
+      curl_args.add(entry.name & '=' & entry.content)
+
+  # url
+  curl_args.add("--")
+  curl_args.add(url.serialize(true))
+
+  # launch
+  let curl_proc = startProcess(gconfig.curl_binary, "", curl_args, options = {})
+
+  # output stream
+  var f: File
+  if not open(f, curl_proc.outputHandle, fmRead): raiseOSError(osLastError())
+  var cs = new(CurlStream)
+  cs.curl = curl_proc
+  cs.f = f
+  cs.readDataImpl = fsReadData
+  cs.closeImpl = (proc(s: Stream) = CurlStream(s).curl.close())
+  cs.atEndImpl = fsAtEnd
+  cs.readDataStrImpl = fsReadDataStr
+  cs.readDataImpl = fsReadData
+  cs.readLineImpl = fsReadLine
+  cs.peekDataImpl = fsPeekData
+  result.s = cs
+
+  # response headers
+  result.contenttype = guessContentType(url.path.serialize())
+  let es = curl_proc.errorStream
+  if not es.atEnd:
+    discard es.readLine() # status code
+  var headers = newHttpHeaders(true)
+  for line in es.lines:
+    let k = line.until(':')
+    if k.len == line.len:
+      continue # invalid, no colon
+    let v = line.substr(k.len + 1).strip()
+    headers.add(k, v)
+  let ct = headers.getOrDefault("Content-Type")
+  if ct != "":
+    result.contenttype = ct.until(';')
+  else:
+    result.contenttype = guessContentType(url.path.serialize())
+
 proc getPage*(loader: FileLoader, url: Url, smethod: HttpMethod = HttpGet, mimetype = "", body: string = "", multipart: MultipartData = nil): LoadResult =
   if url.scheme == "file":
     when defined(windows) or defined(OS2) or defined(DOS):
@@ -43,11 +165,13 @@ proc getPage*(loader: FileLoader, url: Url, smethod: HttpMethod = HttpGet, mimet
     result.contenttype = guessContentType(path)
     result.s = newFileStream(path, fmRead)
   elif url.scheme == "http" or url.scheme == "https":
-    var requestheaders = newHttpHeaders(true)
-    requestheaders.table = loader.headers.table
+    if gconfig.use_curl:
+      return getPageCurl(loader, url, smethod, mimetype, body, cast[MultipartDataClone](multipart))
+    var requestHeaders = newHttpHeaders(true)
+    requestHeaders.table = loader.headers.table
     if mimetype != "":
-      requestheaders["Content-Type"] = mimetype 
-    let resp = loader.http.request(url.serialize(true), smethod, body, requestheaders, multipart)
+      requestHeaders["Content-Type"] = mimetype
+    let resp = loader.http.request(url.serialize(true), smethod, body, requestHeaders, multipart)
     let ct = resp.contentType()
     if ct != "":
       result.contenttype = ct.until(';')