about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/loader/connecterror.nim1
-rw-r--r--src/loader/loader.nim78
-rw-r--r--src/loader/loaderhandle.nim6
3 files changed, 49 insertions, 36 deletions
diff --git a/src/loader/connecterror.nim b/src/loader/connecterror.nim
index c3099b0f..d8b06881 100644
--- a/src/loader/connecterror.nim
+++ b/src/loader/connecterror.nim
@@ -1,4 +1,5 @@
 type ConnectErrorCode* = enum
+  ERROR_CGI_CACHED_BODY_NOT_FOUND = (-18, "cached request body not found")
   ERROR_FAILED_TO_REDIRECT = (-17, "failed to redirect request body")
   ERROR_URL_NOT_IN_CACHE = (-16, "URL was not found in the cache")
   ERROR_FILE_NOT_IN_CACHE = (-15, "file was not found in the cache")
diff --git a/src/loader/loader.nim b/src/loader/loader.nim
index f99af337..e7a45833 100644
--- a/src/loader/loader.nim
+++ b/src/loader/loader.nim
@@ -571,42 +571,51 @@ proc parseCGIPath(ctx: LoaderContext; request: Request): CGIPath =
         break
   return cpath
 
-# Returns a stream on rbtOutput body type.
 proc loadCGI(ctx: LoaderContext; client: ClientData; handle: InputHandle;
-    request: Request; prevURL: URL; insecureSSLNoVerify: bool): PosixStream =
+    request: Request; prevURL: URL; insecureSSLNoVerify: bool) =
   if ctx.config.cgiDir.len == 0:
     handle.sendResult(ERROR_NO_CGI_DIR)
-    return nil
+    return
   let cpath = ctx.parseCGIPath(request)
   if cpath.cmd == "" or cpath.basename in ["", ".", ".."] or
       cpath.basename[0] == '~':
     handle.sendResult(ERROR_INVALID_CGI_PATH)
-    return nil
+    return
   if not fileExists(cpath.cmd):
     handle.sendResult(ERROR_CGI_FILE_NOT_FOUND)
-    return nil
+    return
   var pipefd: array[0..1, cint] # child -> parent
   if pipe(pipefd) == -1:
     handle.sendResult(ERROR_FAIL_SETUP_CGI)
-    return nil
+    return
   # Pipe the request body as stdin for POST.
   var istream: PosixStream = nil # child end (read)
   var ostream: PosixStream = nil # parent end (write)
-  case request.body.t
-  of rbtString, rbtMultipart, rbtOutput:
+  var istream2: PosixStream = nil # child end (read) for rbtCache
+  var cachedHandle: InputHandle = nil # for rbtCache
+  var outputIn: OutputHandle = nil # for rbtOutput
+  if request.body.t == rbtCache:
+    var n: int
+    (istream, n) = client.openCachedItem(request.body.cacheId)
+    if istream == nil:
+      handle.sendResult(ERROR_CGI_CACHED_BODY_NOT_FOUND)
+      return
+    cachedHandle = ctx.findCachedHandle(request.body.cacheId)
+    if cachedHandle != nil: # cached item still open, switch to streaming mode
+      istream2 = istream
+  elif request.body.t == rbtOutput:
+    outputIn = ctx.findOutput(request.body.outputId, client)
+    if outputIn == nil:
+      handle.sendResult(ERROR_FAIL_SETUP_CGI)
+      return
+  if request.body.t in {rbtString, rbtMultipart, rbtOutput} or
+      request.body.t == rbtCache and istream2 != nil:
     var pipefdRead: array[2, cint] # parent -> child
     if pipe(pipefdRead) == -1:
       handle.sendResult(ERROR_FAIL_SETUP_CGI)
       return
     istream = newPosixStream(pipefdRead[0])
     ostream = newPosixStream(pipefdRead[1])
