Simplify amalgamation script

This commit is contained in:
2025-07-21 02:21:33 +02:00
parent c3bfb86707
commit acf4746611
20 changed files with 542 additions and 611 deletions
+3 -26
View File
@@ -11,12 +11,10 @@ OFILES = $(patsubst %.c,%.o,$(CFILES))
# Library names
LIBNAME = chttp
STATIC_LIB = lib$(LIBNAME).a
SHARED_LIB = lib$(LIBNAME).so
# Detect OS and set executable extension
ifeq ($(OS),Windows_NT)
EXT = .exe
SHARED_LIB = $(LIBNAME).dll
else
EXT = .out
endif
@@ -36,10 +34,10 @@ EXAMPLES_ENGINE := $(patsubst %.c,%$(EXT),$(EXAMPLES_ENGINE_SRC))
all: chttp.c chttp.h examples lib
lib: $(STATIC_LIB) $(SHARED_LIB)
lib: $(STATIC_LIB)
chttp.c chttp.h: $(HFILES) $(CFILES)
python misc/amalg.py
python amalg.py
# Object files from source files
%.o: %.c $(HFILES)
@@ -49,10 +47,6 @@ chttp.c chttp.h: $(HFILES) $(CFILES)
$(STATIC_LIB): $(OFILES)
$(AR) rcs $@ $^
# Shared library
$(SHARED_LIB): $(OFILES)
$(CC) -shared -o $@ $^ $(LFLAGS)
examples: $(EXAMPLES_CLIENT) $(EXAMPLES_SERVER) $(EXAMPLES_ENGINE)
examples/client/%$(EXT): examples/client/%.c chttp.c chttp.h
@@ -64,28 +58,11 @@ examples/server/%$(EXT): examples/server/%.c chttp.c chttp.h
examples/engine/%$(EXT): examples/engine/%.c chttp.c chttp.h
$(CC) $(CFLAGS) $< chttp.c -o $@ $(LFLAGS)
# Installation targets
install: install-lib install-headers
install-lib: $(STATIC_LIB) $(SHARED_LIB)
install -d $(LIBDIR)
install -m 644 $(STATIC_LIB) $(LIBDIR)/
install -m 755 $(SHARED_LIB) $(LIBDIR)/
install-headers: chttp.h
install -d $(INCDIR)
install -m 644 chttp.h $(INCDIR)/
uninstall:
rm -f $(LIBDIR)/$(STATIC_LIB)
rm -f $(LIBDIR)/$(SHARED_LIB)
rm -f $(INCDIR)/chttp.h
clean:
rm -f client_example server_example
rm -f examples/client/*$(EXT) examples/server/*$(EXT) examples/engine/*$(EXT)
rm -f $(OFILES)
rm -f $(STATIC_LIB) $(SHARED_LIB)
rm -f $(STATIC_LIB)
rm -f chttp.c chttp.h
.PHONY: all lib examples install install-lib install-headers uninstall clean
+53
View File
@@ -0,0 +1,53 @@
class Amalgamator:
def __init__(self):
self.out = ""
def append_text(self, text):
self.out += text
def append_file(self, file):
self.out += "\n"
self.out += "////////////////////////////////////////////////////////////////////////////////////////\n"
self.out += "// " + file + "\n"
self.out += "////////////////////////////////////////////////////////////////////////////////////////\n"
self.out += "\n"
self.out += "#line 1 \"" + file + "\"\n"
self.out += open(file).read()
if len(self.out) > 0 and self.out[len(self.out)-1] != '\n':
self.out += "\n"
def save(self, file):
open(file, 'w').write(self.out)
desc = """
// This file was generated automatically. Do not modify directly!
"""
header = Amalgamator()
header.append_text("#ifndef HTTP_AMALGAMATION\n")
header.append_text("#define HTTP_AMALGAMATION\n")
header.append_text(desc)
header.append_file("src/basic.h")
header.append_file("src/parse.h")
header.append_file("src/engine.h")
header.append_file("src/cert.h")
header.append_file("src/client.h")
header.append_file("src/server.h")
header.append_file("src/router.h")
header.append_text("#endif // HTTP_AMALGAMATION\n")
header.save("chttp.h")
source = Amalgamator()
source.append_text("#include \"chttp.h\"\n")
source.append_file("src/socket.h")
source.append_file("src/basic.c")
source.append_file("src/parse.c")
source.append_file("src/engine.c")
source.append_file("src/cert.c")
source.append_file("src/socket.c")
source.append_file("src/client.c")
source.append_file("src/server.c")
source.append_file("src/router.c")
source.save("chttp.c")
+350 -239
View File
@@ -1,55 +1,10 @@
/*
* cHTTP Library - Amalgamated Source
* Generated automatically - do not edit manually
*/
#include "chttp.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
//////////////////////////////////////////////////////////////////////
// src/cert.h
//////////////////////////////////////////////////////////////////////
#ifndef CERT_INCLUDED
#define CERT_INCLUDED
#include "basic.h"
// This is an utility to create self-signed certificates
// useful when testing HTTPS servers locally. This is only
// meant to be used by people starting out with a library
// and simplifying the zero to one phase.
//
// The C, O, and CN are respectively country name, organization name,
// and common name of the certificate. For instance:
//
// C="IT"
// O="My Organization"
// CN="my_website.com"
//
// The output is a certificate file in PEM format and a private
// key file with the key used to sign the certificate.
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
HTTP_String cert_file, HTTP_String key_file);
#endif // CERT_INCLUDED//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
// src/socket.h
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/socket.h"
#ifndef SOCKET_INCLUDED
#define SOCKET_INCLUDED
// This is a socket abstraction module for non-blocking TCP and TLS sockets.
@@ -105,13 +60,17 @@ int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
// which means the user needs to call socket_free to free the socket
// as it's not unusable.
#include <stdint.h>
#ifdef HTTPS_ENABLED
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
#endif
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#endif
typedef struct {
int is_ipv6;
@@ -187,9 +146,19 @@ void socket_close (Socket *sock);
void socket_free (Socket *sock);
int socket_wait (Socket **socks, int num_socks);
#endif // SOCKET_INCLUDED//////////////////////////////////////////////////////////////////////
#endif // SOCKET_INCLUDED
////////////////////////////////////////////////////////////////////////////////////////
// src/basic.c
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/basic.c"
#include <stddef.h>
#include <string.h>
#ifndef HTTP_AMALGAMATION
#include "basic.h"
#endif
bool http_streq(HTTP_String s1, HTTP_String s2)
{
@@ -246,6 +215,7 @@ static bool is_printable(char c)
return c >= ' ' && c <= '~';
}
#include <stdio.h>
void print_bytes(HTTP_String prefix, HTTP_String src)
{
if (src.len == 0)
@@ -285,9 +255,22 @@ void print_bytes(HTTP_String prefix, HTTP_String src)
cur++;
}
putc('\n', stream);
}//////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////////////
// src/parse.c
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/parse.c"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#include "basic.h"
#endif
// From RFC 9112
// request-target = origin-form
@@ -1318,9 +1301,28 @@ HTTP_String http_getcookie(HTTP_Request *req, HTTP_String name)
{
// TODO
return (HTTP_String) {NULL, 0};
}//////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////////////
// src/engine.c
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/engine.c"
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h> // TODO: remove some of these headers
#include <stddef.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>
#ifndef HTTP_AMALGAMATION
#include "basic.h"
#include "engine.h"
#endif
// This is the implementation of a byte queue useful
// for systems that need to process engs of bytes.
@@ -2301,9 +2303,16 @@ void http_engine_undo(HTTP_Engine *eng)
eng->state = HTTP_ENGINE_STATE_CLIENT_PREP_URL;
else
eng->state = HTTP_ENGINE_STATE_SERVER_PREP_STATUS;
}//////////////////////////////////////////////////////////////////////
// src/socket.c
//////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////////////
// src/cert.c
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/cert.c"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HTTPS_ENABLED
#include <openssl/pem.h>
@@ -2313,12 +2322,195 @@ void http_engine_undo(HTTP_Engine *eng)
#include <openssl/evp.h>
#include <openssl/bn.h>
#endif
#ifndef HTTP_AMALGAMATION
#include "cert.h"
#endif
#ifdef HTTPS_ENABLED
static EVP_PKEY *generate_rsa_key_pair(int key_bits)
{
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey;
// Create the context for key generation
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx)
return NULL;
if (EVP_PKEY_keygen_init(ctx) <= 0) {
EVP_PKEY_CTX_free(ctx);
return NULL;
}
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, key_bits) <= 0) {
EVP_PKEY_CTX_free(ctx);
return NULL;
}
if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
EVP_PKEY_CTX_free(ctx);
return NULL;
}
EVP_PKEY_CTX_free(ctx);
return pkey;
}
static X509 *create_certificate(EVP_PKEY *pkey, HTTP_String C, HTTP_String O, HTTP_String CN, int days)
{
X509 *x509 = X509_new();
if (!x509)
return NULL;
// Set version (version 3)
X509_set_version(x509, 2);
// Set serial number
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
// Set validity period
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 31536000L * days); // days * seconds_per_year
// Set public key
X509_set_pubkey(x509, pkey);
// Set subject name
X509_NAME *name = X509_get_subject_name(x509);
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*) C.ptr, C.len, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*) O.ptr, O.len, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*) CN.ptr, CN.len, -1, 0);
// Set issuer name (same as subject for self-signed)
X509_set_issuer_name(x509, name);
if (!X509_sign(x509, pkey, EVP_sha256())) {
X509_free(x509);
return NULL;
}
return x509;
}
static int save_private_key(EVP_PKEY *pkey, const char *filename)
{
FILE *fp = fopen(filename, "wb");
if (!fp)
return -1;
// Write private key in PEM format
if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL)) {
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
static int cert_save(X509 *x509, const char *filename)
{
FILE *fp = fopen(filename, "wb");
if (!fp) {
fprintf(stderr, "Error opening file for certificate: %s\n", filename);
return -1;
}
// Write certificate in PEM format
if (!PEM_write_X509(fp, x509)) {
fprintf(stderr, "Error writing certificate\n");
fclose(fp);
return -1;
}
fclose(fp);
printf("Certificate saved to: %s\n", filename);
return 0;
}
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
HTTP_String cert_file, HTTP_String key_file)
{
EVP_PKEY *pkey = generate_rsa_key_pair(2048);
if (pkey == NULL)
return -1;
X509 *x509 = create_certificate(pkey, C, O, CN, 1)
if (x509 == NULL) {
EVP_PKEY_free(pkey);
return -1;
}
if (save_private_key(pkey, key_file) < 0) {
X509_free(x509);
EVP_PKEY_free(pkey);
return -1;
}
if (save_certificate(x509, cert_file) < 0) {
X509_free(x509);
EVP_PKEY_free(pkey);
return -1;
}
X509_free(x509);
EVP_PKEY_free(pkey);
return 0;
}
#else
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
HTTP_String cert_file, HTTP_String key_file)
{
(void) C;
(void) O;
(void) CN;
(void) cert_file;
(void) key_file;
return -1;
}
#endif
////////////////////////////////////////////////////////////////////////////////////////
// src/socket.c
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/socket.c"
#include <assert.h> // TODO: organize these includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#ifdef _WIN32
#include <winsock2.h>
#endif
#ifdef __linux__
#include <poll.h>
#include <netdb.h>
#endif
#ifdef HTTPS_ENABLED
#include <openssl/pem.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#endif
#ifndef HTTP_AMALGAMATION
#include "socket.h"
#endif
void socket_global_init(void)
{
#ifdef HTTPS_ENABLED
@@ -3173,12 +3365,34 @@ int socket_wait(Socket **socks, int num_socks) // TODO: is this used?
}
return -1;
}//////////////////////////////////////////////////////////////////////
// src/client.c
//////////////////////////////////////////////////////////////////////
}
// TODO
#define ERROR printf("error at %s:%d\n", __FILE__, __LINE__);
////////////////////////////////////////////////////////////////////////////////////////
// src/client.c
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/client.c"
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#define POLL WSAPoll
#endif
#ifdef __linux__
#include <poll.h>
#define POLL poll
#endif
#ifndef HTTP_AMALGAMATION
#include "client.h"
#include "socket.h"
#include "engine.h"
#endif
#define CLIENT_MAX_CONNS 256
@@ -3370,7 +3584,7 @@ int http_client_wait(HTTP_Client *client, HTTP_RequestHandle *handle)
if (num_polled == 0)
return -1;
poll(polled, num_polled, -1);
POLL(polled, num_polled, -1);
for (int i = 0; i < num_polled; i++) {
@@ -3445,7 +3659,6 @@ void http_request_line(HTTP_RequestHandle handle, HTTP_Method method, HTTP_Strin
int ret = http_parse_url(url.ptr, url.len, &parsed_url);
if (ret != url.len) {
// TODO
ERROR;
return;
}
@@ -3454,7 +3667,6 @@ void http_request_line(HTTP_RequestHandle handle, HTTP_Method method, HTTP_Strin
secure = true;
} else if (!http_streq(parsed_url.scheme, HTTP_STR("http"))) {
// TODO
ERROR;
return;
}
@@ -3474,7 +3686,6 @@ void http_request_line(HTTP_RequestHandle handle, HTTP_Method method, HTTP_Strin
case HTTP_HOST_MODE_VOID:
// TODO
ERROR;
return;
}
@@ -3619,167 +3830,40 @@ HTTP_Response *http_post(HTTP_String url, HTTP_String *headers, int num_headers,
*phandle = handle;
return res;
}//////////////////////////////////////////////////////////////////////
// src/cert.c
//////////////////////////////////////////////////////////////////////
#ifdef HTTPS_ENABLED
#include <openssl/pem.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#endif
#ifdef HTTPS_ENABLED
static EVP_PKEY *generate_rsa_key_pair(int key_bits)
{
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey;
// Create the context for key generation
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx)
return NULL;
if (EVP_PKEY_keygen_init(ctx) <= 0) {
EVP_PKEY_CTX_free(ctx);
return NULL;
}
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, key_bits) <= 0) {
EVP_PKEY_CTX_free(ctx);
return NULL;
}
if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
EVP_PKEY_CTX_free(ctx);
return NULL;
}
EVP_PKEY_CTX_free(ctx);
return pkey;
}
static X509 *create_certificate(EVP_PKEY *pkey, HTTP_String C, HTTP_String O, HTTP_String CN, int days)
{
X509 *x509 = X509_new();
if (!x509)
return NULL;
// Set version (version 3)
X509_set_version(x509, 2);
// Set serial number
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
// Set validity period
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 31536000L * days); // days * seconds_per_year
// Set public key
X509_set_pubkey(x509, pkey);
// Set subject name
X509_NAME *name = X509_get_subject_name(x509);
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*) C.ptr, C.len, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*) O.ptr, O.len, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*) CN.ptr, CN.len, -1, 0);
// Set issuer name (same as subject for self-signed)
X509_set_issuer_name(x509, name);
if (!X509_sign(x509, pkey, EVP_sha256())) {
X509_free(x509);
return NULL;
}
return x509;
}
static int save_private_key(EVP_PKEY *pkey, const char *filename)
{
FILE *fp = fopen(filename, "wb");
if (!fp)
return -1;
// Write private key in PEM format
if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL)) {
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
static int cert_save(X509 *x509, const char *filename)
{
FILE *fp = fopen(filename, "wb");
if (!fp) {
fprintf(stderr, "Error opening file for certificate: %s\n", filename);
return -1;
}
// Write certificate in PEM format
if (!PEM_write_X509(fp, x509)) {
fprintf(stderr, "Error writing certificate\n");
fclose(fp);
return -1;
}
fclose(fp);
printf("Certificate saved to: %s\n", filename);
return 0;
}
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
HTTP_String cert_file, HTTP_String key_file)
{
EVP_PKEY *pkey = generate_rsa_key_pair(2048);
if (pkey == NULL)
return -1;
X509 *x509 = create_certificate(pkey, C, O, CN, 1)
if (x509 == NULL) {
EVP_PKEY_free(pkey);
return -1;
}
if (save_private_key(pkey, key_file) < 0) {
X509_free(x509);
EVP_PKEY_free(pkey);
return -1;
}
if (save_certificate(x509, cert_file) < 0) {
X509_free(x509);
EVP_PKEY_free(pkey);
return -1;
}
X509_free(x509);
EVP_PKEY_free(pkey);
return 0;
}
#else
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
HTTP_String cert_file, HTTP_String key_file)
{
(void) C;
(void) O;
(void) CN;
(void) cert_file;
(void) key_file;
return -1;
}
#endif//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
// src/server.c
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/server.c"
#include <stdint.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdbool.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#define POLL WSAPoll
#define CLOSE_SOCKET closesocket
#endif
#ifdef __linux__
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>
#define POLL poll
#define CLOSE_SOCKET close
#endif
#ifndef HTTP_AMALGAMATION
#include "engine.h"
#include "socket.h"
#include "server.h"
#endif
#define MAX_CONNS (1<<10)
@@ -3813,12 +3897,12 @@ static int listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int b
{
int flags = fcntl(listen_fd, F_GETFL, 0);
if (flags < 0) {
close(listen_fd);
CLOSE_SOCKET(listen_fd);
return -1;
}
if (fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
close(listen_fd);
CLOSE_SOCKET(listen_fd);
return -1;
}
}
@@ -3835,7 +3919,7 @@ static int listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int b
_Static_assert(sizeof(struct in_addr) == sizeof(HTTP_IPv4));
if (http_parse_ipv4(addr.ptr, addr.len, (HTTP_IPv4*) &addr_buf) < 0) {
close(listen_fd);
CLOSE_SOCKET(listen_fd);
return -1;
}
}
@@ -3845,12 +3929,12 @@ static int listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int b
bind_buf.sin_addr = addr_buf;
bind_buf.sin_port = htons(port);
if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) {
close(listen_fd);
CLOSE_SOCKET(listen_fd);
return -1;
}
if (listen(listen_fd, backlog) < 0) {
close(listen_fd);
CLOSE_SOCKET(listen_fd);
return -1;
}
@@ -3893,7 +3977,7 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port,
else {
if (socket_group_init_server(&server->group, cert_key, private_key) < 0) {
close(server->listen_fd);
CLOSE_SOCKET(server->listen_fd);
free(server);
return NULL;
}
@@ -3901,7 +3985,7 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port,
server->secure_fd = listen_socket(addr, secure_port, reuse_addr, backlog);
if (server->secure_fd < 0) {
socket_group_free(&server->group);
close(server->listen_fd);
CLOSE_SOCKET(server->listen_fd);
free(server);
return NULL;
}
@@ -3930,8 +4014,8 @@ void http_server_free(HTTP_Server *server)
// TODO
}
close(server->secure_fd);
close(server->listen_fd);
CLOSE_SOCKET(server->secure_fd);
CLOSE_SOCKET(server->listen_fd);
if (server->secure_fd != -1)
socket_group_free(&server->group);
free(server);
@@ -4006,7 +4090,7 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl
}
int timeout = -1;
poll(polled, num_polled, timeout);
POLL(polled, num_polled, timeout);
for (int i = 0; i < num_polled; i++) {
@@ -4212,16 +4296,43 @@ void http_response_done(HTTP_ResponseHandle res)
http_engine_free(&conn->engine);
server->num_conns--;
}
}//////////////////////////////////////////////////////////////////////
// src/router.c
//////////////////////////////////////////////////////////////////////
}
#ifndef _WIN32
////////////////////////////////////////////////////////////////////////////////////////
// src/router.c
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/router.c"
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __linux__
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#endif
#ifndef HTTP_AMALGAMATION
#include "router.h"
#endif
#ifndef HTTP_AMALGAMATION
bool is_alpha(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
bool is_digit(char c)
{
return c >= '0' && c <= '9';
}
#endif // HTTP_AMALGAMATION
typedef enum {
ROUTE_STATIC_DIR,
ROUTE_DYNAMIC,
@@ -4638,4 +4749,4 @@ int http_serve(char *addr, int port, HTTP_Router *router)
http_server_free(server);
http_router_free(router);
return 0;
}
}
+80 -56
View File
@@ -1,19 +1,13 @@
/*
* cHTTP Library - Amalgamated Header
* Generated automatically - do not edit manually
*/
#ifndef HTTP_AMALGAMATION
#define HTTP_AMALGAMATION
#ifndef HTTP_AMALGAMATION_H
#define HTTP_AMALGAMATION_H
// This file was generated automatically. Do not modify directly!
#ifdef __cplusplus
extern "C" {
#endif
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
// src/basic.h
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/basic.h"
#ifndef CHTTP_BASIC_INCLUDED
#define CHTTP_BASIC_INCLUDED
@@ -91,13 +85,19 @@ HTTP_String http_trim(HTTP_String s);
#define HTTP_ASSERT(X) {if (!(X)) { __builtin_trap(); }}
#endif
#endif // CHTTP_BASIC_INCLUDED//////////////////////////////////////////////////////////////////////
// src/parse.h
//////////////////////////////////////////////////////////////////////
#endif // CHTTP_BASIC_INCLUDED
////////////////////////////////////////////////////////////////////////////////////////
// src/parse.h
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/parse.h"
#ifndef PARSE_INCLUDED
#define PARSE_INCLUDED
#ifndef HTTP_AMALGAMATION
#include "basic.h"
#endif
#define HTTP_MAX_HEADERS 32
@@ -187,13 +187,19 @@ HTTP_String http_getqueryparam (HTTP_Request *req, HTTP_String name);
HTTP_String http_getbodyparam (HTTP_Request *req, HTTP_String name);
HTTP_String http_getcookie (HTTP_Request *req, HTTP_String name);
#endif // PARSE_INCLUDED//////////////////////////////////////////////////////////////////////
// src/engine.h
//////////////////////////////////////////////////////////////////////
#endif // PARSE_INCLUDED
////////////////////////////////////////////////////////////////////////////////////////
// src/engine.h
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/engine.h"
#ifndef HTTP_ENGINE_INCLUDED
#define HTTP_ENGINE_INCLUDED
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#endif
typedef enum {
HTTP_MEMFUNC_MALLOC,
@@ -309,15 +315,52 @@ void http_engine_bodyack (HTTP_Engine *eng, int num);
void http_engine_done (HTTP_Engine *eng);
void http_engine_undo (HTTP_Engine *eng);
#endif // HTTP_ENGINE_INCLUDED//////////////////////////////////////////////////////////////////////
// src/client.h
//////////////////////////////////////////////////////////////////////
#endif // HTTP_ENGINE_INCLUDED
////////////////////////////////////////////////////////////////////////////////////////
// src/cert.h
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/cert.h"
#ifndef CERT_INCLUDED
#define CERT_INCLUDED
#ifndef HTTP_AMALGAMATION
#include "basic.h"
#endif
// This is an utility to create self-signed certificates
// useful when testing HTTPS servers locally. This is only
// meant to be used by people starting out with a library
// and simplifying the zero to one phase.
//
// The C, O, and CN are respectively country name, organization name,
// and common name of the certificate. For instance:
//
// C="IT"
// O="My Organization"
// CN="my_website.com"
//
// The output is a certificate file in PEM format and a private
// key file with the key used to sign the certificate.
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
HTTP_String cert_file, HTTP_String key_file);
#endif // CERT_INCLUDED
////////////////////////////////////////////////////////////////////////////////////////
// src/client.h
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/client.h"
#ifndef CLIENT_INCLUDED
#define CLIENT_INCLUDED
#include <stdbool.h>
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#endif
// Initialize the global state of cHTTP.
//
@@ -415,15 +458,21 @@ HTTP_Response *http_post(HTTP_String url,
HTTP_String *headers, int num_headers,
HTTP_String body, HTTP_RequestHandle *phandle);
#endif // CLIENT_INCLUDED//////////////////////////////////////////////////////////////////////
// src/server.h
//////////////////////////////////////////////////////////////////////
#endif // CLIENT_INCLUDED
////////////////////////////////////////////////////////////////////////////////////////
// src/server.h
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/server.h"
#ifndef HTTP_SERVER_INCLUDED
#define HTTP_SERVER_INCLUDED
#include <stdint.h>
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#endif
typedef struct {
void *data0;
@@ -450,40 +499,19 @@ void http_response_bodyack (HTTP_ResponseHandle res, int num);
void http_response_undo (HTTP_ResponseHandle res);
void http_response_done (HTTP_ResponseHandle res);
#endif // HTTP_SERVER_INCLUDED//////////////////////////////////////////////////////////////////////
// src/cert.h
//////////////////////////////////////////////////////////////////////
#endif // HTTP_SERVER_INCLUDED
#ifndef CERT_INCLUDED
#define CERT_INCLUDED
#include "basic.h"
// This is an utility to create self-signed certificates
// useful when testing HTTPS servers locally. This is only
// meant to be used by people starting out with a library
// and simplifying the zero to one phase.
//
// The C, O, and CN are respectively country name, organization name,
// and common name of the certificate. For instance:
//
// C="IT"
// O="My Organization"
// CN="my_website.com"
//
// The output is a certificate file in PEM format and a private
// key file with the key used to sign the certificate.
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
HTTP_String cert_file, HTTP_String key_file);
#endif // CERT_INCLUDED//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
// src/router.h
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/router.h"
#ifndef HTTP_ROUTER_INCLUDED
#define HTTP_ROUTER_INCLUDED
#ifndef HTTP_AMALGAMATION
#include "server.h"
#endif
typedef struct HTTP_Router HTTP_Router;
typedef void (*HTTP_RouterFunc)(HTTP_Request*, HTTP_ResponseHandle, void*);;
@@ -496,8 +524,4 @@ void http_router_func (HTTP_Router *router, HTTP_Method method, HTTP_
int http_serve (char *addr, int port, HTTP_Router *router);
#endif // HTTP_ROUTER_INCLUDED
#ifdef __cplusplus
}
#endif
#endif /* HTTP_AMALGAMATION_H */
#endif // HTTP_AMALGAMATION
-276
View File
@@ -1,276 +0,0 @@
#!/usr/bin/env python3
"""
AmalgamationBuilder - Implementation with improved #line support and conditional include preservation
"""
import re
import os
from typing import Set
class AmalgamationBuilder:
def __init__(self):
self.body = ""
self.local_includes: Set[str] = set() # These should deduplicate items
self.global_includes: Set[str] = set()
def add(self, filepath: str):
"""Add a file to the amalgamation"""
try:
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
except FileNotFoundError:
print(f"Warning: File '{filepath}' not found, skipping")
return
original_line_number = 1
skipped_lines = 0
first_content_line = True
in_conditional_block = False
conditional_depth = 0
self.body += "//////////////////////////////////////////////////////////////////////\n"
self.body += "// " + filepath + "\n"
self.body += "//////////////////////////////////////////////////////////////////////\n"
self.body += "\n"
for line in lines:
# Check if we're entering a conditional block
if self._is_conditional_start(line):
if conditional_depth == 0:
in_conditional_block = True
conditional_depth += 1
# Keep conditional blocks in place
self.body += line
original_line_number += 1
continue
# Check if we're exiting a conditional block
if self._is_conditional_end(line):
conditional_depth -= 1
if conditional_depth == 0:
in_conditional_block = False
# Keep conditional blocks in place
self.body += line
original_line_number += 1
continue
# If we're inside a conditional block, keep all lines as-is
if in_conditional_block:
self.body += line
original_line_number += 1
continue
# Check if the line is an include (outside conditional blocks only)
if self._is_include_line(line):
include_info = self._extract_include_info(line)
if include_info:
filename, uses_angular_brackets = include_info
if uses_angular_brackets:
self.global_includes.add(filename)
else:
self.local_includes.add(filename)
original_line_number += 1
skipped_lines += 1
continue
# Check if the line is part of a header guard
if self._is_header_guard_line(line):
original_line_number += 1
skipped_lines += 1
continue
# Skip empty lines at the beginning of files
if first_content_line and line.strip() == '':
original_line_number += 1
skipped_lines += 1
continue
# Add #line directive when starting a new file or after skipping significant content
if first_content_line or skipped_lines > 0:
# Only add #line if we skipped lines or it's a new file
# self.body += f'#line {original_line_number} "{filepath}"\n'
first_content_line = False
skipped_lines = 0
# Append the current line to body
self.body += line
original_line_number += 1
def _is_conditional_start(self, line: str) -> bool:
"""Check if a line starts a conditional compilation block"""
stripped = line.strip()
return (stripped.startswith('#ifdef ') or
stripped.startswith('#ifndef ') or
stripped.startswith('#if '))
def _is_conditional_end(self, line: str) -> bool:
"""Check if a line ends a conditional compilation block"""
stripped = line.strip()
return stripped.startswith('#endif')
def _is_include_line(self, line: str) -> bool:
"""Check if a line is an #include directive"""
# Skip obviously non-include lines for performance
if '#include' not in line:
return False
# Check if this line is commented out
if self._is_line_commented(line):
return False
# Regex to match include pattern
include_pattern = re.compile(r'^\s*#\s*include\s*[<"][^>"]+[>"]')
return bool(include_pattern.match(line.strip()))
def _extract_include_info(self, line: str) -> tuple[str, bool] | None:
"""Extract filename and bracket type from include line"""
# Pattern to capture include details
include_pattern = re.compile(r'^\s*#\s*include\s*([<"])([^>"]+)([>"])')
match = include_pattern.match(line.strip())
if match:
open_delim = match.group(1)
filename = match.group(2)
close_delim = match.group(3)
# Validate matching delimiters
if (open_delim == '<' and close_delim == '>') or \
(open_delim == '"' and close_delim == '"'):
uses_angular_brackets = (open_delim == '<')
return filename, uses_angular_brackets
return None
def _is_header_guard_line(self, line: str) -> bool:
"""Check if a line is part of a header guard (assumes _INCLUDED suffix)"""
stripped = line.strip()
# Simplified header guard patterns for _INCLUDED suffix
header_guard_patterns = [
re.compile(r'#ifndef\s+\w+_INCLUDED'),
re.compile(r'#define\s+\w+_INCLUDED'),
re.compile(r'#endif\s*//.*_INCLUDED'),
re.compile(r'#endif\s*/\*.*_INCLUDED.*\*/'),
]
return any(pattern.match(stripped) for pattern in header_guard_patterns)
def _is_line_commented(self, line: str) -> bool:
"""Basic check if line is commented out"""
include_pos = line.find('#include')
if include_pos == -1:
return False
# Check for // comment before #include
comment_pos = line.find('//')
if comment_pos != -1 and comment_pos < include_pos:
return True
# Check for /* comment before #include (basic case)
before_include = line[:include_pos]
if '/*' in before_include and '*/' not in before_include:
return True
return False
def result(self) -> str:
"""Generate the final amalgamated result"""
result = ""
# Write all global includes at the top
for item in sorted(self.global_includes):
result += f'#include <{item}>\n'
if self.global_includes:
result += "\n" # Add spacing after includes
# Add the body content
result += self.body
return result
def main():
"""Example usage of AmalgamationBuilder"""
# Build the header
print("Building header...")
header_builder = AmalgamationBuilder()
header_files = [
"src/basic.h",
"src/parse.h",
"src/engine.h",
"src/client.h",
"src/server.h",
"src/cert.h",
"src/router.h"
]
for file in header_files:
header_builder.add(file)
header = header_builder.result()
# Build the source
print("Building source...")
source_builder = AmalgamationBuilder()
source_files = [
"src/cert.h",
"src/socket.h",
"src/basic.c",
"src/parse.c",
"src/engine.c",
"src/socket.c",
"src/client.c",
"src/cert.c",
"src/server.c",
"src/router.c"
]
for file in source_files:
source_builder.add(file)
source = source_builder.result()
# Write results
print("Writing chttp.h...")
with open("chttp.h", 'w', encoding='utf-8') as f:
f.write('/*\n')
f.write(' * cHTTP Library - Amalgamated Header\n')
f.write(' * Generated automatically - do not edit manually\n')
f.write(' */\n\n')
f.write('#ifndef HTTP_AMALGAMATION_H\n')
f.write('#define HTTP_AMALGAMATION_H\n\n')
f.write('#ifdef __cplusplus\n')
f.write('extern "C" {\n')
f.write('#endif\n\n')
f.write(header)
f.write('\n#ifdef __cplusplus\n')
f.write('}\n')
f.write('#endif\n\n')
f.write('#endif /* HTTP_AMALGAMATION_H */\n')
print("Writing chttp.c...")
with open("chttp.c", 'w', encoding='utf-8') as f:
f.write('/*\n')
f.write(' * cHTTP Library - Amalgamated Source\n')
f.write(' * Generated automatically - do not edit manually\n')
f.write(' */\n\n')
f.write('#include "chttp.h"\n\n')
f.write(source)
print("Amalgamation complete!")
print(f"Header: {len(header.splitlines())} lines")
print(f"Source: {len(source.splitlines())} lines")
# Show some statistics
print(f"\nHeader global includes: {len(header_builder.global_includes)}")
print(f"Source global includes: {len(source_builder.global_includes)}")
print(f"Local includes found: {len(header_builder.local_includes | source_builder.local_includes)}")
if __name__ == "__main__":
main()
+4 -1
View File
@@ -1,7 +1,10 @@
#include "basic.h"
#include <stddef.h>
#include <string.h>
#ifndef HTTP_AMALGAMATION
#include "basic.h"
#endif
bool http_streq(HTTP_String s1, HTTP_String s2)
{
if (s1.len != s2.len)
+2
View File
@@ -11,7 +11,9 @@
#include <openssl/bn.h>
#endif
#ifndef HTTP_AMALGAMATION
#include "cert.h"
#endif
#ifdef HTTPS_ENABLED
+2
View File
@@ -1,7 +1,9 @@
#ifndef CERT_INCLUDED
#define CERT_INCLUDED
#ifndef HTTP_AMALGAMATION
#include "basic.h"
#endif
// This is an utility to create self-signed certificates
// useful when testing HTTPS servers locally. This is only
+2
View File
@@ -14,9 +14,11 @@
#define POLL poll
#endif
#ifndef HTTP_AMALGAMATION
#include "client.h"
#include "socket.h"
#include "engine.h"
#endif
#define CLIENT_MAX_CONNS 256
+3
View File
@@ -2,7 +2,10 @@
#define CLIENT_INCLUDED
#include <stdbool.h>
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#endif
// Initialize the global state of cHTTP.
//
+2
View File
@@ -9,8 +9,10 @@
#include <stdint.h>
#include <string.h>
#ifndef HTTP_AMALGAMATION
#include "basic.h"
#include "engine.h"
#endif
// This is the implementation of a byte queue useful
// for systems that need to process engs of bytes.
+3
View File
@@ -1,6 +1,9 @@
#ifndef HTTP_ENGINE_INCLUDED
#define HTTP_ENGINE_INCLUDED
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#endif
typedef enum {
HTTP_MEMFUNC_MALLOC,
+3
View File
@@ -2,8 +2,11 @@
#include <stdint.h>
#include <string.h>
#include <assert.h>
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#include "basic.h"
#endif
// From RFC 9112
// request-target = origin-form
+3
View File
@@ -1,6 +1,9 @@
#ifndef PARSE_INCLUDED
#define PARSE_INCLUDED
#ifndef HTTP_AMALGAMATION
#include "basic.h"
#endif
#define HTTP_MAX_HEADERS 32
+4 -1
View File
@@ -13,17 +13,20 @@
#include <sys/stat.h>
#endif
#ifndef HTTP_AMALGAMATION
#include "router.h"
#endif
#ifndef HTTP_AMALGAMATION
bool is_alpha(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
bool is_digit(char c)
{
return c >= '0' && c <= '9';
}
#endif // HTTP_AMALGAMATION
typedef enum {
ROUTE_STATIC_DIR,
+2
View File
@@ -1,7 +1,9 @@
#ifndef HTTP_ROUTER_INCLUDED
#define HTTP_ROUTER_INCLUDED
#ifndef HTTP_AMALGAMATION
#include "server.h"
#endif
typedef struct HTTP_Router HTTP_Router;
typedef void (*HTTP_RouterFunc)(HTTP_Request*, HTTP_ResponseHandle, void*);;
+7
View File
@@ -1,4 +1,5 @@
#include <stdint.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdbool.h>
@@ -10,14 +11,20 @@
#endif
#ifdef __linux__
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>
#define POLL poll
#define CLOSE_SOCKET close
#endif
#ifndef HTTP_AMALGAMATION
#include "engine.h"
#include "socket.h"
#include "server.h"
#endif
#define MAX_CONNS (1<<10)
+3
View File
@@ -2,7 +2,10 @@
#define HTTP_SERVER_INCLUDED
#include <stdint.h>
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#endif
typedef struct {
void *data0;
+14 -12
View File
@@ -1,20 +1,8 @@
#include <poll.h>
#include <assert.h> // TODO: organize these includes
#include "socket.h"
#ifdef HTTPS_ENABLED
#include <openssl/pem.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
@@ -24,9 +12,23 @@
#endif
#ifdef __linux__
#include <poll.h>
#include <netdb.h>
#endif
#ifdef HTTPS_ENABLED
#include <openssl/pem.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#endif
#ifndef HTTP_AMALGAMATION
#include "socket.h"
#endif
void socket_global_init(void)
{
#ifdef HTTPS_ENABLED
+2
View File
@@ -61,7 +61,9 @@
#include <openssl/x509v3.h>
#endif
#ifndef HTTP_AMALGAMATION
#include "parse.h"
#endif
typedef struct {
int is_ipv6;