diff --git a/CHANGELOG.md b/CHANGELOG.md index fe5520f..74a7267 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ + +## 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. diff --git a/src/connection.c b/src/connection.c index 60b6e65..8a920f6 100644 --- a/src/connection.c +++ b/src/connection.c @@ -33,6 +33,11 @@ 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++) { @@ -75,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; } } @@ -153,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]; @@ -161,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) { @@ -293,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); } @@ -325,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); } @@ -425,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) { @@ -478,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; } @@ -518,7 +562,7 @@ 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; @@ -690,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; @@ -804,8 +853,8 @@ static ssize_t splice_forward(connection_t *conn, connection_t *pair) { return -2; } - conn->last_activity = time(NULL); - pair->last_activity = time(NULL); + 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); @@ -1096,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); diff --git a/src/connection.h b/src/connection.h index 919976c..fbb0db7 100644 --- a/src/connection.h +++ b/src/connection.h @@ -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); diff --git a/src/main.c b/src/main.c index c15a179..5138bc5 100644 --- a/src/main.c +++ b/src/main.c @@ -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(); diff --git a/src/types.h b/src/types.h index fe648d2..e74ed29 100644 --- a/src/types.h +++ b/src/types.h @@ -6,6 +6,7 @@ #endif #include #include +#include #include #include #include @@ -103,6 +104,7 @@ typedef struct connection_s { size_t patch_buf_capacity; int splice_pipe[2]; int can_splice; + uint32_t epoll_events; } connection_t; typedef struct {