Improvements on the Windows version

This commit is contained in:
2025-04-17 12:56:15 +02:00
parent 08956a84cf
commit c556935bb7
3 changed files with 459 additions and 196 deletions
+3 -2
View File
@@ -58,8 +58,9 @@ int main(void)
while (!should_exit) { while (!should_exit) {
TinyHTTPRequest *req; TinyHTTPRequest *req;
TinyHTTPResponse res; TinyHTTPResponse res;
if (tinyhttp_server_wait(server, &req, &res, 1000)) int ret = tinyhttp_server_wait(server, &req, &res, 1000);
continue; // Timeout or error if (ret < 0) return -1; // Error
if (ret > 0) continue; // Timeout
tinyhttp_response_status(res, 200); tinyhttp_response_status(res, 200);
tinyhttp_response_send(res); tinyhttp_response_send(res);
} }
+448 -186
View File
@@ -9,6 +9,7 @@
#if defined(_WIN32) #if defined(_WIN32)
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <mswsock.h>
#define CLOSESOCKET closesocket #define CLOSESOCKET closesocket
#elif defined(__linux__) #elif defined(__linux__)
#include <time.h> #include <time.h>
@@ -31,6 +32,48 @@
#define ASSERT(X) {if (!(X)) __builtin_trap();} #define ASSERT(X) {if (!(X)) __builtin_trap();}
#define COUNTOF(X) (sizeof(X)/sizeof((X)[0])) #define COUNTOF(X) (sizeof(X)/sizeof((X)[0]))
#define DUMP_IO 1
////////////////////////////////////////////////////////////////////////////////////
// DEBUG UTILITIES //
////////////////////////////////////////////////////////////////////////////////////
#if DUMP_IO
#include <stdio.h>
static void
print_bytes(char *prefix, char *src, int len)
{
if (src == NULL) {
printf("%s (null)\n", prefix);
return;
}
int cur = 0;
int newline = 1;
while (cur < len) {
int start = cur;
while (cur < len && src[cur] != '\n' && src[cur] != '\r')
cur++;
if (newline) {
printf("%s", prefix);
newline = 0;
}
printf("%.*s", cur - start, src + start);
if (cur < len) {
if (src[cur] == '\r')
printf("\\r");
else {
printf("\\n\n");
newline = 1;
}
cur++;
}
}
if (cur > 0 && src[cur-1] != '\n')
printf("\n");
}
#endif
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
// HTTP REQUEST PARSER // // HTTP REQUEST PARSER //
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
@@ -42,11 +85,11 @@ parse_request_head(char *src, ptrdiff_t len, TinyHTTPRequest *req)
ptrdiff_t cur = 0; ptrdiff_t cur = 0;
int found = 0; int found = 0;
for (ptrdiff_t cur = 0; len - cur > 3; cur++) { for (ptrdiff_t peek = 0; len - peek > 3; peek++) {
if (src[cur+0] == '\r' && if (src[peek+0] == '\r' &&
src[cur+1] == '\n' && // Boyer-Moore? src[peek+1] == '\n' && // Boyer-Moore?
src[cur+2] == '\r' && src[peek+2] == '\r' &&
src[cur+3] == '\n') { src[peek+3] == '\n') {
found = 1; found = 1;
break; break;
} }
@@ -374,41 +417,6 @@ callback_free(TinyHTTPByteQueue *queue, void *ptr, ptrdiff_t len)
queue->memfunc(TINYHTTP_MEM_FREE, ptr, len, queue->memfuncdata); queue->memfunc(TINYHTTP_MEM_FREE, ptr, len, queue->memfuncdata);
} }
#include <stdio.h>
static void
print_bytes(char *prefix, char *src, int len)
{
if (src == NULL) {
printf("%s (null)\n", prefix);
return;
}
int cur = 0;
int newline = 1;
while (cur < len) {
int start = cur;
while (cur < len && src[cur] != '\n' && src[cur] != '\r')
cur++;
if (newline) {
printf("%s", prefix);
newline = 0;
}
printf("%.*s", cur - start, src + start);
if (cur < len) {
if (src[cur] == '\r')
printf("\\r");
else {
printf("\\n\n");
newline = 1;
}
cur++;
}
}
if (cur > 0 && src[cur-1] != '\n')
printf("\n");
}
// Initialize the queue // Initialize the queue
static void static void
byte_queue_init(TinyHTTPByteQueue *queue, unsigned int limit, TinyHTTPMemoryFunc memfunc, void *memfuncdata) byte_queue_init(TinyHTTPByteQueue *queue, unsigned int limit, TinyHTTPMemoryFunc memfunc, void *memfuncdata)
@@ -739,6 +747,7 @@ byte_queue_write_fmt2(TinyHTTPByteQueue *queue, const char *fmt, va_list args)
va_copy(args2, args); va_copy(args2, args);
ptrdiff_t cap; ptrdiff_t cap;
byte_queue_write_setmincap(queue, 128);
char *dst = byte_queue_write_buf(queue, &cap); char *dst = byte_queue_write_buf(queue, &cap);
int len = vsnprintf(dst, cap, fmt, args); int len = vsnprintf(dst, cap, fmt, args);
@@ -933,7 +942,7 @@ int tinyhttp_stream_state(TinyHTTPStream *stream)
} }
// See tinyhttp.h // See tinyhttp.h
char *tinyhttp_stream_net_recv_buf(TinyHTTPStream *stream, ptrdiff_t *cap) char *tinyhttp_stream_recv_buf(TinyHTTPStream *stream, ptrdiff_t *cap)
{ {
// Sticky error // Sticky error
if (stream->state == TINYHTTP_STREAM_FREE) { if (stream->state == TINYHTTP_STREAM_FREE) {
@@ -1039,7 +1048,7 @@ process_next_request(TinyHTTPStream *stream)
} }
// See tinyhttp.h // See tinyhttp.h
void tinyhttp_stream_net_recv_ack(TinyHTTPStream *stream, ptrdiff_t num) void tinyhttp_stream_recv_ack(TinyHTTPStream *stream, ptrdiff_t num)
{ {
// Sticky error // Sticky error
if (stream->state == TINYHTTP_STREAM_FREE) if (stream->state == TINYHTTP_STREAM_FREE)
@@ -1059,7 +1068,7 @@ void tinyhttp_stream_net_recv_ack(TinyHTTPStream *stream, ptrdiff_t num)
} }
// See tinyhttp.h // See tinyhttp.h
char *tinyhttp_stream_net_send_buf(TinyHTTPStream *stream, ptrdiff_t *len) char *tinyhttp_stream_send_buf(TinyHTTPStream *stream, ptrdiff_t *len)
{ {
// Sticky error // Sticky error
if (stream->state == TINYHTTP_STREAM_FREE) if (stream->state == TINYHTTP_STREAM_FREE)
@@ -1069,7 +1078,7 @@ char *tinyhttp_stream_net_send_buf(TinyHTTPStream *stream, ptrdiff_t *len)
} }
// See tinyhttp.h // See tinyhttp.h
void tinyhttp_stream_net_send_ack(TinyHTTPStream *stream, ptrdiff_t num) void tinyhttp_stream_send_ack(TinyHTTPStream *stream, ptrdiff_t num)
{ {
// Sticky error // Sticky error
if (stream->state == TINYHTTP_STREAM_FREE) if (stream->state == TINYHTTP_STREAM_FREE)
@@ -1337,7 +1346,10 @@ void tinyhttp_stream_response_send(TinyHTTPStream *stream)
return; return;
} }
//ptrdiff_t ressize = (byte_queue_offset(&stream->out) - stream->out.lock); #if DUMP_IO
ptrdiff_t ressize = (byte_queue_offset(&stream->out) - stream->out.lock);
print_bytes("R << ", stream->out.data + stream->out.head, ressize);
#endif
byte_queue_read_ack(&stream->in, stream->reqsize); byte_queue_read_ack(&stream->in, stream->reqsize);
byte_queue_read_unlock(&stream->out); byte_queue_read_unlock(&stream->out);
@@ -1363,11 +1375,15 @@ void tinyhttp_stream_response_undo(TinyHTTPStream *stream)
struct TinyHTTPServer { struct TinyHTTPServer {
#if defined(_WIN32) #if defined(_WIN32)
int deinit_winsock;
HANDLE iocp; HANDLE iocp;
OVERLAPPED plain_accept_overlapped; OVERLAPPED plain_accept_overlapped;
OVERLAPPED secure_accept_overlapped; OVERLAPPED secure_accept_overlapped;
SOCKET plain_accept_target; SOCKET plain_accept_target;
SOCKET secure_accept_target; SOCKET secure_accept_target;
LPFN_ACCEPTEX accept_func;
char plain_accept_buf[2 * (sizeof(struct sockaddr_in) + 16)];
char secure_accept_buf[2 * (sizeof(struct sockaddr_in) + 16)];
OVERLAPPED recv_overlapped[TINYHTTP_SERVER_CONN_LIMIT]; OVERLAPPED recv_overlapped[TINYHTTP_SERVER_CONN_LIMIT];
OVERLAPPED send_overlapped[TINYHTTP_SERVER_CONN_LIMIT]; OVERLAPPED send_overlapped[TINYHTTP_SERVER_CONN_LIMIT];
#elif defined(__linux__) #elif defined(__linux__)
@@ -1479,116 +1495,6 @@ response_to_stream(TinyHTTPResponse res)
// to [tinyhttp_server_wait]? Or should connections be marked as pending // to [tinyhttp_server_wait]? Or should connections be marked as pending
// until [tinyhttp_server_send] is called? // until [tinyhttp_server_send] is called?
TinyHTTPServer* tinyhttp_server_init(TinyHTTPServerConfig config,
TinyHTTPMemoryFunc memfunc, void *memfuncdata)
{
TinyHTTPServer *server = memfunc(TINYHTTP_MEM_MALLOC, NULL, sizeof(TinyHTTPServer), memfuncdata);
if (server == NULL)
return NULL;
server->memfunc = memfunc;
server->memfuncdata = memfuncdata;
server->num_conns = 0;
server->ready_head = 0;
server->ready_count = 0;
for (int i = 0; i < TINYHTTP_SERVER_CONN_LIMIT; i++) {
server->stream_gens[i] = 1;
server->stream_sockets[i] = INVALID_SOCKET;
}
server->plain_listen_socket = socket_listen(config.plain_addr,
config.plain_port, config.plain_backlog, config.reuse);
if (server->plain_listen_socket == INVALID_SOCKET) {
free(server);
return NULL;
}
if (config.secure) {
server->secure_listen_socket = socket_listen(config.secure_addr,
config.secure_port, config.secure_backlog, config.reuse);
if (server->secure_listen_socket == INVALID_SOCKET) {
CLOSESOCKET(server->plain_listen_socket);
free(server);
return NULL;
}
} else
server->secure_listen_socket = INVALID_SOCKET;
#if defined(_WIN32)
server->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
if (server->iocp == INVALID_HANDLE_VALUE) {
CLOSESOCKET(server->plain_listen_socket);
if (server->secure_listen_socket != INVALID_SOCKET)
CLOSESOCKET(server->secure_listen_socket);
free(server);
return NULL;
}
#elif defined(__linux__)
server->epoll_fd = epoll_create1(0);
if (server->epoll_fd < 0) {
CLOSESOCKET(server->plain_listen_socket);
if (server->secure_listen_socket != INVALID_SOCKET)
CLOSESOCKET(server->secure_listen_socket);
free(server);
return NULL;
}
struct epoll_event epoll_buf;
epoll_buf.data.fd = -1;
epoll_buf.events = EPOLLIN;
if (epoll_ctl(server->epoll_fd, EPOLL_CTL_ADD, server->plain_listen_socket, &epoll_buf) < 0) {
CLOSESOCKET(server->plain_listen_socket);
if (server->secure_listen_socket != INVALID_SOCKET)
CLOSESOCKET(server->secure_listen_socket);
close(server->epoll_fd);
free(server);
return NULL;
}
if (config.secure) {
epoll_buf.data.fd = -2;
epoll_buf.events = EPOLLIN;
if (epoll_ctl(server->epoll_fd, EPOLL_CTL_ADD, server->secure_listen_socket, &epoll_buf) < 0) {
CLOSESOCKET(server->plain_listen_socket);
if (server->secure_listen_socket != INVALID_SOCKET)
CLOSESOCKET(server->secure_listen_socket);
close(server->epoll_fd);
free(server);
return NULL;
}
}
#endif
return server;
}
void tinyhttp_server_free(TinyHTTPServer *server)
{
for (int i = 0; i < TINYHTTP_SERVER_CONN_LIMIT; i++) {
if (server->stream_sockets[i] != INVALID_SOCKET) {
CLOSESOCKET(server->stream_sockets[i]);
tinyhttp_stream_free(&server->stream_state[i]);
}
}
CLOSESOCKET(server->plain_listen_socket);
if (server->secure_listen_socket != INVALID_SOCKET)
CLOSESOCKET(server->secure_listen_socket);
#if defined(_WIN32)
CloseHandle(server->iocp);
#elif defined(__linux__)
close(server->epoll_fd);
#endif
TinyHTTPMemoryFunc memfunc = server->memfunc;
void *memfuncdata = server->memfuncdata;
memfunc(TINYHTTP_MEM_FREE, server, sizeof(TinyHTTPServer), memfuncdata);
}
static void static void
invalidate_handles_to_stream(TinyHTTPServer *server, TinyHTTPStream *stream) invalidate_handles_to_stream(TinyHTTPServer *server, TinyHTTPStream *stream)
{ {
@@ -1601,6 +1507,41 @@ invalidate_handles_to_stream(TinyHTTPServer *server, TinyHTTPStream *stream)
#if defined(__linux__) #if defined(__linux__)
static void
server_free_platform(TinyHTTPServer *server)
{
close(server->epoll_fd);
}
static int
server_init_platform(TinyHTTPServer *server,
TinyHTTPServerConfig config)
{
server->epoll_fd = epoll_create1(0);
if (server->epoll_fd < 0)
return -1;
struct epoll_event epoll_buf;
epoll_buf.data.fd = -1;
epoll_buf.events = EPOLLIN;
if (epoll_ctl(server->epoll_fd, EPOLL_CTL_ADD, server->plain_listen_socket, &epoll_buf) < 0) {
close(server->epoll_fd);
return -1;
}
if (config.secure) {
epoll_buf.data.fd = -2;
epoll_buf.events = EPOLLIN;
if (epoll_ctl(server->epoll_fd, EPOLL_CTL_ADD, server->secure_listen_socket, &epoll_buf) < 0) {
close(server->epoll_fd);
return -1;
}
}
return 0;
}
static unsigned long long static unsigned long long
get_current_time_ms(void) get_current_time_ms(void)
{ {
@@ -1699,7 +1640,7 @@ process_network_events(TinyHTTPServer *server, int timeout)
if (flags & EPOLLIN) { if (flags & EPOLLIN) {
while (state & TINYHTTP_STREAM_RECV) { while (state & TINYHTTP_STREAM_RECV) {
ptrdiff_t cap; ptrdiff_t cap;
char *dst = tinyhttp_stream_net_recv_buf(stream, &cap); char *dst = tinyhttp_stream_recv_buf(stream, &cap);
if (dst == NULL) if (dst == NULL)
continue; continue;
int ret = recv(sock, dst, cap, 0); int ret = recv(sock, dst, cap, 0);
@@ -1713,16 +1654,19 @@ process_network_events(TinyHTTPServer *server, int timeout)
tinyhttp_stream_free(stream); tinyhttp_stream_free(stream);
break; break;
} }
tinyhttp_stream_net_recv_ack(stream, ret); #if DUMP_IO
print_bytes("N >> ", dst, ret);
#endif
tinyhttp_stream_recv_ack(stream, ret);
state = tinyhttp_stream_state(stream); state = tinyhttp_stream_state(stream);
} }
tinyhttp_stream_net_recv_ack(stream, 0); tinyhttp_stream_recv_ack(stream, 0);
} }
if (flags & EPOLLOUT) { if (flags & EPOLLOUT) {
while (state & TINYHTTP_STREAM_SEND) { while (state & TINYHTTP_STREAM_SEND) {
ptrdiff_t len; ptrdiff_t len;
char *src = tinyhttp_stream_net_send_buf(stream, &len); char *src = tinyhttp_stream_send_buf(stream, &len);
if (src == NULL) if (src == NULL)
continue; continue;
int ret = send(sock, src, len, 0); int ret = send(sock, src, len, 0);
@@ -1736,10 +1680,13 @@ process_network_events(TinyHTTPServer *server, int timeout)
tinyhttp_stream_free(stream); tinyhttp_stream_free(stream);
break; break;
} }
tinyhttp_stream_net_send_ack(stream, ret); #if DUMP_IO
print_bytes("N << ", src, ret);
#endif
tinyhttp_stream_send_ack(stream, ret);
state = tinyhttp_stream_state(stream); state = tinyhttp_stream_state(stream);
} }
tinyhttp_stream_net_send_ack(stream, 0); tinyhttp_stream_send_ack(stream, 0);
} }
int new_state = tinyhttp_stream_state(&server->stream_state[idx]); int new_state = tinyhttp_stream_state(&server->stream_state[idx]);
@@ -1775,6 +1722,98 @@ process_network_events(TinyHTTPServer *server, int timeout)
} }
#elif defined(_WIN32) #elif defined(_WIN32)
static void
server_free_platform(TinyHTTPServer *server)
{
CloseHandle(server->iocp);
if (server->deinit_winsock)
WSACleanup();
}
static SOCKET
start_accept_operation(LPFN_ACCEPTEX *accept_func,
SOCKET listen_socket, OVERLAPPED *overlapped,
char *buf, int buflen)
{
if (*accept_func == NULL) {
LPFN_ACCEPTEX lpfnAcceptEx = NULL;
GUID GuidAcceptEx = WSAID_ACCEPTEX;
unsigned long num;
int ret = WSAIoctl(listen_socket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx, sizeof(GuidAcceptEx),
&lpfnAcceptEx, sizeof(lpfnAcceptEx),
&num, NULL, NULL);
if (ret == SOCKET_ERROR)
return INVALID_SOCKET;
*accept_func = lpfnAcceptEx;
}
SOCKET target_socket = socket(AF_INET, SOCK_STREAM, 0);
if (target_socket == INVALID_SOCKET)
return INVALID_SOCKET;
unsigned long num;
int ok = (*accept_func)((SOCKET) listen_socket,
target_socket, buf,
buflen - ((sizeof(struct sockaddr_in) + 16) * 2),
sizeof(struct sockaddr_in) + 16,
sizeof(struct sockaddr_in) + 16,
&num, overlapped);
if (!ok && GetLastError() != ERROR_IO_PENDING) {
CLOSESOCKET(target_socket);
return INVALID_SOCKET;
}
return target_socket;
}
static int server_init_platform(TinyHTTPServer *server,
TinyHTTPServerConfig config)
{
server->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
if (server->iocp == INVALID_HANDLE_VALUE)
return -1;
if (CreateIoCompletionPort((HANDLE) server->plain_listen_socket, server->iocp, 0, 0) == NULL) {
CloseHandle(server->iocp);
return -1;
}
server->plain_accept_target = start_accept_operation(
&server->accept_func,
server->plain_listen_socket,
&server->plain_accept_overlapped,
server->plain_accept_buf,
sizeof(server->plain_accept_buf));
if (server->plain_accept_target == INVALID_SOCKET)
return -1;
if (config.secure) {
if (CreateIoCompletionPort((HANDLE) server->secure_listen_socket, server->iocp, 0, 0) == NULL) {
CLOSESOCKET(server->plain_accept_target);
CloseHandle(server->iocp);
return -1;
}
server->secure_accept_target = start_accept_operation(
&server->accept_func,
server->secure_listen_socket,
&server->secure_accept_overlapped,
server->secure_accept_buf,
sizeof(server->secure_accept_buf));
if (server->secure_accept_target == INVALID_SOCKET) {
CLOSESOCKET(server->plain_accept_target);
CloseHandle(server->iocp);
return -1;
}
}
return 0;
}
static unsigned long long static unsigned long long
get_current_time_ms(void) get_current_time_ms(void)
{ {
@@ -1788,7 +1827,80 @@ get_current_time_ms(void)
// Convert Windows file time (100ns since 1601-01-01) to // Convert Windows file time (100ns since 1601-01-01) to
// Unix epoch time (seconds since 1970-01-01) // Unix epoch time (seconds since 1970-01-01)
// 116444736000000000 = number of 100ns intervals from 1601 to 1970 // 116444736000000000 = number of 100ns intervals from 1601 to 1970
return (uli.QuadPart - 116444736000000000ULL) / 10000ULL; return (uli.QuadPart - 116444736000000000ULL) / 10000ULL; // TODO: Make sure this is returning miliseconds
}
static int
start_stream_operations(TinyHTTPStream *stream, SOCKET sock,
OVERLAPPED *recv_overlapped, OVERLAPPED *send_overlapped)
{
int state = tinyhttp_stream_state(stream);
if ((state & TINYHTTP_STREAM_RECV) && !(state & TINYHTTP_STREAM_RECV_STARTED)) {
ptrdiff_t cap;
char *dst = tinyhttp_stream_recv_buf(stream, &cap);
memset(recv_overlapped, 0, sizeof(*recv_overlapped));
int ok = ReadFile((HANDLE) sock, dst, cap, NULL, recv_overlapped);
if (!ok && GetLastError() != ERROR_IO_PENDING)
return -1;
}
if (state & TINYHTTP_STREAM_SEND && !(state & TINYHTTP_STREAM_SEND_STARTED)) {
ptrdiff_t len;
char *src = tinyhttp_stream_send_buf(stream, &len);
memset(send_overlapped, 0, sizeof(*send_overlapped));
int ok = WriteFile((HANDLE) sock, src, len, NULL, send_overlapped);
if (!ok && GetLastError() != ERROR_IO_PENDING)
return -1;
}
return 0;
}
static void
intern_accepted_socket(TinyHTTPServer *server, SOCKET accepted_socket, int secure)
{
if (server->num_conns == TINYHTTP_SERVER_CONN_LIMIT) {
CLOSESOCKET(accepted_socket);
return;
}
if (socket_set_block(accepted_socket, 0) < 0) {
CLOSESOCKET(accepted_socket);
return;
}
int idx = 0;
while (server->stream_sockets[idx] != INVALID_SOCKET)
idx++;
server->stream_sockets[idx] = accepted_socket;
TinyHTTPStream *stream = &server->stream_state[idx];
tinyhttp_stream_init(stream, server->memfunc, server->memfuncdata);
int state = tinyhttp_stream_state(stream);
if (CreateIoCompletionPort((HANDLE) accepted_socket, server->iocp, 0, 0) == NULL) {
tinyhttp_stream_free(stream);
server->stream_sockets[idx] = INVALID_SOCKET;
CLOSESOCKET(accepted_socket);
return;
}
OVERLAPPED *recv_overlapped = &server->recv_overlapped[idx];
OVERLAPPED *send_overlapped = &server->send_overlapped[idx];
if (start_stream_operations(stream, accepted_socket, recv_overlapped, send_overlapped) < 0) {
tinyhttp_stream_free(stream);
server->stream_sockets[idx] = INVALID_SOCKET;
CLOSESOCKET(accepted_socket);
return;
}
if (secure) {
// TODO
}
server->num_conns++;
} }
static int static int
@@ -1802,17 +1914,68 @@ process_network_events(TinyHTTPServer *server, int timeout)
BOOL result = GetQueuedCompletionStatus(server->iocp, BOOL result = GetQueuedCompletionStatus(server->iocp,
&transferred, &key, &overlapped, INFINITE); &transferred, &key, &overlapped, INFINITE);
if (!result) { // Handle timeouts and error on the completion function itself
if (!result && overlapped == NULL) {
if (GetLastError() == WAIT_TIMEOUT)
return 0;
return -1;
}
ASSERT(overlapped);
if (overlapped == &server->plain_accept_overlapped) {
if (result) {
// New plain connection
SOCKET accepted_socket = server->plain_accept_target;
server->plain_accept_target = INVALID_SOCKET;
intern_accepted_socket(server, accepted_socket, 0);
} else {
// Accept failed
CLOSESOCKET(server->plain_accept_target);
// TODO // TODO
} }
if (overlapped == &server->plain_accept_overlapped) { server->plain_accept_target = start_accept_operation(
// New plain connection &server->accept_func,
server->plain_listen_socket,
&server->plain_accept_overlapped,
server->plain_accept_buf,
sizeof(server->plain_accept_buf));
if (server->plain_accept_target == INVALID_SOCKET)
return -1; // Can't recover
return 0; return 0;
} }
if (overlapped == &server->secure_accept_overlapped) { if (overlapped == &server->secure_accept_overlapped) {
if (result) {
// New secure connection // New secure connection
SOCKET accepted_socket = server->plain_accept_target;
server->plain_accept_target = INVALID_SOCKET;
intern_accepted_socket(server, accepted_socket, 1);
} else {
// Accept failed
CLOSESOCKET(server->secure_accept_target);
// TODO
}
server->secure_accept_target = start_accept_operation(
&server->accept_func,
server->secure_listen_socket,
&server->secure_accept_overlapped,
server->secure_accept_buf,
sizeof(server->secure_accept_buf));
if (server->secure_accept_target == INVALID_SOCKET)
return -1; // Can't recover
return 0; return 0;
} }
@@ -1823,38 +1986,43 @@ process_network_events(TinyHTTPServer *server, int timeout)
OVERLAPPED *recv_overlapped = &server->recv_overlapped[idx]; OVERLAPPED *recv_overlapped = &server->recv_overlapped[idx];
OVERLAPPED *send_overlapped = &server->send_overlapped[idx]; OVERLAPPED *send_overlapped = &server->send_overlapped[idx];
if (recv_overlapped == overlapped) int state;
tinyhttp_stream_net_recv_ack(stream, transferred); if (!result) {
else { // A read or write operation failed
tinyhttp_stream_free(stream);
state = tinyhttp_stream_state(stream);
} else {
if (recv_overlapped == overlapped) {
#if DUMP_IO
print_bytes("N >> ", stream->in.data + stream->in.head, transferred);
#endif
tinyhttp_stream_recv_ack(stream, transferred);
} else {
ASSERT(send_overlapped == overlapped); ASSERT(send_overlapped == overlapped);
tinyhttp_stream_net_send_ack(stream, transferred); #if DUMP_IO
print_bytes("N << ", stream->out.data + stream->out.head, transferred);
#endif
tinyhttp_stream_send_ack(stream, transferred);
} }
int state = tinyhttp_stream_state(stream); if (start_stream_operations(stream, sock, recv_overlapped, send_overlapped) < 0) {
if ((state & TINYHTTP_STREAM_RECV) && !(state & TINYHTTP_STREAM_RECV_STARTED)) {
ptrdiff_t cap;
char *dst = tinyhttp_stream_net_recv_buf(stream, &cap);
memset(recv_overlapped, 0, sizeof(*recv_overlapped));
int ok = ReadFile((HANDLE) sock, dst, cap, NULL, recv_overlapped);
if (!ok && GetLastError() != ERROR_IO_PENDING) {
tinyhttp_stream_free(stream); tinyhttp_stream_free(stream);
state = tinyhttp_stream_state(stream); state = tinyhttp_stream_state(stream);
} }
} }
if (state & TINYHTTP_STREAM_SEND && !(state & TINYHTTP_STREAM_SEND_STARTED)) { printf("state = %d\n", state);
ptrdiff_t len;
char *src = tinyhttp_stream_net_send_buf(stream, &len); if (state & TINYHTTP_STREAM_READY) {
memset(send_overlapped, 0, sizeof(*send_overlapped)); printf("READY\n"); // TODO: remove me
int ok = WriteFile((HANDLE) sock, src, len, NULL, send_overlapped); int ready_idx = (server->ready_head + server->ready_count) % TINYHTTP_SERVER_CONN_LIMIT;
if (!ok && GetLastError() != ERROR_IO_PENDING) { server->ready_queue[ready_idx] = idx;
tinyhttp_stream_free(stream); server->ready_count++;
state = tinyhttp_stream_state(stream);
}
} }
if (state == TINYHTTP_STREAM_FREE) { if (state == TINYHTTP_STREAM_FREE) {
printf("FREE\n"); // TODO: remove me
// TODO: Remove from the ready list // TODO: Remove from the ready list
CLOSESOCKET(sock); CLOSESOCKET(sock);
invalidate_handles_to_stream(server, stream); invalidate_handles_to_stream(server, stream);
@@ -1866,6 +2034,93 @@ process_network_events(TinyHTTPServer *server, int timeout)
} }
#endif #endif
TinyHTTPServer* tinyhttp_server_init(TinyHTTPServerConfig config,
TinyHTTPMemoryFunc memfunc, void *memfuncdata)
{
TinyHTTPServer *server = memfunc(TINYHTTP_MEM_MALLOC, NULL, sizeof(TinyHTTPServer), memfuncdata);
if (server == NULL)
return NULL;
server->memfunc = memfunc;
server->memfuncdata = memfuncdata;
server->num_conns = 0;
server->ready_head = 0;
server->ready_count = 0;
for (int i = 0; i < TINYHTTP_SERVER_CONN_LIMIT; i++) {
server->stream_gens[i] = 1;
server->stream_sockets[i] = INVALID_SOCKET;
}
server->plain_listen_socket = socket_listen(config.plain_addr,
config.plain_port, config.plain_backlog, config.reuse);
#if defined(_WIN32)
server->deinit_winsock = 0;
if (server->plain_listen_socket == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) {
WSADATA data;
if (WSAStartup(MAKEWORD(2, 2), &data) == NO_ERROR) {
server->deinit_winsock = 1;
server->plain_listen_socket = socket_listen(config.plain_addr,
config.plain_port, config.plain_backlog, config.reuse);
}
}
#endif
if (server->plain_listen_socket == INVALID_SOCKET) {
memfunc(TINYHTTP_MEM_FREE, server, sizeof(TinyHTTPServer), NULL);
return NULL;
}
server->secure_listen_socket = INVALID_SOCKET;
if (config.secure) {
server->secure_listen_socket = socket_listen(config.secure_addr,
config.secure_port, config.secure_backlog, config.reuse);
if (server->secure_listen_socket == INVALID_SOCKET) {
CLOSESOCKET(server->plain_listen_socket);
memfunc(TINYHTTP_MEM_FREE, server, sizeof(TinyHTTPServer), NULL);
return NULL;
}
}
if (server_init_platform(server, config) < 0) {
CLOSESOCKET(server->plain_listen_socket);
if (server->secure_listen_socket != INVALID_SOCKET)
CLOSESOCKET(server->secure_listen_socket);
memfunc(TINYHTTP_MEM_FREE, server, sizeof(TinyHTTPServer), NULL);
return NULL;
}
return server;
}
void tinyhttp_server_free(TinyHTTPServer *server)
{
for (int i = 0; i < TINYHTTP_SERVER_CONN_LIMIT; i++) {
if (server->stream_sockets[i] != INVALID_SOCKET) {
CLOSESOCKET(server->stream_sockets[i]);
tinyhttp_stream_free(&server->stream_state[i]);
}
}
CLOSESOCKET(server->plain_listen_socket);
if (server->secure_listen_socket != INVALID_SOCKET)
CLOSESOCKET(server->secure_listen_socket);
server_free_platform(server);
TinyHTTPMemoryFunc memfunc = server->memfunc;
void *memfuncdata = server->memfuncdata;
memfunc(TINYHTTP_MEM_FREE, server, sizeof(TinyHTTPServer), memfuncdata);
}
int tinyhttp_server_wait(TinyHTTPServer *server, TinyHTTPRequest **req, int tinyhttp_server_wait(TinyHTTPServer *server, TinyHTTPRequest **req,
TinyHTTPResponse *res, int timeout) TinyHTTPResponse *res, int timeout)
{ {
@@ -2001,6 +2256,13 @@ void tinyhttp_response_send(TinyHTTPResponse res)
if (epoll_ctl(server->epoll_fd, EPOLL_CTL_MOD, sock, &epoll_buf) < 0) { if (epoll_ctl(server->epoll_fd, EPOLL_CTL_MOD, sock, &epoll_buf) < 0) {
ASSERT(0); // TODO ASSERT(0); // TODO
} }
#elif defined(_WIN32)
SOCKET sock = server->stream_sockets[idx];
OVERLAPPED *recv_overlapped = &server->recv_overlapped[idx];
OVERLAPPED *send_overlapped = &server->send_overlapped[idx];
if (start_stream_operations(stream, sock, recv_overlapped, send_overlapped) < 0) {
ASSERT(0); // TODO
}
#endif #endif
} }
+5 -5
View File
@@ -179,7 +179,7 @@
#define TINYHTTP_ROUTER_MAX_PATH_COMPONENTS 32 #define TINYHTTP_ROUTER_MAX_PATH_COMPONENTS 32
#define TINYHTTP_HEADER_LIMIT 32 #define TINYHTTP_HEADER_LIMIT 32
#define TINYHTTP_SERVER_CONN_LIMIT (1<<10) #define TINYHTTP_SERVER_CONN_LIMIT 3 // (1<<10)
#define TINYHTTP_SERVER_EPOLL_BATCH_SIZE (1<<10) #define TINYHTTP_SERVER_EPOLL_BATCH_SIZE (1<<10)
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@@ -450,23 +450,23 @@ int tinyhttp_stream_state(TinyHTTPStream *stream);
// the null pointer and a capacity of 0. The caller // the null pointer and a capacity of 0. The caller
// must not write to the pointer but act as if // must not write to the pointer but act as if
// everything went okay. // everything went okay.
char *tinyhttp_stream_recv_buf(TinyHTTPStream *stream, int *cap); char *tinyhttp_stream_recv_buf(TinyHTTPStream *stream, ptrdiff_t *cap);
// Acknowledge the bytes written from the network // Acknowledge the bytes written from the network
// //
// The write must have been initiated by calling // The write must have been initiated by calling
// [tinyhttp_stream_net_recv_buf]. // [tinyhttp_stream_net_recv_buf].
void tinyhttp_stream_recv_ack(TinyHTTPStream *stream, int num); void tinyhttp_stream_recv_ack(TinyHTTPStream *stream, ptrdiff_t num);
// Returns the pointer and capacity of a memory region // Returns the pointer and capacity of a memory region
// where data should be written to the network. // where data should be written to the network.
// //
// When the write is complete, [tinyhttp_stream_net_send_ack] // When the write is complete, [tinyhttp_stream_net_send_ack]
// should be called with the number of consumed bytes. // should be called with the number of consumed bytes.
char *tinyhttp_stream_send_buf(TinyHTTPStream *stream, int *len); char *tinyhttp_stream_send_buf(TinyHTTPStream *stream, ptrdiff_t *len);
// See [tinyhttp_stream_net_send_buf] // See [tinyhttp_stream_net_send_buf]
void tinyhttp_stream_send_ack(TinyHTTPStream *stream, int num); void tinyhttp_stream_send_ack(TinyHTTPStream *stream, ptrdiff_t num);
// Enable/disable connection reuse for this HTTP // Enable/disable connection reuse for this HTTP
// connection. This change takes effect at the start // connection. This change takes effect at the start