--- 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; +}