-  of rbtCache:
-    var n: int
-    (istream, n) = client.openCachedItem(request.body.cacheId)
-    if istream == nil:
-      handle.sendResult(ERROR_FAIL_SETUP_CGI)
-      return
-  of rbtNone: discard
   let contentLen = request.body.contentLength()
   stdout.flushFile()
   stderr.flushFile()
@@ -619,6 +628,8 @@ proc loadCGI(ctx: LoaderContext; client: ClientData; handle: InputHandle;
     discard close(pipefd[1])
     if ostream != nil:
       ostream.sclose() # close write
+    if istream2 != nil:
+      istream2.sclose() # close cache file; we aren't reading it directly
     if istream != nil:
       if istream.fd != 0:
         discard dup2(istream.fd, 0) # dup stdin
@@ -650,18 +661,30 @@ proc loadCGI(ctx: LoaderContext; client: ClientData; handle: InputHandle;
     of rbtString:
       ostream.write(request.body.s)
       ostream.sclose()
-      return nil
     of rbtMultipart:
       let boundary = request.body.multipart.boundary
       for entry in request.body.multipart.entries:
         ostream.writeEntry(entry, boundary)
       ostream.writeEnd(boundary)
       ostream.sclose()
-      return nil
     of rbtOutput:
-      return ostream
-    of rbtCache, rbtNone:
-      return nil
+      ostream.setBlocking(false)
+      let output = outputIn.tee(ostream, ctx.getOutputId(), client.pid)
+      ctx.put(output)
+      output.suspended = false
+      if not output.isEmpty:
+        ctx.register(output)
+    of rbtCache:
+      if ostream != nil:
+        let handle = newInputHandle(ostream, ctx.getOutputId(), client.pid,
+          suspended = false)
+        handle.stream = istream2
+        ostream.setBlocking(false)
+        ctx.loadStreamRegular(handle, cachedHandle)
+        assert handle.stream == nil
+        handle.close()
+    of rbtNone:
+      discard
 
 proc loadStream(ctx: LoaderContext; client: ClientData; handle: InputHandle;
     request: Request) =
@@ -775,23 +798,10 @@ proc loadResource(ctx: LoaderContext; client: ClientData;
           redo = true
           continue
     if request.url.scheme == "cgi-bin":
-      let ostream = ctx.loadCGI(client, handle, request, prevurl,
-        config.insecureSSLNoVerify)
+      ctx.loadCGI(client, handle, request, prevurl, config.insecureSSLNoVerify)
       if handle.stream != nil:
-        if ostream != nil:
-          let outputIn = ctx.findOutput(request.body.outputId, client)
-          if outputIn != nil:
-            ostream.setBlocking(false)
-            let output = outputIn.tee(ostream, ctx.getOutputId(), client.pid)
-            ctx.put(output)
-            output.suspended = false
-            if not output.isEmpty:
-              ctx.register(output)
-          else:
-            ostream.sclose()
         ctx.addFd(handle)
       else:
-        assert ostream == nil
         handle.close()
     elif request.url.scheme == "stream":
       ctx.loadStream(client, handle, request)
diff --git a/src/loader/loaderhandle.nim b/src/loader/loaderhandle.nim
index acca6e34..b43149fb 100644
--- a/src/loader/loaderhandle.nim
+++ b/src/loader/loaderhandle.nim
@@ -1,5 +1,6 @@
 import std/deques
 import std/net
+import std/posix
 import std/tables
 
 import io/bufwriter
@@ -68,14 +69,15 @@ when defined(debug):
     return s
 
 # Create a new loader handle, with the output stream ostream.
-proc newInputHandle*(ostream: PosixStream; outputId, pid: int): InputHandle =
+proc newInputHandle*(ostream: PosixStream; outputId, pid: int;
+    suspended = true): InputHandle =
   let handle = InputHandle(cacheId: -1)
   handle.outputs.add(OutputHandle(
     stream: ostream,
     parent: handle,
     outputId: outputId,
     ownerPid: pid,
-    suspended: true
+    suspended: suspended
   ))
   return handle