// retoor #include "client_handler.h" #include "buffer.h" #include "http.h" #include "http_response.h" #include "config.h" #include "rate_limit.h" #include "auth.h" #include "monitor.h" #include "dashboard.h" #include "upstream.h" #include #include #include extern connection_t connections[MAX_FDS]; extern time_t cached_time; static int client_has_complete_request(connection_t *conn) { size_t available = conn->read_buf.tail - conn->read_buf.head; if (available < 4) return 0; char *headers_end = memmem(conn->read_buf.data + conn->read_buf.head, available, "\r\n\r\n", 4); return headers_end != NULL; } int client_check_rate_limit(connection_t *conn) { if (!rate_limit_check(conn->client_ip)) { http_response_send_error(conn, 429, "Too Many Requests", "429 Too Many Requests - Rate limit exceeded"); return 0; } return 1; } int client_check_route_auth(connection_t *conn, route_config_t *route, const char *data, size_t len) { if (!route || !route->use_auth) return 1; char auth_header[512] = ""; http_find_header_value(data, len, "Authorization", auth_header, sizeof(auth_header)); char error_msg[256] = ""; if (!auth_check_route_basic_auth(route, auth_header[0] ? auth_header : NULL, error_msg, sizeof(error_msg))) { http_response_send_auth_required(conn, route->hostname); return 0; } return 1; } int client_handle_internal_route(connection_t *conn, const char *data, size_t len) { if (!http_uri_is_internal_route(conn->request.uri)) { return 0; } conn->state = CLIENT_STATE_SERVING_INTERNAL; if (strcmp(conn->request.uri, "/rproxy/dashboard") == 0) { dashboard_serve(conn, data, len); } else if (strcmp(conn->request.uri, "/rproxy/api/stats") == 0) { dashboard_serve_stats_api(conn, data, len); } else { http_response_send_error(conn, 404, "Not Found", "404 Not Found"); } return 1; } static void client_setup_vhost_stats(connection_t *conn) { if (!conn->vhost_stats && conn->request.host[0] != '\0') { conn->vhost_stats = monitor_get_or_create_vhost_stats(conn->request.host); if (conn->vhost_stats) { monitor_record_request_start(conn->vhost_stats, conn->request.is_websocket); monitor_record_method(conn->vhost_stats, http_method_from_string(conn->request.method)); } } } void client_handle_read(connection_t *conn) { if (!conn || conn->state == CLIENT_STATE_CLOSING) return; size_t available = buffer_available_write(&conn->read_buf); if (available == 0) { if (buffer_ensure_capacity(&conn->read_buf, conn->read_buf.capacity * 2) < 0) { http_response_send_error(conn, 413, "Request Entity Too Large", "413 Request Entity Too Large"); return; } available = buffer_available_write(&conn->read_buf); } ssize_t n = read(conn->fd, conn->read_buf.data + conn->read_buf.tail, available); if (n > 0) { conn->read_buf.tail += (size_t)n; conn->last_activity = cached_time; } else if (n == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) { conn->state = CLIENT_STATE_CLOSING; return; } if (conn->state == CLIENT_STATE_READING_HEADERS) { if (!client_has_complete_request(conn)) return; size_t data_len = conn->read_buf.tail - conn->read_buf.head; char *data = conn->read_buf.data + conn->read_buf.head; char *headers_end = memmem(data, data_len, "\r\n\r\n", 4); size_t request_len = headers_end ? (size_t)(headers_end - data) + 4 : data_len; int parse_result = http_parse_request(data, data_len, &conn->request); if (parse_result <= 0) { http_response_send_error(conn, 400, "Bad Request", "400 Bad Request - Invalid HTTP request"); return; } client_setup_vhost_stats(conn); if (!client_check_rate_limit(conn)) return; if (client_handle_internal_route(conn, data, data_len)) { conn->read_buf.head += request_len; if (conn->read_buf.head >= conn->read_buf.tail) { conn->read_buf.head = 0; conn->read_buf.tail = 0; } return; } route_config_t *route = config_find_route(conn->request.host); if (!route) { http_response_send_error(conn, 502, "Bad Gateway", "502 Bad Gateway - No route configured"); return; } if (!client_check_route_auth(conn, route, data, data_len)) return; conn->state = CLIENT_STATE_FORWARDING; upstream_connect(conn, data, data_len); } }