fix multiple reads could corrupt the httpclient buffer content

Wed, 25 Feb 2026 22:16:20 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 25 Feb 2026 22:16:20 +0100
changeset 704
778dcf4ad63c
parent 703
395c62fac7e5
child 705
30de3bfd0412

fix multiple reads could corrupt the httpclient buffer content

src/server/daemon/event.h file | annotate | diff | comparison | revisions
src/server/daemon/event_bsd.c file | annotate | diff | comparison | revisions
src/server/daemon/event_linux.c file | annotate | diff | comparison | revisions
src/server/daemon/event_linux.h file | annotate | diff | comparison | revisions
src/server/daemon/event_solaris.c file | annotate | diff | comparison | revisions
src/server/proxy/httpclient.c file | annotate | diff | comparison | revisions
src/server/proxy/httpclient.h file | annotate | diff | comparison | revisions
src/server/test/httpclient.c file | annotate | diff | comparison | revisions
src/server/test/httpclient.h file | annotate | diff | comparison | revisions
src/server/test/main.c file | annotate | diff | comparison | revisions
src/server/util/socket.c file | annotate | diff | comparison | revisions
--- a/src/server/daemon/event.h	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/daemon/event.h	Wed Feb 25 22:16:20 2026 +0100
@@ -120,6 +120,7 @@
 
 int ev_pollin(EventHandler *h, int fd, Event *event);
 int ev_pollout(EventHandler *h, int fd, Event *event);
+int ev_poll(EventHandler *h, int fd, Event *event);
 int ev_remove_poll(EventHandler *h, int fd);
 int ev_send(EventHandler *h, Event *event);
 
--- a/src/server/daemon/event_bsd.c	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/daemon/event_bsd.c	Wed Feb 25 22:16:20 2026 +0100
@@ -199,6 +199,20 @@
     return kevent(h->kqueue, &kev, 1, NULL, 0, NULL);
 }
 
+int ev_poll(EventHandler *h, int fd, Event *event) {
+    event->events = EVENT_POLLOUT;
+    int filter = 0;
+    if(event->events & EVENT_POLLOUT) {
+        filter |= EVFILT_WRITE;
+    }
+    if(event->events & EVENT_POLIN) {
+        filter |= EVFILT_READ;
+    }
+    struct kevent kev;
+    EV_SET(&kev, fd, filter, EV_ADD, 0, 0, event);
+    return kevent(h->kqueue, &kev, 1, NULL, 0, NULL);
+}
+
 int ev_remove_poll(EventHandler *h, int fd) {
     struct kevent kev;
     EV_SET(&kev, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
--- a/src/server/daemon/event_linux.c	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/daemon/event_linux.c	Wed Feb 25 22:16:20 2026 +0100
@@ -278,8 +278,8 @@
     }
 }
 
-int ev_convert2sys_events(int events) {
-    int e = EPOLLET;
+uint32_t ev_convert2sys_events(int events) {
+    uint32_t e = EPOLLET | EPOLLRDHUP;
     if((events & EVENT_POLLIN) == EVENT_POLLIN) {
         e |= EPOLLIN;
     }
@@ -309,6 +309,15 @@
     return epoll_ctl(ev->ep, EPOLL_CTL_ADD, fd, &epev);
 }
 
+int ev_poll(EventHandler *h, int fd, Event *event) {
+    EventHandlerLinux *ev = (EventHandlerLinux*)h;
+    event->object = (intptr_t)fd;
+    struct epoll_event epev;
+    epev.events = ev_convert2sys_events(event->events);
+    epev.data.ptr = event;
+    return epoll_ctl(ev->ep, EPOLL_CTL_ADD, fd, &epev);
+}
+
 int ev_remove_poll(EventHandler *h, int fd) {
     EventHandlerLinux *ev = (EventHandlerLinux*)h;
     return epoll_ctl(ev->ep, EPOLL_CTL_DEL, fd, NULL);
--- a/src/server/daemon/event_linux.h	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/daemon/event_linux.h	Wed Feb 25 22:16:20 2026 +0100
@@ -111,7 +111,7 @@
 
 void ev_handle_events(EventHandlerLinux *ev);
 
-int ev_convert2sys_events(int events);
+uint32_t ev_convert2sys_events(int events);
 
 void ev_queue_free(EventQueue *queue);
 
--- a/src/server/daemon/event_solaris.c	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/daemon/event_solaris.c	Wed Feb 25 22:16:20 2026 +0100
@@ -202,6 +202,17 @@
             event);
 }
 
