Simplify amalgamation script
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
#include <openssl/bn.h>
|
||||
#endif
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "cert.h"
|
||||
#endif
|
||||
|
||||
#ifdef HTTPS_ENABLED
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
#define CLIENT_INCLUDED
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "parse.h"
|
||||
#endif
|
||||
|
||||
// Initialize the global state of cHTTP.
|
||||
//
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef HTTP_ENGINE_INCLUDED
|
||||
#define HTTP_ENGINE_INCLUDED
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "parse.h"
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
HTTP_MEMFUNC_MALLOC,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef PARSE_INCLUDED
|
||||
#define PARSE_INCLUDED
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "basic.h"
|
||||
#endif
|
||||
|
||||
#define HTTP_MAX_HEADERS 32
|
||||
|
||||
|
||||
+4
-1
@@ -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,
|
||||
|
||||
@@ -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*);;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
#define HTTP_SERVER_INCLUDED
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "parse.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
void *data0;
|
||||
|
||||
+14
-12
@@ -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
|
||||
|
||||
@@ -61,7 +61,9 @@
|
||||
#include <openssl/x509v3.h>
|
||||
#endif
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "parse.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int is_ipv6;
|
||||
|
||||
Reference in New Issue
Block a user