QUOTE: Never too old to chase dreams.

libhttp

A basic HTTP Framework

commit 9cf6f814e802841666ed6d891f676b891205f18d
parent b5ec0c12b2b23718acb41bdc2af75829805b3411
Author: Sophie <info@soophie.de>
Date:   Mon,  3 Feb 2025 08:42:22 +0000

feat: Added socket read timeout

Diffstat:
Msrc/libhttp.h | 99+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
1 file changed, 57 insertions(+), 42 deletions(-)

diff --git a/src/libhttp.h b/src/libhttp.h @@ -6,15 +6,17 @@ #include <sys/socket.h> typedef enum { - HTTP_ERR_OK = 0, - HTTP_ERR_NO_SOCKET = 1, - HTTP_ERR_UNKNOWN_HOST = 2, - HTTP_ERR_NO_ADDR_REUSE = 3, - HTTP_ERR_ADDR_IN_USE = 4, - HTTP_ERR_NO_LISTEN = 5, - HTTP_ERR_NO_SSL_CONTEXT = 6, - HTTP_ERR_INVALID_CERTIFICATE = 7, - HTTP_ERR_INVALID_PRIVATE_KEY = 8, + HTTP_ERR_OK = 0, + HTTP_ERR_NO_SOCKET = 1, + HTTP_ERR_UNKNOWN_HOST = 2, + HTTP_ERR_NO_ADDR_REUSE = 3, + HTTP_ERR_ADDR_IN_USE = 4, + HTTP_ERR_NO_LISTEN = 5, + HTTP_ERR_NO_SSL_CONTEXT = 6, + HTTP_ERR_INVALID_CERTIFICATE = 7, + HTTP_ERR_INVALID_PRIVATE_KEY = 8, + HTTP_ERR_READ_TIMEOUT = 9, + HTTP_ERR_REQUEST_TOO_LONG = 10, } http_error_t; typedef enum { @@ -164,8 +166,8 @@ void http_close(http_t *http); #include <pthread.h> #include <netdb.h> #include <poll.h> +#include <errno.h> #include <signal.h> -#include <fcntl.h> #include <openssl/ssl.h> #include <sys/socket.h> @@ -450,12 +452,12 @@ void http_response_free(http_response_t *response) { } } -http_request_t http_read_request(http_conn_t *conn) { +http_error_t http_read_request(http_conn_t *conn, http_request_t *request) { int len = 0; int ln = 0; int start = 0; char buffer[HTTP_MAX_REQUEST_HEAD_SIZE]; - http_request_t request = { + *request = (http_request_t) { .conn = conn, .method = NULL, .url = NULL, @@ -472,10 +474,15 @@ http_request_t http_read_request(http_conn_t *conn) { if (read == 0) { break; } + // read timeout + if (read == -1 || read == SSL_ERROR_SYSCALL) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + return HTTP_ERR_READ_TIMEOUT; + } + } // break if too long if (len == HTTP_MAX_REQUEST_HEAD_SIZE) { - perror("request is too long"); - break; + return HTTP_ERR_REQUEST_TOO_LONG; } buffer[len] = c; len++; @@ -498,23 +505,23 @@ http_request_t http_read_request(http_conn_t *conn) { // read method if (arg == 0) { int method_length = i - start; - request.method = (char *) malloc(sizeof(char) * (method_length + 1)); - memcpy(request.method, line + start, method_length); - request.method[method_length] = '\0'; + request->method = (char *) malloc(sizeof(char) * (method_length + 1)); + memcpy(request->method, line + start, method_length); + request->method[method_length] = '\0'; } // read url if (arg == 1) { int url_length = i - start; - request.url = (char *) malloc(sizeof(char) * (url_length + 1)); - memcpy(request.url, line + start, url_length); - request.url[url_length] = '\0'; + request->url = (char *) malloc(sizeof(char) * (url_length + 1)); + memcpy(request->url, line + start, url_length); + request->url[url_length] = '\0'; } // read version if (arg == 2) { int version_length = i - start; - request.version = (char *) malloc(sizeof(char) * (version_length + 1)); - memcpy(request.version, line + start, version_length); - request.version[version_length] = '\0'; + request->version = (char *) malloc(sizeof(char) * (version_length + 1)); + memcpy(request->version, line + start, version_length); + request->version[version_length] = '\0'; } start = i + 1; arg++; @@ -524,9 +531,9 @@ http_request_t http_read_request(http_conn_t *conn) { // read headers else { int sep = 0; - request.headers = (http_header_t *) realloc(request.headers, sizeof(http_header_t) * (request.headers_len + 1)); - http_header_t *header = &request.headers[request.headers_len]; - request.headers_len++; + request->headers = (http_header_t *) realloc(request->headers, sizeof(http_header_t) * (request->headers_len + 1)); + http_header_t *header = &request->headers[request->headers_len]; + request->headers_len++; for (int i = 0; i < line_len; i++) { if (line[i] == ':') { int key_len = i; @@ -551,20 +558,20 @@ http_request_t http_read_request(http_conn_t *conn) { if (len >= 4) { char *slice = buffer + (len - 4); if (strncmp(slice, "\r\n\r\n", 4) == 0) { - char *content_length = http_header_get(&request, (char *) "Content-Length"); + char *content_length = http_header_get(request, (char *) "Content-Length"); if (content_length != NULL) { - request.body_len = strtol(content_length, NULL, 10); - if (request.body_len > 0) { - request.body = (char *) malloc(sizeof(char) * (request.body_len + 1)); - http_read(conn, request.body, request.body_len); - request.body[request.body_len] = '\0'; + request->body_len = strtol(content_length, NULL, 10); + if (request->body_len > 0) { + request->body = (char *) malloc(sizeof(char) * (request->body_len + 1)); + http_read(conn, request->body, request->body_len); + request->body[request->body_len] = '\0'; } } break; } } } - return request; + return HTTP_ERR_OK; } void http_write_response(http_conn_t *conn, http_response_t response) { @@ -597,27 +604,35 @@ void *http_handle_client(void *ptr) { pthread_exit(0); } http_conn_t *conn = (http_conn_t *) ptr; + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + if (setsockopt(conn->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)) < 0) { + close(conn->sockfd); + free(conn); + pthread_exit(0); + } if (conn->curr_bind->hosts_len > 0) { SSL_CTX_set_tlsext_servername_arg(conn->http->ssl_ctx, (void *) conn); conn->ssl = SSL_new(conn->http->ssl_ctx); SSL_set_fd(conn->ssl, conn->sockfd); if (SSL_accept(conn->ssl) <= 0) { + close(conn->sockfd); + free(conn); pthread_exit(0); } } - int flags = fcntl(conn->sockfd, F_GETFL, 0); - if (flags == -1) { - close(conn->sockfd); - free(conn); - pthread_exit(0); - } - flags |= O_NONBLOCK; - if (fcntl(conn->sockfd, F_SETFL, flags) == -1) { + http_request_t request; + http_error_t err = http_read_request(conn, &request); + if (err != HTTP_ERR_OK) { + if (conn->ssl != NULL) { + SSL_shutdown(conn->ssl); + SSL_free(conn->ssl); + } close(conn->sockfd); free(conn); pthread_exit(0); } - http_request_t request = http_read_request(conn); if (request.method != NULL && request.url != NULL) { if (conn->http->on_request_fn != NULL) { http_response_t response = conn->http->on_request_fn(&request);