+int ev_poll(EventHandler *h, int fd, Event *event) {
+    EventHandlerSolaris *ev = (EventHandlerSolaris*)h;
+    event->object = (intptr_t)fd;
+    return port_associate(
+            ev->port,
+            PORT_SOURCE_FD,
+            (uintptr_t)fd,
+            ev_convert2sys_events(event->events),
+            event);
+}
+
 int ev_remove_poll(EventHandler *h, int fd) {
     EventHandlerSolaris *ev = (EventHandlerSolaris*)h;
     return port_dissociate(ev->port, PORT_SOURCE_FD, (uintptr_t)fd);
--- a/src/server/proxy/httpclient.c	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/proxy/httpclient.c	Wed Feb 25 22:16:20 2026 +0100
@@ -194,22 +194,19 @@
     return http_client_add_request_header(client, cx_mutstr("transfer-encoding"), cx_mutstr("chunked"));
 }
 
-static int client_start_poll(HttpClient *client, int in) {
+static int client_start_poll(HttpClient *client) {
     client->event.fn = client_connected;
     client->event.finish = client_finished;
-    if(in) {
-        return ev_pollin(client->ev, client->socketfd, &client->event);
-    } else {
-        return ev_pollout(client->ev, client->socketfd, &client->event);
-    }
+    return ev_poll(client->ev, client->socketfd, &client->event);
 }
 
 int http_client_start(HttpClient *client) {
+    client->event.events = EVENT_POLLOUT;
     client->event.cookie = client;
     if(client->socketfd != -1) {
         int ret = client_connected(client->ev, &client->event);
         if(ret != 0) {
-            return client_start_poll(client, 1); // TODO: check event type
+            return client_start_poll(client);
         }
         return 0;
     }
@@ -222,12 +219,12 @@
         return 1;
     }
     client->socketfd = socketfd;
-    
+      
     int ret = 1;
     if(connect(socketfd, client->addr, client->addrlen)) {
         int err = errno;
         if(err == EINPROGRESS) {
-            ret = client_start_poll(client, 1);
+            ret = client_start_poll(client);
         } else {
             log_ereport(LOG_FAILURE, "http-client-start: connect failed: %s", strerror(err));
         }
@@ -326,7 +323,7 @@
     event->events = EVENT_POLLIN;
     client->stage = 1;
     
-    if(client_read_response_header(client)) {
+    if(client_read_response_header(client)) {  
         return client->error == 0;
     }
     int ret = 0;
@@ -472,9 +469,9 @@
     
     unsigned char *buffer = client->buffer.inbuf + client->buffer.cursize;
     size_t nbytes = client->buffer.maxsize - client->buffer.cursize;
-          
+    
     ssize_t r;
-    while((r = read(client->socketfd, buffer, nbytes)) > 0) {     
+    while((r = read(client->socketfd, client->buffer.inbuf + client->buffer.cursize, client->buffer.maxsize - client->buffer.cursize)) > 0) {     
         client->buffer.cursize += r;
         if(!client->response_header_complete) {
             switch(http_parser_process(client->parser)) {
--- a/src/server/proxy/httpclient.h	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/proxy/httpclient.h	Wed Feb 25 22:16:20 2026 +0100
@@ -62,7 +62,7 @@
     
     int socketfd;
     HttpStream *stream;
-    
+      
     HeaderArray *request_headers;
     HeaderArray *response_headers;
     
--- a/src/server/test/httpclient.c	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/test/httpclient.c	Wed Feb 25 22:16:20 2026 +0100
@@ -43,6 +43,21 @@
 static pthread_cond_t test_cond;
 static volatile int test_finished;
 
+void http_client_tests_init(void) {
+    pthread_mutex_init(&test_mutex, NULL);
+    pthread_cond_init(&test_cond, NULL);
+    
+    EventHandlerConfig config;
+    config.isdefault = 0;
+    config.name = cx_str(NULL);
+    config.nthreads = 1;
+    test_eventhandler = evhandler_create(&config);
+}
+
+void http_client_tests_cleanup(void) {
+    ev_instance_shutdown(test_eventhandler->instances[0]);
+}
+
 static void test_response_finished(HttpClient *client, void *userdata) {
     pthread_mutex_lock(&test_mutex);
     pthread_cond_signal(&test_cond);
@@ -53,21 +68,10 @@
 
 static ssize_t test_response_body_write(HttpClient *client, void *buf, size_t nbytes, void *userdata) {
     char *str = buf;
-    return cxBufferWrite(buf, 1, nbytes, userdata);
+    return cxBufferWrite(str, 1, nbytes, userdata);
 }
 
 CX_TEST_SUBROUTINE(test_httpclient, cxstring *response_blocks, size_t num_blocks, CxBuffer *out, int error_interval) {
-    pthread_mutex_init(&test_mutex, NULL);
-    pthread_cond_init(&test_cond, NULL);
-    
-    if(!test_eventhandler) {
-        EventHandlerConfig config;
-        config.isdefault = 0;
-        config.name = cx_str(NULL);
-        config.nthreads = 1;
-        test_eventhandler = evhandler_create(&config);
-    }
-    
     HttpClient *client = http_client_new(test_eventhandler->instances[0]);
     client->response_body_write = test_response_body_write;
     client->response_body_write_userdata = out;
@@ -80,7 +84,7 @@
     http_client_set_socket(client, fds[0]);
     http_client_set_method(client, "GET");
     http_client_set_uri(client, "/testx01");
-    
+     
     test_finished = 0;
     int ret = http_client_start(client);
     CX_TEST_ASSERT(ret == 0);
@@ -97,13 +101,13 @@
         }
     }
     
-    close(fds[1]);
-    
     pthread_mutex_lock(&test_mutex);
     if(test_finished == 0) {
         pthread_cond_wait(&test_cond, &test_mutex);
     }
     pthread_mutex_unlock(&test_mutex); 
+    
+    close(fds[1]);
 }
 
 CX_TEST(test_http_client_simple_get1) {
--- a/src/server/test/httpclient.h	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/test/httpclient.h	Wed Feb 25 22:16:20 2026 +0100
@@ -35,6 +35,9 @@
 extern "C" {
 #endif
 
+void http_client_tests_init(void);
+void http_client_tests_cleanup(void);
+    
 CX_TEST(test_http_client_simple_get1);
 CX_TEST(test_http_client_simple_get_line_io);
 CX_TEST(test_http_client_simple_get_small_blocksize);
--- a/src/server/test/main.c	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/test/main.c	Wed Feb 25 22:16:20 2026 +0100
@@ -75,6 +75,8 @@
     pblock_init_default_keys();
     vfs_init();
     
+    http_client_tests_init();
+    
     //test();
     
     //printf("%s", "Webserver Test Suite\n====================\n\n");
@@ -222,6 +224,8 @@
     cx_test_run_stdout(suite);
     fflush(stdout);
     
+    http_client_tests_cleanup();
+    
     return EXIT_SUCCESS;
 }
 
--- a/src/server/util/socket.c	Tue Feb 24 21:28:06 2026 +0100
+++ b/src/server/util/socket.c	Wed Feb 25 22:16:20 2026 +0100
@@ -65,7 +65,7 @@
     if(port) {
         *port = ntohs(addr.sin_port);
     }
-    
+        
     return socket_fd;
 }
 

mercurial