about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/loader.nim190
1 files changed, 94 insertions, 96 deletions
diff --git a/src/server/loader.nim b/src/server/loader.nim
index 535d6f16..e76d9fd9 100644
--- a/src/server/loader.nim
+++ b/src/server/loader.nim
@@ -135,10 +135,9 @@ type
     contentLen: uint64
     startTime: Time
 
-  LoaderContext = ref object
+  LoaderContext = object
     pid: int
     pagerClient: ClientHandle
-    alive: bool
     config: LoaderConfig
     handleMap: seq[LoaderHandle]
     pollData: PollData
@@ -336,7 +335,8 @@ iterator outputHandles(ctx: LoaderContext): OutputHandle {.inline.} =
     if it != nil and it of OutputHandle:
       yield OutputHandle(it)
 
-func findOutput(ctx: LoaderContext; id: int; client: ClientHandle): OutputHandle =
+func findOutput(ctx: var LoaderContext; id: int;
+    client: ClientHandle): OutputHandle =
   assert id != -1
   for it in ctx.outputHandles:
     if it.outputId == id:
@@ -361,33 +361,33 @@ func find(cacheMap: openArray[CachedItem]; id: int): int =
 type PushBufferResult = enum
   pbrDone, pbrUnregister
 
-proc register(ctx: LoaderContext; handle: InputHandle) =
+proc register(ctx: var LoaderContext; handle: InputHandle) =
   assert not handle.registered
   ctx.pollData.register(handle.stream.fd, cshort(POLLIN))
   handle.registered = true
 
-proc unregister(ctx: LoaderContext; handle: InputHandle) =
+proc unregister(ctx: var LoaderContext; handle: InputHandle) =
   assert handle.registered
   ctx.pollData.unregister(int(handle.stream.fd))
   handle.registered = false
 
-proc register(ctx: LoaderContext; output: OutputHandle) =
+proc register(ctx: var LoaderContext; output: OutputHandle) =
   assert not output.registered
   ctx.pollData.register(int(output.stream.fd), cshort(POLLOUT))
   output.registered = true
 
-proc unregister(ctx: LoaderContext; output: OutputHandle) =
+proc unregister(ctx: var LoaderContext; output: OutputHandle) =
   assert output.registered
   ctx.pollData.unregister(int(output.stream.fd))
   output.registered = false
 
-proc register(ctx: LoaderContext; client: ClientHandle) =
+proc register(ctx: var LoaderContext; client: ClientHandle) =
   assert not client.registered
   ctx.clientMap[client.pid] = client
   ctx.pollData.register(client.stream.fd, cshort(POLLIN))
   client.registered = true
 
-proc unregister(ctx: LoaderContext; client: ClientHandle) =
+proc unregister(ctx: var LoaderContext; client: ClientHandle) =
   assert client.registered
   ctx.clientMap.del(client.pid)
   ctx.pollData.unregister(int(client.stream.fd))
@@ -395,8 +395,8 @@ proc unregister(ctx: LoaderContext; client: ClientHandle) =
 
 # Either write data to the target output, or append it to the list of buffers to
 # write and register the output in our selector.
-proc pushBuffer(ctx: LoaderContext; output: OutputHandle; buffer: LoaderBuffer;
-    si: int): PushBufferResult =
+proc pushBuffer(ctx: var LoaderContext; output: OutputHandle;
+    buffer: LoaderBuffer; si: int): PushBufferResult =
   if output.suspended:
     if output.currentBuffer == nil:
       output.currentBuffer = buffer
