First version of the refactor

This commit is contained in:
2025-11-21 12:53:03 +01:00
parent 7be22fed4b
commit 879dc74e34
55 changed files with 4929 additions and 10370 deletions
+99 -79
View File
@@ -1,98 +1,118 @@
#ifndef CLIENT_INCLUDED
#define CLIENT_INCLUDED
#include <stdbool.h>
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#ifndef HTTP_CLIENT_CAPACITY
// The maximum ammount of requests that can be performed
// in parallel.
#define HTTP_CLIENT_CAPACITY (1<<7)
#endif
// Initialize the global state of cHTTP.
//
// cHTTP tries to avoid global state. What this function
// does is call the global initialization functions of
// its dependencies (OpenSSL and Winsock)
int http_global_init(void);
typedef enum {
HTTP_CLIENT_CONN_FREE,
HTTP_CLIENT_CONN_WAIT_LINE,
HTTP_CLIENT_CONN_WAIT_HEADER,
HTTP_CLIENT_CONN_WAIT_BODY,
HTTP_CLIENT_CONN_FLUSHING,
HTTP_CLIENT_CONN_BUFFERING,
HTTP_CLIENT_CONN_COMPLETE,
} HTTP_ClientConnState;
// Free the global state of cHTTP.
void http_global_free(void);
// Opaque type describing an "HTTP client". Any request
// that is started must always be associated to an HTTP
// client object.
typedef struct HTTP_Client HTTP_Client;
// Handle for a pending request. This should be considered
// opaque. Don't read or modify its fields!
// Fields of this struct are private
typedef struct {
void *data0;
int data1;
int data2;
} HTTP_RequestBuilder;
HTTP_ClientConnState state;
ByteQueue input;
ByteQueue output;
} HTTP_ClientConn;
// Initialize a client object. If something goes wrong,
// NULL is returned.
HTTP_Client *http_client_init(void);
// Fields of this struct are private
typedef struct {
// Deinitialize a client object
// Array of connections. The counter contains the
// number of structs such that state=FREE.
int num_conns;
HTTP_ClientConn conns[HTTP_CLIENT_CAPACITY];
// Queue of indices referring to connections that
// are in the COMPLETE state.
int num_ready;
int ready_head;
int ready[HTTP_CLIENT_CAPACITY];
// Asynchronous TCP and TLS socket abstraction
SocketManager sockets;
// The server object doesn't interact with this
// field directly, it just initializes the socket
// manager with a pointer to it. This allows
// allocating the exact number of sockets we
// will need.
Socket socket_pool[HTTP_CLIENT_CAPACITY];
} HTTP_Client;
// Initialize an HTTP client object. This allows one to
// perform a number of requests in parallel.
int http_client_init(HTTP_Client *client);
// Release resources associated to a client object.
void http_client_free(HTTP_Client *client);
// Create a request object associated to the given client.
// On success, 0 is returned and the handle is initialized.
// On error, -1 is returned.
int http_client_get_builder(HTTP_Client *client, HTTP_RequestBuilder *builder);
// When a thread is blocked waiting for client events,
// other threads can call this function to wake it up.
int http_client_wakeup(HTTP_Client *client);
void http_request_builder_user_data(HTTP_RequestBuilder builder, void *user_data);
typedef struct {
HTTP_Client *client;
uint16_t index;
uint16_t gen;
} HTTP_RequestBuilder;
// Enable/disable I/O tracing for the specified request.
// This must be done when the request is in the initialization
// phase.
void http_request_builder_trace(HTTP_RequestBuilder builder, bool trace);
// Create a new request builder object. If the response
// pointer is NULL, a brand new builder is created. If
// response isn't NULL (and http_free_response wasn't
// called on it yet), the connection associated to that
// previous exchange is reused. Note that it's up to the
// user to make sure the requests are targeting the same
// host. Returns 0 on success, -1 on error.
int http_client_get_builder(HTTP_Client *client,
HTTP_Response *response, HTTP_RequestBuilder *builder);
// Set the method and URL of the specified request object.
// This must be the first thing you do after http_client_request
// is called (you may http_request_trace before, but nothing
// else!)
void http_request_builder_line(HTTP_RequestBuilder builder, HTTP_Method method, HTTP_String url);
// Set the URL of the current request. This is the first
// function of the request builder that the user must call.
void http_request_builder_url(HTTP_RequestBuilder builder, String url);
// Append a header to the specified request. You must call
// this after http_request_line and may do so multiple times.
void http_request_builder_header(HTTP_RequestBuilder builder, HTTP_String str);
// After the URL, the user may set zero or more headers.
void http_request_builder_header(HTTP_RequestBuilder builder, String str);
// Append some data to the request's body. You must call
// this after either http_request_line or http_request_header.
void http_request_builder_body(HTTP_RequestBuilder builder, HTTP_String str);
// Append bytes to the request's body. You can call this
// any amount of times, as long as it's after having set
// the URL.
void http_request_builder_body(HTTP_RequestBuilder builder, String str);
// Mark the initialization of the request as completed and
// perform the request.
void http_request_builder_submit(HTTP_RequestBuilder builder);
// Mark this request as complete. This invalidates the
// builder.
void http_request_builder_send(HTTP_RequestBuilder builder);
// Free resources associated to a request. This must be called
// after the request has completed.
//
// TODO: allow aborting pending requests
void http_response_free(HTTP_Response *res);
// List all low-level socket events the client is
// waiting for such that the caller can call poll()
// with it.
int http_client_register_events(HTTP_Client *client,
struct pollfd *polled, int max_polled);
// Wait for the completion of one request associated to
// the client. The handle of the resolved request is returned
// through the handle output parameter. If you're not
// interested in which request completed (like when you
// have only one pending request), you can pass NULL.
//
// On error -1 is retutned, else 0 is returned and the
// handle is initialized.
//
// Note that calling this function when no requests are
// pending is considered an error.
int http_client_wait(HTTP_Client *client, HTTP_Response **res, void **user_data);
// The caller has waited for poll() to return and some
// I/O events to be triggered, so now the HTTP client
// can continue its buffering and flushing operations.
int http_client_process_events(HTTP_Client *client,
struct pollfd *polled, int num_polled);
// TODO: comment
HTTP_Response *http_get(HTTP_String url,
HTTP_String *headers, int num_headers);
// After some I/O events were processes, some responses
// may be availabe. This function returns one of the
// buffered responses. If a request was available, true
// is returned. If no more are avaiable, false is returned.
// The returned response must either be freed using the
// http_free_response function or reused by passing it
// to http_client_get_builder.
bool http_client_next_response(HTTP_Client *client,
HTTP_Response **response);
// TODO: comment
HTTP_Response *http_post(HTTP_String url,
HTTP_String *headers, int num_headers,
HTTP_String body);
#endif // CLIENT_INCLUDED
// Free a response object. You can't access its fields
// again after this.
void http_free_response(HTTP_Response *res);