Fix issues that caused the server to hang
This commit is contained in:
@@ -6,7 +6,7 @@ chttp.c chttp.h: $(wildcard src/*.c src/*.h) misc/amalg.py Makefile
|
|||||||
python misc/amalg.py
|
python misc/amalg.py
|
||||||
|
|
||||||
example: main.c chttp.c chttp.h
|
example: main.c chttp.c chttp.h
|
||||||
gcc main.c chttp.c -o example -Wfatal-errors -lws2_32
|
gcc main.c chttp.c -o example -ggdb
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm chttp.c chttp.h
|
rm chttp.c chttp.h
|
||||||
|
|||||||
@@ -1879,13 +1879,12 @@ void socket_manager_free(SocketManager *sm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int socket_manager_listen_tcp(SocketManager *sm,
|
int socket_manager_listen_tcp(SocketManager *sm,
|
||||||
HTTP_String addr, Port port)
|
HTTP_String addr, Port port, int backlog,
|
||||||
|
bool reuse_addr)
|
||||||
{
|
{
|
||||||
if (sm->plain_sock != NATIVE_SOCKET_INVALID)
|
if (sm->plain_sock != NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
bool reuse_addr = false;
|
|
||||||
int backlog = 32;
|
|
||||||
sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
||||||
if (sm->plain_sock == NATIVE_SOCKET_INVALID)
|
if (sm->plain_sock == NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1894,14 +1893,13 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file,
|
HTTP_String addr, Port port, int backlog,
|
||||||
|
bool reuse_addr, HTTP_String cert_file,
|
||||||
HTTP_String key_file)
|
HTTP_String key_file)
|
||||||
{
|
{
|
||||||
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
bool reuse_addr = false;
|
|
||||||
int backlog = 32;
|
|
||||||
sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
||||||
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -2374,6 +2372,12 @@ static int socket_manager_register_events_nolock(
|
|||||||
continue;
|
continue;
|
||||||
j++;
|
j++;
|
||||||
|
|
||||||
|
// TODO: comment
|
||||||
|
if (s->state == SOCKET_STATE_DIED || s->state == SOCKET_STATE_ESTABLISHED_READY) {
|
||||||
|
reg->num_polled = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (s->events) {
|
if (s->events) {
|
||||||
reg->polled[reg->num_polled].fd = s->sock;
|
reg->polled[reg->num_polled].fd = s->sock;
|
||||||
reg->polled[reg->num_polled].events = s->events;
|
reg->polled[reg->num_polled].events = s->events;
|
||||||
@@ -2423,6 +2427,9 @@ static int socket_manager_translate_events_nolock(
|
|||||||
int num_events = 0;
|
int num_events = 0;
|
||||||
for (int i = 0; i < reg->num_polled; i++) {
|
for (int i = 0; i < reg->num_polled; i++) {
|
||||||
|
|
||||||
|
if (!reg->polled[i].revents)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (reg->polled[i].fd == sm->plain_sock ||
|
if (reg->polled[i].fd == sm->plain_sock ||
|
||||||
reg->polled[i].fd == sm->secure_sock) {
|
reg->polled[i].fd == sm->secure_sock) {
|
||||||
|
|
||||||
@@ -3849,6 +3856,10 @@ int http_server_init(HTTP_Server *server)
|
|||||||
server->input_buffer_limit = 1<<20;
|
server->input_buffer_limit = 1<<20;
|
||||||
server->output_buffer_limit = 1<<20;
|
server->output_buffer_limit = 1<<20;
|
||||||
|
|
||||||
|
server->trace_bytes = false;
|
||||||
|
server->reuse_addr = false;
|
||||||
|
server->backlog = 32;
|
||||||
|
|
||||||
server->num_conns = 0;
|
server->num_conns = 0;
|
||||||
for (int i = 0; i < HTTP_SERVER_CAPACITY; i++) {
|
for (int i = 0; i < HTTP_SERVER_CAPACITY; i++) {
|
||||||
server->conns[i].state = HTTP_SERVER_CONN_FREE;
|
server->conns[i].state = HTTP_SERVER_CONN_FREE;
|
||||||
@@ -3888,10 +3899,26 @@ void http_server_set_output_limit(HTTP_Server *server, uint32_t limit)
|
|||||||
server->output_buffer_limit = limit;
|
server->output_buffer_limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void http_server_set_trace_bytes(HTTP_Server *server, bool value)
|
||||||
|
{
|
||||||
|
server->trace_bytes = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_server_set_reuse_addr(HTTP_Server *server, bool reuse)
|
||||||
|
{
|
||||||
|
server->reuse_addr = reuse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_server_set_backlog(HTTP_Server *server, int backlog)
|
||||||
|
{
|
||||||
|
server->backlog = backlog;
|
||||||
|
}
|
||||||
|
|
||||||
int http_server_listen_tcp(HTTP_Server *server,
|
int http_server_listen_tcp(HTTP_Server *server,
|
||||||
HTTP_String addr, Port port)
|
HTTP_String addr, Port port)
|
||||||
{
|
{
|
||||||
if (socket_manager_listen_tcp(&server->sockets, addr, port) < 0)
|
if (socket_manager_listen_tcp(&server->sockets, addr,
|
||||||
|
port, server->backlog, server->reuse_addr) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -3901,7 +3928,8 @@ int http_server_listen_tls(HTTP_Server *server,
|
|||||||
HTTP_String key_file_name)
|
HTTP_String key_file_name)
|
||||||
{
|
{
|
||||||
if (socket_manager_listen_tls(&server->sockets, addr,
|
if (socket_manager_listen_tls(&server->sockets, addr,
|
||||||
port, cert_file_name, key_file_name) < 0)
|
port, server->backlog, server->reuse_addr,
|
||||||
|
cert_file_name, key_file_name) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -3961,7 +3989,7 @@ check_request_buffer(HTTP_Server *server, HTTP_ServerConn *conn)
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Ready
|
// Ready
|
||||||
assert(ret == 1);
|
assert(ret > 0);
|
||||||
|
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_STATUS;
|
conn->state = HTTP_SERVER_CONN_WAIT_STATUS;
|
||||||
conn->request_len = ret;
|
conn->request_len = ret;
|
||||||
@@ -3975,6 +4003,67 @@ check_request_buffer(HTTP_Server *server, HTTP_ServerConn *conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
http_server_conn_process_events(HTTP_Server *server, HTTP_ServerConn *conn)
|
||||||
|
{
|
||||||
|
if (conn->state == HTTP_SERVER_CONN_FLUSHING) {
|
||||||
|
|
||||||
|
ByteView src = byte_queue_read_buf(&conn->output);
|
||||||
|
|
||||||
|
int num = 0;
|
||||||
|
if (src.len)
|
||||||
|
num = socket_send(&server->sockets, conn->handle, src.ptr, src.len);
|
||||||
|
|
||||||
|
if (server->trace_bytes)
|
||||||
|
print_bytes(HTTP_STR("<< "), (HTTP_String) { src.ptr, num });
|
||||||
|
|
||||||
|
byte_queue_read_ack(&conn->output, num);
|
||||||
|
|
||||||
|
if (byte_queue_error(&conn->output)) {
|
||||||
|
socket_close(&server->sockets, conn->handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte_queue_empty(&conn->output)) {
|
||||||
|
// We finished sending the response. Now we can
|
||||||
|
// either close the connection or process a new
|
||||||
|
// buffered request.
|
||||||
|
if (conn->closing) {
|
||||||
|
socket_close(&server->sockets, conn->handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conn->state = HTTP_SERVER_CONN_BUFFERING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->state == HTTP_SERVER_CONN_BUFFERING) {
|
||||||
|
|
||||||
|
int min_recv = 1<<10;
|
||||||
|
byte_queue_write_setmincap(&conn->input, min_recv);
|
||||||
|
|
||||||
|
// Note that it's extra important that we don't
|
||||||
|
// buffer while the user is building the response.
|
||||||
|
// If we did that, a resize would invalidate all
|
||||||
|
// pointers on the parsed request structure.
|
||||||
|
ByteView dst = byte_queue_write_buf(&conn->input);
|
||||||
|
|
||||||
|
int num = 0;
|
||||||
|
if (dst.len)
|
||||||
|
num = socket_recv(&server->sockets, conn->handle, dst.ptr, dst.len);
|
||||||
|
|
||||||
|
if (server->trace_bytes)
|
||||||
|
print_bytes(HTTP_STR(">> "), (HTTP_String) { dst.ptr, num });
|
||||||
|
|
||||||
|
byte_queue_write_ack(&conn->input, num);
|
||||||
|
|
||||||
|
if (byte_queue_error(&conn->output)) {
|
||||||
|
socket_close(&server->sockets, conn->handle);
|
||||||
|
} else {
|
||||||
|
check_request_buffer(server, conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int http_server_process_events(HTTP_Server *server,
|
int http_server_process_events(HTTP_Server *server,
|
||||||
EventRegister *reg)
|
EventRegister *reg)
|
||||||
{
|
{
|
||||||
@@ -4001,13 +4090,13 @@ int http_server_process_events(HTTP_Server *server,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
int j = 0;
|
||||||
while (server->conns[i].state != HTTP_SERVER_CONN_FREE) {
|
while (server->conns[j].state != HTTP_SERVER_CONN_FREE) {
|
||||||
i++;
|
j++;
|
||||||
assert(i < HTTP_SERVER_CAPACITY);
|
assert(i < HTTP_SERVER_CAPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = &server->conns[i];
|
conn = &server->conns[j];
|
||||||
http_server_conn_init(conn,
|
http_server_conn_init(conn,
|
||||||
events[i].handle,
|
events[i].handle,
|
||||||
server->input_buffer_limit,
|
server->input_buffer_limit,
|
||||||
@@ -4017,45 +4106,7 @@ int http_server_process_events(HTTP_Server *server,
|
|||||||
socket_set_user(&server->sockets, events[i].handle, conn);
|
socket_set_user(&server->sockets, events[i].handle, conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->state == HTTP_SERVER_CONN_BUFFERING) {
|
http_server_conn_process_events(server, conn);
|
||||||
|
|
||||||
int min_recv = 1<<10;
|
|
||||||
byte_queue_write_setmincap(&conn->input, min_recv);
|
|
||||||
|
|
||||||
// Note that it's extra important that we don't
|
|
||||||
// buffer while the user is building the response.
|
|
||||||
// If we did that, a resize would invalidate all
|
|
||||||
// pointers on the parsed request structure.
|
|
||||||
int num = 0;
|
|
||||||
ByteView dst = byte_queue_write_buf(&conn->input);
|
|
||||||
if (dst.len) num = socket_recv(&server->sockets, conn->handle, dst.ptr, dst.len);
|
|
||||||
byte_queue_write_ack(&conn->input, num);
|
|
||||||
|
|
||||||
if (byte_queue_error(&conn->output))
|
|
||||||
socket_close(&server->sockets, conn->handle);
|
|
||||||
else
|
|
||||||
check_request_buffer(server, conn);
|
|
||||||
|
|
||||||
} else if (conn->state == HTTP_SERVER_CONN_FLUSHING) {
|
|
||||||
|
|
||||||
int num = 0;
|
|
||||||
ByteView src = byte_queue_read_buf(&conn->output);
|
|
||||||
if (src.len) num = socket_send(&server->sockets, conn->handle, src.ptr, src.len);
|
|
||||||
byte_queue_read_ack(&conn->output, num);
|
|
||||||
|
|
||||||
if (byte_queue_error(&conn->output))
|
|
||||||
socket_close(&server->sockets, conn->handle);
|
|
||||||
else if (byte_queue_empty(&conn->output)) {
|
|
||||||
// We finished sending the response. Now we can
|
|
||||||
// either close the connection or process a new
|
|
||||||
// buffered request.
|
|
||||||
if (conn->closing) {
|
|
||||||
socket_close(&server->sockets, conn->handle);
|
|
||||||
} else {
|
|
||||||
check_request_buffer(server, conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4225,10 +4276,10 @@ static void append_special_headers(HTTP_ServerConn *conn)
|
|||||||
HTTP_String s;
|
HTTP_String s;
|
||||||
|
|
||||||
if (conn->closing) {
|
if (conn->closing) {
|
||||||
s = HTTP_STR("Connection: Close");
|
s = HTTP_STR("Connection: Close\r\n");
|
||||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||||
} else {
|
} else {
|
||||||
s = HTTP_STR("Connection: Keep-Alive");
|
s = HTTP_STR("Connection: Keep-Alive\r\n");
|
||||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4275,7 +4326,7 @@ void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str)
|
|||||||
byte_queue_write(&conn->output, str.ptr, str.len);
|
byte_queue_write(&conn->output, str.ptr, str.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str)
|
void http_response_builder_send(HTTP_ResponseBuilder builder)
|
||||||
{
|
{
|
||||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
HTTP_ServerConn *conn = builder_to_conn(builder);
|
||||||
if (conn == NULL)
|
if (conn == NULL)
|
||||||
@@ -4299,6 +4350,8 @@ void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str)
|
|||||||
|
|
||||||
conn->state = HTTP_SERVER_CONN_FLUSHING;
|
conn->state = HTTP_SERVER_CONN_FLUSHING;
|
||||||
conn->gen++;
|
conn->gen++;
|
||||||
|
|
||||||
|
http_server_conn_process_events(builder.server, conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>-
|
#include <errno.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
@@ -493,7 +493,8 @@ void socket_manager_free(SocketManager *sm);
|
|||||||
// can only be used once per manager.
|
// can only be used once per manager.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_manager_listen_tcp(SocketManager *sm,
|
int socket_manager_listen_tcp(SocketManager *sm,
|
||||||
HTTP_String addr, Port port);
|
HTTP_String addr, Port port, int backlog,
|
||||||
|
bool reuse_addr);
|
||||||
|
|
||||||
// Same as the previous function, but incoming
|
// Same as the previous function, but incoming
|
||||||
// connections will be interpreted as TLS. You
|
// connections will be interpreted as TLS. You
|
||||||
@@ -503,7 +504,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
|||||||
// and secure connections.
|
// and secure connections.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file,
|
HTTP_String addr, Port port, int backlog,
|
||||||
|
bool reuse_addr, HTTP_String cert_file,
|
||||||
HTTP_String key_file);
|
HTTP_String key_file);
|
||||||
|
|
||||||
// If the socket manager was configures to accept
|
// If the socket manager was configures to accept
|
||||||
@@ -1012,6 +1014,10 @@ typedef struct {
|
|||||||
uint32_t input_buffer_limit;
|
uint32_t input_buffer_limit;
|
||||||
uint32_t output_buffer_limit;
|
uint32_t output_buffer_limit;
|
||||||
|
|
||||||
|
bool trace_bytes;
|
||||||
|
bool reuse_addr;
|
||||||
|
int backlog;
|
||||||
|
|
||||||
// Array of connections. The counter contains the
|
// Array of connections. The counter contains the
|
||||||
// number of structs such that state=FREE.
|
// number of structs such that state=FREE.
|
||||||
int num_conns;
|
int num_conns;
|
||||||
@@ -1054,6 +1060,15 @@ void http_server_free(HTTP_Server *server);
|
|||||||
void http_server_set_input_limit(HTTP_Server *server, uint32_t limit);
|
void http_server_set_input_limit(HTTP_Server *server, uint32_t limit);
|
||||||
void http_server_set_output_limit(HTTP_Server *server, uint32_t limit);
|
void http_server_set_output_limit(HTTP_Server *server, uint32_t limit);
|
||||||
|
|
||||||
|
// TODO: Comment
|
||||||
|
void http_server_set_trace_bytes(HTTP_Server *server, bool value);
|
||||||
|
|
||||||
|
// TODO: Comment
|
||||||
|
void http_server_set_reuse_addr(HTTP_Server *server, bool reuse);
|
||||||
|
|
||||||
|
// TODO: comment
|
||||||
|
void http_server_set_backlog(HTTP_Server *server, int backlog);
|
||||||
|
|
||||||
// Enable listening for plain HTTP requests at the
|
// Enable listening for plain HTTP requests at the
|
||||||
// specified interface.
|
// specified interface.
|
||||||
int http_server_listen_tcp(HTTP_Server *server,
|
int http_server_listen_tcp(HTTP_Server *server,
|
||||||
@@ -1062,9 +1077,8 @@ int http_server_listen_tcp(HTTP_Server *server,
|
|||||||
// Enable listening for HTTPS requests at the specified
|
// Enable listening for HTTPS requests at the specified
|
||||||
// interfact, using the specified certificate and key
|
// interfact, using the specified certificate and key
|
||||||
// to verify the connection.
|
// to verify the connection.
|
||||||
int http_server_listen_tls(HTTP_Server *server,
|
int http_server_listen_tls(HTTP_Server *server, HTTP_String addr, Port port,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
HTTP_String cert_file_name, HTTP_String key_file_name);
|
||||||
HTTP_String key_file_name);
|
|
||||||
|
|
||||||
// Add the certificate for an additional domain when
|
// Add the certificate for an additional domain when
|
||||||
// the server is listening for HTTPS requests.
|
// the server is listening for HTTPS requests.
|
||||||
@@ -1120,7 +1134,7 @@ void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str);
|
|||||||
|
|
||||||
// Mark the response as complete. This will invalidate
|
// Mark the response as complete. This will invalidate
|
||||||
// the response builder handle.
|
// the response builder handle.
|
||||||
void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str);
|
void http_response_builder_send(HTTP_ResponseBuilder builder);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Copyright 2025 Francesco Cozzuto
|
// Copyright 2025 Francesco Cozzuto
|
||||||
|
|||||||
@@ -1,5 +1,52 @@
|
|||||||
|
#include "chttp.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define POLL WSAPoll
|
||||||
|
#else
|
||||||
|
#define POLL poll
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
HTTP_Server server;
|
||||||
|
if (http_server_init(&server) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
http_server_set_reuse_addr(&server, true);
|
||||||
|
http_server_set_trace_bytes(&server, true);
|
||||||
|
|
||||||
|
if (http_server_listen_tcp(&server, HTTP_STR("127.0.0.1"), 8080) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
void *ptrs[HTTP_SERVER_CAPACITY+3];
|
||||||
|
struct pollfd polled[HTTP_SERVER_CAPACITY+3];
|
||||||
|
|
||||||
|
EventRegister reg = {
|
||||||
|
.ptrs=ptrs,
|
||||||
|
.polled=polled,
|
||||||
|
.max_polled=HTTP_SERVER_CAPACITY+3,
|
||||||
|
.num_polled=0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (http_server_register_events(&server, ®) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (reg.num_polled > 0)
|
||||||
|
POLL(reg.polled, reg.num_polled, -1);
|
||||||
|
|
||||||
|
if (http_server_process_events(&server, ®) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
HTTP_Request *request;
|
||||||
|
HTTP_ResponseBuilder builder;
|
||||||
|
if (http_server_next_request(&server, &request, &builder)) {
|
||||||
|
http_response_builder_status(builder, 200);
|
||||||
|
http_response_builder_send(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http_server_free(&server);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>-
|
#include <errno.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|||||||
+95
-49
@@ -21,6 +21,10 @@ int http_server_init(HTTP_Server *server)
|
|||||||
server->input_buffer_limit = 1<<20;
|
server->input_buffer_limit = 1<<20;
|
||||||
server->output_buffer_limit = 1<<20;
|
server->output_buffer_limit = 1<<20;
|
||||||
|
|
||||||
|
server->trace_bytes = false;
|
||||||
|
server->reuse_addr = false;
|
||||||
|
server->backlog = 32;
|
||||||
|
|
||||||
server->num_conns = 0;
|
server->num_conns = 0;
|
||||||
for (int i = 0; i < HTTP_SERVER_CAPACITY; i++) {
|
for (int i = 0; i < HTTP_SERVER_CAPACITY; i++) {
|
||||||
server->conns[i].state = HTTP_SERVER_CONN_FREE;
|
server->conns[i].state = HTTP_SERVER_CONN_FREE;
|
||||||
@@ -60,10 +64,26 @@ void http_server_set_output_limit(HTTP_Server *server, uint32_t limit)
|
|||||||
server->output_buffer_limit = limit;
|
server->output_buffer_limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void http_server_set_trace_bytes(HTTP_Server *server, bool value)
|
||||||
|
{
|
||||||
|
server->trace_bytes = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_server_set_reuse_addr(HTTP_Server *server, bool reuse)
|
||||||
|
{
|
||||||
|
server->reuse_addr = reuse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_server_set_backlog(HTTP_Server *server, int backlog)
|
||||||
|
{
|
||||||
|
server->backlog = backlog;
|
||||||
|
}
|
||||||
|
|
||||||
int http_server_listen_tcp(HTTP_Server *server,
|
int http_server_listen_tcp(HTTP_Server *server,
|
||||||
HTTP_String addr, Port port)
|
HTTP_String addr, Port port)
|
||||||
{
|
{
|
||||||
if (socket_manager_listen_tcp(&server->sockets, addr, port) < 0)
|
if (socket_manager_listen_tcp(&server->sockets, addr,
|
||||||
|
port, server->backlog, server->reuse_addr) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -73,7 +93,8 @@ int http_server_listen_tls(HTTP_Server *server,
|
|||||||
HTTP_String key_file_name)
|
HTTP_String key_file_name)
|
||||||
{
|
{
|
||||||
if (socket_manager_listen_tls(&server->sockets, addr,
|
if (socket_manager_listen_tls(&server->sockets, addr,
|
||||||
port, cert_file_name, key_file_name) < 0)
|
port, server->backlog, server->reuse_addr,
|
||||||
|
cert_file_name, key_file_name) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -133,7 +154,7 @@ check_request_buffer(HTTP_Server *server, HTTP_ServerConn *conn)
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Ready
|
// Ready
|
||||||
assert(ret == 1);
|
assert(ret > 0);
|
||||||
|
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_STATUS;
|
conn->state = HTTP_SERVER_CONN_WAIT_STATUS;
|
||||||
conn->request_len = ret;
|
conn->request_len = ret;
|
||||||
@@ -147,6 +168,67 @@ check_request_buffer(HTTP_Server *server, HTTP_ServerConn *conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
http_server_conn_process_events(HTTP_Server *server, HTTP_ServerConn *conn)
|
||||||
|
{
|
||||||
|
if (conn->state == HTTP_SERVER_CONN_FLUSHING) {
|
||||||
|
|
||||||
|
ByteView src = byte_queue_read_buf(&conn->output);
|
||||||
|
|
||||||
|
int num = 0;
|
||||||
|
if (src.len)
|
||||||
|
num = socket_send(&server->sockets, conn->handle, src.ptr, src.len);
|
||||||
|
|
||||||
|
if (server->trace_bytes)
|
||||||
|
print_bytes(HTTP_STR("<< "), (HTTP_String) { src.ptr, num });
|
||||||
|
|
||||||
|
byte_queue_read_ack(&conn->output, num);
|
||||||
|
|
||||||
|
if (byte_queue_error(&conn->output)) {
|
||||||
|
socket_close(&server->sockets, conn->handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte_queue_empty(&conn->output)) {
|
||||||
|
// We finished sending the response. Now we can
|
||||||
|
// either close the connection or process a new
|
||||||
|
// buffered request.
|
||||||
|
if (conn->closing) {
|
||||||
|
socket_close(&server->sockets, conn->handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conn->state = HTTP_SERVER_CONN_BUFFERING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->state == HTTP_SERVER_CONN_BUFFERING) {
|
||||||
|
|
||||||
|
int min_recv = 1<<10;
|
||||||
|
byte_queue_write_setmincap(&conn->input, min_recv);
|
||||||
|
|
||||||
|
// Note that it's extra important that we don't
|
||||||
|
// buffer while the user is building the response.
|
||||||
|
// If we did that, a resize would invalidate all
|
||||||
|
// pointers on the parsed request structure.
|
||||||
|
ByteView dst = byte_queue_write_buf(&conn->input);
|
||||||
|
|
||||||
|
int num = 0;
|
||||||
|
if (dst.len)
|
||||||
|
num = socket_recv(&server->sockets, conn->handle, dst.ptr, dst.len);
|
||||||
|
|
||||||
|
if (server->trace_bytes)
|
||||||
|
print_bytes(HTTP_STR(">> "), (HTTP_String) { dst.ptr, num });
|
||||||
|
|
||||||
|
byte_queue_write_ack(&conn->input, num);
|
||||||
|
|
||||||
|
if (byte_queue_error(&conn->output)) {
|
||||||
|
socket_close(&server->sockets, conn->handle);
|
||||||
|
} else {
|
||||||
|
check_request_buffer(server, conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int http_server_process_events(HTTP_Server *server,
|
int http_server_process_events(HTTP_Server *server,
|
||||||
EventRegister *reg)
|
EventRegister *reg)
|
||||||
{
|
{
|
||||||
@@ -173,13 +255,13 @@ int http_server_process_events(HTTP_Server *server,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
int j = 0;
|
||||||
while (server->conns[i].state != HTTP_SERVER_CONN_FREE) {
|
while (server->conns[j].state != HTTP_SERVER_CONN_FREE) {
|
||||||
i++;
|
j++;
|
||||||
assert(i < HTTP_SERVER_CAPACITY);
|
assert(i < HTTP_SERVER_CAPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = &server->conns[i];
|
conn = &server->conns[j];
|
||||||
http_server_conn_init(conn,
|
http_server_conn_init(conn,
|
||||||
events[i].handle,
|
events[i].handle,
|
||||||
server->input_buffer_limit,
|
server->input_buffer_limit,
|
||||||
@@ -189,45 +271,7 @@ int http_server_process_events(HTTP_Server *server,
|
|||||||
socket_set_user(&server->sockets, events[i].handle, conn);
|
socket_set_user(&server->sockets, events[i].handle, conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->state == HTTP_SERVER_CONN_BUFFERING) {
|
http_server_conn_process_events(server, conn);
|
||||||
|
|
||||||
int min_recv = 1<<10;
|
|
||||||
byte_queue_write_setmincap(&conn->input, min_recv);
|
|
||||||
|
|
||||||
// Note that it's extra important that we don't
|
|
||||||
// buffer while the user is building the response.
|
|
||||||
// If we did that, a resize would invalidate all
|
|
||||||
// pointers on the parsed request structure.
|
|
||||||
int num = 0;
|
|
||||||
ByteView dst = byte_queue_write_buf(&conn->input);
|
|
||||||
if (dst.len) num = socket_recv(&server->sockets, conn->handle, dst.ptr, dst.len);
|
|
||||||
byte_queue_write_ack(&conn->input, num);
|
|
||||||
|
|
||||||
if (byte_queue_error(&conn->output))
|
|
||||||
socket_close(&server->sockets, conn->handle);
|
|
||||||
else
|
|
||||||
check_request_buffer(server, conn);
|
|
||||||
|
|
||||||
} else if (conn->state == HTTP_SERVER_CONN_FLUSHING) {
|
|
||||||
|
|
||||||
int num = 0;
|
|
||||||
ByteView src = byte_queue_read_buf(&conn->output);
|
|
||||||
if (src.len) num = socket_send(&server->sockets, conn->handle, src.ptr, src.len);
|
|
||||||
byte_queue_read_ack(&conn->output, num);
|
|
||||||
|
|
||||||
if (byte_queue_error(&conn->output))
|
|
||||||
socket_close(&server->sockets, conn->handle);
|
|
||||||
else if (byte_queue_empty(&conn->output)) {
|
|
||||||
// We finished sending the response. Now we can
|
|
||||||
// either close the connection or process a new
|
|
||||||
// buffered request.
|
|
||||||
if (conn->closing) {
|
|
||||||
socket_close(&server->sockets, conn->handle);
|
|
||||||
} else {
|
|
||||||
check_request_buffer(server, conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,10 +441,10 @@ static void append_special_headers(HTTP_ServerConn *conn)
|
|||||||
HTTP_String s;
|
HTTP_String s;
|
||||||
|
|
||||||
if (conn->closing) {
|
if (conn->closing) {
|
||||||
s = HTTP_STR("Connection: Close");
|
s = HTTP_STR("Connection: Close\r\n");
|
||||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||||
} else {
|
} else {
|
||||||
s = HTTP_STR("Connection: Keep-Alive");
|
s = HTTP_STR("Connection: Keep-Alive\r\n");
|
||||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +491,7 @@ void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str)
|
|||||||
byte_queue_write(&conn->output, str.ptr, str.len);
|
byte_queue_write(&conn->output, str.ptr, str.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str)
|
void http_response_builder_send(HTTP_ResponseBuilder builder)
|
||||||
{
|
{
|
||||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
HTTP_ServerConn *conn = builder_to_conn(builder);
|
||||||
if (conn == NULL)
|
if (conn == NULL)
|
||||||
@@ -471,4 +515,6 @@ void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str)
|
|||||||
|
|
||||||
conn->state = HTTP_SERVER_CONN_FLUSHING;
|
conn->state = HTTP_SERVER_CONN_FLUSHING;
|
||||||
conn->gen++;
|
conn->gen++;
|
||||||
|
|
||||||
|
http_server_conn_process_events(builder.server, conn);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-4
@@ -97,6 +97,10 @@ typedef struct {
|
|||||||
uint32_t input_buffer_limit;
|
uint32_t input_buffer_limit;
|
||||||
uint32_t output_buffer_limit;
|
uint32_t output_buffer_limit;
|
||||||
|
|
||||||
|
bool trace_bytes;
|
||||||
|
bool reuse_addr;
|
||||||
|
int backlog;
|
||||||
|
|
||||||
// Array of connections. The counter contains the
|
// Array of connections. The counter contains the
|
||||||
// number of structs such that state=FREE.
|
// number of structs such that state=FREE.
|
||||||
int num_conns;
|
int num_conns;
|
||||||
@@ -139,6 +143,15 @@ void http_server_free(HTTP_Server *server);
|
|||||||
void http_server_set_input_limit(HTTP_Server *server, uint32_t limit);
|
void http_server_set_input_limit(HTTP_Server *server, uint32_t limit);
|
||||||
void http_server_set_output_limit(HTTP_Server *server, uint32_t limit);
|
void http_server_set_output_limit(HTTP_Server *server, uint32_t limit);
|
||||||
|
|
||||||
|
// TODO: Comment
|
||||||
|
void http_server_set_trace_bytes(HTTP_Server *server, bool value);
|
||||||
|
|
||||||
|
// TODO: Comment
|
||||||
|
void http_server_set_reuse_addr(HTTP_Server *server, bool reuse);
|
||||||
|
|
||||||
|
// TODO: comment
|
||||||
|
void http_server_set_backlog(HTTP_Server *server, int backlog);
|
||||||
|
|
||||||
// Enable listening for plain HTTP requests at the
|
// Enable listening for plain HTTP requests at the
|
||||||
// specified interface.
|
// specified interface.
|
||||||
int http_server_listen_tcp(HTTP_Server *server,
|
int http_server_listen_tcp(HTTP_Server *server,
|
||||||
@@ -147,9 +160,8 @@ int http_server_listen_tcp(HTTP_Server *server,
|
|||||||
// Enable listening for HTTPS requests at the specified
|
// Enable listening for HTTPS requests at the specified
|
||||||
// interfact, using the specified certificate and key
|
// interfact, using the specified certificate and key
|
||||||
// to verify the connection.
|
// to verify the connection.
|
||||||
int http_server_listen_tls(HTTP_Server *server,
|
int http_server_listen_tls(HTTP_Server *server, HTTP_String addr, Port port,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
HTTP_String cert_file_name, HTTP_String key_file_name);
|
||||||
HTTP_String key_file_name);
|
|
||||||
|
|
||||||
// Add the certificate for an additional domain when
|
// Add the certificate for an additional domain when
|
||||||
// the server is listening for HTTPS requests.
|
// the server is listening for HTTPS requests.
|
||||||
@@ -205,4 +217,4 @@ void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str);
|
|||||||
|
|
||||||
// Mark the response as complete. This will invalidate
|
// Mark the response as complete. This will invalidate
|
||||||
// the response builder handle.
|
// the response builder handle.
|
||||||
void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str);
|
void http_response_builder_send(HTTP_ResponseBuilder builder);
|
||||||
|
|||||||
+15
-7
@@ -177,13 +177,12 @@ void socket_manager_free(SocketManager *sm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int socket_manager_listen_tcp(SocketManager *sm,
|
int socket_manager_listen_tcp(SocketManager *sm,
|
||||||
HTTP_String addr, Port port)
|
HTTP_String addr, Port port, int backlog,
|
||||||
|
bool reuse_addr)
|
||||||
{
|
{
|
||||||
if (sm->plain_sock != NATIVE_SOCKET_INVALID)
|
if (sm->plain_sock != NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
bool reuse_addr = false;
|
|
||||||
int backlog = 32;
|
|
||||||
sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
||||||
if (sm->plain_sock == NATIVE_SOCKET_INVALID)
|
if (sm->plain_sock == NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -192,14 +191,13 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file,
|
HTTP_String addr, Port port, int backlog,
|
||||||
|
bool reuse_addr, HTTP_String cert_file,
|
||||||
HTTP_String key_file)
|
HTTP_String key_file)
|
||||||
{
|
{
|
||||||
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
bool reuse_addr = false;
|
|
||||||
int backlog = 32;
|
|
||||||
sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
||||||
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -365,7 +363,7 @@ static void socket_update(Socket *s)
|
|||||||
struct sockaddr_in6 buf;
|
struct sockaddr_in6 buf;
|
||||||
buf.sin6_family = AF_INET6;
|
buf.sin6_family = AF_INET6;
|
||||||
buf.sin6_port = htons(addr.port);
|
buf.sin6_port = htons(addr.port);
|
||||||
memcpy(&buf.sin6_addr, &addr.ipv6, sizeof(IPv6));
|
memcpy(&buf.sin6_addr, &addr.ipv6, sizeof(HTTP_IPv6));
|
||||||
ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf));
|
ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -672,6 +670,13 @@ static int socket_manager_register_events_nolock(
|
|||||||
continue;
|
continue;
|
||||||
j++;
|
j++;
|
||||||
|
|
||||||
|
// If at least one socket can be processed, return an
|
||||||
|
// empty list.
|
||||||
|
if (s->state == SOCKET_STATE_DIED || s->state == SOCKET_STATE_ESTABLISHED_READY) {
|
||||||
|
reg->num_polled = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (s->events) {
|
if (s->events) {
|
||||||
reg->polled[reg->num_polled].fd = s->sock;
|
reg->polled[reg->num_polled].fd = s->sock;
|
||||||
reg->polled[reg->num_polled].events = s->events;
|
reg->polled[reg->num_polled].events = s->events;
|
||||||
@@ -721,6 +726,9 @@ static int socket_manager_translate_events_nolock(
|
|||||||
int num_events = 0;
|
int num_events = 0;
|
||||||
for (int i = 0; i < reg->num_polled; i++) {
|
for (int i = 0; i < reg->num_polled; i++) {
|
||||||
|
|
||||||
|
if (!reg->polled[i].revents)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (reg->polled[i].fd == sm->plain_sock ||
|
if (reg->polled[i].fd == sm->plain_sock ||
|
||||||
reg->polled[i].fd == sm->secure_sock) {
|
reg->polled[i].fd == sm->secure_sock) {
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -236,7 +236,8 @@ void socket_manager_free(SocketManager *sm);
|
|||||||
// can only be used once per manager.
|
// can only be used once per manager.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_manager_listen_tcp(SocketManager *sm,
|
int socket_manager_listen_tcp(SocketManager *sm,
|
||||||
HTTP_String addr, Port port);
|
HTTP_String addr, Port port, int backlog,
|
||||||
|
bool reuse_addr);
|
||||||
|
|
||||||
// Same as the previous function, but incoming
|
// Same as the previous function, but incoming
|
||||||
// connections will be interpreted as TLS. You
|
// connections will be interpreted as TLS. You
|
||||||
@@ -246,7 +247,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
|||||||
// and secure connections.
|
// and secure connections.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file,
|
HTTP_String addr, Port port, int backlog,
|
||||||
|
bool reuse_addr, HTTP_String cert_file,
|
||||||
HTTP_String key_file);
|
HTTP_String key_file);
|
||||||
|
|
||||||
// If the socket manager was configures to accept
|
// If the socket manager was configures to accept
|
||||||
|
|||||||
Reference in New Issue
Block a user