about summary refs log tree commit diff stats
path: root/src/io/loader.nim
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-09-06 12:33:30 +0200
committerbptato <nincsnevem662@gmail.com>2022-09-06 13:39:26 +0200
commite38402dfa1bbc33db6b9d9736517eb45533d595c (patch)
tree9bbe6a589f1cba631198640bb167a664de69ca12 /src/io/loader.nim
parent3223a3364ae9f17dd2dc25bcbf2e644380db462f (diff)
downloadchawan-e38402dfa1bbc33db6b9d9736517eb45533d595c.tar.gz
Use unix domain sockets for IPC
Diffstat (limited to 'src/io/loader.nim')
-rw-r--r--src/io/loader.nim126
1 files changed, 74 insertions, 52 deletions
diff --git a/src/io/loader.nim b/src/io/loader.nim
index 4a1e35e7..31b51930 100644
--- a/src/io/loader.nim
+++ b/src/io/loader.nim
@@ -1,30 +1,44 @@
+# A file loader server (?)
+# The idea here is that we receive requests with a socket, then respond to each
+# with a response (ideally a document.)
+# For now, the protocol looks like:
+# C: Request
+# S: res (0 => success, _ => error)
+# if success:
+#  S: status code
+#  S: headers
+#  S: response body
+#
+# The body is passed to the stream as-is, so effectively nothing can follow it.
+
 import options
 import streams
 import tables
+import net
+import os
 when defined(posix):
   import posix
 
 import bindings/curl
 import io/http
-import io/process
 import io/request
 import io/serialize
-import types/mime
+import io/socketstream
 import types/url
+import utils/twtstr
 
-const DefaultHeaders = {
+const DefaultHeaders0 = {
   "User-Agent": "chawan",
   "Accept": "text/html,text/*;q=0.5",
   "Accept-Language": "en;q=1.0",
   "Pragma": "no-cache",
   "Cache-Control": "no-cache",
-}.toTable().newHeaderList()
+}.toTable()
+let DefaultHeaders = DefaultHeaders0.newHeaderList()
 
 type FileLoader* = ref object
   defaultHeaders*: HeaderList
-  process*: int
-  istream*: Stream
-  ostream*: Stream
+  process*: Pid
 
 proc loadFile(url: Url, ostream: Stream) =
   when defined(windows) or defined(OS2) or defined(DOS):
@@ -33,13 +47,12 @@ proc loadFile(url: Url, ostream: Stream) =
     let path = url.path.serialize_unicode()
   let istream = newFileStream(path, fmRead)
   if istream == nil:
-    ostream.swrite(1)
+    ostream.swrite(-1) # error
     ostream.flush()
   else:
     ostream.swrite(0)
     ostream.swrite(200) # ok
-    ostream.swrite(guessContentType(path))
-    ostream.swrite(none(Url))
+    ostream.swrite(newHeaderList())
     while not istream.atEnd:
       const bufferSize = 4096
       var buffer {.noinit.}: array[bufferSize, char]
@@ -47,13 +60,10 @@ proc loadFile(url: Url, ostream: Stream) =
         let n = readData(istream, addr buffer[0], bufferSize)
         if n == 0:
           break
-        ostream.swrite(n)
         ostream.writeData(addr buffer[0], n)
         ostream.flush()
         if n < bufferSize:
           break
-      ostream.swrite("")
-      ostream.flush()
 
 proc loadResource(loader: FileLoader, request: Request, ostream: Stream) =
   case request.url.scheme
@@ -61,14 +71,31 @@ proc loadResource(loader: FileLoader, request: Request, ostream: Stream) =
     loadFile(request.url, ostream)
   of "http", "https":
     loadHttp(request, ostream)
+  else:
+    ostream.swrite(-1) # error
+    ostream.flush()
+
+const SocketDirectory = "/tmp/cha/"
+const SocketPathPrefix = SocketDirectory & "cha_sock_"
+func getSocketPath(pid: Pid): string =
+  SocketPathPrefix & $pid
 
 proc runFileLoader(loader: FileLoader) =
   if curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK:
