# HG changeset patch # User Olaf Wintermann # Date 1715506019 -7200 # Node ID ec22d4ccd081e6cca2f351d9ff69454a9670d412 # Parent 2c3fe06a9210ef1a31caf1616443e6a0222d4d2c fix cgi poll event handling diff -r 2c3fe06a9210 -r ec22d4ccd081 src/server/safs/cgi.c --- 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) { diff -r 2c3fe06a9210 -r ec22d4ccd081 src/server/safs/cgi.h --- 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);