diff --git a/src/cr_api.c b/src/cr_api.c index c3d2256..a828f87 100644 --- a/src/cr_api.c +++ b/src/cr_api.c @@ -118,8 +118,8 @@ query_value(const char *query, const char *key, char *out, size_t out_size) { return -1; } -static const char * -status_text_for(int status) { +static const char * +status_text_for(int status) { switch (status) { case 200: return "OK"; @@ -141,13 +141,31 @@ status_text_for(int status) { return "Bad Gateway"; case 503: return "Service Unavailable"; - default: - return "OK"; - } -} - -void -http_send_response(int fd, int status, const char *content_type, const uint8_t *body, size_t body_len) { + default: + return "OK"; + } +} + +static int +socket_send_all(int fd, const void *buf, size_t len) { + const uint8_t *p = (const uint8_t *)buf; + size_t sent = 0; + while (sent < len) { + ssize_t n = send(fd, p + sent, len - sent, 0); + if (n > 0) { + sent += (size_t)n; + continue; + } + if (n < 0 && errno == EINTR) { + continue; + } + return -1; + } + return 0; +} + +void +http_send_response(int fd, int status, const char *content_type, const uint8_t *body, size_t body_len) { char header[512]; int n = snprintf(header, sizeof(header), "HTTP/1.1 %d %s\r\n" @@ -157,15 +175,17 @@ http_send_response(int fd, int status, const char *content_type, const uint8_t * "Access-Control-Allow-Origin: *\r\n" "Cache-Control: no-cache\r\n" "\r\n", - status, status_text_for(status), content_type ? content_type : "application/octet-stream", - (unsigned int)body_len); - if (n > 0) { - send(fd, header, (size_t)n, 0); - if (body_len > 0) { - send(fd, body, body_len, 0); - } - } -} + status, status_text_for(status), content_type ? content_type : "application/octet-stream", + (unsigned int)body_len); + if (n > 0) { + if (socket_send_all(fd, header, (size_t)n) != 0) { + return; + } + if (body_len > 0) { + (void)socket_send_all(fd, body, body_len); + } + } +} void http_send_json(int fd, int status, const char *body) { @@ -3722,20 +3742,22 @@ void http_send_png_asset(int fd) { char header[256]; size_t len = sizeof(g_cheatrunner_png); - int n = snprintf(header, sizeof(header), + int n = snprintf(header, sizeof(header), "HTTP/1.1 200 OK\r\n" "Content-Type: image/png\r\n" "Content-Length: %u\r\n" "Connection: close\r\n" "Access-Control-Allow-Origin: *\r\n" - "Cache-Control: public, max-age=86400\r\n" - "\r\n", - (unsigned int)len); - if (n > 0) { - send(fd, header, (size_t)n, 0); - send(fd, g_cheatrunner_png, len, 0); - } -} + "Cache-Control: public, max-age=86400\r\n" + "\r\n", + (unsigned int)len); + if (n > 0) { + if (socket_send_all(fd, header, (size_t)n) != 0) { + return; + } + (void)socket_send_all(fd, g_cheatrunner_png, len); + } +} void http_route(int fd, const char *method, const char *path, const char *query, const char *token_header, diff --git a/src/cr_http.c b/src/cr_http.c index 3230128..168a861 100644 --- a/src/cr_http.c +++ b/src/cr_http.c @@ -36,6 +36,24 @@ long long cr_http_too_many_requests_count(void) { return g_too_many_requests_cou typedef struct { int fd; char ip[64]; } http_client_ctx_t; static void http_handle_client(int fd, const char *client_ip); +static int +socket_send_all(int fd, const void *buf, size_t len) { + const uint8_t *p = (const uint8_t *)buf; + size_t sent = 0; + while (sent < len) { + ssize_t n = send(fd, p + sent, len - sent, 0); + if (n > 0) { + sent += (size_t)n; + continue; + } + if (n < 0 && errno == EINTR) { + continue; + } + return -1; + } + return 0; +} + static void * http_client_thread(void *arg) { http_client_ctx_t *ctx = (http_client_ctx_t *)arg; @@ -122,8 +140,8 @@ http_send_json(int fd, int status, const char *body) { "\r\n", status, (unsigned int)strlen(payload)); if (n > 0) { - send(fd, header, (size_t)n, 0); - send(fd, payload, strlen(payload), 0); + (void)socket_send_all(fd, header, (size_t)n); + (void)socket_send_all(fd, payload, strlen(payload)); } } @@ -139,9 +157,10 @@ http_handle_client(int fd, const char *client_ip) { size_t body_len = 0; struct timeval tv; - tv.tv_sec = 3; + tv.tv_sec = 10; tv.tv_usec = 0; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); for (;;) { if (off >= MAX_REQ_SIZE) { @@ -149,6 +168,9 @@ http_handle_client(int fd, const char *client_ip) { } int n = recv(fd, req + off, MAX_REQ_SIZE - off, 0); if (n <= 0) { + if (n < 0 && errno == EINTR) { + continue; + } if (off == 0) { free(req); return; @@ -196,6 +218,9 @@ http_handle_client(int fd, const char *client_ip) { } int n = recv(fd, req + off, MAX_REQ_SIZE - off, 0); if (n <= 0) { + if (n < 0 && errno == EINTR) { + continue; + } break; } off += (size_t)n; @@ -320,7 +345,12 @@ http_server_thread(void *arg) { if (g_shutdown_requested) { break; } - break; + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } + log_msg("[HTTP] accept() failed: %d", errno); + sleep(1); + continue; } char client_ip[64] = {0}; inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip)); diff --git a/src/http_client.c b/src/http_client.c index 07f1d16..5451cb3 100644 --- a/src/http_client.c +++ b/src/http_client.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "http_client.h" @@ -45,6 +46,14 @@ http_ssl_cb(void) { return 0; } +static int +http_should_retry(int rc, int status_code) { + if (rc != 0) { + return 1; + } + return status_code == 429 || status_code == 500 || status_code == 502 || status_code == 503 || status_code == 504; +} + static int http_init(http_ctx_t *ctx, const char *agent, const char *url, int timeout_ms) { int err = 0; @@ -141,6 +150,13 @@ http_request(http_ctx_t *ctx, uint8_t **data, size_t *len, int *status_out, size for (;;) { if ((cap - off) < 4096) { size_t new_cap = cap * 2; + if (max_bytes > 0 && new_cap > max_bytes) { + new_cap = max_bytes; + } + if (new_cap <= cap) { + free(buf); + return -2; + } uint8_t *nbuf = realloc(buf, new_cap); if (!nbuf) { free(buf); @@ -221,6 +237,9 @@ http_get_url_ex_timeout(const char *agent, const char *url, size_t max_bytes, in size_t body_len = 0; int status = -1; int rc = 0; + int init_rc = 0; + int attempts = 3; + int effective_timeout_ms = timeout_ms > 0 ? timeout_ms : 15000; if (!url || !data_out || !len_out) { return -1; @@ -235,16 +254,30 @@ http_get_url_ex_timeout(const char *agent, const char *url, size_t max_bytes, in agent = "CheatRunner/0.1"; } - if (http_init(&ctx, agent, url, timeout_ms) < 0) { + for (int attempt = 0; attempt < attempts; attempt++) { + body = NULL; + body_len = 0; + status = -1; + rc = -1; + + init_rc = http_init(&ctx, agent, url, effective_timeout_ms); + if (init_rc >= 0) { + rc = http_request(&ctx, &body, &body_len, &status, max_bytes); + } http_fini(&ctx); - return -1; + + if (!http_should_retry(rc, status) || attempt == attempts - 1) { + break; + } + free(body); + body = NULL; + body_len = 0; + usleep((useconds_t)((attempt + 1) * 250000)); } - rc = http_request(&ctx, &body, &body_len, &status, max_bytes); if (status_out) { *status_out = status; } - http_fini(&ctx); if (rc != 0) { free(body); return rc;