about summary refs log tree commit diff stats
path: root/src/loader
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-06-20 20:04:14 +0200
committerbptato <nincsnevem662@gmail.com>2024-06-20 20:06:45 +0200
commit2ab1e53b4bc15af3319994fdb25bb739b4b8e6db (patch)
tree22e32f721c783f3ee934498ec95b58f5a117ac67 /src/loader
parent60dc37269cd2dc8cdf23d9f77680f6af9490032f (diff)
downloadchawan-2ab1e53b4bc15af3319994fdb25bb739b4b8e6db.tar.gz
loader: better error handling
we no longer crash on broken codecs. yay
Diffstat (limited to 'src/loader')
-rw-r--r--src/loader/connecterror.nim1
-rw-r--r--src/loader/loader.nim42
-rw-r--r--src/loader/loaderhandle.nim7
3 files changed, 30 insertions, 20 deletions
diff --git a/src/loader/connecterror.nim b/src/loader/connecterror.nim
index 08f7b436..c3099b0f 100644
--- a/src/loader/connecterror.nim
+++ b/src/loader/connecterror.nim
@@ -1,4 +1,5 @@
 type ConnectErrorCode* = enum
+  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")
   ERROR_FAILED_TO_EXECUTE_CGI_SCRIPT = (-14, "failed to execute CGI script")
diff --git a/src/loader/loader.nim b/src/loader/loader.nim
index 89d97cde..f0ecb384 100644
--- a/src/loader/loader.nim
+++ b/src/loader/loader.nim
@@ -216,16 +216,21 @@ proc getOutputId(ctx: LoaderContext): int =
 
 proc redirectToStream(ctx: LoaderContext; output: OutputHandle;
     ps: PosixStream): bool =
-  if output.currentBuffer != nil:
-    let n = ps.sendData(output.currentBuffer, output.currentBufferIdx)
-    if unlikely(n < output.currentBuffer.len - output.currentBufferIdx):
-      ps.sclose()
-      return false
-  for buffer in output.buffers:
-    let n = ps.sendData(buffer)
-    if unlikely(n < buffer.len):
-      ps.sclose()
-      return false
+  try:
+    if output.currentBuffer != nil:
+      let n = ps.sendData(output.currentBuffer, output.currentBufferIdx)
+      if unlikely(n < output.currentBuffer.len - output.currentBufferIdx):
+        ps.sclose()
+        return false
+    for buffer in output.buffers:
+      let n = ps.sendData(buffer)
+      if unlikely(n < buffer.len):
+        ps.sclose()
+        return false
+  except ErrorBrokenPipe:
+    # ps or output is dead; give up.
+    ps.sclose()
+    return false
   if output.istreamAtEnd:
     ps.sclose()
   elif output.parent != nil:
@@ -329,8 +334,7 @@ proc loadStreamRegular(ctx: LoaderContext; handle, cachedHandle: LoaderHandle) =
     handle.outputs.del(i)
   for output in handle.outputs:
     if r == hrrBrokenPipe:
-      output.ostream.sclose()
-      output.ostream = nil
+      output.oclose()
     elif cachedHandle != nil:
       output.parent = cachedHandle
       cachedHandle.outputs.add(output)
@@ -341,8 +345,7 @@ proc loadStreamRegular(ctx: LoaderContext; handle, cachedHandle: LoaderHandle) =
       ctx.outputMap[output.ostream.fd] = output
     else:
       assert output.ostream.fd notin ctx.outputMap
-      output.ostream.sclose()
-      output.ostream = nil
+      output.oclose()
   handle.outputs.setLen(0)
   handle.iclose()
 
@@ -418,13 +421,16 @@ proc loadResource(ctx: LoaderContext; client: ClientData; config: LoaderClientCo
       handle.loadCGI(request, ctx.config.cgiDir, prevurl,
         config.insecureSSLNoVerify, ostream)
       if handle.istream != nil:
-        ctx.addFd(handle)
         if ostream != nil:
           let output = ctx.findOutput(request.body.outputId, client)
           if output != nil:
-            doAssert ctx.redirectToStream(output, ostream)
+            if not ctx.redirectToStream(output, ostream):
+              # give up.
+              handle.rejectHandle(ERROR_FAILED_TO_REDIRECT)
+              return
           else:
             ostream.sclose()
+        ctx.addFd(handle)
       else:
         handle.close()
     elif request.url.scheme == "stream":
@@ -704,6 +710,7 @@ proc acceptConnection(ctx: LoaderContext) =
         ctx.resume(stream, client, r)
   except ErrorBrokenPipe:
     # receiving end died while reading the file; give up.
+    assert stream.fd notin ctx.outputMap
     stream.sclose()
 
 proc exitLoader(ctx: LoaderContext) =
@@ -820,8 +827,7 @@ proc finishCycle(ctx: LoaderContext; unregRead: var seq[LoaderHandle];
       if output.registered:
         ctx.selector.unregister(output.ostream.fd)
       ctx.outputMap.del(output.ostream.fd)
-      output.ostream.sclose()
-      output.ostream = nil
+      output.oclose()
       let handle = output.parent
       if handle != nil: # may be nil if from loadStream S_ISREG
         let i = handle.outputs.find(output)
diff --git a/src/loader/loaderhandle.nim b/src/loader/loaderhandle.nim
index 31a41571..42f10c6e 100644
--- a/src/loader/loaderhandle.nim
+++ b/src/loader/loaderhandle.nim
@@ -185,10 +185,13 @@ proc iclose*(handle: LoaderHandle) =
     handle.istream.sclose()
     handle.istream = nil
 
+proc oclose*(output: OutputHandle) =
+  output.ostream.sclose()
+  output.ostream = nil
+
 proc close*(handle: LoaderHandle) =
   handle.iclose()
   for output in handle.outputs:
     #TODO assert not output.registered
     if output.ostream != nil:
-      output.ostream.sclose()
-      output.ostream = nil
+      output.oclose()