fix cgi poll event handling

Sun, 12 May 2024 11:26:59 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 12 May 2024 11:26:59 +0200
changeset 516
ec22d4ccd081
parent 515
2c3fe06a9210
child 517
be62c9604377

fix cgi poll event handling

src/server/safs/cgi.c file | annotate | diff | comparison | revisions
src/server/safs/cgi.h file | annotate | diff | comparison | revisions
--- a/src/server/safs/cgi.c	Sun Apr 07 10:25:01 2024 +0200
+++ b/src/server/safs/cgi.c	Sun May 12 11:26:59 2024 +0200
@@ -184,7 +184,7 @@
     writeev->fn = cgi_writeevent;
     writeev->finish = cgi_event_finish;
     
-    
+    handler->readev = readev;
     handler->writeev = writeev;
     
     net_setnonblock(sn->csd, 1);
@@ -263,6 +263,7 @@
             handler->writebuf_size = remaining;
             handler->writebuf_pos = 0;
             
+            /*
             // initialize poll, if it isn't already active
             if(!handler->poll_out) {
                 if(event_pollout(ev, sn->csd, handler->writeev)) {
@@ -272,6 +273,7 @@
                 handler->events++;
                 handler->poll_out = TRUE;
             }
+            */
         } else {
             handler->result = REQ_ABORTED;
             log_ereport(LOG_FAILURE, "cgi_try_write: %s", strerror(net_errno(sn->csd)));
@@ -285,27 +287,62 @@
 int cgi_stdout_readevent(EventHandler *ev, Event *event) {
     CGIHandler *handler = event->cookie;
     
-    int ret = cgi_read_output(handler, ev);
-    if(ret == 0) {
-        handler->wait_read = FALSE;
+    CgiIOResult ret = cgi_read_output(handler, ev);
+    switch(ret) {
+        case CGI_IO_COMPLETE: {
+            break;
+        }
+        case CGI_IO_NEED_READ: {
+            return 1;
+        }
+        case CGI_IO_NEED_WRITE: {
+            if(event_pollout(ev, handler->parser->sn->csd, handler->readev)) {
+                handler->result = REQ_ABORTED;
+            } else {
+                handler->poll_out = TRUE;
+            }
+            break;
+        }
+        case CGI_IO_ERROR: {
+            break;
+        }
     }
-    return ret;
+    
+    handler->wait_read = FALSE;
+    return 0;
 }
 
 int cgi_writeevent(EventHandler *ev, Event *event) {
     CGIHandler *handler = event->cookie;
     
     // cgi_read_output will try to flush the buffer
-    int ret = cgi_read_output(handler, ev);
-    if(ret == 0) {
-        handler->poll_out = FALSE;
+    CgiIOResult ret = cgi_read_output(handler, ev);
+    switch(ret) {
+        case CGI_IO_COMPLETE: {
+            break;
+        }
+        case CGI_IO_NEED_READ: {
+            if(ev_pollin(ev, handler->process.out[0], event)) {
+                handler->result = REQ_ABORTED;
+            } else {
+                handler->wait_read = TRUE;
+            }
+        }
+        case CGI_IO_NEED_WRITE: {
+            return 1;
+        }
+        case CGI_IO_ERROR: {
+            break;
+        }
     }
-    return ret;
+    
+    handler->poll_out = FALSE;
+    return 0;
 }
 
 
 
-int cgi_read_output(CGIHandler *handler, EventHandler *ev) {
+CgiIOResult cgi_read_output(CGIHandler *handler, EventHandler *ev) {
     CGIResponseParser *parser = handler->parser;
     Session *sn = parser->sn;
     Request *rq = parser->rq;
@@ -315,15 +352,16 @@
     if(cgi_try_write_flush(handler, sn)) {
         if(handler->result == REQ_ABORTED) {
             log_ereport(LOG_DEBUG, "cgi-send: req: %p write failed: %s: abort", handler->parser->rq, strerror(net_errno(sn->csd)), rq);
-            return 0;
+            return CGI_IO_ERROR;
         } else {
-            return 1;
+            return CGI_IO_NEED_WRITE;
         }
     }
     
     char buf[4096]; // I/O buffer
     ssize_t r;
     
+    int ret = CGI_IO_COMPLETE;
     handler->result = REQ_PROCEED;
     while((r = read(handler->process.out[0], buf, 4096)) > 0) {
         if(parser->cgiheader) {
@@ -335,7 +373,7 @@
                         "broken cgi script response: path: %s", handler->path);
                 protocol_status(sn, rq, 500, NULL);
                 handler->result = REQ_ABORTED;
-                return 0;
+                return CGI_IO_ERROR;
             } else if(ret == 1) {
                 WS_ASSERT(pos <= r);
                 
@@ -349,22 +387,23 @@
                 handler->response = http_create_response(sn, rq);
                 if(!handler->response) {
                     handler->result = REQ_ABORTED;
-                    return 0;
+                    return CGI_IO_ERROR;
                 }
                 
                 int send_response = http_send_response(handler->response);
                 if(send_response < 0) {
                     handler->result = REQ_ABORTED;
+                    ret = CGI_IO_ERROR;
                     break;
                 } else if(send_response == 1) {
                     // EWOULDBLOCK
                     if(!handler->poll_out) {
                         if(event_pollout(ev, sn->csd, handler->writeev)) {
                             handler->result = REQ_ABORTED;
-                            return 0;
+                            return CGI_IO_ERROR;
                         }
                         handler->poll_out = TRUE;
-                        return 1;
+                        return CGI_IO_NEED_WRITE;
                     }
                 } else {
                     handler->response = NULL;
@@ -372,22 +411,22 @@
                 
                 if(pos < r) {
                     if(cgi_try_write(handler, ev, sn, &buf[pos], r-pos)) {
-                        return handler->result == REQ_ABORTED ? 0 : 1;
+                        return handler->result == REQ_ABORTED ? CGI_IO_ERROR : CGI_IO_NEED_WRITE;
                     }
                 }
             }
         } else {
             parser->response_length += r;
             if(cgi_try_write(handler, ev, sn, buf, r)) {
-                return handler->result == REQ_ABORTED ? 0 : 1;
+                return handler->result == REQ_ABORTED ? CGI_IO_ERROR : CGI_IO_NEED_WRITE;
             }
         }
     }
     if(r < 0 && errno == EWOULDBLOCK) {
-        return 1;
+        return CGI_IO_NEED_READ;
     }
     handler->cgi_eof = TRUE; 
-    return 0;
+    return ret;
 }
 
 int cgi_stderr_readevent(EventHandler *ev, Event *event) {
--- a/src/server/safs/cgi.h	Sun Apr 07 10:25:01 2024 +0200
+++ b/src/server/safs/cgi.h	Sun May 12 11:26:59 2024 +0200
@@ -35,7 +35,16 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+    
+enum CgiIOResult {
+    CGI_IO_COMPLETE = 0,
+    CGI_IO_NEED_READ,
+    CGI_IO_NEED_WRITE,
+    CGI_IO_ERROR
+};
 
+typedef enum CgiIOResult CgiIOResult;
+    
 typedef struct {
     int in[2];
     int out[2];
@@ -64,6 +73,12 @@
     char *path;
     
     /*
+     * event object for pollin
+     * active by default and my be deactivated/reactivated in some cases
+     */
+    Event *readev;
+    
+    /*
      * event object prepared for pollout
      * only activated if write returns EWOULDBLOCK
      */
@@ -155,7 +170,7 @@
 int cgi_event_finish(EventHandler *ev, Event *event);
 int cgi_writeevent(EventHandler *ev, Event *event);
 
-int cgi_read_output(CGIHandler *handler, EventHandler *ev);
+CgiIOResult cgi_read_output(CGIHandler *handler, EventHandler *ev);
 
 CGIResponseParser* cgi_parser_new(Session *sn, Request *rq);
 void cgi_parser_free(CGIResponseParser *parser);

mercurial