Add example using the HTTP engine with I/O completion ports
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
.PHONY: all report
|
||||
|
||||
OSTAG = LINUX
|
||||
#OSTAG = WINDOWS
|
||||
#OSTAG = LINUX
|
||||
OSTAG = WINDOWS
|
||||
|
||||
EXT_WINDOWS = .exe
|
||||
EXT_LINUX = .out
|
||||
@@ -15,6 +15,7 @@ EXT = ${EXT_${OSTAG}}
|
||||
all:
|
||||
gcc -o simple_server$(EXT) examples/simple_server.c tinyhttp.c -Wall -Wextra -DHTTP_CLIENT=0 -DHTTP_ROUTER=0 $(LFLAGS)
|
||||
gcc -o blocking_server_with_engine$(EXT) examples/blocking_server_with_engine.c tinyhttp.c -Wall -Wextra -DHTTP_CLIENT=0 -DHTTP_ROUTER=0 $(LFLAGS) -g3
|
||||
gcc -o iocp_server_with_engine.exe examples/iocp_server_with_engine.c tinyhttp.c -Wall -Wextra -lws2_32
|
||||
gcc -o test$(EXT) tests/test.c tests/test_branch_coverage_parse.c tests/test_branch_coverage_engine.c tests/test_fuzz_engine.c tinyhttp.c -fprofile-arcs -ftest-coverage $(LFLAGS)
|
||||
|
||||
report:
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <mswsock.h>
|
||||
|
||||
#include "../tinyhttp.h"
|
||||
|
||||
#define MAX_CLIENTS (1<<10)
|
||||
|
||||
// Callback used by the engine to manage dynamic memory
|
||||
static void *memfunc(HTTP_MemoryFuncTag tag,
|
||||
void *ptr, int len, void *data)
|
||||
{
|
||||
(void) data;
|
||||
switch (tag) {
|
||||
|
||||
case HTTP_MEMFUNC_MALLOC:
|
||||
return malloc(len);
|
||||
|
||||
case HTTP_MEMFUNC_FREE:
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static SOCKET start_accept(LPFN_ACCEPTEX AcceptEx, SOCKET listen_fd, OVERLAPPED *accept_ov, char *buf, int bufsize)
|
||||
{
|
||||
SOCKET accept_target = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (accept_target == INVALID_SOCKET)
|
||||
return INVALID_SOCKET;
|
||||
|
||||
unsigned long num;
|
||||
memset(accept_ov, 0, sizeof(OVERLAPPED));
|
||||
BOOL ok = AcceptEx(listen_fd, accept_target, buf, bufsize - ((sizeof(struct sockaddr_in) + 16) * 2),
|
||||
sizeof(struct sockaddr_in) + 16,
|
||||
sizeof(struct sockaddr_in) + 16,
|
||||
&num, accept_ov);
|
||||
if (!ok && WSAGetLastError() != ERROR_IO_PENDING) {
|
||||
closesocket(accept_target);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return accept_target;
|
||||
}
|
||||
|
||||
static void start_recv_or_send(SOCKET sock, OVERLAPPED *recv_ov, OVERLAPPED *send_ov, HTTP_Engine *eng)
|
||||
{
|
||||
HTTP_EngineState state = http_engine_state(eng);
|
||||
|
||||
if (state == HTTP_ENGINE_STATE_SERVER_RECV_BUF) {
|
||||
|
||||
int cap;
|
||||
char *dst = http_engine_recvbuf(eng, &cap);
|
||||
|
||||
memset(recv_ov, 0, sizeof(OVERLAPPED));
|
||||
int ok = ReadFile((HANDLE) sock, dst, cap, NULL, recv_ov);
|
||||
if (!ok && GetLastError() != ERROR_IO_PENDING)
|
||||
http_engine_close(eng);
|
||||
|
||||
} else if (state == HTTP_ENGINE_STATE_SERVER_SEND_BUF) {
|
||||
|
||||
int len;
|
||||
char *src = http_engine_sendbuf(eng, &len);
|
||||
|
||||
memset(send_ov, 0, sizeof(OVERLAPPED));
|
||||
int ok = WriteFile((HANDLE) sock, src, len, NULL, send_ov);
|
||||
if (!ok && GetLastError() != ERROR_IO_PENDING)
|
||||
http_engine_close(eng);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Interface and port the server will be listening on.
|
||||
char *addr = "127.0.0.1";
|
||||
int port = 8080;
|
||||
|
||||
// Initialize the winsock subsystem
|
||||
WSADATA wd;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wd))
|
||||
return -1;
|
||||
|
||||
// Create the I/O completion port object
|
||||
HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
|
||||
if (iocp == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
// Create the listening socket
|
||||
SOCKET listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (listen_fd == INVALID_SOCKET)
|
||||
return -1;
|
||||
|
||||
// Register the socket into the IOCP
|
||||
if (CreateIoCompletionPort((HANDLE) listen_fd, iocp, 0, 0) == NULL)
|
||||
return -1;
|
||||
|
||||
// Ignore the cooldown time for the bound interface to
|
||||
// avoid that annoying "address in use" error
|
||||
int reuse = 1;
|
||||
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &reuse, sizeof(reuse));
|
||||
|
||||
// Fill out the address struct
|
||||
struct sockaddr_in bind_buf;
|
||||
{
|
||||
struct in_addr addr_buf;
|
||||
if (inet_pton(AF_INET, addr, &addr_buf) != 1)
|
||||
return -1;
|
||||
|
||||
bind_buf.sin_family = AF_INET;
|
||||
bind_buf.sin_port = htons(port);
|
||||
bind_buf.sin_addr = addr_buf;
|
||||
memset(&bind_buf.sin_zero, 0, sizeof(bind_buf.sin_zero));
|
||||
}
|
||||
|
||||
// Associate the listening socket to the interface
|
||||
if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0)
|
||||
return -1;
|
||||
|
||||
// Allow incoming connections
|
||||
if (listen(listen_fd, 32) < 0)
|
||||
return -1;
|
||||
|
||||
// Get the AcceptEx function pointer
|
||||
LPFN_ACCEPTEX AcceptEx = NULL;
|
||||
GUID GuidAcceptEx = WSAID_ACCEPTEX;
|
||||
unsigned long num;
|
||||
int ret = WSAIoctl(listen_fd,
|
||||
SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
&GuidAcceptEx, sizeof(GuidAcceptEx),
|
||||
&AcceptEx, sizeof(AcceptEx),
|
||||
&num, NULL, NULL);
|
||||
if (ret == SOCKET_ERROR)
|
||||
return -1;
|
||||
|
||||
OVERLAPPED accept_ov;
|
||||
char accept_buf[2 * (sizeof(struct sockaddr_in) + 16)];
|
||||
SOCKET accept_target = start_accept(AcceptEx, listen_fd, &accept_ov, accept_buf, (int) sizeof(accept_buf));
|
||||
if (accept_target == INVALID_SOCKET)
|
||||
return -1;
|
||||
|
||||
OVERLAPPED recv_overlapped[MAX_CLIENTS];
|
||||
OVERLAPPED send_overlapped[MAX_CLIENTS];
|
||||
SOCKET sockets[MAX_CLIENTS];
|
||||
HTTP_Engine engs[MAX_CLIENTS];
|
||||
|
||||
for (int i = 0; i < MAX_CLIENTS; i++)
|
||||
sockets[i] = INVALID_SOCKET;
|
||||
|
||||
for (;;) {
|
||||
DWORD timeout = INFINITE;
|
||||
|
||||
DWORD transferred;
|
||||
ULONG_PTR key;
|
||||
OVERLAPPED *overlapped;
|
||||
BOOL result = GetQueuedCompletionStatus(iocp, &transferred, &key, &overlapped, timeout);
|
||||
if (!result && overlapped == NULL) {
|
||||
if (GetLastError() == WAIT_TIMEOUT)
|
||||
continue; // Go back to waiting
|
||||
return -1; // Error
|
||||
}
|
||||
|
||||
if (overlapped == &accept_ov) {
|
||||
// A new client connected
|
||||
|
||||
int i = 0;
|
||||
while (i < MAX_CLIENTS && sockets[i] != INVALID_SOCKET)
|
||||
i++;
|
||||
|
||||
if (i == MAX_CLIENTS)
|
||||
closesocket(accept_target); // Server limit reached
|
||||
else {
|
||||
|
||||
sockets[i] = accept_target;
|
||||
http_engine_init(&engs[i], 0, memfunc, NULL);
|
||||
|
||||
// Register the socket into the IOCP
|
||||
if (CreateIoCompletionPort((HANDLE) sockets[i], iocp, i, 0) == NULL)
|
||||
return -1;
|
||||
|
||||
start_recv_or_send(sockets[i], &recv_overlapped[i], &send_overlapped[i], &engs[i]);
|
||||
|
||||
// Check that the recv or send operation was started.
|
||||
// If now, remove the connection
|
||||
if (http_engine_state(&engs[i]) == HTTP_ENGINE_STATE_CLIENT_CLOSED) {
|
||||
closesocket(sockets[i]);
|
||||
sockets[i] = INVALID_SOCKET;
|
||||
http_engine_free(&engs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
accept_target = start_accept(AcceptEx, listen_fd, &accept_ov, accept_buf, (int) sizeof(accept_buf));
|
||||
if (accept_target == INVALID_SOCKET)
|
||||
return -1;
|
||||
|
||||
// Go back to waiting
|
||||
continue;
|
||||
}
|
||||
|
||||
// Complete the current operation
|
||||
if (0) {}
|
||||
else if (overlapped == &recv_overlapped[key]) http_engine_recvack(&engs[key], transferred);
|
||||
else if (overlapped == &send_overlapped[key]) http_engine_sendack(&engs[key], transferred);
|
||||
|
||||
if (http_engine_state(&engs[key]) == HTTP_ENGINE_STATE_SERVER_PREP_STATUS) {
|
||||
|
||||
// See blocking_server_with_engine.c to learn about how to
|
||||
// build a response
|
||||
http_engine_status(&engs[key], 200);
|
||||
http_engine_body(&engs[key], "Hello, world!", -1);
|
||||
http_engine_done(&engs[key]);
|
||||
}
|
||||
|
||||
start_recv_or_send(sockets[key],
|
||||
&recv_overlapped[key],
|
||||
&send_overlapped[key],
|
||||
&engs[key]);
|
||||
|
||||
if (http_engine_state(&engs[key]) == HTTP_ENGINE_STATE_SERVER_CLOSED) {
|
||||
http_engine_free(&engs[key]);
|
||||
closesocket(sockets[key]);
|
||||
sockets[key] = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user