@@ -429,11 +429,11 @@ proc pushBuffer(ctx: LoaderContext; output: OutputHandle; buffer: LoaderBuffer;
     output.buffers.addLast(buffer)
   pbrDone
 
-proc getOutputId(ctx: LoaderContext): int =
+proc getOutputId(ctx: var LoaderContext): int =
   result = ctx.outputNum
   inc ctx.outputNum
 
-proc redirectToFile(ctx: LoaderContext; output: OutputHandle;
+proc redirectToFile(ctx: var LoaderContext; output: OutputHandle;
     targetPath: string; fileOutput: out OutputHandle; osent: out uint64): bool =
   fileOutput = nil
   osent = 0
@@ -476,12 +476,12 @@ proc redirectToFile(ctx: LoaderContext; output: OutputHandle;
       fileOutput.url = output.url
   return true
 
-proc getTempFile(ctx: LoaderContext): string =
+proc getTempFile(ctx: var LoaderContext): string =
   result = ctx.config.tmpdir / "chaltmp" & $ctx.pid & "-" & $ctx.tmpfSeq
   inc ctx.tmpfSeq
 
-proc addCacheFile(ctx: LoaderContext; client: ClientHandle; output: OutputHandle):
-    int =
+proc addCacheFile(ctx: var LoaderContext; client: ClientHandle;
+    output: OutputHandle): int =
   if output.parent != nil and output.parent.cacheId != -1:
     # may happen e.g. if client tries to cache a `cache:' URL
     return output.parent.cacheId
@@ -509,19 +509,19 @@ proc openCachedItem(client: ClientHandle; id: int): (PosixStream, int) =
     return (ps, n)
   return (nil, -1)
 
-proc put(ctx: LoaderContext; handle: LoaderHandle) =
+proc put(ctx: var LoaderContext; handle: LoaderHandle) =
   let fd = int(handle.stream.fd)
   if ctx.handleMap.len <= fd:
     ctx.handleMap.setLen(fd + 1)
   assert ctx.handleMap[fd] == nil
   ctx.handleMap[fd] = handle
 
-proc unset(ctx: LoaderContext; handle: LoaderHandle) =
+proc unset(ctx: var LoaderContext; handle: LoaderHandle) =
   let fd = int(handle.stream.fd)
   if fd < ctx.handleMap.len:
     ctx.handleMap[fd] = nil
 
-proc addFd(ctx: LoaderContext; handle: InputHandle) =
+proc addFd(ctx: var LoaderContext; handle: InputHandle) =
   let output = handle.output
   output.stream.setBlocking(false)
   handle.stream.setBlocking(false)
@@ -687,7 +687,7 @@ type HandleReadResult = enum
   hrrDone, hrrUnregister, hrrBrokenPipe
 
 # Called whenever there is more data available to read.
-proc handleRead(ctx: LoaderContext; handle: InputHandle;
+proc handleRead(ctx: var LoaderContext; handle: InputHandle;
     unregWrite: var seq[OutputHandle]): HandleReadResult =
   var unregs = 0
   let maxUnregs = handle.outputs.len
@@ -734,7 +734,8 @@ proc handleRead(ctx: LoaderContext; handle: InputHandle;
 # cachedHandle is used for attaching the output handle to another
 # InputHandle when loadFromCache is called while a download is still
 # ongoing (and thus some parts of the document are not cached yet).
-proc loadStreamRegular(ctx: LoaderContext; handle, cachedHandle: InputHandle) =
+proc loadStreamRegular(ctx: var LoaderContext;
+    handle, cachedHandle: InputHandle) =
   assert handle.parser == nil # parser is only used with CGI
   var unregWrite: seq[OutputHandle] = @[]
   let r = ctx.handleRead(handle, unregWrite)
@@ -860,7 +861,7 @@ proc parseCGIPath(ctx: LoaderContext; request: Request): CGIPath =
         break
   return cpath
 
-proc loadCGI(ctx: LoaderContext; client: ClientHandle; handle: InputHandle;
+proc loadCGI(ctx: var LoaderContext; client: ClientHandle; handle: InputHandle;
     request: Request; prevURL: URL; config: LoaderClientConfig) =
   let cpath = ctx.parseCGIPath(request)
   if cpath.cmd == "" or cpath.basename in ["", ".", ".."] or
@@ -1006,8 +1007,8 @@ func findPassedFd(client: ClientHandle; name: string): int =
       return i
   return -1
 
-proc loadStream(ctx: LoaderContext; client: ClientHandle; handle: InputHandle;
-    request: Request) =
+proc loadStream(ctx: var LoaderContext; client: ClientHandle;
+    handle: InputHandle; request: Request) =
   let i = client.findPassedFd(request.url.pathname)
   if i == -1:
     handle.sendResult(ceFileNotFound, "stream not found")
@@ -1027,8 +1028,8 @@ proc loadStream(ctx: LoaderContext; client: ClientHandle; handle: InputHandle;
     # not loading from cache, so cachedHandle is nil
     ctx.loadStreamRegular(handle, nil)
 
-proc loadFromCache(ctx: LoaderContext; client: ClientHandle; handle: InputHandle;
-    request: Request) =
+proc loadFromCache(ctx: var LoaderContext; client: ClientHandle;
+    handle: InputHandle; request: Request) =
   let id = parseInt32(request.url.pathname).get(-1)
   let startFrom = parseInt32(request.url.search.substr(1)).get(0)
   let (ps, n) = client.openCachedItem(id)
@@ -1052,7 +1053,7 @@ proc loadFromCache(ctx: LoaderContext; client: ClientHandle; handle: InputHandle
 # Data URL handler.
 # Moved back into loader from CGI, because data URLs can get extremely long
 # and thus no longer fit into the environment.
-proc loadDataSend(ctx: LoaderContext; handle: InputHandle; s, ct: string) =
+proc loadDataSend(ctx: var LoaderContext; handle: InputHandle; s, ct: string) =
   handle.sendResult(0)
   handle.sendStatus(200)
   handle.sendHeaders(newHeaders({"Content-Type": ct}))
@@ -1079,7 +1080,7 @@ proc loadDataSend(ctx: LoaderContext; handle: InputHandle; s, ct: string) =
     else:
       output.oclose()
 
-proc loadData(ctx: LoaderContext; handle: InputHandle; request: Request) =
+proc loadData(ctx: var LoaderContext; handle: InputHandle; request: Request) =
   let url = request.url
   var ct = url.pathname.until(',')
   if AllChars - Ascii + Controls - {'\t'} in ct:
@@ -1185,7 +1186,7 @@ proc parseDownloadActions(ctx: LoaderContext; s: string): seq[DownloadAction] =
   result.sort(proc(a, b: DownloadAction): int = return cmp(a.n, b.n),
     Descending)
 
-proc loadAbout(ctx: LoaderContext; handle: InputHandle; request: Request) =
+proc loadAbout(ctx: var LoaderContext; handle: InputHandle; request: Request) =
   let url = request.url
   case url.pathname
   of "blank":
@@ -1243,7 +1244,7 @@ proc loadAbout(ctx: LoaderContext; handle: InputHandle; request: Request) =
   else:
     handle.rejectHandle(ceInvalidURL, "invalid download URL")
 
-proc loadResource(ctx: LoaderContext; client: ClientHandle;
+proc loadResource(ctx: var LoaderContext; client: ClientHandle;
     config: LoaderClientConfig; request: Request; handle: InputHandle) =
   var redo = true
   var tries = 0
@@ -1307,7 +1308,7 @@ proc setupRequestDefaults(request: Request; config: LoaderClientConfig) =
     if r != "":
       request.headers["Referer"] = r
 
-proc load(ctx: LoaderContext; stream: SocketStream; request: Request;
+proc load(ctx: var LoaderContext; stream: SocketStream; request: Request;
     client: ClientHandle; config: LoaderClientConfig) =
   var sy {.noinit.}: array[2, cint]
   var fail = false
@@ -1331,21 +1332,21 @@ proc load(ctx: LoaderContext; stream: SocketStream; request: Request;
       request.setupRequestDefaults(config)
       ctx.loadResource(client, config, request, handle)
 
-proc load(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
+proc load(ctx: var LoaderContext; stream: SocketStream; client: ClientHandle;
     r: var BufferedReader) =
   var request: Request
   r.sread(request)
   ctx.load(stream, request, client, client.config)
 
-proc loadConfig(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
-    r: var BufferedReader) =
+proc loadConfig(ctx: var LoaderContext; stream: SocketStream;
+    client: ClientHandle; r: var BufferedReader) =
   var request: Request
   var config: LoaderClientConfig
   r.sread(request)
   r.sread(config)
   ctx.load(stream, request, client, config)
 
-proc getCacheFile(ctx: LoaderContext; stream: SocketStream;
+proc getCacheFile(ctx: var LoaderContext; stream: SocketStream;
     r: var BufferedReader) =
   var cacheId: int
   var sourcePid: int
@@ -1359,7 +1360,7 @@ proc getCacheFile(ctx: LoaderContext; stream: SocketStream;
     else:
       w.swrite("")
 
-proc addClient(ctx: LoaderContext; stream: SocketStream;
+proc addClient(ctx: var LoaderContext; stream: SocketStream;
     r: var BufferedReader) =
   var pid: int
   var config: LoaderClientConfig
@@ -1391,7 +1392,7 @@ proc addClient(ctx: LoaderContext; stream: SocketStream;
       w.swrite(false)
   discard close(sy[1])
 
-proc removeClient(ctx: LoaderContext; stream: SocketStream;
+proc removeClient(ctx: var LoaderContext; stream: SocketStream;
     r: var BufferedReader) =
   var pid: int
   r.sread(pid)
@@ -1399,8 +1400,8 @@ proc removeClient(ctx: LoaderContext; stream: SocketStream;
     let client = ctx.clientMap[pid]
     ctx.unregClient.add(client)
 
-proc addCacheFile(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
-    r: var BufferedReader) =
+proc addCacheFile(ctx: var LoaderContext; stream: SocketStream;
+    client: ClientHandle; r: var BufferedReader) =
   var outputId: int
   var targetPid: int
   r.sread(outputId)
@@ -1414,7 +1415,7 @@ proc addCacheFile(ctx: LoaderContext; stream: SocketStream; client: ClientHandle
   stream.withPacketWriter w:
     w.swrite(id)
 
-proc redirectToFile(ctx: LoaderContext; stream: SocketStream;
+proc redirectToFile(ctx: var LoaderContext; stream: SocketStream;
     r: var BufferedReader) =
   var outputId: int
   var targetPath: string
@@ -1448,7 +1449,7 @@ proc redirectToFile(ctx: LoaderContext; stream: SocketStream;
   stream.withPacketWriter w:
     w.swrite(success)
 
-proc shareCachedItem(ctx: LoaderContext; stream: SocketStream;
+proc shareCachedItem(ctx: var LoaderContext; stream: SocketStream;
     r: var BufferedReader) =
   # share a cached file with another buffer. this is for newBufferFrom
   # (i.e. view source)
@@ -1481,7 +1482,7 @@ proc openCachedItem(ctx: LoaderContext; stream: SocketStream;
   if ps != nil:
     ps.sclose()
 
-proc passFd(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
+proc passFd(ctx: var LoaderContext; stream: SocketStream; client: ClientHandle;
     r: var BufferedReader) =
   var id: string
   r.sread(id)
@@ -1489,7 +1490,7 @@ proc passFd(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
   #TODO cloexec?
   client.passedFdMap.add((id, newPosixStream(fd)))
 
-proc addPipe(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
+proc addPipe(ctx: var LoaderContext; stream: SocketStream; client: ClientHandle;
     r: var BufferedReader) =
   var id: string
   r.sread(id)
@@ -1506,7 +1507,7 @@ proc addPipe(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
     ps.setCloseOnExec()
     client.passedFdMap.add((id, ps))
 
-proc removeCachedItem(ctx: LoaderContext; stream: SocketStream;
+proc removeCachedItem(ctx: var LoaderContext; stream: SocketStream;
     client: ClientHandle; r: var BufferedReader) =
   var id: int
   r.sread(id)
@@ -1518,7 +1519,7 @@ proc removeCachedItem(ctx: LoaderContext; stream: SocketStream;
     if item.refc == 0:
       discard unlink(cstring(item.path))
 
-proc tee(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
+proc tee(ctx: var LoaderContext; stream: SocketStream; client: ClientHandle;
     r: var BufferedReader) =
   var sourceId: int
   var targetPid: int
@@ -1543,7 +1544,8 @@ proc tee(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
     stream.withPacketWriter w:
       w.swrite(-1)
 
-proc addAuth(ctx: LoaderContext; stream: SocketStream; r: var BufferedReader) =
+proc addAuth(ctx: var LoaderContext; stream: SocketStream;
+    r: var BufferedReader) =
   var url: URL
   r.sread(url)
   let origin = url.authOrigin
@@ -1560,7 +1562,7 @@ proc addAuth(ctx: LoaderContext; stream: SocketStream; r: var BufferedReader) =
     ctx.authMap.add(item)
     ctx.pagerClient.authMap.add(item)
 
-proc suspend(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
+proc suspend(ctx: var LoaderContext; stream: SocketStream; client: ClientHandle;
     r: var BufferedReader) =
   var ids: seq[int]
   r.sread(ids)
@@ -1572,7 +1574,7 @@ proc suspend(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
         # do not waste cycles trying to push into output
         ctx.unregister(output)
 
-proc resume(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
+proc resume(ctx: var LoaderContext; stream: SocketStream; client: ClientHandle;
     r: var BufferedReader) =
   var ids: seq[int]
   r.sread(ids)
@@ -1583,7 +1585,7 @@ proc resume(ctx: LoaderContext; stream: SocketStream; client: ClientHandle;
       if not output.isEmpty or output.istreamAtEnd:
         ctx.register(output)
 
-proc readCommand(ctx: LoaderContext; client: ClientHandle) =
+proc readCommand(ctx: var LoaderContext; client: ClientHandle) =
   let stream = SocketStream(client.stream)
   try:
     assert not client.stream.isend
@@ -1650,51 +1652,9 @@ proc exitLoader(ctx: LoaderContext) =
           discard unlink(cstring(it.path))
   exitnow(1)
 
-var gctx: LoaderContext
-proc initLoaderContext(config: LoaderConfig; stream: SocketStream):
-    LoaderContext =
-  var ctx = LoaderContext(
-    alive: true,
-    config: config,
-    pid: getCurrentProcessId()
-  )
-  gctx = ctx
-  onSignal SIGTERM:
-    discard sig
-    gctx.exitLoader()
-  for dir in ctx.config.cgiDir.mitems:
-    if dir.len > 0 and dir[^1] != '/':
-      dir &= '/'
-  stream.withPacketReader r:
-    var cmd: LoaderCommand
-    r.sread(cmd)
-    doAssert cmd == lcAddClient
-    var pid: int
-    var config: LoaderClientConfig
-    r.sread(pid)
-    r.sread(config)
-    stream.withPacketWriter w:
-      w.swrite(true)
-    ctx.pagerClient = ClientHandle(stream: stream, pid: pid, config: config)
-    ctx.register(ctx.pagerClient)
-    ctx.put(ctx.pagerClient)
-  # for CGI
-  putEnv("SERVER_SOFTWARE", "Chawan")
-  putEnv("SERVER_PROTOCOL", "HTTP/1.0")
-  putEnv("SERVER_NAME", "localhost")
-  putEnv("SERVER_PORT", "80")
-  putEnv("REMOTE_HOST", "localhost")
-  putEnv("REMOTE_ADDR", "127.0.0.1")
-  putEnv("GATEWAY_INTERFACE", "CGI/1.1")
-  putEnv("CHA_INSECURE_SSL_NO_VERIFY", "0")
-  putEnv("CHA_TMP_DIR", config.tmpdir)
-  putEnv("CHA_DIR", config.configdir)
-  putEnv("CHA_BOOKMARK", config.bookmark)
-  return ctx
-
 # This is only called when an OutputHandle could not read enough of one (or
 # more) buffers, and we asked select to notify us when it will be available.
-proc handleWrite(ctx: LoaderContext; output: OutputHandle;
+proc handleWrite(ctx: var LoaderContext; output: OutputHandle;
     unregWrite: var seq[OutputHandle]) =
   while output.currentBuffer != nil:
     let buffer = output.currentBuffer
@@ -1718,7 +1678,7 @@ proc handleWrite(ctx: LoaderContext; output: OutputHandle;
       # all buffers sent, no need to select on this output again for now
       ctx.unregister(output)
 
-proc finishCycle(ctx: LoaderContext) =
+proc finishCycle(ctx: var LoaderContext) =
   # Unregister handles queued for unregistration.
   # It is possible for both unregRead and unregWrite to contain duplicates. To
   # avoid double-close/double-unregister, we set the istream/ostream of
@@ -1764,9 +1724,8 @@ proc finishCycle(ctx: LoaderContext) =
   ctx.unregWrite.setLen(0)
   ctx.unregClient.setLen(0)
 
-proc runFileLoader*(config: LoaderConfig; controlStream: SocketStream) =
-  var ctx = initLoaderContext(config, controlStream)
-  while ctx.alive:
+proc loaderLoop(ctx: var LoaderContext) =
+  while true:
     ctx.pollData.poll(-1)
     for event in ctx.pollData.events:
       let efd = int(event.fd)
@@ -1793,3 +1752,42 @@ proc runFileLoader*(config: LoaderConfig; controlStream: SocketStream) =
           ctx.unregClient.add(ClientHandle(handle))
     ctx.finishCycle()
   ctx.exitLoader()
+
+proc runFileLoader*(config: LoaderConfig; stream: SocketStream) =
+  var ctx {.global.}: LoaderContext
+  ctx = LoaderContext(
+    config: config,
+    pid: getCurrentProcessId()
+  )
+  onSignal SIGTERM:
+    discard sig
+    ctx.exitLoader()
+  for dir in ctx.config.cgiDir.mitems:
+    if dir.len > 0 and dir[^1] != '/':
+      dir &= '/'
+  stream.withPacketReader r:
+    var cmd: LoaderCommand
+    r.sread(cmd)
+    doAssert cmd == lcAddClient
+    var pid: int
+    var config: LoaderClientConfig
+    r.sread(pid)
+    r.sread(config)
+    stream.withPacketWriter w:
+      w.swrite(true)
+    ctx.pagerClient = ClientHandle(stream: stream, pid: pid, config: config)
+  ctx.register(ctx.pagerClient)
+  ctx.put(ctx.pagerClient)
+  # for CGI
+  putEnv("SERVER_SOFTWARE", "Chawan")
+  putEnv("SERVER_PROTOCOL", "HTTP/1.0")
+  putEnv("SERVER_NAME", "localhost")
+  putEnv("SERVER_PORT", "80")
+  putEnv("REMOTE_HOST", "localhost")
+  putEnv("REMOTE_ADDR", "127.0.0.1")
+  putEnv("GATEWAY_INTERFACE", "CGI/1.1")
+  putEnv("CHA_INSECURE_SSL_NO_VERIFY", "0")
+  putEnv("CHA_TMP_DIR", config.tmpdir)
+  putEnv("CHA_DIR", config.configdir)
+  putEnv("CHA_BOOKMARK", config.bookmark)
+  ctx.loaderLoop()