fix proxy event handling

Thu, 26 Feb 2026 21:35:53 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 26 Feb 2026 21:35:53 +0100
changeset 707
5fb102d2c745
parent 706
df64b4b79912
child 708
027b16665f13

fix proxy event handling

src/server/proxy/httpclient.c file | annotate | diff | comparison | revisions
src/server/proxy/httpclient.h file | annotate | diff | comparison | revisions
src/server/safs/proxy.c file | annotate | diff | comparison | revisions
src/server/util/io.c file | annotate | diff | comparison | revisions
--- a/src/server/proxy/httpclient.c	Wed Feb 25 23:14:47 2026 +0100
+++ b/src/server/proxy/httpclient.c	Thu Feb 26 21:35:53 2026 +0100
@@ -39,6 +39,7 @@
 
 static int client_connected(EventHandler *ev, Event *event);
 static int client_io(EventHandler *ev, Event *event);
+static int client_process(HttpClient *client, Event *event);
 static int client_finished(EventHandler *ev, Event *event);
 
 static int client_send_buf(HttpClient *client);
@@ -239,11 +240,14 @@
     return ret;
 }
 
-int http_client_process(HttpClient *client) {
-    if(client->stage < 0) {
-        return 0;
+int http_client_process(HttpClient *client, Event *event) {
+    int ret = client_process(client, event);
+    if(ret && client->error == 0 && client->event.fn == NULL) {
+        if(client_start_poll(client)) {
+            client->error = 1;
+        }
     }
-    return client_io(client->ev, &client->event);
+    return ret;
 }
 
 size_t http_client_message_buf_size_available(HttpClient *client) {
@@ -296,6 +300,7 @@
 
 static int client_connected(EventHandler *ev, Event *event) {
     HttpClient *client = event->cookie;
+    client->last_event = event;
     if(client->stage < 0) {
         return 0;
     }
@@ -310,6 +315,11 @@
 
 static int client_io(EventHandler *ev, Event *event) {
     HttpClient *client = event->cookie;
+    return client_process(client, event);
+}
+
+static int client_process(HttpClient *client, Event *event) {
+    client->last_event = event;
     if(client->stage < 0) {
         return 0;
     }
@@ -330,7 +340,23 @@
     }
     
     // writing complete, switch to read events
-    event->events = EVENT_POLLIN;
+    
+    if(client->event.events != EVENT_POLLIN) {
+        if(&client->event != event) {
+            // The current event, that invoked client_process, is not the
+            // actual HttpClient event.
+            // Remove the current HttpClient poll event and re-add it later
+            // if needed
+            if(ev_remove_poll(client->ev, client->socketfd)) {
+                client->error = 1;
+                return 1;
+            }
+            client->event.fn = NULL;
+        }
+        client->event.events = EVENT_POLLIN;
+        
+    }
+    
     client->stage = 1;
     
     if(client_read_response_header(client)) {  
@@ -357,6 +383,7 @@
     
     close(client->socketfd);
     client->socketfd = -1;
+    client->stage = -1;
     
     // request finished
     if(client->response_finished) {
@@ -381,7 +408,7 @@
     if(w <= 0) {
         if(errno != EAGAIN) {
             // TODO: log correct host
-            log_ereport(LOG_VERBOSE, "http-client %s - %s: write failed: %s", "localhost", client->uri, strerror(errno));
+            log_ereport(LOG_FAILURE, "http-client %s - %s: write failed: %s", "localhost", client->uri, strerror(errno));
             client->error = 1;
         }
         return 1;
--- a/src/server/proxy/httpclient.h	Wed Feb 25 23:14:47 2026 +0100
+++ b/src/server/proxy/httpclient.h	Thu Feb 26 21:35:53 2026 +0100
@@ -141,6 +141,10 @@
     void (*response_finished)(HttpClient *, void *);
     void *response_finished_userdata;
     
+    /*
+     * The last event handler, that processed the http client
+     */
+    Event *last_event;
     
     // internals
     HttpParser *parser;
@@ -219,7 +223,7 @@
 /*
  * Handle HttpClient IO and process the request/response
  */
-int http_client_process(HttpClient *client);
+int http_client_process(HttpClient *client, Event *event);
 
 /*
  * Adds message data, that will be sent to client->socketfd. This function
--- a/src/server/safs/proxy.c	Wed Feb 25 23:14:47 2026 +0100
+++ b/src/server/safs/proxy.c	Thu Feb 26 21:35:53 2026 +0100
@@ -130,6 +130,17 @@
         ret = REQ_ABORTED;
     }
     
+    if(client->last_event != &proxy->event && proxy->event.fn != NULL) {
+        if(!ev_remove_poll(client->ev, proxy->event.object)) {
+            proxy_unref(proxy);
+        } else {
+            log_ereport(LOG_FAILURE, "proxy_response_finished: cannot remove poll");
+        }
+        proxy->event.finish = NULL;
+    }
+    
+    // return to nsapi threadpool
+    net_setnonblock(proxy->sn->csd, 0);
     nsapi_function_return(proxy->sn, proxy->rq, ret);
     
     proxy_unref(proxy);
@@ -137,7 +148,7 @@
 
 static int proxy_request_read_event(EventHandler *eh, Event *event) {
     ProxyRequest *proxy = event->cookie;
-    if(http_client_process(proxy->client)) {
+    if(http_client_process(proxy->client, event)) {
         if(proxy->client->error == 0) {
             return 1;
         }
@@ -161,7 +172,7 @@
         if(st->io_errno == EWOULDBLOCK) {
             ret = HTTP_CLIENT_CALLBACK_WOULD_BLOCK;
             // is there already an poll event for csd?
-            if(!proxy->event.cookie) {
+            if(!proxy->event.fn) {
                 proxy->ref++;
                 proxy->event.cookie = proxy;
                 proxy->event.fn = proxy_request_read_event;
@@ -181,16 +192,41 @@
 static ssize_t proxy_response_write(HttpClient *client, void *buf, size_t nbytes, void *userdata) {
     ProxyRequest *proxy = userdata;
     ssize_t ret = net_write(proxy->sn->csd, buf, nbytes);
-    // TODO: handle errors
+    if(ret == NETBUF_EOF) {
+        ret = HTTP_CLIENT_CALLBACK_ERROR;
+    } else if(ret < 0) {
+        IOStream *st = proxy->sn->csd;
+        if(st->io_errno == EWOULDBLOCK) {
+            // Is there already an poll event for csd?
+            if(client->last_event == &proxy->event) {
+                // Yes, there is already an event, and it even is the event that triggert this call.
+                // Make sure it is a write event
+                proxy->event.events = EVENT_POLLOUT;
+                return HTTP_CLIENT_CALLBACK_WOULD_BLOCK;
+            } else if(proxy->event.fn != NULL) {
+                // There is an inactive event, probably a read event
+                // Remove the old event before we add a write event
+                if(ev_remove_poll(client->ev, proxy->event.object)) {
+                    log_ereport(LOG_FAILURE, "proxy_response_write: cannot remove poll");
+                    return HTTP_CLIENT_CALLBACK_ERROR;
+                }
+            } else {
+                proxy->ref++;
+                proxy->event.cookie = proxy;
+                proxy->event.fn = proxy_request_read_event;
+                proxy->event.finish = proxy_request_read_finished;
+            }
+            // Add write event
+            if(event_pollout(client->ev, proxy->sn->csd, &proxy->event)) {
+                proxy_unref(proxy);
+                log_ereport(LOG_FAILURE, "proxy_response_write: cannot add write poll");
+            }
+            return HTTP_CLIENT_CALLBACK_ERROR;
+        }
+    }
     return ret;
 }
 
-static void proxy_request_finished(HttpClient *client, void *userdata) {
-    ProxyRequest *proxy = userdata;
-    net_setnonblock(proxy->sn->csd, 0);
-    nsapi_saf_return(proxy->sn, proxy->rq, REQ_PROCEED);
-}
-
 int http_reverse_proxy_service(pblock *param, Session *sn, Request *rq) {
     EventHandler *ev = sn->ev;
     const char *method = pblock_findkeyval(pb_key_method, rq->reqpb);
@@ -396,7 +432,7 @@
     client->response_start_userdata = proxy;
     client->response_body_write = proxy_response_write;
     client->response_body_write_userdata = proxy;
-    client->response_finished = proxy_request_finished;
+    client->response_finished = proxy_response_finished;
     client->response_finished_userdata = proxy;
     
     net_setnonblock(sn->csd, 1);
--- a/src/server/util/io.c	Wed Feb 25 23:14:47 2026 +0100
+++ b/src/server/util/io.c	Thu Feb 26 21:35:53 2026 +0100
@@ -360,7 +360,7 @@
 
 WSBool httpstream_eof(IOStream *st) {
     HttpStream *http = (HttpStream*)st;
-    return http->read_eof;
+    return http->read_eof || http->read == http->max_read;
 }
 
 int64_t httpstream_written(IOStream *st) {

mercurial