Compare commits
5 Commits
7c174d0df6
...
248c5647e2
| Author | SHA1 | Date | |
|---|---|---|---|
| 248c5647e2 | |||
| c5af589ceb | |||
| 87e165dc52 | |||
| a5353c4db8 | |||
| b356a13b57 |
@ -1,3 +1,4 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
name: Build and Test
|
||||
|
||||
on:
|
||||
@ -20,7 +21,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc make libssl-dev libsqlite3-dev
|
||||
sudo apt-get install -y gcc make libssl-dev libsqlite3-dev bc
|
||||
|
||||
- name: Build
|
||||
run: make all
|
||||
@ -28,5 +29,22 @@ jobs:
|
||||
- name: Run tests
|
||||
run: make test
|
||||
|
||||
- name: Build legacy
|
||||
run: make legacy
|
||||
|
||||
- name: Clean
|
||||
run: make clean
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc make libssl-dev libsqlite3-dev bc gcovr lcov
|
||||
|
||||
- name: Run coverage
|
||||
run: make coverage
|
||||
|
||||
34
CHANGELOG.md
Normal file
34
CHANGELOG.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Version 0.4.0 - 2025-12-15
|
||||
|
||||
Add comprehensive tests for the auth, buffer, and connection modules. Enhance the build process with logging test support and an increased minimum coverage threshold.
|
||||
|
||||
**Changes:** 8 files, 2046 lines
|
||||
**Languages:** C (2036 lines), Other (10 lines)
|
||||
|
||||
## Version 0.3.0 - 2025-12-15
|
||||
|
||||
Adds socket optimization functions that enable TCP_NODELAY for reduced connection latency and enhance upstream handling. Includes tests for connection optimizations, caching, and cleanup to verify functionality.
|
||||
|
||||
**Changes:** 2 files, 146 lines
|
||||
**Languages:** C (146 lines)
|
||||
|
||||
## Version 0.2.0 - 2025-12-15
|
||||
|
||||
Optimizes performance through enhanced socket settings and caching, reducing latency in connections. Adds host header validation to client requests and improves DNS resolution error handling for more reliable network operations.
|
||||
|
||||
**Changes:** 4 files, 79 lines
|
||||
**Languages:** C (79 lines)
|
||||
|
||||
## Version 0.1.0 - 2025-12-15
|
||||
|
||||
Implements zero-copy data forwarding to enhance connection performance. Caches patch buffers in connection structures and optimizes buffer compaction during read handling to reduce memory allocations.
|
||||
|
||||
**Changes:** 2 files, 153 lines
|
||||
**Languages:** C (153 lines)
|
||||
10
Makefile
10
Makefile
@ -42,7 +42,8 @@ TEST_SOURCES = $(TESTS_DIR)/test_main.c \
|
||||
$(TESTS_DIR)/test_dashboard.c \
|
||||
$(TESTS_DIR)/test_health_check.c \
|
||||
$(TESTS_DIR)/test_ssl_handler.c \
|
||||
$(TESTS_DIR)/test_connection.c
|
||||
$(TESTS_DIR)/test_connection.c \
|
||||
$(TESTS_DIR)/test_logging.c
|
||||
|
||||
TEST_OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(TEST_SOURCES)))
|
||||
|
||||
@ -64,7 +65,7 @@ TEST_LIB_OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(TEST_LIB_SOURCES))
|
||||
|
||||
TEST_TARGET = rproxy_test
|
||||
|
||||
MIN_COVERAGE = 60
|
||||
MIN_COVERAGE = 69
|
||||
COVERAGE_MODULES = auth.c buffer.c config.c http.c logging.c patch.c rate_limit.c monitor.c dashboard.c health_check.c ssl_handler.c connection.c
|
||||
|
||||
.PHONY: all clean test legacy run coverage coverage-html valgrind
|
||||
@ -164,6 +165,9 @@ $(BUILD_DIR)/test_ssl_handler.o: $(TESTS_DIR)/test_ssl_handler.c
|
||||
$(BUILD_DIR)/test_connection.o: $(TESTS_DIR)/test_connection.c
|
||||
$(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/test_logging.o: $(TESTS_DIR)/test_logging.c
|
||||
$(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@
|
||||
|
||||
$(TEST_TARGET): $(BUILD_DIR) $(TEST_OBJECTS) $(TEST_LIB_OBJECTS)
|
||||
$(CC) $(TEST_OBJECTS) $(TEST_LIB_OBJECTS) -o $@ $(LDFLAGS)
|
||||
|
||||
@ -193,6 +197,7 @@ coverage: clean
|
||||
$(CC) $(CFLAGS_COV) -Isrc -c tests/test_health_check.c -o build/test_health_check.o
|
||||
$(CC) $(CFLAGS_COV) -Isrc -c tests/test_ssl_handler.c -o build/test_ssl_handler.o
|
||||
$(CC) $(CFLAGS_COV) -Isrc -c tests/test_connection.c -o build/test_connection.o
|
||||
$(CC) $(CFLAGS_COV) -Isrc -c tests/test_logging.c -o build/test_logging.o
|
||||
$(CC) $(CFLAGS_COV) -c src/buffer.c -o build/buffer.o
|
||||
$(CC) $(CFLAGS_COV) -c src/logging.c -o build/logging.o
|
||||
$(CC) $(CFLAGS_COV) -c src/config.c -o build/config.o
|
||||
@ -276,6 +281,7 @@ valgrind: clean
|
||||
$(CC) $(CFLAGS_DEBUG) -Isrc -c tests/test_health_check.c -o build/test_health_check.o
|
||||
$(CC) $(CFLAGS_DEBUG) -Isrc -c tests/test_ssl_handler.c -o build/test_ssl_handler.o
|
||||
$(CC) $(CFLAGS_DEBUG) -Isrc -c tests/test_connection.c -o build/test_connection.o
|
||||
$(CC) $(CFLAGS_DEBUG) -Isrc -c tests/test_logging.c -o build/test_logging.o
|
||||
$(CC) $(CFLAGS_DEBUG) -c src/buffer.c -o build/buffer.o
|
||||
$(CC) $(CFLAGS_DEBUG) -c src/logging.c -o build/logging.o
|
||||
$(CC) $(CFLAGS_DEBUG) -c src/config.c -o build/config.o
|
||||
|
||||
216
src/connection.c
216
src/connection.c
@ -22,13 +22,29 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#ifndef SPLICE_F_MOVE
|
||||
#define SPLICE_F_MOVE 1
|
||||
#endif
|
||||
#ifndef SPLICE_F_NONBLOCK
|
||||
#define SPLICE_F_NONBLOCK 2
|
||||
#endif
|
||||
#endif
|
||||
|
||||
connection_t connections[MAX_FDS];
|
||||
int epoll_fd = -1;
|
||||
time_t cached_time = 0;
|
||||
|
||||
void connection_update_cached_time(void) {
|
||||
cached_time = time(NULL);
|
||||
}
|
||||
|
||||
void connection_init_all(void) {
|
||||
for (int i = 0; i < MAX_FDS; i++) {
|
||||
connections[i].type = CONN_TYPE_UNUSED;
|
||||
connections[i].fd = -1;
|
||||
connections[i].splice_pipe[0] = -1;
|
||||
connections[i].splice_pipe[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,20 +80,57 @@ void connection_set_tcp_keepalive(int fd) {
|
||||
}
|
||||
}
|
||||
|
||||
void connection_set_tcp_nodelay(int fd) {
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) < 0) {
|
||||
log_debug("setsockopt TCP_NODELAY failed for fd %d: %s", fd, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TCP_QUICKACK
|
||||
void connection_set_tcp_quickack(int fd) {
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, &yes, sizeof(yes)) < 0) {
|
||||
log_debug("setsockopt TCP_QUICKACK failed for fd %d: %s", fd, strerror(errno));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void connection_optimize_socket(int fd, int is_upstream) {
|
||||
connection_set_tcp_nodelay(fd);
|
||||
connection_set_tcp_keepalive(fd);
|
||||
#ifdef TCP_QUICKACK
|
||||
if (!is_upstream) {
|
||||
connection_set_tcp_quickack(fd);
|
||||
}
|
||||
#endif
|
||||
int sndbuf = is_upstream ? 262144 : 131072;
|
||||
int rcvbuf = is_upstream ? 524288 : 131072;
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
|
||||
}
|
||||
|
||||
void connection_add_to_epoll(int fd, uint32_t events) {
|
||||
struct epoll_event event = { .data.fd = fd, .events = events };
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1) {
|
||||
log_error("epoll_ctl_add failed");
|
||||
close(fd);
|
||||
} else if (fd >= 0 && fd < MAX_FDS) {
|
||||
connections[fd].epoll_events = events;
|
||||
}
|
||||
}
|
||||
|
||||
void connection_modify_epoll(int fd, uint32_t events) {
|
||||
if (fd >= 0 && fd < MAX_FDS && connections[fd].epoll_events == events) {
|
||||
return;
|
||||
}
|
||||
struct epoll_event event = { .data.fd = fd, .events = events };
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) == -1) {
|
||||
if(errno != EBADF && errno != ENOENT) {
|
||||
log_debug("epoll_ctl_mod failed for fd %d: %s", fd, strerror(errno));
|
||||
}
|
||||
} else if (fd >= 0 && fd < MAX_FDS) {
|
||||
connections[fd].epoll_events = events;
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +195,7 @@ void connection_accept(int listener_fd) {
|
||||
}
|
||||
|
||||
connection_set_non_blocking(client_fd);
|
||||
connection_set_tcp_keepalive(client_fd);
|
||||
connection_optimize_socket(client_fd, 0);
|
||||
connection_add_to_epoll(client_fd, EPOLLIN);
|
||||
|
||||
connection_t *conn = &connections[client_fd];
|
||||
@ -150,7 +203,7 @@ void connection_accept(int listener_fd) {
|
||||
conn->type = CONN_TYPE_CLIENT;
|
||||
conn->state = CLIENT_STATE_READING_HEADERS;
|
||||
conn->fd = client_fd;
|
||||
conn->last_activity = time(NULL);
|
||||
conn->last_activity = cached_time;
|
||||
|
||||
if (buffer_init(&conn->read_buf, CHUNK_SIZE) < 0 ||
|
||||
buffer_init(&conn->write_buf, CHUNK_SIZE) < 0) {
|
||||
@ -186,6 +239,9 @@ void connection_close(int fd) {
|
||||
pair->patch_blocked = 0;
|
||||
pair->half_closed = 0;
|
||||
pair->write_shutdown = 0;
|
||||
pair->splice_pipe[0] = -1;
|
||||
pair->splice_pipe[1] = -1;
|
||||
pair->can_splice = 0;
|
||||
} else if (conn->type == CONN_TYPE_CLIENT && pair->type == CONN_TYPE_UPSTREAM) {
|
||||
log_debug("Client fd %d is closing. Closing orphaned upstream pair fd %d.", fd, pair->fd);
|
||||
pair->pair = NULL;
|
||||
@ -217,6 +273,20 @@ void connection_close(int fd) {
|
||||
buffer_free(&conn->read_buf);
|
||||
buffer_free(&conn->write_buf);
|
||||
|
||||
if (conn->patch_buf) {
|
||||
free(conn->patch_buf);
|
||||
conn->patch_buf = NULL;
|
||||
conn->patch_buf_capacity = 0;
|
||||
}
|
||||
|
||||
if (conn->type == CONN_TYPE_UPSTREAM && conn->splice_pipe[0] >= 0) {
|
||||
close(conn->splice_pipe[0]);
|
||||
close(conn->splice_pipe[1]);
|
||||
}
|
||||
conn->splice_pipe[0] = -1;
|
||||
conn->splice_pipe[1] = -1;
|
||||
conn->can_splice = 0;
|
||||
|
||||
if (conn->config) {
|
||||
config_ref_dec(conn->config);
|
||||
}
|
||||
@ -224,13 +294,18 @@ void connection_close(int fd) {
|
||||
memset(conn, 0, sizeof(connection_t));
|
||||
conn->type = CONN_TYPE_UNUSED;
|
||||
conn->fd = -1;
|
||||
conn->splice_pipe[0] = -1;
|
||||
conn->splice_pipe[1] = -1;
|
||||
}
|
||||
|
||||
int connection_do_read(connection_t *conn) {
|
||||
if (!conn) return -1;
|
||||
|
||||
buffer_t *buf = &conn->read_buf;
|
||||
buffer_compact(buf);
|
||||
|
||||
if (buf->head > buf->capacity / 4) {
|
||||
buffer_compact(buf);
|
||||
}
|
||||
|
||||
size_t available = buffer_available_write(buf);
|
||||
if (available == 0) {
|
||||
@ -260,7 +335,7 @@ int connection_do_read(connection_t *conn) {
|
||||
|
||||
if (bytes_read > 0) {
|
||||
buf->tail += bytes_read;
|
||||
conn->last_activity = time(NULL);
|
||||
conn->last_activity = cached_time;
|
||||
if (conn->vhost_stats) {
|
||||
monitor_record_bytes(conn->vhost_stats, 0, bytes_read);
|
||||
}
|
||||
@ -292,7 +367,7 @@ int connection_do_write(connection_t *conn) {
|
||||
|
||||
if (written > 0) {
|
||||
buffer_consume(buf, written);
|
||||
conn->last_activity = time(NULL);
|
||||
conn->last_activity = cached_time;
|
||||
if (conn->vhost_stats) {
|
||||
monitor_record_bytes(conn->vhost_stats, written, 0);
|
||||
}
|
||||
@ -392,7 +467,7 @@ static int try_upstream_connect(struct sockaddr_in *addr, int *out_fd) {
|
||||
}
|
||||
|
||||
connection_set_non_blocking(up_fd);
|
||||
connection_set_tcp_keepalive(up_fd);
|
||||
connection_optimize_socket(up_fd, 1);
|
||||
|
||||
int connect_result = connect(up_fd, (struct sockaddr*)addr, sizeof(*addr));
|
||||
if (connect_result < 0 && errno != EINPROGRESS) {
|
||||
@ -445,8 +520,10 @@ void connection_connect_to_upstream(connection_t *client, const char *data, size
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
int gai_err = getaddrinfo(route->upstream_host, NULL, &hints, &result);
|
||||
if (gai_err != 0) {
|
||||
log_debug("DNS resolution failed for %s: %s", route->upstream_host, gai_strerror(gai_err));
|
||||
if (gai_err != 0 || !result || !result->ai_addr) {
|
||||
if (gai_err == 0 && result) freeaddrinfo(result);
|
||||
log_debug("DNS resolution failed for %s: %s", route->upstream_host,
|
||||
gai_err ? gai_strerror(gai_err) : "no address returned");
|
||||
connection_send_error_response(client, 502, "Bad Gateway", "Cannot resolve upstream hostname");
|
||||
return;
|
||||
}
|
||||
@ -485,7 +562,9 @@ void connection_connect_to_upstream(connection_t *client, const char *data, size
|
||||
memset(up, 0, sizeof(connection_t));
|
||||
up->type = CONN_TYPE_UPSTREAM;
|
||||
up->fd = up_fd;
|
||||
up->last_activity = time(NULL);
|
||||
up->last_activity = cached_time;
|
||||
up->splice_pipe[0] = -1;
|
||||
up->splice_pipe[1] = -1;
|
||||
|
||||
client->pair = up;
|
||||
up->pair = client;
|
||||
@ -495,6 +574,19 @@ void connection_connect_to_upstream(connection_t *client, const char *data, size
|
||||
up->config = client->config;
|
||||
config_ref_inc(up->config);
|
||||
|
||||
int use_splice = !patch_has_rules(&route->patches) && !route->use_ssl;
|
||||
if (use_splice) {
|
||||
if (pipe(up->splice_pipe) == 0) {
|
||||
up->can_splice = 1;
|
||||
client->can_splice = 1;
|
||||
client->splice_pipe[0] = up->splice_pipe[0];
|
||||
client->splice_pipe[1] = up->splice_pipe[1];
|
||||
} else {
|
||||
up->splice_pipe[0] = -1;
|
||||
up->splice_pipe[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer_init(&up->read_buf, CHUNK_SIZE) < 0) {
|
||||
cleanup_upstream_partial(up, up_fd, client, 0, 0);
|
||||
connection_send_error_response(client, 502, "Bad Gateway", "Memory allocation failed");
|
||||
@ -642,6 +734,11 @@ static void handle_client_read(connection_t *conn) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (conn->request.host[0] == '\0') {
|
||||
connection_send_error_response(conn, 400, "Bad Request", "Missing Host header.");
|
||||
return;
|
||||
}
|
||||
|
||||
long long body_len = (conn->request.content_length > 0) ? conn->request.content_length : 0;
|
||||
size_t total_request_len = headers_len + body_len;
|
||||
|
||||
@ -724,6 +821,49 @@ static void handle_client_read(connection_t *conn) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static ssize_t splice_forward(connection_t *conn, connection_t *pair) {
|
||||
if (conn->splice_pipe[0] < 0 || conn->splice_pipe[1] < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytes_to_pipe = splice(conn->fd, NULL, conn->splice_pipe[1], NULL,
|
||||
CHUNK_SIZE, SPLICE_F_NONBLOCK | SPLICE_F_MOVE);
|
||||
|
||||
if (bytes_to_pipe <= 0) {
|
||||
if (bytes_to_pipe == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
return -1;
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
ssize_t bytes_from_pipe = splice(conn->splice_pipe[0], NULL, pair->fd, NULL,
|
||||
bytes_to_pipe, SPLICE_F_NONBLOCK | SPLICE_F_MOVE);
|
||||
|
||||
if (bytes_from_pipe < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
char discard[CHUNK_SIZE];
|
||||
ssize_t discarded = read(conn->splice_pipe[0], discard, bytes_to_pipe);
|
||||
(void)discarded;
|
||||
return -1;
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
conn->last_activity = cached_time;
|
||||
pair->last_activity = cached_time;
|
||||
|
||||
if (conn->vhost_stats) {
|
||||
monitor_record_bytes(conn->vhost_stats, bytes_from_pipe, bytes_to_pipe);
|
||||
}
|
||||
|
||||
return bytes_from_pipe;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void handle_forwarding(connection_t *conn) {
|
||||
connection_t *pair = conn->pair;
|
||||
|
||||
@ -741,6 +881,40 @@ static void handle_forwarding(connection_t *conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
int is_response = (conn->type == CONN_TYPE_UPSTREAM);
|
||||
int use_splice = conn->can_splice &&
|
||||
buffer_available_read(&conn->read_buf) == 0 &&
|
||||
buffer_available_read(&pair->write_buf) == 0 &&
|
||||
(!is_response || conn->response_headers_parsed);
|
||||
|
||||
if (use_splice) {
|
||||
ssize_t splice_result = splice_forward(conn, pair);
|
||||
|
||||
if (splice_result == 0) {
|
||||
log_debug("EOF via splice on fd %d", conn->fd);
|
||||
conn->half_closed = 1;
|
||||
if (pair->fd != -1 && !pair->write_shutdown) {
|
||||
shutdown(pair->fd, SHUT_WR);
|
||||
pair->write_shutdown = 1;
|
||||
}
|
||||
if (pair->half_closed) {
|
||||
connection_close(conn->fd);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (splice_result > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (splice_result == -2) {
|
||||
connection_close(conn->fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int bytes_read = connection_do_read(conn);
|
||||
|
||||
if (conn->type == CONN_TYPE_CLIENT && bytes_read > 0) {
|
||||
@ -845,18 +1019,23 @@ static void handle_forwarding(connection_t *conn) {
|
||||
|
||||
char *output_data = src_data;
|
||||
size_t output_len = src_len;
|
||||
char *patched_buf = NULL;
|
||||
|
||||
if (should_patch) {
|
||||
size_t max_output = src_len * 4;
|
||||
if (max_output < CHUNK_SIZE) max_output = CHUNK_SIZE;
|
||||
patched_buf = malloc(max_output);
|
||||
|
||||
if (patched_buf) {
|
||||
patch_result_t result = patch_apply(&route->patches, src_data, src_len, patched_buf, max_output);
|
||||
if (conn->patch_buf_capacity < max_output) {
|
||||
char *new_buf = realloc(conn->patch_buf, max_output);
|
||||
if (new_buf) {
|
||||
conn->patch_buf = new_buf;
|
||||
conn->patch_buf_capacity = max_output;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->patch_buf) {
|
||||
patch_result_t result = patch_apply(&route->patches, src_data, src_len, conn->patch_buf, conn->patch_buf_capacity);
|
||||
|
||||
if (result.should_block) {
|
||||
free(patched_buf);
|
||||
log_info("Blocked content during patching on fd %d", conn->fd);
|
||||
conn->patch_blocked = 1;
|
||||
if (is_response) {
|
||||
@ -869,7 +1048,7 @@ static void handle_forwarding(connection_t *conn) {
|
||||
}
|
||||
|
||||
if (result.output_len > 0 && result.size_delta != 0) {
|
||||
output_data = patched_buf;
|
||||
output_data = conn->patch_buf;
|
||||
output_len = result.output_len;
|
||||
if ((result.size_delta > 0 && conn->content_length_delta > LONG_MAX - result.size_delta) ||
|
||||
(result.size_delta < 0 && conn->content_length_delta < LONG_MIN - result.size_delta)) {
|
||||
@ -879,7 +1058,7 @@ static void handle_forwarding(connection_t *conn) {
|
||||
}
|
||||
log_debug("Patched data: %zu -> %zu bytes (delta: %ld)", src_len, output_len, result.size_delta);
|
||||
} else if (result.output_len > 0) {
|
||||
output_data = patched_buf;
|
||||
output_data = conn->patch_buf;
|
||||
output_len = result.output_len;
|
||||
}
|
||||
}
|
||||
@ -887,7 +1066,6 @@ static void handle_forwarding(connection_t *conn) {
|
||||
|
||||
size_t space_needed = pair->write_buf.tail + output_len;
|
||||
if (buffer_ensure_capacity(&pair->write_buf, space_needed) < 0) {
|
||||
if (patched_buf) free(patched_buf);
|
||||
log_debug("Failed to buffer data for fd %d, closing connection", conn->fd);
|
||||
connection_close(conn->fd);
|
||||
return;
|
||||
@ -897,8 +1075,6 @@ static void handle_forwarding(connection_t *conn) {
|
||||
pair->write_buf.tail += output_len;
|
||||
buffer_consume(&conn->read_buf, data_to_forward);
|
||||
|
||||
if (patched_buf) free(patched_buf);
|
||||
|
||||
connection_do_write(pair);
|
||||
connection_modify_epoll(pair->fd, EPOLLIN | EPOLLOUT);
|
||||
}
|
||||
@ -969,7 +1145,7 @@ static void handle_ssl_handshake(connection_t *conn) {
|
||||
}
|
||||
|
||||
static void handle_write_event(connection_t *conn) {
|
||||
conn->last_activity = time(NULL);
|
||||
conn->last_activity = cached_time;
|
||||
|
||||
if (conn->type == CONN_TYPE_UPSTREAM && conn->ssl && !conn->ssl_handshake_done) {
|
||||
handle_ssl_handshake(conn);
|
||||
|
||||
@ -6,7 +6,9 @@
|
||||
|
||||
extern connection_t connections[MAX_FDS];
|
||||
extern int epoll_fd;
|
||||
extern time_t cached_time;
|
||||
|
||||
void connection_update_cached_time(void);
|
||||
void connection_init_all(void);
|
||||
void connection_setup_listener(int port);
|
||||
void connection_accept(int listener_fd);
|
||||
@ -16,6 +18,8 @@ void connection_cleanup_idle(void);
|
||||
|
||||
int connection_set_non_blocking(int fd);
|
||||
void connection_set_tcp_keepalive(int fd);
|
||||
void connection_set_tcp_nodelay(int fd);
|
||||
void connection_optimize_socket(int fd, int is_upstream);
|
||||
void connection_add_to_epoll(int fd, uint32_t events);
|
||||
void connection_modify_epoll(int fd, uint32_t events);
|
||||
|
||||
|
||||
@ -196,11 +196,13 @@ int main(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
connection_update_cached_time();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
connection_handle_event(&events[i]);
|
||||
}
|
||||
|
||||
time_t current_time = time(NULL);
|
||||
time_t current_time = cached_time;
|
||||
|
||||
if (current_time > last_monitor_update) {
|
||||
monitor_update();
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <sqlite3.h>
|
||||
@ -99,6 +100,11 @@ typedef struct connection_s {
|
||||
int response_headers_parsed;
|
||||
long original_content_length;
|
||||
long content_length_delta;
|
||||
char *patch_buf;
|
||||
size_t patch_buf_capacity;
|
||||
int splice_pipe[2];
|
||||
int can_splice;
|
||||
uint32_t epoll_events;
|
||||
} connection_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -85,6 +85,78 @@ void test_auth_route_basic_auth(void) {
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_auth_route_full_flow(void) {
|
||||
TEST_SUITE_BEGIN("Auth Route Full Flow");
|
||||
|
||||
route_config_t route;
|
||||
memset(&route, 0, sizeof(route));
|
||||
route.use_auth = 1;
|
||||
strcpy(route.username, "routeuser");
|
||||
strcpy(route.password_hash, "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8");
|
||||
|
||||
char error[256];
|
||||
|
||||
int result = auth_check_route_basic_auth(&route, "Basic cm91dGV1c2VyOnBhc3N3b3Jk", error, sizeof(error));
|
||||
TEST_ASSERT_EQ(1, result, "Valid route credentials accepted");
|
||||
|
||||
result = auth_check_route_basic_auth(&route, "Basic d3Jvbmd1c2VyOnBhc3N3b3Jk", error, sizeof(error));
|
||||
TEST_ASSERT_EQ(0, result, "Wrong username rejected");
|
||||
|
||||
result = auth_check_route_basic_auth(&route, "Basic cm91dGV1c2VyOndyb25ncGFzcw==", error, sizeof(error));
|
||||
TEST_ASSERT_EQ(0, result, "Wrong password rejected");
|
||||
|
||||
result = auth_check_route_basic_auth(&route, "Bearer token123", error, sizeof(error));
|
||||
TEST_ASSERT_EQ(0, result, "Bearer auth rejected for route");
|
||||
|
||||
result = auth_check_route_basic_auth(&route, "Basic !!invalid!!", error, sizeof(error));
|
||||
TEST_ASSERT_EQ(0, result, "Invalid base64 rejected for route");
|
||||
|
||||
result = auth_check_route_basic_auth(&route, "Basic bm9jb2xvbg==", error, sizeof(error));
|
||||
TEST_ASSERT_EQ(0, result, "Base64 without colon rejected for route");
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_auth_base64_edge_cases(void) {
|
||||
TEST_SUITE_BEGIN("Auth Base64 Edge Cases");
|
||||
|
||||
auth_init("admin", "pass");
|
||||
|
||||
char error[256];
|
||||
|
||||
int result = auth_check_basic_auth("Basic YWRtaW46cGFzcw==", error, sizeof(error));
|
||||
TEST_ASSERT_EQ(1, result, "Standard base64 works");
|
||||
|
||||
result = auth_check_basic_auth("Basic YTo=", error, sizeof(error));
|
||||
TEST_ASSERT_EQ(0, result, "Short credentials rejected");
|
||||
|
||||
result = auth_check_basic_auth("Basic YTpi", error, sizeof(error));
|
||||
TEST_ASSERT_EQ(0, result, "Minimal base64 without padding");
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_auth_error_messages(void) {
|
||||
TEST_SUITE_BEGIN("Auth Error Messages");
|
||||
|
||||
auth_init("admin", "secret");
|
||||
|
||||
char error[256];
|
||||
memset(error, 0, sizeof(error));
|
||||
|
||||
auth_check_basic_auth(NULL, error, sizeof(error));
|
||||
TEST_ASSERT(strlen(error) > 0, "Error message set for NULL header");
|
||||
|
||||
memset(error, 0, sizeof(error));
|
||||
auth_check_basic_auth("Invalid", error, sizeof(error));
|
||||
TEST_ASSERT(strlen(error) > 0, "Error message set for invalid method");
|
||||
|
||||
auth_check_basic_auth(NULL, NULL, 0);
|
||||
TEST_ASSERT(1, "NULL error buffer handled");
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_auth_disabled_passthrough(void) {
|
||||
TEST_SUITE_BEGIN("Auth Disabled Passthrough");
|
||||
|
||||
@ -103,5 +175,8 @@ void run_auth_tests(void) {
|
||||
test_auth_check_credentials();
|
||||
test_auth_check_basic_auth();
|
||||
test_auth_route_basic_auth();
|
||||
test_auth_route_full_flow();
|
||||
test_auth_base64_edge_cases();
|
||||
test_auth_error_messages();
|
||||
test_auth_disabled_passthrough();
|
||||
}
|
||||
|
||||
@ -149,6 +149,123 @@ void test_buffer_multiple_operations(void) {
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_buffer_null_safety(void) {
|
||||
TEST_SUITE_BEGIN("Buffer NULL Safety");
|
||||
|
||||
TEST_ASSERT_EQ(-1, buffer_init(NULL, 1024), "NULL buffer init returns -1");
|
||||
|
||||
buffer_free(NULL);
|
||||
TEST_ASSERT(1, "NULL buffer free doesn't crash");
|
||||
|
||||
TEST_ASSERT_EQ(0, buffer_available_read(NULL), "NULL buffer read returns 0");
|
||||
TEST_ASSERT_EQ(0, buffer_available_write(NULL), "NULL buffer write returns 0");
|
||||
|
||||
TEST_ASSERT_EQ(-1, buffer_ensure_capacity(NULL, 1024), "NULL buffer ensure capacity returns -1");
|
||||
|
||||
buffer_compact(NULL);
|
||||
TEST_ASSERT(1, "NULL buffer compact doesn't crash");
|
||||
|
||||
buffer_consume(NULL, 100);
|
||||
TEST_ASSERT(1, "NULL buffer consume doesn't crash");
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_buffer_consume_overflow(void) {
|
||||
TEST_SUITE_BEGIN("Buffer Consume Overflow");
|
||||
|
||||
buffer_t buf;
|
||||
buffer_init(&buf, 64);
|
||||
|
||||
memcpy(buf.data, "TEST", 4);
|
||||
buf.tail = 4;
|
||||
|
||||
buffer_consume(&buf, 100);
|
||||
TEST_ASSERT_EQ(0, buf.head, "Head reset after over-consume");
|
||||
TEST_ASSERT_EQ(0, buf.tail, "Tail reset after over-consume");
|
||||
TEST_ASSERT_EQ(0, buffer_available_read(&buf), "No data after over-consume");
|
||||
|
||||
buffer_free(&buf);
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_buffer_compact_edge_cases(void) {
|
||||
TEST_SUITE_BEGIN("Buffer Compact Edge Cases");
|
||||
|
||||
buffer_t buf;
|
||||
buffer_init(&buf, 64);
|
||||
|
||||
buffer_compact(&buf);
|
||||
TEST_ASSERT_EQ(0, buf.head, "Compact empty buffer - head is 0");
|
||||
TEST_ASSERT_EQ(0, buf.tail, "Compact empty buffer - tail is 0");
|
||||
|
||||
memcpy(buf.data, "TEST", 4);
|
||||
buf.tail = 4;
|
||||
buf.head = 0;
|
||||
buffer_compact(&buf);
|
||||
TEST_ASSERT_EQ(0, buf.head, "Compact with head=0 unchanged");
|
||||
|
||||
buffer_free(&buf);
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_buffer_capacity_limits(void) {
|
||||
TEST_SUITE_BEGIN("Buffer Capacity Limits");
|
||||
|
||||
buffer_t buf;
|
||||
buffer_init(&buf, 64);
|
||||
|
||||
int result = buffer_ensure_capacity(&buf, MAX_BUFFER_SIZE + 1);
|
||||
TEST_ASSERT_EQ(-1, result, "Exceeding MAX_BUFFER_SIZE returns -1");
|
||||
|
||||
buffer_free(&buf);
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_buffer_large_growth(void) {
|
||||
TEST_SUITE_BEGIN("Buffer Large Growth");
|
||||
|
||||
buffer_t buf;
|
||||
buffer_init(&buf, 64);
|
||||
|
||||
int result = buffer_ensure_capacity(&buf, 1024);
|
||||
TEST_ASSERT_EQ(0, result, "Grow to 1024 succeeds");
|
||||
TEST_ASSERT(buf.capacity >= 1024, "Capacity at least 1024");
|
||||
|
||||
result = buffer_ensure_capacity(&buf, 4096);
|
||||
TEST_ASSERT_EQ(0, result, "Grow to 4096 succeeds");
|
||||
TEST_ASSERT(buf.capacity >= 4096, "Capacity at least 4096");
|
||||
|
||||
result = buffer_ensure_capacity(&buf, 65536);
|
||||
TEST_ASSERT_EQ(0, result, "Grow to 65536 succeeds");
|
||||
TEST_ASSERT(buf.capacity >= 65536, "Capacity at least 65536");
|
||||
|
||||
buffer_free(&buf);
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_buffer_near_max_capacity(void) {
|
||||
TEST_SUITE_BEGIN("Buffer Near Max Capacity");
|
||||
|
||||
buffer_t buf;
|
||||
buffer_init(&buf, 1024);
|
||||
|
||||
int result = buffer_ensure_capacity(&buf, MAX_BUFFER_SIZE - 1024);
|
||||
TEST_ASSERT_EQ(0, result, "Grow to near max succeeds");
|
||||
|
||||
result = buffer_ensure_capacity(&buf, MAX_BUFFER_SIZE);
|
||||
TEST_ASSERT_EQ(0, result, "Grow to exactly max succeeds");
|
||||
TEST_ASSERT(buf.capacity <= MAX_BUFFER_SIZE, "Capacity at max");
|
||||
|
||||
buffer_free(&buf);
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void run_buffer_tests(void) {
|
||||
test_buffer_init();
|
||||
test_buffer_read_write();
|
||||
@ -156,4 +273,10 @@ void run_buffer_tests(void) {
|
||||
test_buffer_compact();
|
||||
test_buffer_ensure_capacity();
|
||||
test_buffer_multiple_operations();
|
||||
test_buffer_null_safety();
|
||||
test_buffer_consume_overflow();
|
||||
test_buffer_compact_edge_cases();
|
||||
test_buffer_capacity_limits();
|
||||
test_buffer_large_growth();
|
||||
test_buffer_near_max_capacity();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,7 @@ extern void run_dashboard_tests(void);
|
||||
extern void run_health_check_tests(void);
|
||||
extern void run_ssl_handler_tests(void);
|
||||
extern void run_connection_tests(void);
|
||||
extern void run_logging_tests(void);
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
@ -54,6 +55,7 @@ int main(int argc, char *argv[]) {
|
||||
run_health_check_tests();
|
||||
run_ssl_handler_tests();
|
||||
run_connection_tests();
|
||||
run_logging_tests();
|
||||
|
||||
test_summary();
|
||||
|
||||
|
||||
@ -230,6 +230,91 @@ void test_monitor_update(void) {
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_monitor_record_request_end(void) {
|
||||
TEST_SUITE_BEGIN("Monitor Record Request End");
|
||||
|
||||
monitor_init(NULL);
|
||||
|
||||
vhost_stats_t *stats = monitor_get_or_create_vhost_stats("timing.test.com");
|
||||
TEST_ASSERT(stats != NULL, "Stats created");
|
||||
|
||||
double start_time = 1000.0;
|
||||
monitor_record_request_end(stats, start_time);
|
||||
TEST_ASSERT(stats->avg_request_time_ms >= 0, "Avg request time recorded");
|
||||
|
||||
monitor_record_request_end(stats, start_time);
|
||||
monitor_record_request_end(stats, start_time);
|
||||
TEST_ASSERT(1, "Multiple request end calls work");
|
||||
|
||||
monitor_record_request_end(NULL, 0);
|
||||
TEST_ASSERT(1, "NULL stats doesn't crash");
|
||||
|
||||
monitor_cleanup();
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_monitor_deque_overflow(void) {
|
||||
TEST_SUITE_BEGIN("Monitor Deque Overflow Behavior");
|
||||
|
||||
history_deque_t dq;
|
||||
history_deque_init(&dq, 3);
|
||||
|
||||
history_deque_push(&dq, 1.0, 10.0);
|
||||
history_deque_push(&dq, 2.0, 20.0);
|
||||
history_deque_push(&dq, 3.0, 30.0);
|
||||
TEST_ASSERT_EQ(3, dq.count, "Count is at capacity");
|
||||
|
||||
history_deque_push(&dq, 4.0, 40.0);
|
||||
TEST_ASSERT_EQ(3, dq.count, "Count stays at capacity");
|
||||
|
||||
history_deque_push(&dq, 5.0, 50.0);
|
||||
history_deque_push(&dq, 6.0, 60.0);
|
||||
TEST_ASSERT_EQ(3, dq.count, "Count still at capacity after multiple overflows");
|
||||
|
||||
free(dq.points);
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_monitor_network_deque_overflow(void) {
|
||||
TEST_SUITE_BEGIN("Monitor Network Deque Overflow");
|
||||
|
||||
network_history_deque_t dq;
|
||||
network_history_deque_init(&dq, 3);
|
||||
|
||||
network_history_deque_push(&dq, 1.0, 100.0, 50.0);
|
||||
network_history_deque_push(&dq, 2.0, 200.0, 100.0);
|
||||
network_history_deque_push(&dq, 3.0, 300.0, 150.0);
|
||||
TEST_ASSERT_EQ(3, dq.count, "Count is at capacity");
|
||||
|
||||
network_history_deque_push(&dq, 4.0, 400.0, 200.0);
|
||||
TEST_ASSERT_EQ(3, dq.count, "Count stays at capacity");
|
||||
|
||||
free(dq.points);
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_monitor_disk_deque_overflow(void) {
|
||||
TEST_SUITE_BEGIN("Monitor Disk Deque Overflow");
|
||||
|
||||
disk_history_deque_t dq;
|
||||
disk_history_deque_init(&dq, 3);
|
||||
|
||||
disk_history_deque_push(&dq, 1.0, 10.0, 5.0);
|
||||
disk_history_deque_push(&dq, 2.0, 20.0, 10.0);
|
||||
disk_history_deque_push(&dq, 3.0, 30.0, 15.0);
|
||||
TEST_ASSERT_EQ(3, dq.count, "Count is at capacity");
|
||||
|
||||
disk_history_deque_push(&dq, 4.0, 40.0, 20.0);
|
||||
TEST_ASSERT_EQ(3, dq.count, "Count stays at capacity");
|
||||
|
||||
free(dq.points);
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void run_monitor_tests(void) {
|
||||
test_history_deque_init();
|
||||
test_history_deque_push();
|
||||
@ -241,4 +326,8 @@ void run_monitor_tests(void) {
|
||||
test_monitor_record_request();
|
||||
test_monitor_record_bytes();
|
||||
test_monitor_update();
|
||||
test_monitor_record_request_end();
|
||||
test_monitor_deque_overflow();
|
||||
test_monitor_network_deque_overflow();
|
||||
test_monitor_disk_deque_overflow();
|
||||
}
|
||||
|
||||
@ -224,6 +224,93 @@ void test_patch_apply_block_rule(void) {
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_patch_apply_small_output_buffer(void) {
|
||||
TEST_SUITE_BEGIN("Patch Apply Small Output Buffer");
|
||||
|
||||
patch_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.rule_count = 1;
|
||||
strcpy(config.rules[0].key, "x");
|
||||
config.rules[0].key_len = 1;
|
||||
strcpy(config.rules[0].value, "longer");
|
||||
config.rules[0].value_len = 6;
|
||||
config.rules[0].is_null = 0;
|
||||
|
||||
const char *input = "x x x x x x x x x x";
|
||||
char output[10];
|
||||
patch_result_t result = patch_apply(&config, input, strlen(input), output, sizeof(output));
|
||||
|
||||
TEST_ASSERT_EQ(0, result.should_block, "Small buffer does not block");
|
||||
TEST_ASSERT(result.output_len <= sizeof(output), "Output truncated to buffer size");
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_patch_apply_null_input(void) {
|
||||
TEST_SUITE_BEGIN("Patch Apply NULL Input");
|
||||
|
||||
patch_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.rule_count = 1;
|
||||
strcpy(config.rules[0].key, "test");
|
||||
config.rules[0].key_len = 4;
|
||||
strcpy(config.rules[0].value, "demo");
|
||||
config.rules[0].value_len = 4;
|
||||
|
||||
char output[256];
|
||||
patch_result_t result = patch_apply(&config, NULL, 0, output, sizeof(output));
|
||||
TEST_ASSERT_EQ(0, result.should_block, "NULL input does not block");
|
||||
|
||||
result = patch_apply(NULL, "test", 4, output, sizeof(output));
|
||||
TEST_ASSERT_EQ(0, result.should_block, "NULL config does not block");
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_patch_check_block_edge_cases(void) {
|
||||
TEST_SUITE_BEGIN("Patch Check Block Edge Cases");
|
||||
|
||||
patch_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.rule_count = 1;
|
||||
strcpy(config.rules[0].key, "verylongkeythatwontmatch");
|
||||
config.rules[0].key_len = strlen("verylongkeythatwontmatch");
|
||||
config.rules[0].is_null = 1;
|
||||
|
||||
const char *short_data = "hi";
|
||||
TEST_ASSERT_EQ(0, patch_check_for_block(&config, short_data, strlen(short_data)),
|
||||
"Key longer than data doesn't match");
|
||||
|
||||
config.rules[0].key_len = 0;
|
||||
TEST_ASSERT_EQ(0, patch_check_for_block(&config, "any data", 8),
|
||||
"Zero length key doesn't match");
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_patch_apply_only_block_rules(void) {
|
||||
TEST_SUITE_BEGIN("Patch Apply Only Block Rules");
|
||||
|
||||
patch_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.rule_count = 2;
|
||||
strcpy(config.rules[0].key, "bad1");
|
||||
config.rules[0].key_len = 4;
|
||||
config.rules[0].is_null = 1;
|
||||
strcpy(config.rules[1].key, "bad2");
|
||||
config.rules[1].key_len = 4;
|
||||
config.rules[1].is_null = 1;
|
||||
|
||||
const char *input = "good content here";
|
||||
char output[256];
|
||||
patch_result_t result = patch_apply(&config, input, strlen(input), output, sizeof(output));
|
||||
|
||||
TEST_ASSERT_EQ(0, result.should_block, "No block when content is clean");
|
||||
TEST_ASSERT_EQ(strlen(input), result.output_len, "Output unchanged with only block rules");
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void run_patch_tests(void) {
|
||||
test_patch_has_rules();
|
||||
test_patch_check_for_block();
|
||||
@ -234,4 +321,8 @@ void run_patch_tests(void) {
|
||||
test_patch_apply_no_match();
|
||||
test_patch_apply_empty_config();
|
||||
test_patch_apply_block_rule();
|
||||
test_patch_apply_small_output_buffer();
|
||||
test_patch_apply_null_input();
|
||||
test_patch_check_block_edge_cases();
|
||||
test_patch_apply_only_block_rules();
|
||||
}
|
||||
|
||||
@ -191,6 +191,85 @@ void test_ssl_reinit(void) {
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_ssl_handshake_already_done(void) {
|
||||
TEST_SUITE_BEGIN("SSL Handshake Already Done");
|
||||
|
||||
connection_t conn;
|
||||
memset(&conn, 0, sizeof(conn));
|
||||
conn.ssl_handshake_done = 1;
|
||||
|
||||
ssl_set_verify(0);
|
||||
ssl_init();
|
||||
conn.ssl = SSL_new(ssl_ctx);
|
||||
TEST_ASSERT(conn.ssl != NULL, "SSL object created");
|
||||
|
||||
int result = ssl_do_handshake(&conn);
|
||||
TEST_ASSERT_EQ(1, result, "Already done returns 1");
|
||||
|
||||
SSL_free(conn.ssl);
|
||||
ssl_cleanup();
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_ssl_read_no_handshake(void) {
|
||||
TEST_SUITE_BEGIN("SSL Read Without Handshake");
|
||||
|
||||
connection_t conn;
|
||||
memset(&conn, 0, sizeof(conn));
|
||||
conn.ssl_handshake_done = 0;
|
||||
|
||||
ssl_set_verify(0);
|
||||
ssl_init();
|
||||
conn.ssl = SSL_new(ssl_ctx);
|
||||
TEST_ASSERT(conn.ssl != NULL, "SSL object created");
|
||||
|
||||
char buf[100];
|
||||
int result = ssl_read(&conn, buf, sizeof(buf));
|
||||
TEST_ASSERT_EQ(-1, result, "Read without handshake returns -1");
|
||||
|
||||
SSL_free(conn.ssl);
|
||||
ssl_cleanup();
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_ssl_write_no_handshake(void) {
|
||||
TEST_SUITE_BEGIN("SSL Write Without Handshake");
|
||||
|
||||
connection_t conn;
|
||||
memset(&conn, 0, sizeof(conn));
|
||||
conn.ssl_handshake_done = 0;
|
||||
|
||||
ssl_set_verify(0);
|
||||
ssl_init();
|
||||
conn.ssl = SSL_new(ssl_ctx);
|
||||
TEST_ASSERT(conn.ssl != NULL, "SSL object created");
|
||||
|
||||
const char *buf = "test";
|
||||
int result = ssl_write(&conn, buf, strlen(buf));
|
||||
TEST_ASSERT_EQ(-1, result, "Write without handshake returns -1");
|
||||
|
||||
SSL_free(conn.ssl);
|
||||
ssl_cleanup();
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void test_ssl_verify_with_ca(void) {
|
||||
TEST_SUITE_BEGIN("SSL Verify With CA Settings");
|
||||
|
||||
ssl_set_verify(1);
|
||||
ssl_set_ca_file("/etc/ssl/certs/ca-certificates.crt");
|
||||
ssl_set_ca_path("/etc/ssl/certs");
|
||||
ssl_init();
|
||||
TEST_ASSERT(ssl_ctx != NULL, "Context created with CA settings");
|
||||
|
||||
ssl_cleanup();
|
||||
|
||||
TEST_SUITE_END();
|
||||
}
|
||||
|
||||
void run_ssl_handler_tests(void) {
|
||||
test_ssl_init_cleanup();
|
||||
test_ssl_multiple_init();
|
||||
@ -202,4 +281,8 @@ void run_ssl_handler_tests(void) {
|
||||
test_ssl_handshake_null();
|
||||
test_ssl_read_write_null();
|
||||
test_ssl_reinit();
|
||||
test_ssl_handshake_already_done();
|
||||
test_ssl_read_no_handshake();
|
||||
test_ssl_write_no_handshake();
|
||||
test_ssl_verify_with_ca();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user