Fix windows compilation errors

This commit is contained in:
2025-07-21 12:06:23 +02:00
parent acf4746611
commit 5cb3977c3c
14 changed files with 286 additions and 227 deletions
+3 -1
View File
@@ -1,7 +1,7 @@
CC = gcc CC = gcc
CFLAGS = -I. -Wall -Wextra -O0 -g3 CFLAGS = -I. -Wall -Wextra -O0 -g3
LFLAGS = -lssl -lcrypto LFLAGS =
AR = ar AR = ar
CFILES = $(shell find src -name "*.c") CFILES = $(shell find src -name "*.c")
@@ -15,8 +15,10 @@ STATIC_LIB = lib$(LIBNAME).a
# Detect OS and set executable extension # Detect OS and set executable extension
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
EXT = .exe EXT = .exe
LFLAGS = -lws2_32
else else
EXT = .out EXT = .out
LFLAGS += -lssl -lcrypto
endif endif
# Installation directories # Installation directories
+3 -2
View File
@@ -1,7 +1,7 @@
Allow no TLS builds Allow no TLS builds
make sure no TLS builds work on windows make sure no TLS builds work on windows
Allow resolving requests from different threads Allow resolving requests from different threads
Find a way to make sure http.h and http.c are always up to date Find a way to make sure chttp.h and chttp.c are always up to date
parse Transfer-Encoding parse Transfer-Encoding
Make it cross-platform Make it cross-platform
Find a way to compile OpenSSL on windows Find a way to compile OpenSSL on windows
@@ -9,4 +9,5 @@ handle 3xx client redirections
add discussion on string management add discussion on string management
add discussion on error management add discussion on error management
check that virtual hosts over HTTPS work check that virtual hosts over HTTPS work
add examples for the router add examples for the router
what is client-side SNI? Do we need it?
+132 -107
View File
@@ -62,6 +62,27 @@
#include <stdint.h> #include <stdint.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#define SOCKET_TYPE SOCKET
#define BAD_SOCKET INVALID_SOCKET
#define POLL WSAPoll
#define CLOSE_SOCKET closesocket
#endif
#ifdef __linux__
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>
#define SOCKET_TYPE int
#define BAD_SOCKET -1
#define POLL poll
#define CLOSE_SOCKET close
#endif
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
@@ -100,7 +121,7 @@ typedef enum {
typedef struct { typedef struct {
SocketState state; SocketState state;
SocketWantEvent event; SocketWantEvent event;
int fd; SOCKET_TYPE fd;
#if HTTPS_ENABLED #if HTTPS_ENABLED
SSL *ssl; SSL *ssl;
SSL_CTX *ssl_ctx; SSL_CTX *ssl_ctx;
@@ -128,6 +149,9 @@ typedef struct {
#endif #endif
} SocketGroup; } SocketGroup;
SOCKET_TYPE listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog);
void socket_global_init (void); void socket_global_init (void);
void socket_global_free (void); void socket_global_free (void);
int socket_group_init (SocketGroup *group); int socket_group_init (SocketGroup *group);
@@ -135,7 +159,7 @@ int socket_group_init_server(SocketGroup *group, HTTP_String cert_file,
int socket_group_add_domain(SocketGroup *group, HTTP_String domain, HTTP_String cert_key, HTTP_String private_key); int socket_group_add_domain(SocketGroup *group, HTTP_String domain, HTTP_String cert_key, HTTP_String private_key);
void socket_group_free (SocketGroup *group); void socket_group_free (SocketGroup *group);
SocketState socket_state (Socket *sock); SocketState socket_state (Socket *sock);
void socket_accept (Socket *sock, SocketGroup *group, int fd); void socket_accept (Socket *sock, SocketGroup *group, SOCKET_TYPE fd);
void socket_connect (Socket *sock, SocketGroup *group, HTTP_String host, uint16_t port); void socket_connect (Socket *sock, SocketGroup *group, HTTP_String host, uint16_t port);
void socket_connect_ipv4 (Socket *sock, SocketGroup *group, HTTP_IPv4 addr, uint16_t port); void socket_connect_ipv4 (Socket *sock, SocketGroup *group, HTTP_IPv4 addr, uint16_t port);
void socket_connect_ipv6 (Socket *sock, SocketGroup *group, HTTP_IPv6 addr, uint16_t port); void socket_connect_ipv6 (Socket *sock, SocketGroup *group, HTTP_IPv6 addr, uint16_t port);
@@ -2491,11 +2515,15 @@ int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h>
#define POLL WSAPoll
#endif #endif
#ifdef __linux__ #ifdef __linux__
#include <poll.h> #include <poll.h>
#include <netdb.h> #include <netdb.h>
#include <fcntl.h>
#define POLL poll
#endif #endif
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
@@ -2511,6 +2539,71 @@ int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
#include "socket.h" #include "socket.h"
#endif #endif
static int set_socket_blocking(SOCKET_TYPE sock, bool value)
{
#ifdef _WIN32
u_long mode = !value;
if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR)
return -1;
#endif
#ifdef __linux__
int flags = fcntl(listen_fd, F_GETFL, 0);
if (flags < 0)
return -1;
if (fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK) < 0)
return BAD_SOCKET;
#endif
return 0;
}
SOCKET_TYPE listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog)
{
SOCKET_TYPE listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == BAD_SOCKET)
return BAD_SOCKET;
if (set_socket_blocking(listen_fd, false) < 0) {
CLOSE_SOCKET(listen_fd);
return BAD_SOCKET;
}
if (reuse_addr) {
int one = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one));
}
struct in_addr addr_buf;
if (addr.len == 0)
addr_buf.s_addr = htonl(INADDR_ANY);
else {
_Static_assert(sizeof(struct in_addr) == sizeof(HTTP_IPv4));
if (http_parse_ipv4(addr.ptr, addr.len, (HTTP_IPv4*) &addr_buf) < 0) {
CLOSE_SOCKET(listen_fd);
return BAD_SOCKET;
}
}
struct sockaddr_in bind_buf;
bind_buf.sin_family = AF_INET;
bind_buf.sin_addr = addr_buf;
bind_buf.sin_port = htons(port);
if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) { // TODO: how does bind fail on windows?
CLOSE_SOCKET(listen_fd);
return BAD_SOCKET;
}
if (listen(listen_fd, backlog) < 0) { // TODO: how does listen fail on windows?
CLOSE_SOCKET(listen_fd);
return BAD_SOCKET;
}
return listen_fd;
}
void socket_global_init(void) void socket_global_init(void)
{ {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
@@ -2764,7 +2857,7 @@ SocketState socket_state(Socket *sock)
return sock->state; return sock->state;
} }
void socket_accept(Socket *sock, SocketGroup *group, int fd) void socket_accept(Socket *sock, SocketGroup *group, SOCKET_TYPE fd)
{ {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
sock->ssl = NULL; sock->ssl = NULL;
@@ -2785,13 +2878,12 @@ void socket_accept(Socket *sock, SocketGroup *group, int fd)
sock->addr_cursor = 0; sock->addr_cursor = 0;
sock->hostname = NULL; sock->hostname = NULL;
sock->port = 0; sock->port = 0;
// Set non-blocking mode for the accepted socket if (set_socket_blocking(fd, false) < 0) {
int flags = fcntl(fd, F_GETFL, 0); // TODO: fail on error sock->state = SOCKET_STATE_DIED;
if (flags >= 0) { return;
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
} }
// Start the TLS handshake process // Start the TLS handshake process
socket_update(sock); socket_update(sock);
} }
@@ -2946,8 +3038,8 @@ void socket_update(Socket *sock)
} }
#endif #endif
if (sock->fd != -1) if (sock->fd != BAD_SOCKET)
close(sock->fd); CLOSE_SOCKET(sock->fd);
// If cursor reached the end, die // If cursor reached the end, die
if (sock->addr_cursor >= sock->addr_count) { if (sock->addr_cursor >= sock->addr_count) {
@@ -2959,8 +3051,8 @@ void socket_update(Socket *sock)
// Take current address // Take current address
AddrInfo *ai = &sock->addr_list[sock->addr_cursor]; AddrInfo *ai = &sock->addr_list[sock->addr_cursor];
int family = ai->is_ipv6 ? AF_INET6 : AF_INET; int family = ai->is_ipv6 ? AF_INET6 : AF_INET;
int fd = socket(family, SOCK_STREAM, 0); SOCKET_TYPE fd = socket(family, SOCK_STREAM, 0);
if (fd < 0) { if (fd == BAD_SOCKET) {
// Try next address // Try next address
sock->addr_cursor++; sock->addr_cursor++;
sock->event = SOCKET_WANT_NONE; sock->event = SOCKET_WANT_NONE;
@@ -2969,9 +3061,12 @@ void socket_update(Socket *sock)
break; break;
} }
// Set non-blocking if (set_socket_blocking(fd, false) < 0) {
int flags = fcntl(fd, F_GETFL, 0); CLOSE_SOCKET(fd);
if (flags >= 0) fcntl(fd, F_SETFL, flags | O_NONBLOCK); // TODO: Handle error by setting the socket to DIED sock->event = SOCKET_WANT_NONE;
sock->state = SOCKET_STATE_DIED;
break;
}
// Prepare sockaddr // Prepare sockaddr
int ret; int ret;
@@ -2998,7 +3093,7 @@ void socket_update(Socket *sock)
break; break;
} }
if (ret < 0 && errno == EINPROGRESS) { if (ret < 0 && errno == EINPROGRESS) { // TODO: I'm pretty sure all the error numbers need to be changed for windows
// Connection pending // Connection pending
sock->fd = fd; sock->fd = fd;
sock->event = SOCKET_WANT_WRITE; sock->event = SOCKET_WANT_WRITE;
@@ -3025,7 +3120,7 @@ void socket_update(Socket *sock)
// Check connect result // Check connect result
int err = 0; int err = 0;
socklen_t len = sizeof(err); socklen_t len = sizeof(err);
if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0 || err != 0) { if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*) &err, &len) < 0 || err != 0) {
close(sock->fd); close(sock->fd);
// If remote peer not working, try next address // If remote peer not working, try next address
if (err == ECONNREFUSED || err == ETIMEDOUT || err == ENETUNREACH || err == EHOSTUNREACH) { if (err == ECONNREFUSED || err == ETIMEDOUT || err == ENETUNREACH || err == EHOSTUNREACH) {
@@ -3302,9 +3397,9 @@ void socket_free(Socket *sock)
SSL_free(sock->ssl); SSL_free(sock->ssl);
#endif #endif
if (sock->fd >= 0) { if (sock->fd != BAD_SOCKET) {
close(sock->fd); CLOSE_SOCKET(sock->fd);
sock->fd = -1; sock->fd = BAD_SOCKET;
} }
if (sock->hostname) { if (sock->hostname) {
@@ -3344,7 +3439,7 @@ int socket_wait(Socket **socks, int num_socks) // TODO: is this used?
polled[i].revents = 0; polled[i].revents = 0;
} }
int ret = poll(polled, num_socks, -1); int ret = POLL(polled, num_socks, -1);
if (ret < 0) if (ret < 0)
return -1; return -1;
@@ -3842,23 +3937,6 @@ HTTP_Response *http_post(HTTP_String url, HTTP_String *headers, int num_headers,
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#define POLL WSAPoll
#define CLOSE_SOCKET closesocket
#endif
#ifdef __linux__
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>
#define POLL poll
#define CLOSE_SOCKET close
#endif
#ifndef HTTP_AMALGAMATION #ifndef HTTP_AMALGAMATION
#include "engine.h" #include "engine.h"
#include "socket.h" #include "socket.h"
@@ -3877,8 +3955,8 @@ typedef struct {
struct HTTP_Server { struct HTTP_Server {
SocketGroup group; SocketGroup group;
int listen_fd; SOCKET_TYPE listen_fd;
int secure_fd; SOCKET_TYPE secure_fd;
int num_conns; int num_conns;
Connection conns[MAX_CONNS]; Connection conns[MAX_CONNS];
@@ -3888,59 +3966,6 @@ struct HTTP_Server {
int ready[MAX_CONNS]; int ready[MAX_CONNS];
}; };
static int listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog)
{
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0)
return -1;
{
int flags = fcntl(listen_fd, F_GETFL, 0);
if (flags < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
if (fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
}
if (reuse_addr) {
int one = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
struct in_addr addr_buf;
if (addr.len == 0)
addr_buf.s_addr = htonl(INADDR_ANY);
else {
_Static_assert(sizeof(struct in_addr) == sizeof(HTTP_IPv4));
if (http_parse_ipv4(addr.ptr, addr.len, (HTTP_IPv4*) &addr_buf) < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
}
struct sockaddr_in bind_buf;
bind_buf.sin_family = AF_INET;
bind_buf.sin_addr = addr_buf;
bind_buf.sin_port = htons(port);
if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
if (listen(listen_fd, backlog) < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
return listen_fd;
}
HTTP_Server *http_server_init(HTTP_String addr, uint16_t port) HTTP_Server *http_server_init(HTTP_String addr, uint16_t port)
{ {
return http_server_init_ex(addr, port, 0, HTTP_STR(""), HTTP_STR("")); return http_server_init_ex(addr, port, 0, HTTP_STR(""), HTTP_STR(""));
@@ -3963,17 +3988,17 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port,
} }
if (port == 0) if (port == 0)
server->listen_fd = -1; server->listen_fd = BAD_SOCKET;
else { else {
server->listen_fd = listen_socket(addr, port, reuse_addr, backlog); server->listen_fd = listen_socket(addr, port, reuse_addr, backlog);
if (server->listen_fd < 0) { if (server->listen_fd == BAD_SOCKET) {
free(server); free(server);
return NULL; return NULL;
} }
} }
if (secure_port == 0) if (secure_port == 0)
server->secure_fd = -1; server->secure_fd = BAD_SOCKET;
else { else {
if (socket_group_init_server(&server->group, cert_key, private_key) < 0) { if (socket_group_init_server(&server->group, cert_key, private_key) < 0) {
@@ -3983,7 +4008,7 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port,
} }
server->secure_fd = listen_socket(addr, secure_port, reuse_addr, backlog); server->secure_fd = listen_socket(addr, secure_port, reuse_addr, backlog);
if (server->secure_fd < 0) { if (server->secure_fd == BAD_SOCKET) {
socket_group_free(&server->group); socket_group_free(&server->group);
CLOSE_SOCKET(server->listen_fd); CLOSE_SOCKET(server->listen_fd);
free(server); free(server);
@@ -4016,7 +4041,7 @@ void http_server_free(HTTP_Server *server)
CLOSE_SOCKET(server->secure_fd); CLOSE_SOCKET(server->secure_fd);
CLOSE_SOCKET(server->listen_fd); CLOSE_SOCKET(server->listen_fd);
if (server->secure_fd != -1) if (server->secure_fd != BAD_SOCKET)
socket_group_free(&server->group); socket_group_free(&server->group);
free(server); free(server);
} }
@@ -4048,7 +4073,7 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl
if (server->num_conns < MAX_CONNS) { if (server->num_conns < MAX_CONNS) {
if (server->listen_fd != -1) { if (server->listen_fd != BAD_SOCKET) {
polled[num_polled].fd = server->listen_fd; polled[num_polled].fd = server->listen_fd;
polled[num_polled].events = POLLIN; polled[num_polled].events = POLLIN;
polled[num_polled].revents = 0; polled[num_polled].revents = 0;
@@ -4056,7 +4081,7 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl
num_polled++; num_polled++;
} }
if (server->secure_fd != -1) { if (server->secure_fd != BAD_SOCKET) {
polled[num_polled].fd = server->secure_fd; polled[num_polled].fd = server->secure_fd;
polled[num_polled].events = POLLIN; polled[num_polled].events = POLLIN;
polled[num_polled].revents = 0; polled[num_polled].revents = 0;
@@ -4102,7 +4127,10 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl
if ((polled[i].revents & POLLIN) && server->num_conns < MAX_CONNS) { if ((polled[i].revents & POLLIN) && server->num_conns < MAX_CONNS) {
int new_fd = accept(polled[i].fd, NULL, NULL); SOCKET_TYPE new_fd = accept(polled[i].fd, NULL, NULL);
if (new_fd == BAD_SOCKET) {
// TODO
}
int k = 0; int k = 0;
while (server->conns[k].used) while (server->conns[k].used)
@@ -4211,16 +4239,13 @@ void http_response_status(HTTP_ResponseHandle res, int status)
http_engine_status(&conn->engine, status); http_engine_status(&conn->engine, status);
} }
void http_response_header(HTTP_ResponseHandle res, const char *fmt, ...) void http_response_header(HTTP_ResponseHandle res, HTTP_String str)
{ {
Connection *conn = handle2conn(res); Connection *conn = handle2conn(res);
if (conn == NULL) if (conn == NULL)
return; return;
va_list args; http_engine_header(&conn->engine, str);
va_start(args, fmt);
http_engine_header_fmt2(&conn->engine, fmt, args);
va_end(args);
} }
void http_response_body(HTTP_ResponseHandle res, HTTP_String str) void http_response_body(HTTP_ResponseHandle res, HTTP_String str)
+5 -2
View File
@@ -16,7 +16,7 @@
// String type used throughout cHTTP. // String type used throughout cHTTP.
typedef struct { typedef struct {
char *ptr; char *ptr;
long len; int len;
} HTTP_String; } HTTP_String;
// Compare two strings and return true iff they have // Compare two strings and return true iff they have
@@ -33,6 +33,9 @@ bool http_streqcase(HTTP_String s1, HTTP_String s2);
// the new one references the contents of the original one. // the new one references the contents of the original one.
HTTP_String http_trim(HTTP_String s); HTTP_String http_trim(HTTP_String s);
// TODO: comment
void print_bytes(HTTP_String prefix, HTTP_String src);
// Macro to simplify converting string literals to // Macro to simplify converting string literals to
// HTTP_String. // HTTP_String.
// //
@@ -491,7 +494,7 @@ void http_server_free (HTTP_Server *server);
int http_server_wait (HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandle *handle); int http_server_wait (HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandle *handle);
int http_server_add_website (HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file); int http_server_add_website (HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
void http_response_status (HTTP_ResponseHandle res, int status); void http_response_status (HTTP_ResponseHandle res, int status);
void http_response_header (HTTP_ResponseHandle res, const char *fmt, ...); void http_response_header (HTTP_ResponseHandle res, HTTP_String str);
void http_response_body (HTTP_ResponseHandle res, HTTP_String str); void http_response_body (HTTP_ResponseHandle res, HTTP_String str);
void http_response_bodycap (HTTP_ResponseHandle res, int mincap); void http_response_bodycap (HTTP_ResponseHandle res, int mincap);
char* http_response_bodybuf (HTTP_ResponseHandle res, int *cap); char* http_response_bodybuf (HTTP_ResponseHandle res, int *cap);
+1 -1
View File
@@ -207,7 +207,7 @@ int main(void)
// See blocking_server_with_engine.c to learn about how to // See blocking_server_with_engine.c to learn about how to
// build a response // build a response
http_engine_status(&engs[key], 200); http_engine_status(&engs[key], 200);
http_engine_body(&engs[key], "Hello, world!", -1); http_engine_body(&engs[key], HTTP_STR("Hello, world!"));
http_engine_done(&engs[key]); http_engine_done(&engs[key]);
} }
+1 -1
View File
@@ -71,7 +71,7 @@ int main(void)
http_response_status(res, 200); http_response_status(res, 200);
// Then zero or more headers // Then zero or more headers
http_response_header(res, "Content-Type: text/plain"); http_response_header(res, HTTP_STR("Content-Type: text/plain"));
// Then you can write zero or more chunks of the response body // Then you can write zero or more chunks of the response body
http_response_body(res, HTTP_STR("Hello")); http_response_body(res, HTTP_STR("Hello"));
+1 -1
View File
@@ -23,7 +23,7 @@ int main(void)
if (ret < 0) return -1; if (ret < 0) return -1;
http_response_status(res, 200); http_response_status(res, 200);
http_response_header(res, "Content-Type: text/plain"); http_response_header(res, HTTP_STR("Content-Type: text/plain"));
// The previous example used the *_body function to // The previous example used the *_body function to
// write the response body in chunks: // write the response body in chunks:
+2 -2
View File
@@ -21,7 +21,7 @@ int main(void)
// Say we are building a request.. // Say we are building a request..
http_response_status(res, 200); http_response_status(res, 200);
http_response_header(res, "Content-Type: text/plain"); http_response_header(res, HTTP_STR("Content-Type: text/plain"));
// .. and in the middle of building an error condition // .. and in the middle of building an error condition
// occurs. Maybe a file was missing or an allocation fails. // occurs. Maybe a file was missing or an allocation fails.
@@ -39,7 +39,7 @@ int main(void)
// Now we are back to setting the status code // Now we are back to setting the status code
http_response_status(res, 500); http_response_status(res, 500);
http_response_header(res, "Content-Type: text/plain"); http_response_header(res, HTTP_STR("Content-Type: text/plain"));
http_response_body(res, HTTP_STR("An error occurred!")); http_response_body(res, HTTP_STR("An error occurred!"));
http_response_done(res); http_response_done(res);
+1 -1
View File
@@ -64,7 +64,7 @@ int main(void)
if (ret < 0) return -1; if (ret < 0) return -1;
http_response_status(res, 200); http_response_status(res, 200);
http_response_header(res, "Content-Type: text/plain"); http_response_header(res, HTTP_STR("Content-Type: text/plain"));
http_response_body(res, HTTP_STR("Hello")); http_response_body(res, HTTP_STR("Hello"));
http_response_body(res, HTTP_STR(", world!")); http_response_body(res, HTTP_STR(", world!"));
http_response_done(res); http_response_done(res);
+4 -1
View File
@@ -6,7 +6,7 @@
// String type used throughout cHTTP. // String type used throughout cHTTP.
typedef struct { typedef struct {
char *ptr; char *ptr;
long len; int len;
} HTTP_String; } HTTP_String;
// Compare two strings and return true iff they have // Compare two strings and return true iff they have
@@ -23,6 +23,9 @@ bool http_streqcase(HTTP_String s1, HTTP_String s2);
// the new one references the contents of the original one. // the new one references the contents of the original one.
HTTP_String http_trim(HTTP_String s); HTTP_String http_trim(HTTP_String s);
// TODO: comment
void print_bytes(HTTP_String prefix, HTTP_String src);
// Macro to simplify converting string literals to // Macro to simplify converting string literals to
// HTTP_String. // HTTP_String.
// //
+15 -85
View File
@@ -3,23 +3,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#define POLL WSAPoll
#define CLOSE_SOCKET closesocket
#endif
#ifdef __linux__
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>
#define POLL poll
#define CLOSE_SOCKET close
#endif
#ifndef HTTP_AMALGAMATION #ifndef HTTP_AMALGAMATION
#include "engine.h" #include "engine.h"
#include "socket.h" #include "socket.h"
@@ -38,8 +21,8 @@ typedef struct {
struct HTTP_Server { struct HTTP_Server {
SocketGroup group; SocketGroup group;
int listen_fd; SOCKET_TYPE listen_fd;
int secure_fd; SOCKET_TYPE secure_fd;
int num_conns; int num_conns;
Connection conns[MAX_CONNS]; Connection conns[MAX_CONNS];
@@ -49,59 +32,6 @@ struct HTTP_Server {
int ready[MAX_CONNS]; int ready[MAX_CONNS];
}; };
static int listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog)
{
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0)
return -1;
{
int flags = fcntl(listen_fd, F_GETFL, 0);
if (flags < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
if (fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
}
if (reuse_addr) {
int one = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
struct in_addr addr_buf;
if (addr.len == 0)
addr_buf.s_addr = htonl(INADDR_ANY);
else {
_Static_assert(sizeof(struct in_addr) == sizeof(HTTP_IPv4));
if (http_parse_ipv4(addr.ptr, addr.len, (HTTP_IPv4*) &addr_buf) < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
}
struct sockaddr_in bind_buf;
bind_buf.sin_family = AF_INET;
bind_buf.sin_addr = addr_buf;
bind_buf.sin_port = htons(port);
if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
if (listen(listen_fd, backlog) < 0) {
CLOSE_SOCKET(listen_fd);
return -1;
}
return listen_fd;
}
HTTP_Server *http_server_init(HTTP_String addr, uint16_t port) HTTP_Server *http_server_init(HTTP_String addr, uint16_t port)
{ {
return http_server_init_ex(addr, port, 0, HTTP_STR(""), HTTP_STR("")); return http_server_init_ex(addr, port, 0, HTTP_STR(""), HTTP_STR(""));
@@ -124,17 +54,17 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port,
} }
if (port == 0) if (port == 0)
server->listen_fd = -1; server->listen_fd = BAD_SOCKET;
else { else {
server->listen_fd = listen_socket(addr, port, reuse_addr, backlog); server->listen_fd = listen_socket(addr, port, reuse_addr, backlog);
if (server->listen_fd < 0) { if (server->listen_fd == BAD_SOCKET) {
free(server); free(server);
return NULL; return NULL;
} }
} }
if (secure_port == 0) if (secure_port == 0)
server->secure_fd = -1; server->secure_fd = BAD_SOCKET;
else { else {
if (socket_group_init_server(&server->group, cert_key, private_key) < 0) { if (socket_group_init_server(&server->group, cert_key, private_key) < 0) {
@@ -144,7 +74,7 @@ HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port,
} }
server->secure_fd = listen_socket(addr, secure_port, reuse_addr, backlog); server->secure_fd = listen_socket(addr, secure_port, reuse_addr, backlog);
if (server->secure_fd < 0) { if (server->secure_fd == BAD_SOCKET) {
socket_group_free(&server->group); socket_group_free(&server->group);
CLOSE_SOCKET(server->listen_fd); CLOSE_SOCKET(server->listen_fd);
free(server); free(server);
@@ -177,7 +107,7 @@ void http_server_free(HTTP_Server *server)
CLOSE_SOCKET(server->secure_fd); CLOSE_SOCKET(server->secure_fd);
CLOSE_SOCKET(server->listen_fd); CLOSE_SOCKET(server->listen_fd);
if (server->secure_fd != -1) if (server->secure_fd != BAD_SOCKET)
socket_group_free(&server->group); socket_group_free(&server->group);
free(server); free(server);
} }
@@ -209,7 +139,7 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl
if (server->num_conns < MAX_CONNS) { if (server->num_conns < MAX_CONNS) {
if (server->listen_fd != -1) { if (server->listen_fd != BAD_SOCKET) {
polled[num_polled].fd = server->listen_fd; polled[num_polled].fd = server->listen_fd;
polled[num_polled].events = POLLIN; polled[num_polled].events = POLLIN;
polled[num_polled].revents = 0; polled[num_polled].revents = 0;
@@ -217,7 +147,7 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl
num_polled++; num_polled++;
} }
if (server->secure_fd != -1) { if (server->secure_fd != BAD_SOCKET) {
polled[num_polled].fd = server->secure_fd; polled[num_polled].fd = server->secure_fd;
polled[num_polled].events = POLLIN; polled[num_polled].events = POLLIN;
polled[num_polled].revents = 0; polled[num_polled].revents = 0;
@@ -263,7 +193,10 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandl
if ((polled[i].revents & POLLIN) && server->num_conns < MAX_CONNS) { if ((polled[i].revents & POLLIN) && server->num_conns < MAX_CONNS) {
int new_fd = accept(polled[i].fd, NULL, NULL); SOCKET_TYPE new_fd = accept(polled[i].fd, NULL, NULL);
if (new_fd == BAD_SOCKET) {
// TODO
}
int k = 0; int k = 0;
while (server->conns[k].used) while (server->conns[k].used)
@@ -372,16 +305,13 @@ void http_response_status(HTTP_ResponseHandle res, int status)
http_engine_status(&conn->engine, status); http_engine_status(&conn->engine, status);
} }
void http_response_header(HTTP_ResponseHandle res, const char *fmt, ...) void http_response_header(HTTP_ResponseHandle res, HTTP_String str)
{ {
Connection *conn = handle2conn(res); Connection *conn = handle2conn(res);
if (conn == NULL) if (conn == NULL)
return; return;
va_list args; http_engine_header(&conn->engine, str);
va_start(args, fmt);
http_engine_header_fmt2(&conn->engine, fmt, args);
va_end(args);
} }
void http_response_body(HTTP_ResponseHandle res, HTTP_String str) void http_response_body(HTTP_ResponseHandle res, HTTP_String str)
+1 -1
View File
@@ -24,7 +24,7 @@ void http_server_free (HTTP_Server *server);
int http_server_wait (HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandle *handle); int http_server_wait (HTTP_Server *server, HTTP_Request **req, HTTP_ResponseHandle *handle);
int http_server_add_website (HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file); int http_server_add_website (HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
void http_response_status (HTTP_ResponseHandle res, int status); void http_response_status (HTTP_ResponseHandle res, int status);
void http_response_header (HTTP_ResponseHandle res, const char *fmt, ...); void http_response_header (HTTP_ResponseHandle res, HTTP_String str);
void http_response_body (HTTP_ResponseHandle res, HTTP_String str); void http_response_body (HTTP_ResponseHandle res, HTTP_String str);
void http_response_bodycap (HTTP_ResponseHandle res, int mincap); void http_response_bodycap (HTTP_ResponseHandle res, int mincap);
char* http_response_bodybuf (HTTP_ResponseHandle res, int *cap); char* http_response_bodybuf (HTTP_ResponseHandle res, int *cap);
+91 -20
View File
@@ -9,11 +9,15 @@
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h>
#define POLL WSAPoll
#endif #endif
#ifdef __linux__ #ifdef __linux__
#include <poll.h> #include <poll.h>
#include <netdb.h> #include <netdb.h>
#include <fcntl.h>
#define POLL poll
#endif #endif
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
@@ -29,6 +33,71 @@
#include "socket.h" #include "socket.h"
#endif #endif
static int set_socket_blocking(SOCKET_TYPE sock, bool value)
{
#ifdef _WIN32
u_long mode = !value;
if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR)
return -1;
#endif
#ifdef __linux__
int flags = fcntl(listen_fd, F_GETFL, 0);
if (flags < 0)
return -1;
if (fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK) < 0)
return BAD_SOCKET;
#endif
return 0;
}
SOCKET_TYPE listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog)
{
SOCKET_TYPE listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == BAD_SOCKET)
return BAD_SOCKET;
if (set_socket_blocking(listen_fd, false) < 0) {
CLOSE_SOCKET(listen_fd);
return BAD_SOCKET;
}
if (reuse_addr) {
int one = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one));
}
struct in_addr addr_buf;
if (addr.len == 0)
addr_buf.s_addr = htonl(INADDR_ANY);
else {
_Static_assert(sizeof(struct in_addr) == sizeof(HTTP_IPv4));
if (http_parse_ipv4(addr.ptr, addr.len, (HTTP_IPv4*) &addr_buf) < 0) {
CLOSE_SOCKET(listen_fd);
return BAD_SOCKET;
}
}
struct sockaddr_in bind_buf;
bind_buf.sin_family = AF_INET;
bind_buf.sin_addr = addr_buf;
bind_buf.sin_port = htons(port);
if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) { // TODO: how does bind fail on windows?
CLOSE_SOCKET(listen_fd);
return BAD_SOCKET;
}
if (listen(listen_fd, backlog) < 0) { // TODO: how does listen fail on windows?
CLOSE_SOCKET(listen_fd);
return BAD_SOCKET;
}
return listen_fd;
}
void socket_global_init(void) void socket_global_init(void)
{ {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
@@ -282,7 +351,7 @@ SocketState socket_state(Socket *sock)
return sock->state; return sock->state;
} }
void socket_accept(Socket *sock, SocketGroup *group, int fd) void socket_accept(Socket *sock, SocketGroup *group, SOCKET_TYPE fd)
{ {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
sock->ssl = NULL; sock->ssl = NULL;
@@ -303,13 +372,12 @@ void socket_accept(Socket *sock, SocketGroup *group, int fd)
sock->addr_cursor = 0; sock->addr_cursor = 0;
sock->hostname = NULL; sock->hostname = NULL;
sock->port = 0; sock->port = 0;
// Set non-blocking mode for the accepted socket if (set_socket_blocking(fd, false) < 0) {
int flags = fcntl(fd, F_GETFL, 0); // TODO: fail on error sock->state = SOCKET_STATE_DIED;
if (flags >= 0) { return;
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
} }
// Start the TLS handshake process // Start the TLS handshake process
socket_update(sock); socket_update(sock);
} }
@@ -464,8 +532,8 @@ void socket_update(Socket *sock)
} }
#endif #endif
if (sock->fd != -1) if (sock->fd != BAD_SOCKET)
close(sock->fd); CLOSE_SOCKET(sock->fd);
// If cursor reached the end, die // If cursor reached the end, die
if (sock->addr_cursor >= sock->addr_count) { if (sock->addr_cursor >= sock->addr_count) {
@@ -477,8 +545,8 @@ void socket_update(Socket *sock)
// Take current address // Take current address
AddrInfo *ai = &sock->addr_list[sock->addr_cursor]; AddrInfo *ai = &sock->addr_list[sock->addr_cursor];
int family = ai->is_ipv6 ? AF_INET6 : AF_INET; int family = ai->is_ipv6 ? AF_INET6 : AF_INET;
int fd = socket(family, SOCK_STREAM, 0); SOCKET_TYPE fd = socket(family, SOCK_STREAM, 0);
if (fd < 0) { if (fd == BAD_SOCKET) {
// Try next address // Try next address
sock->addr_cursor++; sock->addr_cursor++;
sock->event = SOCKET_WANT_NONE; sock->event = SOCKET_WANT_NONE;
@@ -487,9 +555,12 @@ void socket_update(Socket *sock)
break; break;
} }
// Set non-blocking if (set_socket_blocking(fd, false) < 0) {
int flags = fcntl(fd, F_GETFL, 0); CLOSE_SOCKET(fd);
if (flags >= 0) fcntl(fd, F_SETFL, flags | O_NONBLOCK); // TODO: Handle error by setting the socket to DIED sock->event = SOCKET_WANT_NONE;
sock->state = SOCKET_STATE_DIED;
break;
}
// Prepare sockaddr // Prepare sockaddr
int ret; int ret;
@@ -516,7 +587,7 @@ void socket_update(Socket *sock)
break; break;
} }
if (ret < 0 && errno == EINPROGRESS) { if (ret < 0 && errno == EINPROGRESS) { // TODO: I'm pretty sure all the error numbers need to be changed for windows
// Connection pending // Connection pending
sock->fd = fd; sock->fd = fd;
sock->event = SOCKET_WANT_WRITE; sock->event = SOCKET_WANT_WRITE;
@@ -543,7 +614,7 @@ void socket_update(Socket *sock)
// Check connect result // Check connect result
int err = 0; int err = 0;
socklen_t len = sizeof(err); socklen_t len = sizeof(err);
if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0 || err != 0) { if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*) &err, &len) < 0 || err != 0) {
close(sock->fd); close(sock->fd);
// If remote peer not working, try next address // If remote peer not working, try next address
if (err == ECONNREFUSED || err == ETIMEDOUT || err == ENETUNREACH || err == EHOSTUNREACH) { if (err == ECONNREFUSED || err == ETIMEDOUT || err == ENETUNREACH || err == EHOSTUNREACH) {
@@ -820,9 +891,9 @@ void socket_free(Socket *sock)
SSL_free(sock->ssl); SSL_free(sock->ssl);
#endif #endif
if (sock->fd >= 0) { if (sock->fd != BAD_SOCKET) {
close(sock->fd); CLOSE_SOCKET(sock->fd);
sock->fd = -1; sock->fd = BAD_SOCKET;
} }
if (sock->hostname) { if (sock->hostname) {
@@ -862,7 +933,7 @@ int socket_wait(Socket **socks, int num_socks) // TODO: is this used?
polled[i].revents = 0; polled[i].revents = 0;
} }
int ret = poll(polled, num_socks, -1); int ret = POLL(polled, num_socks, -1);
if (ret < 0) if (ret < 0)
return -1; return -1;
+26 -2
View File
@@ -55,6 +55,27 @@
#include <stdint.h> #include <stdint.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#define SOCKET_TYPE SOCKET
#define BAD_SOCKET INVALID_SOCKET
#define POLL WSAPoll
#define CLOSE_SOCKET closesocket
#endif
#ifdef __linux__
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>
#define SOCKET_TYPE int
#define BAD_SOCKET -1
#define POLL poll
#define CLOSE_SOCKET close
#endif
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
@@ -93,7 +114,7 @@ typedef enum {
typedef struct { typedef struct {
SocketState state; SocketState state;
SocketWantEvent event; SocketWantEvent event;
int fd; SOCKET_TYPE fd;
#if HTTPS_ENABLED #if HTTPS_ENABLED
SSL *ssl; SSL *ssl;
SSL_CTX *ssl_ctx; SSL_CTX *ssl_ctx;
@@ -121,6 +142,9 @@ typedef struct {
#endif #endif
} SocketGroup; } SocketGroup;
SOCKET_TYPE listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog);
void socket_global_init (void); void socket_global_init (void);
void socket_global_free (void); void socket_global_free (void);
int socket_group_init (SocketGroup *group); int socket_group_init (SocketGroup *group);
@@ -128,7 +152,7 @@ int socket_group_init_server(SocketGroup *group, HTTP_String cert_file,
int socket_group_add_domain(SocketGroup *group, HTTP_String domain, HTTP_String cert_key, HTTP_String private_key); int socket_group_add_domain(SocketGroup *group, HTTP_String domain, HTTP_String cert_key, HTTP_String private_key);
void socket_group_free (SocketGroup *group); void socket_group_free (SocketGroup *group);
SocketState socket_state (Socket *sock); SocketState socket_state (Socket *sock);
void socket_accept (Socket *sock, SocketGroup *group, int fd); void socket_accept (Socket *sock, SocketGroup *group, SOCKET_TYPE fd);
void socket_connect (Socket *sock, SocketGroup *group, HTTP_String host, uint16_t port); void socket_connect (Socket *sock, SocketGroup *group, HTTP_String host, uint16_t port);
void socket_connect_ipv4 (Socket *sock, SocketGroup *group, HTTP_IPv4 addr, uint16_t port); void socket_connect_ipv4 (Socket *sock, SocketGroup *group, HTTP_IPv4 addr, uint16_t port);
void socket_connect_ipv6 (Socket *sock, SocketGroup *group, HTTP_IPv6 addr, uint16_t port); void socket_connect_ipv6 (Socket *sock, SocketGroup *group, HTTP_IPv6 addr, uint16_t port);