diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1feb88c..2f2e537 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,14 @@
+
+## Version 0.6.0 - 2025-12-29
+
+Integrates monitoring metrics into connection handling to provide real-time insights into network performance. Redesigns the dashboard with advanced metrics and charts for enhanced visualization of system data.
+
+**Changes:** 9 files, 1245 lines
+**Languages:** C (1245 lines)
+
## Version 0.5.0 - 2025-12-28
Enhances authentication security by preventing timing attacks and clearing sensitive memory, while adding rate limiting to protect against abusive client requests. Enables SSL hostname verification and preferred cipher suites for improved connection security, and fixes request denial when rate limit allocation fails.
diff --git a/src/connection.c b/src/connection.c
index 167903c..be51977 100755
--- a/src/connection.c
+++ b/src/connection.c
@@ -215,6 +215,7 @@ void connection_accept(int listener_fd) {
}
monitor.active_connections++;
+ monitor.total_connections_accepted++;
log_debug("New connection on fd %d from %s, total: %d",
client_fd, conn->client_ip, monitor.active_connections);
}
@@ -527,6 +528,9 @@ void connection_connect_to_upstream(connection_t *client, const char *data, size
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");
+ if (client->vhost_stats) {
+ monitor_record_error(client->vhost_stats, ERROR_TYPE_DNS);
+ }
connection_send_error_response(client, 502, "Bad Gateway", "Cannot resolve upstream hostname");
return;
}
@@ -555,10 +559,20 @@ void connection_connect_to_upstream(connection_t *client, const char *data, size
if (up_fd < 0) {
log_debug("All %d connection attempts failed for %s:%d",
MAX_UPSTREAM_RETRIES, route->upstream_host, route->upstream_port);
+ if (client->vhost_stats) {
+ monitor_record_upstream_connect(client->vhost_stats, 0, 0);
+ }
connection_send_error_response(client, 502, "Bad Gateway", "Failed to connect to upstream");
return;
}
+ if (client->vhost_stats) {
+ monitor_record_upstream_connect(client->vhost_stats, 1, 0);
+ if (retry_count > 0) {
+ client->vhost_stats->upstream_retries += retry_count;
+ }
+ }
+
connection_add_to_epoll(up_fd, EPOLLIN | EPOLLOUT);
connection_t *up = &connections[up_fd];
@@ -821,6 +835,10 @@ static void handle_client_read(connection_t *conn) {
conn->vhost_stats = monitor_get_or_create_vhost_stats(conn->request.host);
monitor_record_request_start(conn->vhost_stats, conn->request.is_websocket);
+ monitor_record_method(conn->vhost_stats, http_method_from_string(conn->request.method));
+ if (conn->request.content_length > 0) {
+ monitor_record_request_size(conn->vhost_stats, conn->request.content_length);
+ }
conn->state = CLIENT_STATE_FORWARDING;
connection_connect_to_upstream(conn, data_start, len_to_forward);
@@ -867,6 +885,7 @@ static ssize_t splice_forward(connection_t *conn, connection_t *pair) {
if (conn->vhost_stats) {
monitor_record_bytes(conn->vhost_stats, bytes_from_pipe, bytes_to_pipe);
+ monitor_record_splice_transfer(conn->vhost_stats, bytes_from_pipe);
}
return bytes_from_pipe;
@@ -998,10 +1017,19 @@ static void handle_forwarding(connection_t *conn) {
conn->original_content_length = http_get_content_length(src_data, headers_end);
conn->response_headers_parsed = 1;
- log_debug("Response Content-Type: %s, textual: %d, content-length: %ld",
+ int status_code = http_extract_status_code(src_data, headers_end);
+ if (status_code > 0 && conn->vhost_stats) {
+ monitor_record_status(conn->vhost_stats, status_code);
+ }
+ if (conn->original_content_length > 0 && conn->vhost_stats) {
+ monitor_record_response_size(conn->vhost_stats, conn->original_content_length);
+ }
+
+ log_debug("Response Content-Type: %s, textual: %d, content-length: %ld, status: %d",
content_type[0] ? content_type : "(none)",
conn->is_textual_content,
- conn->original_content_length);
+ conn->original_content_length,
+ status_code);
}
}
@@ -1084,6 +1112,10 @@ static void handle_forwarding(connection_t *conn) {
pair->write_buf.tail += output_len;
buffer_consume(&conn->read_buf, data_to_forward);
+ if (conn->vhost_stats) {
+ monitor_record_buffer_transfer(conn->vhost_stats, output_len);
+ }
+
connection_do_write(pair);
connection_modify_epoll(pair->fd, EPOLLIN | EPOLLOUT);
}
@@ -1096,6 +1128,9 @@ static void handle_ssl_handshake(connection_t *conn) {
time_t elapsed = time(NULL) - conn->ssl_handshake_start;
if (elapsed > SSL_HANDSHAKE_TIMEOUT_SEC) {
log_debug("SSL handshake timeout for fd %d after %ld seconds", conn->fd, (long)elapsed);
+ if (conn->vhost_stats) {
+ monitor_record_error(conn->vhost_stats, ERROR_TYPE_TIMEOUT);
+ }
if (conn->pair) {
connection_send_error_response(conn->pair, 504, "Gateway Timeout", "SSL handshake timeout");
}
@@ -1144,6 +1179,9 @@ static void handle_ssl_handshake(connection_t *conn) {
connection_modify_epoll(conn->fd, EPOLLOUT);
} else {
log_debug("SSL handshake failed for fd %d: %d", conn->fd, ssl_error);
+ if (conn->vhost_stats) {
+ monitor_record_error(conn->vhost_stats, ERROR_TYPE_SSL);
+ }
if (conn->pair) {
connection_send_error_response(conn->pair, 502, "Bad Gateway", "SSL handshake failed");
} else {
@@ -1218,6 +1256,9 @@ void connection_handle_event(struct epoll_event *event) {
if (event->events & (EPOLLERR | EPOLLHUP)) {
if (event->events & EPOLLERR) {
log_debug("EPOLLERR on fd %d", fd);
+ if (conn->vhost_stats) {
+ monitor_record_error(conn->vhost_stats, ERROR_TYPE_CONNECTION);
+ }
}
connection_close(fd);
return;
@@ -1267,6 +1308,9 @@ void connection_cleanup_idle(void) {
conn->fd != -1) {
if (current_time - conn->last_activity > CONNECTION_TIMEOUT) {
log_debug("Closing idle connection fd=%d", i);
+ if (conn->vhost_stats) {
+ monitor_record_error(conn->vhost_stats, ERROR_TYPE_TIMEOUT);
+ }
connection_close(i);
}
}
diff --git a/src/dashboard.c b/src/dashboard.c
index 7b3b9bc..ccba962 100755
--- a/src/dashboard.c
+++ b/src/dashboard.c
@@ -19,275 +19,120 @@ static const char *DASHBOARD_HTML =
" \n"
" \n"
"\n"
"
\n"
" \n"
-"\n"
-" \n"
-"
CPU Usage
\n"
-"
\n"
+"
\n"
+"
\n"
+"
\n"
+"
\n"
+"
\n"
"
\n"
-"\n"
-"
\n"
-"
Memory Usage
\n"
-"
\n"
+"
\n"
-"\n"
-"
\n"
-"
Network I/O
\n"
-"
\n"
-"
\n"
+"
\n"
-"\n"
-"
\n"
-"
Disk I/O
\n"
-"
\n"
-"
\n"
-"
\n"
-"\n"
-"
\n"
-"
Load Average
\n"
-"
\n"
-"
\n"
-"
\n"
-"
\n"
-"
\n"
-"
\n"
-"
\n"
-"\n"
-"
\n"
-"
\n"
-" \n"
-" \n"
-" | Virtual Host | \n"
-" HTTP Req | \n"
-" WS Req | \n"
-" Total Req | \n"
-" Avg Resp (ms) | \n"
-" Sent | \n"
-" Received | \n"
-"
\n"
-" \n"
-" \n"
-"
\n"
-"
\n"
-"\n"
+"
Load Average
\n"
+"
\n"
+"
\n"
+"
| Virtual Host | RPS | Total | p50 | p99 | 2xx | 4xx | 5xx | Err% | Sent | Recv |
|---|
\n"
" \n"
"\n"
"