-    eprint "Failed to initialize libcurl."
-    quit(1)
-  let istream = newFileStream(stdin)
-  let ostream = newFileStream(stdout)
+    raise newException(Defect, "Failed to initialize libcurl.")
+  let path = getSocketPath(getpid())
+  discard unlink(cstring(path))
+  createDir(SocketDirectory)
+  let sock = newSocket(Domain.AF_UNIX, SockType.SOCK_STREAM, Protocol.IPPROTO_IP)
+  bindUnix(sock, path)
+  listen(sock)
+  stdout.write(char(0u8))
+  stdout.flushFile()
   while true:
+    var sock2: Socket
+    sock.accept(sock2)
+    let istream = newSocketStream(sock2, nil)
+    let ostream = newSocketStream(nil, sock2)
     try:
       let request = istream.readRequest()
       for k, v in loader.defaultHeaders.table:
@@ -79,62 +106,57 @@ proc runFileLoader(loader: FileLoader) =
       # End-of-file, quit.
       # TODO this should be EOFError
       break
-  istream.close()
-  ostream.close()
+    close(sock2)
   curl_global_cleanup()
+  close(sock)
+  discard unlink(cstring(path))
   quit(0)
 
-proc doRequest*(loader: FileLoader, request: Request): LoadResult =
-  if loader.istream != nil:
-    loader.istream.swrite(request)
-    loader.istream.flush()
-    loader.ostream.sread(result.res)
-    if result.res == 0:
-      loader.ostream.sread(result.status)
-      loader.ostream.sread(result.contenttype)
-      loader.ostream.sread(result.redirect)
-      result.body = newReadableStream(loader.ostream)
-  else:
-    raise newException(Defect, "Error: no loader process")
+proc doRequest*(loader: FileLoader, request: Request): Response =
+  let sock = newSocket(Domain.AF_UNIX, SockType.SOCK_STREAM, Protocol.IPPROTO_IP)
+  let path = getSocketPath(loader.process)
+  connectUnix(sock, path)
+  let istream = newSocketStream(nil, sock)
+  let ostream = newSocketStream(sock, nil)
+  istream.swrite(request)
+  istream.flush()
+  ostream.sread(result.res)
+  if result.res == 0:
+    ostream.sread(result.status)
+    ostream.sread(result.headers)
+    if "Content-Type" in result.headers.table:
+      result.contenttype = result.headers.table["Content-Type"][0].until(';')
+    if "Location" in result.headers.table:
+      let location = result.headers.table["Location"][0]
+      result.redirect = parseUrl(location, some(request.url))
+    # Only a stream of the response body may arrive after this point.
+    result.body = ostream
 
 proc newFileLoader*(defaultHeaders: HeaderList): FileLoader =
   new(result)
   result.defaultHeaders = defaultHeaders
   when defined(posix):
-    var pipefd_a: array[0..1, cint]
     var pipefd_b: array[0..1, cint]
-    if pipe(pipefd_a) == -1:
-      eprint "Failed to open pipe."
-      quit(1)
     if pipe(pipefd_b) == -1:
-      eprint "Failed to open pipe."
-      quit(1)
-    let pid = doFork()
-    if pid == 0:
+      raise newException(Defect, "Failed to open pipe.")
+    let pid = fork()
+    if pid == -1:
+      raise newException(Defect, "Failed to fork network process")
+    elif pid == 0:
       # child process
-      let readfd = pipefd_a[0] # get read a
-      discard close(pipefd_a[1]) # close write a
       let writefd = pipefd_b[1] # get write b
       discard close(pipefd_b[0]) # close read b
-      discard dup2(readfd, stdin.getFileHandle())
       discard dup2(writefd, stdout.getFileHandle())
       result.runFileLoader()
     else:
       result.process = pid
-      let writefd = pipefd_a[1] # get write a
-      discard close(pipefd_a[0]) # close read a
       let readfd = pipefd_b[0] # get read b
       discard close(pipefd_b[1]) # close write b
       var readf: File
-      var writef: File
       if not open(readf, FileHandle(readfd), fmRead):
-        eprint "Failed to open output handle."
-        quit(1)
-      if not open(writef, FileHandle(writefd), fmWrite):
-        eprint "Failed to open input handle."
-        quit(1)
-      result.ostream = newFileStream(readf)
-      result.istream = newFileStream(writef)
+        raise newException(Defect, "Failed to open output handle.")
+      var n: uint8
+      assert newFileStream(readf).readUint8() == 0u8
 
 proc newFileLoader*(): FileLoader =
   newFileLoader(DefaultHeaders)