# HG changeset patch # User Olaf Wintermann # Date 1771088904 -3600 # Node ID 879005903b2b78eca422ef4f78f5640dbeba8cdd # Parent 73987de73246fd077c5d56fe2e84b412f4f5048a implement basic http client IO diff -r 73987de73246 -r 879005903b2b src/server/daemon/httpparser.c --- a/src/server/daemon/httpparser.c Fri Feb 13 12:18:12 2026 +0100 +++ b/src/server/daemon/httpparser.c Sat Feb 14 18:08:24 2026 +0100 @@ -58,6 +58,7 @@ } void http_parser_update_request(HttpParser *parser, HTTPRequest *request) { + request->request_line = parser->start_line; request->method = parser->method; request->uri = parser->uri; request->httpv = parser->httpv; diff -r 73987de73246 -r 879005903b2b src/server/daemon/httprequest.c --- a/src/server/daemon/httprequest.c Fri Feb 13 12:18:12 2026 +0100 +++ b/src/server/daemon/httprequest.c Sat Feb 14 18:08:24 2026 +0100 @@ -480,7 +480,7 @@ array->len = 0; array->alloc = 16; array->headers = calloc(16, sizeof(Header)); - if(array->headers) { + if(!array->headers) { free(array); return NULL; } diff -r 73987de73246 -r 879005903b2b src/server/proxy/httpclient.c --- 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 +#include #include #include +#include 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; +} diff -r 73987de73246 -r 879005903b2b src/server/proxy/httpclient.h --- a/src/server/proxy/httpclient.h Fri Feb 13 12:18:12 2026 +0100 +++ b/src/server/proxy/httpclient.h Sat Feb 14 18:08:24 2026 +0100 @@ -39,6 +39,11 @@ #ifdef __cplusplus extern "C" { #endif + +#define HTTP_CLIENT_BUFFER_SIZE 4096 + +#define HTTP_CLIENT_CALLBACK_WOULD_BLOCK -1 +#define HTTP_CLIENT_CALLBACK_ERROR -2 typedef struct HttpClient HttpClient; struct HttpClient { @@ -76,8 +81,11 @@ * Response body write function * * int response_body_write(HttpClient *client, void *buf, size_t size, void *userdata) + * + * Return: number of processed bytes, + * HTTP_CLIENT_CALLBACK_WOULD_BLOCK or HTTP_CLIENT_CALLBACK_ERROR. */ - int (*response_body_write)(HttpClient *, void *, size_t, void *); + ssize_t (*response_body_write)(HttpClient *, void *, size_t, void *); void *response_body_write_userdata; /* @@ -90,9 +98,15 @@ // internals + HttpParser *parser; + netbuf buffer; char *req_buffer; size_t req_buffer_len; + size_t req_buffer_pos; + + int error; + int header_complete; Event readev; Event writeev; diff -r 73987de73246 -r 879005903b2b src/server/safs/proxy.c --- a/src/server/safs/proxy.c Fri Feb 13 12:18:12 2026 +0100 +++ b/src/server/safs/proxy.c Sat Feb 14 18:08:24 2026 +0100 @@ -28,10 +28,13 @@ #include "proxy.h" +#include +#include + #include "../util/pblock.h" - #include "../proxy/httpclient.h" +// Gets the uri part from an http request line static cxstring get_uri_from_clfreq(const char *clfreq) { cxstring uri = { NULL, 0 }; @@ -71,6 +74,27 @@ } +typedef struct ProxyData { + Session *sn; + Request *rq; +} ProxyData; + +static int proxy_response_start(HttpClient *client, int status, char *message, void *userdata) { + ProxyData *proxy = userdata; + + protocol_status(proxy->sn, proxy->rq, status, message); + protocol_start_response(proxy->sn, proxy->rq); + + return 0; +} + +static ssize_t proxy_response_write(HttpClient *client, void *buf, size_t nbytes, void *userdata) { + ProxyData *proxy = userdata; + ssize_t ret = net_write(proxy->sn->csd, buf, nbytes); + // TODO: handle errors + return ret; +} + 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); @@ -97,6 +121,13 @@ return REQ_ABORTED; } + // test address + struct sockaddr_in address; + inet_pton(AF_INET, "127.0.0.1", &address.sin_addr); + address.sin_family = AF_INET; + address.sin_port = htons(8080); + http_client_set_addr(client, (struct sockaddr*)&address, sizeof(address)); + // add request headers to the client CxIterator i = pblock_iterator(rq->headers); cx_foreach(pb_entry*, entry, i) { @@ -107,7 +138,17 @@ } } + ProxyData *proxy = pool_malloc(sn->pool, sizeof(ProxyData)); + proxy->sn = sn; + proxy->rq = rq; + client->response_start = proxy_response_start; + client->response_start_userdata = proxy; + client->response_body_write = proxy_response_write; + client->response_body_write_userdata = proxy; + + //net_setnonblock(sn->csd, 1); if(http_client_start(client)) { + //net_setnonblock(sn->csd, 0); http_client_free(client); return REQ_ABORTED; }