src/server/proxy/httpclient.c

changeset 671
879005903b2b
parent 669
ccdc97fd8204
--- a/src/server/proxy/httpclient.c	Fri Feb 13 12:18:12 2026 +0100
+++ b/src/server/proxy/httpclient.c	Sat Feb 14 18:08:24 2026 +0100
@@ -29,10 +29,15 @@
 #include "httpclient.h"
 
 #include <cx/buffer.h>
+#include <cx/string.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
 
 static int client_connected(EventHandler *ev, Event *event);
+static int client_io(EventHandler *ev, Event *event);
+
+static int client_send_request(HttpClient *client);
 
 HttpClient* http_client_new(EventHandler *ev) {
     CxMempool *mp = cxMempoolCreate(32, CX_MEMPOOL_TYPE_PURE);
@@ -52,15 +57,27 @@
     }
     
     memset(client, 0, sizeof(HttpClient));
+    client->ev = ev;
     client->request_headers = req_headers;
     client->response_headers = resp_headers;
     
+    client->buffer.maxsize = HTTP_CLIENT_BUFFER_SIZE;
+    client->buffer.inbuf = malloc(HTTP_CLIENT_BUFFER_SIZE);
+    HttpParser *parser = http_parser_new2(1, &client->buffer, resp_headers);
+    if(!parser || !client->buffer.inbuf) {
+        http_client_free(client);
+        return NULL;
+    }
+    client->parser = parser;
+    
     return client;
 }
 
 void http_client_free(HttpClient *client) {
     cxMempoolFree(client->mp);
     header_array_free(client->request_headers);
+    http_parser_free(client->parser);
+    free(client->buffer.inbuf);
     free(client->addr);
     free(client->method);
     free(client->uri);
@@ -104,8 +121,8 @@
         *ptr = newvalue;
     } else {
         *ptr = NULL;
-        return 0;
     }
+    return 0;
 }
 
 int http_client_set_method_len(HttpClient *client, const char *method, size_t len) {
@@ -149,11 +166,23 @@
         close(socketfd);
         return 1;
     }
+    client->socketfd = socketfd;
     
     client->writeev.cookie = client;
     client->writeev.fn = client_connected;
     
-    int ret = ev_pollout(client->ev, socketfd, &client->writeev);
+    int ret = 1;
+    if(connect(socketfd, client->addr, client->addrlen)) {
+        int err = errno;
+        if(err == EINPROGRESS) {
+            ret = ev_pollout(client->ev, socketfd, &client->writeev);
+        } else {
+            log_ereport(LOG_FAILURE, "http-client-start: connect failed: %s", strerror(err));
+        }
+    } else {
+        // TODO: call client_connected directly
+    }
+    
     if(ret) {
         close(socketfd);
     }
@@ -197,9 +226,101 @@
         // TODO: set error
         return 0; // end
     }
+    event->fn = client_io;
+    
+    return client_io(ev, event);
+}
+
+static int client_io(EventHandler *ev, Event *event) {
+    HttpClient *client = event->cookie;
+    if(client->req_buffer_pos < client->req_buffer_len) {
+        if(client_send_request(client)) {
+            if(client->error) {
+                return 0; // TODO: set error
+            }
+            return 1;
+        } 
+    }
+    
+    // make sure to receive read-ready events in the future
+    event->events |= EVENT_POLLIN;
+    
+    
+    char *buffer;
+    size_t nbytes;
+    if(client->header_complete) {
+        buffer = client->buffer.inbuf;
+        nbytes = client->buffer.maxsize;
+    } else {
+        buffer = client->buffer.inbuf + client->buffer.pos;
+        nbytes = client->buffer.maxsize - client->buffer.cursize;
+    }
     
     
-    return 1;
+    ssize_t r;
+    while((r = read(client->socketfd, buffer, nbytes)) > 0) {
+        client->buffer.cursize += r;
+        if(!client->header_complete) {
+            switch(http_parser_process(client->parser)) {
+                case 0: { // finish
+                    if(!http_parser_validate(client->parser)) {
+                        client->error = 1;
+                        return 0;
+                    }
+                    
+                    client->header_complete = 1;
+                    if(client->response_start) {
+                        cxmutstr msg = client->parser->msg;
+                        char t = msg.ptr[msg.length];
+                        msg.ptr[msg.length] = 0;
+                        int ret = client->response_start(client, client->parser->status, msg.ptr, client->response_start_userdata);
+                        msg.ptr[msg.length] = t;
+                        
+                        // TODO: check ret
+                    }
+                    break;
+                }
+                case 1: { // need more data
+                    continue;
+                }
+                case 2: { // error
+                    client->error = 1;
+                    return 0;
+                }
+            }
+        }
+        
+        // header complete
+        
+        char *out = client->buffer.inbuf + client->buffer.pos;
+        size_t len = client->buffer.cursize - client->buffer.pos;
+        
+        if(client->response_body_write) {
+            int ret = client->response_body_write(client, out, len, client->response_body_write_userdata);
+            // TODO: check ret
+        }
+        
+        client->buffer.pos = 0;
+        client->buffer.cursize = 0;
+    }
+    
+    
+    return 0;
 }
 
-
+static int client_send_request(HttpClient *client) {
+    size_t nbytes = client->req_buffer_len - client->req_buffer_pos;
+    ssize_t w = write(client->socketfd, client->req_buffer + client->req_buffer_pos, nbytes);
+    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));
+            client->error = 1;
+        }
+        return 1;
+    }
+    
+    client->req_buffer_pos += w;
+    
+    return client->req_buffer_pos < client->req_buffer_len;
+}

mercurial