Refactoring for an improved event loop
This commit is contained in:
+52
-143
@@ -1,166 +1,75 @@
|
||||
#ifndef SOCKET_INCLUDED
|
||||
#define SOCKET_INCLUDED
|
||||
// This is a socket abstraction module for non-blocking TCP and TLS sockets.
|
||||
//
|
||||
// Sockets may be in a number of states based on if they are plain TCP or TLS
|
||||
// sockets. Users generally only care about when the connection is established
|
||||
// or is terminated.
|
||||
//
|
||||
// Sockets can be created by connecting to a server using one of these:
|
||||
//
|
||||
// socket_connect
|
||||
// socket_connect_ipv4
|
||||
// socket_connect_ipv6
|
||||
//
|
||||
// They allow connecting to a remote host by specifying its name, of IP address.
|
||||
// Or by interning a socket accepted by a listening socket:
|
||||
//
|
||||
// socket_accept
|
||||
//
|
||||
// after creation, the event field will hold one of the values:
|
||||
//
|
||||
// SOCKET_WANT_READ
|
||||
// SOCKET_WANT_WRITE
|
||||
//
|
||||
// Which respectively mean that the socket object needs to read or write
|
||||
// from the underlying socket, and to do so non-blockingly, the caller needs
|
||||
// to wait for the socket being ready for that operation. This is one way
|
||||
// to do it:
|
||||
//
|
||||
// // Translate the socket event field to poll() flags
|
||||
// int events;
|
||||
// if (sock.event == SOCKET_WANT_READ)
|
||||
// events = POLLIN;
|
||||
// else if (sock.event == SOCKET_WANT_WRITE)
|
||||
// events = POLLOUT;
|
||||
//
|
||||
// // block until the socket is ready
|
||||
// struct pollfd buf;
|
||||
// buf.fd = sock.fd;
|
||||
// buf.events = events;
|
||||
// buf.revents = 0;
|
||||
// poll(&buf, 1, -1);
|
||||
//
|
||||
// whenever a socket is ready, the user must call the socket_update
|
||||
// function. Then, if the socket is in the SOCKET_STATE_ESTABLISHED_READY
|
||||
// state, the user can call one of
|
||||
//
|
||||
// socket_close
|
||||
// socket_read
|
||||
// socket_write
|
||||
//
|
||||
// At any point the socket could reach the SOCKET_STATE_DIED state,
|
||||
// which means the user needs to call socket_free to free the socket
|
||||
// as it's not unusable.
|
||||
|
||||
#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
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#endif
|
||||
// This module implements the socket state machine to encapsulate
|
||||
// the complexity of non-blocking TCP and TLS sockets.
|
||||
//
|
||||
// A socket is represented by the "Socket" structure, which may
|
||||
// be in a number of states. As far as an user of the interface
|
||||
// is concerned, the socket may be DIED, READY, or in an internal
|
||||
// state that requires waiting for an event. Therefore, if the
|
||||
// socket is not DIED or READY, the user needs to wait for the
|
||||
// events specified in the [socket->events] field, then call the
|
||||
// socket_update function. At some point the socket will become
|
||||
// either READY or DIED.
|
||||
//
|
||||
// When the socket reaches the DIED state, the user must call
|
||||
// socket_free.
|
||||
//
|
||||
// If the socket is ESTABLISHED_READY, the user may call socket_read,
|
||||
// socket_write, or socket_close on it.
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "sec.h"
|
||||
#include "parse.h"
|
||||
#include "socket_raw.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int is_ipv6;
|
||||
union {
|
||||
HTTP_IPv4 ipv4;
|
||||
HTTP_IPv6 ipv6;
|
||||
} addr;
|
||||
} AddrInfo;
|
||||
typedef struct PendingConnect PendingConnect;
|
||||
|
||||
// These should only be relevant to socket.c
|
||||
typedef enum {
|
||||
SOCKET_STATE_PENDING,
|
||||
SOCKET_STATE_CONNECTING,
|
||||
SOCKET_STATE_CONNECTED,
|
||||
SOCKET_STATE_ACCEPTED,
|
||||
SOCKET_STATE_FREE,
|
||||
SOCKET_STATE_DIED,
|
||||
SOCKET_STATE_ESTABLISHED_WAIT,
|
||||
SOCKET_STATE_ESTABLISHED_READY,
|
||||
SOCKET_STATE_PENDING,
|
||||
SOCKET_STATE_ACCEPTED,
|
||||
SOCKET_STATE_CONNECTED,
|
||||
SOCKET_STATE_CONNECTING,
|
||||
SOCKET_STATE_SHUTDOWN,
|
||||
SOCKET_STATE_DIED
|
||||
} SocketState;
|
||||
|
||||
typedef enum {
|
||||
SOCKET_WANT_NONE,
|
||||
SOCKET_WANT_READ,
|
||||
SOCKET_WANT_WRITE,
|
||||
} SocketWantEvent;
|
||||
|
||||
typedef struct {
|
||||
SocketState state;
|
||||
SocketWantEvent event;
|
||||
SOCKET_TYPE fd;
|
||||
#if HTTPS_ENABLED
|
||||
|
||||
RAW_SOCKET raw;
|
||||
int events;
|
||||
|
||||
void *user_data;
|
||||
PendingConnect *pending_connect;
|
||||
|
||||
#ifdef HTTPS_ENABLED
|
||||
SSL *ssl;
|
||||
SSL_CTX *ssl_ctx;
|
||||
#endif
|
||||
AddrInfo *addr_list;
|
||||
int addr_count;
|
||||
int addr_cursor;
|
||||
char *hostname;
|
||||
uint16_t port;
|
||||
|
||||
SecureContext *sec;
|
||||
|
||||
} Socket;
|
||||
|
||||
#ifdef HTTPS_ENABLED
|
||||
typedef struct {
|
||||
char name[128];
|
||||
SSL_CTX *ssl_ctx;
|
||||
} Domain;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
#ifdef HTTPS_ENABLED
|
||||
SSL_CTX *ssl_ctx;
|
||||
int num_domains;
|
||||
int max_domains;
|
||||
Domain *domains;
|
||||
#endif
|
||||
} SocketGroup;
|
||||
|
||||
SOCKET_TYPE listen_socket(HTTP_String addr, uint16_t port, bool reuse_addr, int backlog);
|
||||
|
||||
|
||||
void socket_global_init (void);
|
||||
void socket_global_free (void);
|
||||
int socket_group_init (SocketGroup *group);
|
||||
int socket_group_init_server(SocketGroup *group, HTTP_String cert_file, HTTP_String key_file);
|
||||
int socket_group_add_domain(SocketGroup *group, HTTP_String domain, HTTP_String cert_key, HTTP_String private_key);
|
||||
void socket_group_free (SocketGroup *group);
|
||||
SocketState socket_state (Socket *sock);
|
||||
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_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_update (Socket *sock);
|
||||
int socket_read (Socket *sock, char *dst, int max);
|
||||
int socket_write (Socket *sock, char *src, int len);
|
||||
void socket_close (Socket *sock);
|
||||
void socket_free (Socket *sock);
|
||||
int socket_wait (Socket **socks, int num_socks);
|
||||
void socket_connect(Socket *sock, SecureContext *sec, HTTP_String hostname, uint16_t port, void *user_data);
|
||||
void socket_connect_ipv4(Socket *sock, SecureContext *sec, HTTP_IPv4 addr, uint16_t port, void *user_data);
|
||||
void socket_connect_ipv6(Socket *sock, SecureContext *sec, HTTP_IPv6 addr, uint16_t port, void *user_data);
|
||||
void socket_accept(Socket *sock, SecureContext *sec, RAW_SOCKET raw);
|
||||
void socket_update(Socket *sock);
|
||||
void socket_close(Socket *sock);
|
||||
bool socket_ready(Socket *sock);
|
||||
bool socket_died(Socket *sock);
|
||||
int socket_read(Socket *sock, char *dst, int max);
|
||||
int socket_write(Socket *sock, char *src, int len);
|
||||
void socket_free(Socket *sock);
|
||||
bool socket_secure(Socket *sock);
|
||||
void socket_set_user_data(Socket *sock, void *user_data);
|
||||
void* socket_get_user_data(Socket *sock);
|
||||
|
||||
#endif // SOCKET_INCLUDED
|
||||
Reference in New Issue
Block a user