Fix compiler errors
This commit is contained in:
@@ -217,6 +217,46 @@ void byte_queue_write(ByteQueue *queue, void *ptr, uint32_t len)
|
||||
}
|
||||
}
|
||||
|
||||
void byte_queue_write_fmt2(ByteQueue *queue,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
if (queue->flags & BYTE_QUEUE_ERROR)
|
||||
return;
|
||||
|
||||
va_list args2;
|
||||
va_copy(args2, args);
|
||||
|
||||
byte_queue_write_setmincap(queue, 128);
|
||||
ByteView dst = byte_queue_write_buf(queue);
|
||||
|
||||
int len = vsnprintf(dst.ptr, dst.len, fmt, args);
|
||||
if (len < 0) {
|
||||
queue->flags |= BYTE_QUEUE_ERROR;
|
||||
va_end(args2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > dst.len) {
|
||||
byte_queue_write_ack(queue, 0);
|
||||
byte_queue_write_setmincap(queue, len+1);
|
||||
dst = byte_queue_write_buf(queue);
|
||||
vsnprintf(dst.ptr, dst.len, fmt, args2);
|
||||
}
|
||||
|
||||
byte_queue_write_ack(queue, len);
|
||||
|
||||
va_end(args2);
|
||||
}
|
||||
|
||||
void byte_queue_write_fmt(ByteQueue *queue,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
byte_queue_write_fmt2(queue, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
ByteQueueOffset byte_queue_offset(ByteQueue *queue)
|
||||
{
|
||||
if (queue->flags & BYTE_QUEUE_ERROR)
|
||||
|
||||
@@ -117,6 +117,13 @@ int byte_queue_write_setmincap(ByteQueue *queue, uint32_t mincap);
|
||||
// short hand for write_buf/memcpy/write_ack
|
||||
void byte_queue_write(ByteQueue *queue, void *ptr, uint32_t len);
|
||||
|
||||
// Write the result of the format into the queue
|
||||
void byte_queue_write_fmt(ByteQueue *queue, const char *fmt, ...);
|
||||
|
||||
// Write the result of the format into the queue
|
||||
void byte_queue_write_fmt2(ByteQueue *queue, const char *fmt,
|
||||
va_list args);
|
||||
|
||||
// Returns the current offset inside the queue
|
||||
ByteQueueOffset byte_queue_offset(ByteQueue *queue);
|
||||
|
||||
|
||||
+26
-11
@@ -1,4 +1,14 @@
|
||||
|
||||
void http_client_conn_init(HTTP_ClientConn *conn)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void http_client_conn_free(HTTP_ClientConn *conn)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
int http_client_init(HTTP_Client *client)
|
||||
{
|
||||
client->num_conns = 0;
|
||||
@@ -35,22 +45,26 @@ int http_client_wakeup(HTTP_Client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_client_get_builder(HTTP_Client *client, HTTP_RequestBuilder *builder)
|
||||
int http_client_get_builder(HTTP_Client *client,
|
||||
HTTP_Response *response, HTTP_RequestBuilder *builder)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void http_request_builder_line(HTTP_RequestBuilder builder, xxxx)
|
||||
void http_request_builder_url(HTTP_RequestBuilder builder,
|
||||
HTTP_String url)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void http_request_builder_header(HTTP_RequestBuilder builder, String str)
|
||||
void http_request_builder_header(HTTP_RequestBuilder builder,
|
||||
HTTP_String str)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void http_request_builder_body(HTTP_RequestBuilder builder, String str)
|
||||
void http_request_builder_body(HTTP_RequestBuilder builder,
|
||||
HTTP_String str)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
@@ -61,18 +75,19 @@ int http_request_builder_send(HTTP_RequestBuilder builder)
|
||||
}
|
||||
|
||||
int http_client_register_events(HTTP_Client *client,
|
||||
struct pollfd *polled, int max_polled)
|
||||
EventRegister *reg)
|
||||
{
|
||||
return socket_manager_register_events(
|
||||
&client->sockets, polled, max_polled);
|
||||
if (socket_manager_register_events(&client->sockets, reg) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_client_process_events(HTTP_Client *client,
|
||||
struct pollfd *polled, int num_polled)
|
||||
EventRegister *reg)
|
||||
{
|
||||
SocketEvent events[HTTP_CLIENT_CAPACITY];
|
||||
int num_events = socket_manager_translate_events(
|
||||
&client->sockets, polled, num_polled);
|
||||
&client->sockets, events, reg);
|
||||
|
||||
for (int i = 0; i < num_events; i++) {
|
||||
|
||||
@@ -92,9 +107,9 @@ int http_client_process_events(HTTP_Client *client,
|
||||
}
|
||||
|
||||
bool http_client_next_response(HTTP_Client *client,
|
||||
HTTP_Response **response);
|
||||
HTTP_Response **response)
|
||||
{
|
||||
if (server->num_ready == 0)
|
||||
if (client->num_ready == 0)
|
||||
return false;
|
||||
|
||||
HTTP_ClientConn *conn = &client->conns[client->ready_head];
|
||||
|
||||
+11
-9
@@ -20,6 +20,7 @@ typedef struct {
|
||||
HTTP_ClientConnState state;
|
||||
ByteQueue input;
|
||||
ByteQueue output;
|
||||
HTTP_Response response;
|
||||
} HTTP_ClientConn;
|
||||
|
||||
// Fields of this struct are private
|
||||
@@ -77,31 +78,32 @@ int http_client_get_builder(HTTP_Client *client,
|
||||
|
||||
// Set the URL of the current request. This is the first
|
||||
// function of the request builder that the user must call.
|
||||
void http_request_builder_url(HTTP_RequestBuilder builder, String url);
|
||||
void http_request_builder_url(HTTP_RequestBuilder builder, HTTP_String url);
|
||||
|
||||
// After the URL, the user may set zero or more headers.
|
||||
void http_request_builder_header(HTTP_RequestBuilder builder, String str);
|
||||
void http_request_builder_header(HTTP_RequestBuilder builder, HTTP_String str);
|
||||
|
||||
// Append bytes to the request's body. You can call this
|
||||
// any amount of times, as long as it's after having set
|
||||
// the URL.
|
||||
void http_request_builder_body(HTTP_RequestBuilder builder, String str);
|
||||
void http_request_builder_body(HTTP_RequestBuilder builder, HTTP_String str);
|
||||
|
||||
// Mark this request as complete. This invalidates the
|
||||
// builder.
|
||||
void http_request_builder_send(HTTP_RequestBuilder builder);
|
||||
// Returns 0 on success, -1 on error.
|
||||
int http_request_builder_send(HTTP_RequestBuilder builder);
|
||||
|
||||
// List all low-level socket events the client is
|
||||
// waiting for such that the caller can call poll()
|
||||
// with it.
|
||||
// Resets the event register with the list of descriptors
|
||||
// the client wants monitored. Returns 0 on success, -1 if
|
||||
// the event register's capacity isn't large enough.
|
||||
int http_client_register_events(HTTP_Client *client,
|
||||
struct pollfd *polled, int max_polled);
|
||||
EventRegister *reg);
|
||||
|
||||
// The caller has waited for poll() to return and some
|
||||
// I/O events to be triggered, so now the HTTP client
|
||||
// can continue its buffering and flushing operations.
|
||||
int http_client_process_events(HTTP_Client *client,
|
||||
struct pollfd *polled, int num_polled);
|
||||
EventRegister *reg);
|
||||
|
||||
// After some I/O events were processes, some responses
|
||||
// may be availabe. This function returns one of the
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <poll.h>
|
||||
|
||||
+135
-30
@@ -5,6 +5,7 @@ static void http_server_conn_init(HTTP_ServerConn *conn,
|
||||
{
|
||||
conn->state = HTTP_SERVER_CONN_BUFFERING;
|
||||
conn->handle = handle;
|
||||
conn->closing = false;
|
||||
byte_queue_init(&conn->input, input_buffer_limit);
|
||||
byte_queue_init(&conn->output, output_buffer_limit);
|
||||
}
|
||||
@@ -21,8 +22,10 @@ int http_server_init(HTTP_Server *server)
|
||||
server->output_buffer_limit = 1<<20;
|
||||
|
||||
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].gen = 0;
|
||||
}
|
||||
|
||||
server->num_ready = 0;
|
||||
server->ready_head = 0;
|
||||
@@ -58,7 +61,7 @@ void http_server_set_output_limit(HTTP_Server *server, uint32_t limit)
|
||||
}
|
||||
|
||||
int http_server_listen_tcp(HTTP_Server *server,
|
||||
String addr, Port port)
|
||||
HTTP_String addr, Port port)
|
||||
{
|
||||
if (socket_manager_listen_tcp(&server->sockets, addr, port) < 0)
|
||||
return -1;
|
||||
@@ -66,8 +69,8 @@ int http_server_listen_tcp(HTTP_Server *server,
|
||||
}
|
||||
|
||||
int http_server_listen_tls(HTTP_Server *server,
|
||||
String addr, Port port, String cert_file_name,
|
||||
String key_file_name)
|
||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
||||
HTTP_String key_file_name)
|
||||
{
|
||||
if (socket_manager_listen_tls(&server->sockets, addr,
|
||||
port, cert_file_name, key_file_name) < 0)
|
||||
@@ -92,9 +95,11 @@ int http_server_wakeup(HTTP_Server *server)
|
||||
}
|
||||
|
||||
int http_server_register_events(HTTP_Server *server,
|
||||
struct pollfd *polled, int max_polled)
|
||||
EventRegister *reg)
|
||||
{
|
||||
return socket_manager_register_events(&server->sockets, polled, max_polled);
|
||||
if (socket_manager_register_events(&server->sockets, reg) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Look at the head of the input buffer to see if
|
||||
@@ -142,27 +147,11 @@ check_request_buffer(HTTP_Server *server, HTTP_ServerConn *conn)
|
||||
}
|
||||
}
|
||||
|
||||
bool http_server_next_request(HTTP_Server *server,
|
||||
HTTP_Request **request, HTTP_ResponseBuilder *builder)
|
||||
{
|
||||
if (server->num_ready == 0)
|
||||
return false;
|
||||
|
||||
HTTP_ServerConn *conn = &server->conns[server->ready_head];
|
||||
server->ready_head = (server->ready_head + 1) % HTTP_SERVER_CAPACITY;
|
||||
server->num_ready--;
|
||||
|
||||
assert(conn->state == HTTP_SERVER_CONN_WAIT_STATUS);
|
||||
*request = &conn->request;
|
||||
*builder = (HTTP_ResponseBuilder) { server, conn - server->conns, conn->gen };
|
||||
return true;
|
||||
}
|
||||
|
||||
int http_server_process_events(HTTP_Server *server,
|
||||
struct pollfd *polled, int num_polled)
|
||||
EventRegister *reg)
|
||||
{
|
||||
SocketEvent events[HTTP_SERVER_CAPACITY];
|
||||
int num_events = socket_manager_translate_events(&server->sockets, polled, num_polled);
|
||||
int num_events = socket_manager_translate_events(&server->sockets, events, reg);
|
||||
if (num_events < 0)
|
||||
return -1;
|
||||
|
||||
@@ -245,6 +234,22 @@ int http_server_process_events(HTTP_Server *server,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool http_server_next_request(HTTP_Server *server,
|
||||
HTTP_Request **request, HTTP_ResponseBuilder *builder)
|
||||
{
|
||||
if (server->num_ready == 0)
|
||||
return false;
|
||||
|
||||
HTTP_ServerConn *conn = &server->conns[server->ready_head];
|
||||
server->ready_head = (server->ready_head + 1) % HTTP_SERVER_CAPACITY;
|
||||
server->num_ready--;
|
||||
|
||||
assert(conn->state == HTTP_SERVER_CONN_WAIT_STATUS);
|
||||
*request = &conn->request;
|
||||
*builder = (HTTP_ResponseBuilder) { server, conn - server->conns, conn->gen };
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get a connection pointer from a response builder.
|
||||
// If the builder is invalid, returns NULL.
|
||||
// Note that only connections in the responding states
|
||||
@@ -261,17 +266,86 @@ builder_to_conn(HTTP_ResponseBuilder builder)
|
||||
if (builder.index > HTTP_SERVER_CAPACITY)
|
||||
return NULL;
|
||||
|
||||
HTTP_ServerConn *conn = server->conns[builder.index];
|
||||
HTTP_ServerConn *conn = &server->conns[builder.index];
|
||||
if (builder.gen != conn->gen)
|
||||
return NULL;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
static const char*
|
||||
get_status_text(int code)
|
||||
{
|
||||
switch(code) {
|
||||
|
||||
case 100: return "Continue";
|
||||
case 101: return "Switching Protocols";
|
||||
case 102: return "Processing";
|
||||
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 202: return "Accepted";
|
||||
case 203: return "Non-Authoritative Information";
|
||||
case 204: return "No Content";
|
||||
case 205: return "Reset Content";
|
||||
case 206: return "Partial Content";
|
||||
case 207: return "Multi-Status";
|
||||
case 208: return "Already Reported";
|
||||
|
||||
case 300: return "Multiple Choices";
|
||||
case 301: return "Moved Permanently";
|
||||
case 302: return "Found";
|
||||
case 303: return "See Other";
|
||||
case 304: return "Not Modified";
|
||||
case 305: return "Use Proxy";
|
||||
case 306: return "Switch Proxy";
|
||||
case 307: return "Temporary Redirect";
|
||||
case 308: return "Permanent Redirect";
|
||||
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 402: return "Payment Required";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 406: return "Not Acceptable";
|
||||
case 407: return "Proxy Authentication Required";
|
||||
case 408: return "Request Timeout";
|
||||
case 409: return "Conflict";
|
||||
case 410: return "Gone";
|
||||
case 411: return "Length Required";
|
||||
case 412: return "Precondition Failed";
|
||||
case 413: return "Request Entity Too Large";
|
||||
case 414: return "Request-URI Too Long";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 416: return "Requested Range Not Satisfiable";
|
||||
case 417: return "Expectation Failed";
|
||||
case 418: return "I'm a teapot";
|
||||
case 420: return "Enhance your calm";
|
||||
case 422: return "Unprocessable Entity";
|
||||
case 426: return "Upgrade Required";
|
||||
case 429: return "Too many requests";
|
||||
case 431: return "Request Header Fields Too Large";
|
||||
case 449: return "Retry With";
|
||||
case 451: return "Unavailable For Legal Reasons";
|
||||
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
case 502: return "Bad Gateway";
|
||||
case 503: return "Service Unavailable";
|
||||
case 504: return "Gateway Timeout";
|
||||
case 505: return "HTTP Version Not Supported";
|
||||
case 509: return "Bandwidth Limit Exceeded";
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
static void
|
||||
write_status(HTTP_ServerConn *conn, int status)
|
||||
{
|
||||
byte_queue_write(&conn->output, xxx);
|
||||
byte_queue_write_fmt(&conn->output,
|
||||
"HTTP/1.1 %d %s\r\n",
|
||||
status, get_status_text(status));
|
||||
}
|
||||
|
||||
void http_response_builder_status(HTTP_ResponseBuilder builder, int status)
|
||||
@@ -291,7 +365,7 @@ void http_response_builder_status(HTTP_ResponseBuilder builder, int status)
|
||||
conn->state = HTTP_SERVER_CONN_WAIT_HEADER;
|
||||
}
|
||||
|
||||
void http_response_builder_header(HTTP_ResponseBuilder builder, String str)
|
||||
void http_response_builder_header(HTTP_ResponseBuilder builder, HTTP_String str)
|
||||
{
|
||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
||||
if (conn == NULL)
|
||||
@@ -300,17 +374,48 @@ void http_response_builder_header(HTTP_ResponseBuilder builder, String str)
|
||||
if (conn->state != HTTP_SERVER_CONN_WAIT_HEADER)
|
||||
return;
|
||||
|
||||
byte_queue_write(&conn->output, xxx);
|
||||
// TODO: Check that the header is valid
|
||||
|
||||
byte_queue_write(&conn->output, str.ptr, str.len);
|
||||
byte_queue_write(&conn->output, "\r\n", 2);
|
||||
}
|
||||
|
||||
static void append_special_headers(HTTP_ServerConn *conn)
|
||||
{
|
||||
// TODO
|
||||
HTTP_String s;
|
||||
|
||||
if (conn->closing) {
|
||||
s = HTTP_STR("Connection: Close");
|
||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||
} else {
|
||||
s = HTTP_STR("Connection: Keep-Alive");
|
||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||
}
|
||||
|
||||
s = HTTP_STR("Content-Length: ");
|
||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||
|
||||
conn->content_length_value_offset = byte_queue_offset(&conn->output);
|
||||
|
||||
#define TEN_SPACES " "
|
||||
_Static_assert(sizeof(TEN_SPACES) == 10+1);
|
||||
|
||||
s = HTTP_STR(TEN_SPACES "\r\n");
|
||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||
|
||||
byte_queue_write(&conn->output, "\r\n", 2);
|
||||
conn->content_length_offset = byte_queue_offset(&conn->output);
|
||||
}
|
||||
|
||||
static void patch_special_headers(HTTP_ServerConn *conn)
|
||||
{
|
||||
// TODO
|
||||
int content_length = byte_queue_size_from_offset(&conn->output, conn->content_length_offset);
|
||||
|
||||
char tmp[11];
|
||||
int len = snprintf(tmp, sizeof(tmp), "%d", content_length);
|
||||
assert(len > 0 && len < 11);
|
||||
|
||||
byte_queue_patch(&conn->output, conn->content_length_value_offset, tmp, len);
|
||||
}
|
||||
|
||||
void http_response_builder_body(HTTP_ResponseBuilder builder, String str)
|
||||
|
||||
+31
-7
@@ -49,6 +49,17 @@ typedef struct {
|
||||
// Data being sent to the client
|
||||
ByteQueue output;
|
||||
|
||||
// Generation counter. This is used to invalidate
|
||||
// response builders that refer to this connection.
|
||||
uint16_t gen;
|
||||
|
||||
// This is set during the WAIT_XXX states or
|
||||
// the FLUSHING state. When the connection
|
||||
// completes flushing and no more bytes are
|
||||
// in the output buffer, it frees the connection
|
||||
// instead of turning it back to BUFFERING.
|
||||
bool closing;
|
||||
|
||||
// When the state is WAIT_STATUS, WAIT_HEADER,
|
||||
// or WAIT_BODY, this contains the parsed version
|
||||
// of the buffered request.
|
||||
@@ -64,6 +75,19 @@ typedef struct {
|
||||
// scratch.
|
||||
ByteQueueOffset response_offset;
|
||||
|
||||
// When the first byte of the response content is
|
||||
// written, before it are prepended special headers,
|
||||
// including Content-Length and Connection. This
|
||||
// offset points to the first byte that comes after
|
||||
// the string "Content-Length: ".
|
||||
ByteQueueOffset content_length_value_offset;
|
||||
|
||||
// Similarly to the previous field, this one points
|
||||
// to the first byte of the body. This allows calculating
|
||||
// the length of the response content byte subtracting
|
||||
// it from the offset reached when the response is marked
|
||||
// as done.
|
||||
ByteQueueOffset content_length_offset;
|
||||
} HTTP_ServerConn;
|
||||
|
||||
typedef struct {
|
||||
@@ -118,7 +142,7 @@ void http_server_set_output_limit(HTTP_Server *server, uint32_t limit);
|
||||
// Enable listening for plain HTTP requests at the
|
||||
// specified interface.
|
||||
int http_server_listen_tcp(HTTP_Server *server,
|
||||
String addr, Port port);
|
||||
HTTP_String addr, Port port);
|
||||
|
||||
// Enable listening for HTTPS requests at the specified
|
||||
// interfact, using the specified certificate and key
|
||||
@@ -136,17 +160,17 @@ int http_server_add_certificate(HTTP_Server *server,
|
||||
// other threads can call this function to wake it up.
|
||||
int http_server_wakeup(HTTP_Server *server);
|
||||
|
||||
// List all low-level socket events the server is
|
||||
// waiting for such that the caller can call poll()
|
||||
// with it.
|
||||
// Resets the event register with the list of descriptors
|
||||
// the server wants monitored. Returns 0 on success, -1 if
|
||||
// the event register's capacity isn't large enough.
|
||||
int http_server_register_events(HTTP_Server *server,
|
||||
struct pollfd *polled, int max_polled);
|
||||
EventRegister *reg);
|
||||
|
||||
// The caller has waited for poll() to return and some
|
||||
// I/O events to be triggered, so now the HTTP server
|
||||
// can continue its buffering and flushing operations.
|
||||
int http_server_process_events(HTTP_Server *server,
|
||||
struct pollfd *polled, int num_polled);
|
||||
EventRegister *reg);
|
||||
|
||||
typedef struct {
|
||||
HTTP_Server *server;
|
||||
@@ -174,7 +198,7 @@ void http_response_builder_status(HTTP_ResponseBuilder builder, int status);
|
||||
// Append a header to the response. This can only be
|
||||
// used after having set the status and before appending
|
||||
// to the body.
|
||||
void http_response_builder_header(HTTP_ResponseBuilder builder, String str);
|
||||
void http_response_builder_header(HTTP_ResponseBuilder builder, HTTP_String str);
|
||||
|
||||
// Append some bytes to the response's body
|
||||
void http_response_builder_body(HTTP_ResponseBuilder builder, String str);
|
||||
|
||||
+96
-63
@@ -68,7 +68,7 @@ static int set_socket_blocking(NATIVE_SOCKET sock, bool value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NATIVE_SOCKET create_listen_socket(String addr,
|
||||
static NATIVE_SOCKET create_listen_socket(HTTP_String addr,
|
||||
Port port, bool reuse_addr, int backlog)
|
||||
{
|
||||
NATIVE_SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
@@ -169,7 +169,7 @@ void socket_manager_free(SocketManager *sm)
|
||||
}
|
||||
|
||||
int socket_manager_listen_tcp(SocketManager *sm,
|
||||
String addr, Port port)
|
||||
HTTP_String addr, Port port)
|
||||
{
|
||||
if (sm->plain_sock != NATIVE_SOCKET_INVALID)
|
||||
return -1;
|
||||
@@ -184,8 +184,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
||||
}
|
||||
|
||||
int socket_manager_listen_tls(SocketManager *sm,
|
||||
String addr, Port port, String cert_file_name,
|
||||
String key_file_name)
|
||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
||||
HTTP_String key_file_name)
|
||||
{
|
||||
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
||||
return -1;
|
||||
@@ -452,38 +452,41 @@ int socket_manager_wakeup(SocketManager *sm)
|
||||
}
|
||||
|
||||
static int socket_manager_register_events_nolock(
|
||||
SocketManager *sm, struct pollfd *polled, int max_polled)
|
||||
SocketManager *sm, EventRegister *reg)
|
||||
{
|
||||
// The poll array must be able to hold descriptors
|
||||
// for a socket manager at full capacity. Note that
|
||||
// other than having a number of connection sockets,
|
||||
// the manager also needs 2 for the listeners and
|
||||
// one for the wakeup self-pipe.
|
||||
if (max_polled < sm->max_used+3)
|
||||
if (reg->max_polled < sm->max_used+3)
|
||||
return -1;
|
||||
int num_polled = 0;
|
||||
reg->num_polled = 0;
|
||||
|
||||
polled[num_polled].fd = sm->wait_sock;
|
||||
polled[num_polled].events = 0;
|
||||
polled[num_polled].revents = 0;
|
||||
num_polled++;
|
||||
reg->polled[reg->num_polled].fd = sm->wait_sock;
|
||||
reg->polled[reg->num_polled].events = POLLIN;
|
||||
reg->polled[reg->num_polled].revents = 0;
|
||||
reg->ptrs[reg->num_polled] = NULL;
|
||||
reg->num_polled++;
|
||||
|
||||
// If the manager isn't at full capacity, monitor
|
||||
// the listener sockets for incoming connections.
|
||||
if (sm->num_used < sm->max_used) {
|
||||
|
||||
if (sm->plain_sock != NATIVE_SOCKET_INVALID) {
|
||||
polled[num_polled].fd = sm->plain_sock;
|
||||
polled[num_polled].events = POLLIN;
|
||||
polled[num_polled].revents = 0;
|
||||
num_polled++;
|
||||
reg->polled[reg->num_polled].fd = sm->plain_sock;
|
||||
reg->polled[reg->num_polled].events = POLLIN;
|
||||
reg->polled[reg->num_polled].revents = 0;
|
||||
reg->ptrs[reg->num_polled] = NULL;
|
||||
reg->num_polled++;
|
||||
}
|
||||
|
||||
if (sm->secure_sock != NATIVE_SOCKET_INVALID) {
|
||||
polled[num_polled].fd = sm->secure_sock;
|
||||
polled[num_polled].events = POLLIN;
|
||||
polled[num_polled].revents = 0;
|
||||
num_polled++;
|
||||
reg->polled[reg->num_polled].fd = sm->secure_sock;
|
||||
reg->polled[reg->num_polled].events = POLLIN;
|
||||
reg->polled[reg->num_polled].revents = 0;
|
||||
reg->ptrs[reg->num_polled] = NULL;
|
||||
reg->num_polled++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,28 +501,25 @@ static int socket_manager_register_events_nolock(
|
||||
continue;
|
||||
j++;
|
||||
|
||||
if (s->state == SOCKET_STATE_DIED || s->state == SOCKET_STATE_ESTABLISHED_READY)
|
||||
return 0;
|
||||
|
||||
if (s->events) {
|
||||
polled[num_polled].fd = s->sock;
|
||||
polled[num_polled].events = s->events;
|
||||
polled[num_polled].revents = 0;
|
||||
num_polled++;
|
||||
reg->polled[reg->num_polled].fd = s->sock;
|
||||
reg->polled[reg->num_polled].events = s->events;
|
||||
reg->polled[reg->num_polled].revents = 0;
|
||||
reg->ptrs[reg->num_polled] = s;
|
||||
reg->num_polled++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_polled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_manager_register_events(SocketManager *sm,
|
||||
struct pollfd *polled, int max_polled)
|
||||
EventRegister *reg)
|
||||
{
|
||||
if (mutex_lock(&sm->mutex) < 0)
|
||||
return -1;
|
||||
|
||||
int ret = socket_manager_register_events_nolock(
|
||||
sm, polled, max_polled);
|
||||
int ret = socket_manager_register_events_nolock(sm, reg);
|
||||
|
||||
if (mutex_unlock(&sm->mutex) < 0)
|
||||
return -1;
|
||||
@@ -538,14 +538,14 @@ static Socket *handle_to_socket(SocketManager *sm, SocketHandle handle)
|
||||
}
|
||||
|
||||
static int socket_manager_translate_events_nolock(
|
||||
SocketManager *sm, SocketEvent *events, int max_events,
|
||||
struct pollfd *polled, int num_polled)
|
||||
SocketManager *sm, SocketEvent *events,
|
||||
EventRegister *reg)
|
||||
{
|
||||
int num_events = 0;
|
||||
for (int i = 0; i < num_polled; i++) {
|
||||
for (int i = 0; i < reg->num_polled; i++) {
|
||||
|
||||
if (polled[i].fd == sm->plain_sock ||
|
||||
polled[i].fd == sm->secure_sock) {
|
||||
if (reg->polled[i].fd == sm->plain_sock ||
|
||||
reg->polled[i].fd == sm->secure_sock) {
|
||||
|
||||
// We only listen for input events from the listener
|
||||
// if the socket pool isn't fool. This ensures that
|
||||
@@ -561,7 +561,7 @@ static int socket_manager_translate_events_nolock(
|
||||
|
||||
// Determine whether the event came from
|
||||
// the encrypted listener or not.
|
||||
bool secure = (polled[i].fd == sm->secure_sock);
|
||||
bool secure = (reg->polled[i].fd == sm->secure_sock);
|
||||
|
||||
Socket *s = sm->sockets;
|
||||
while (s->state != SOCKET_STATE_FREE) {
|
||||
@@ -569,7 +569,7 @@ static int socket_manager_translate_events_nolock(
|
||||
assert(s - sm->sockets < + sm->max_used);
|
||||
}
|
||||
|
||||
NATIVE_SOCKET sock = accept(polled[i].fd, NULL, NULL);
|
||||
NATIVE_SOCKET sock = accept(reg->polled[i].fd, NULL, NULL);
|
||||
if (sock == NATIVE_SOCKET_INVALID)
|
||||
continue;
|
||||
|
||||
@@ -592,12 +592,14 @@ static int socket_manager_translate_events_nolock(
|
||||
|
||||
sm->num_used++;
|
||||
|
||||
} else if (polled[i].fd == sm->wait_sock) {
|
||||
} else if (reg->polled[i].fd == sm->wait_sock) {
|
||||
|
||||
// TODO: consume
|
||||
|
||||
} else {
|
||||
if (polled[i].revents)
|
||||
|
||||
Socket *s = reg->ptrs[i];
|
||||
if (reg->polled[i].revents)
|
||||
socket_update(s);
|
||||
}
|
||||
}
|
||||
@@ -608,14 +610,11 @@ static int socket_manager_translate_events_nolock(
|
||||
continue;
|
||||
j++;
|
||||
|
||||
if (num_events == max_events)
|
||||
break;
|
||||
|
||||
if (s->state == SOCKET_DIED) {
|
||||
if (s->state == SOCKET_STATE_DIED) {
|
||||
|
||||
events[num_events++] = (SocketEvent) {
|
||||
SOCKET_EVENT_DISCONNECT,
|
||||
SOCKET_HANDE_INVALID,
|
||||
SOCKET_HANDLE_INVALID,
|
||||
s->user
|
||||
};
|
||||
|
||||
@@ -628,7 +627,7 @@ static int socket_manager_translate_events_nolock(
|
||||
if (s->num_addr > 1)
|
||||
free(s->addrs);
|
||||
}
|
||||
s->num_used--;
|
||||
sm->num_used--;
|
||||
|
||||
} else if (s->state == SOCKET_STATE_ESTABLISHED_READY) {
|
||||
events[num_events++] = (SocketEvent) {
|
||||
@@ -643,18 +642,16 @@ static int socket_manager_translate_events_nolock(
|
||||
}
|
||||
|
||||
int socket_manager_translate_events(SocketManager *sm,
|
||||
SocketEvent *events, int max_events, struct pollfd *polled,
|
||||
int num_polled)
|
||||
SocketEvent *events, EventRegister *reg)
|
||||
{
|
||||
if (mutex_lock(&sm->mutex) < 0)
|
||||
return -1;
|
||||
|
||||
socket_manager_translate_events_nolock(
|
||||
sm, events, max_events, polled, num_polled);
|
||||
int ret = socket_manager_translate_events_nolock(sm, events, reg);
|
||||
|
||||
if (mutex_unlock(&sm->mutex) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int resolve_connect_targets(ConnectTarget *targets,
|
||||
@@ -666,7 +663,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
||||
case CONNECT_TARGET_NAME:
|
||||
{
|
||||
char portstr[16];
|
||||
int len = snprintf(portstr, sizeof(portstr), "%u", port);
|
||||
int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port);
|
||||
if (len < 0 || len >= (int) sizeof(portstr))
|
||||
return -1;
|
||||
|
||||
@@ -674,8 +671,14 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
char hostname[1<<9]; // TODO: Is this a good length?
|
||||
if (targets[i].name.len >= (int) sizeof(hostname))
|
||||
return -1;
|
||||
memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
|
||||
hostname[targets[i].name.len] = '\0';
|
||||
|
||||
struct addrinfo *res = NULL;
|
||||
int ret = getaddrinfo(pending_connect->hostname, portstr, &hints, &res);
|
||||
int ret = getaddrinfo(hostname, portstr, &hints, &res);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
@@ -692,7 +695,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
||||
IPv6 ipv6 = *(IPv6*) &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr;
|
||||
if (num_resolved < max_resolved) {
|
||||
resolved[num_resolved].is_ipv4 = false;
|
||||
resolved[num_resolved].ipv4 = ipv6;
|
||||
resolved[num_resolved].ipv6 = ipv6;
|
||||
resolved[num_resolved].port = targets[i].port;
|
||||
num_resolved++;
|
||||
}
|
||||
@@ -723,6 +726,8 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
||||
return num_resolved;
|
||||
}
|
||||
|
||||
#define MAX_CONNECT_TARGETS 16
|
||||
|
||||
int socket_connect(SocketManager *sm, int num_targets,
|
||||
ConnectTarget *targets, bool secure, void *user)
|
||||
{
|
||||
@@ -759,10 +764,29 @@ int socket_connect(SocketManager *sm, int num_targets,
|
||||
s->state = SOCKET_STATE_PENDING;
|
||||
s->sock = NATIVE_SOCKET_INVALID;
|
||||
s->user = user;
|
||||
s->num_used++;
|
||||
sm->num_used++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool would_block(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int err = WSAGetLastError();
|
||||
return err == WSAEWOULDBLOCK;
|
||||
#else
|
||||
return errno == EAGAIN || errno == EWOULDBLOCK;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool interrupted(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return false;
|
||||
#else
|
||||
return errno == EINTR;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int socket_recv_nolock(SocketManager *sm, SocketHandle handle,
|
||||
char *dst, int max)
|
||||
{
|
||||
@@ -840,14 +864,14 @@ static int socket_send_nolock(SocketManager *sm, SocketHandle handle,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!socket_secure(s)) {
|
||||
if (!is_secure(s)) {
|
||||
int ret = send(s->sock, src, len, 0);
|
||||
if (ret < 0) {
|
||||
if (would_block()) {
|
||||
s->state = SOCKET_STATE_ESTABLISHED_WAIT;
|
||||
s->events = POLLOUT;
|
||||
} else if (!interrupted()) {
|
||||
s->state = SOCKET_DIED;
|
||||
s->state = SOCKET_STATE_DIED;
|
||||
s->events = 0;
|
||||
}
|
||||
ret = 0;
|
||||
@@ -888,19 +912,25 @@ int socket_send(SocketManager *sm, SocketHandle handle,
|
||||
return ret;
|
||||
}
|
||||
|
||||
void socket_close(SocketManager *sm, SocketHandle handle)
|
||||
int socket_close(SocketManager *sm, SocketHandle handle)
|
||||
{
|
||||
if (mutex_lock(&sm->mutex) < 0)
|
||||
return -1;
|
||||
|
||||
// TODO: maybe we don't want to always set to SHUTDOWN. What if the socket is DIED for instance?
|
||||
s->state = SOCKET_STATE_SHUTDOWN;
|
||||
s->events = 0;
|
||||
socket_update(s);
|
||||
int ret;
|
||||
Socket *s = handle_to_socket(sm, handle);
|
||||
if (s == NULL)
|
||||
ret = -1;
|
||||
else {
|
||||
// TODO: maybe we don't want to always set to SHUTDOWN. What if the socket is DIED for instance?
|
||||
s->state = SOCKET_STATE_SHUTDOWN;
|
||||
s->events = 0;
|
||||
socket_update(s);
|
||||
}
|
||||
|
||||
if (mutex_unlock(&sm->mutex) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int socket_is_secure(SocketManager *sm, SocketHandle handle)
|
||||
@@ -926,11 +956,14 @@ int socket_set_user(SocketManager *sm, SocketHandle handle, void *user)
|
||||
if (mutex_lock(&sm->mutex) < 0)
|
||||
return -1;
|
||||
|
||||
int ret;
|
||||
Socket *s = handle_to_socket(sm, handle);
|
||||
if (s == NULL)
|
||||
ret = -1;
|
||||
else
|
||||
else {
|
||||
s->user = user;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (mutex_unlock(&sm->mutex) < 0)
|
||||
return -1;
|
||||
|
||||
+23
-13
@@ -221,7 +221,7 @@ void socket_manager_free(SocketManager *sm);
|
||||
// can only be used once per manager.
|
||||
// Returns 0 on success, -1 on error.
|
||||
int socket_manager_listen_tcp(SocketManager *sm,
|
||||
String addr, Port port);
|
||||
HTTP_String addr, Port port);
|
||||
|
||||
// Same as the previous function, but incoming
|
||||
// connections will be interpreted as TLS. You
|
||||
@@ -231,8 +231,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
||||
// and secure connections.
|
||||
// Returns 0 on success, -1 on error.
|
||||
int socket_manager_listen_tls(SocketManager *sm,
|
||||
String addr, Port port, String cert_file_name,
|
||||
String key_file_name);
|
||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
||||
HTTP_String key_file_name);
|
||||
|
||||
// If the socket manager was configures to accept
|
||||
// TLS connections, this adds additional certificates
|
||||
@@ -249,22 +249,32 @@ int socket_manager_add_certificate(SocketManager *sm,
|
||||
// Returns 0 on success, -1 on error.
|
||||
int socket_manager_wakeup(SocketManager *sm);
|
||||
|
||||
// Writes to the polled array the list of sockets
|
||||
// that the socket manager is monitoring and which
|
||||
// events associated to that socket.
|
||||
// Returns the number of items written to the polled
|
||||
// array.
|
||||
typedef struct {
|
||||
void **ptrs;
|
||||
struct pollfd *polled;
|
||||
int num_polled;
|
||||
int max_polled;
|
||||
} EventRegister;
|
||||
|
||||
// Resets the event register with the list of descriptors
|
||||
// the socket manager wants monitored. Returns 0 on
|
||||
// success, -1 if the event register's capacity isn't
|
||||
// large enough.
|
||||
int socket_manager_register_events(SocketManager *sm,
|
||||
struct pollfd *polled, int max_polled);
|
||||
EventRegister *reg);
|
||||
|
||||
// After poll() is called on the previously registered
|
||||
// pollfd array and the revents fields are set, this
|
||||
// function processes those events to produce higher-level
|
||||
// socket events. Returns the number of socket events
|
||||
// written to the output array, or -1 on error.
|
||||
//
|
||||
// The maximum number of events this will write
|
||||
// to the events array is equal to the numero of
|
||||
// socket structs provided to the socket manager
|
||||
// via the init function.
|
||||
int socket_manager_translate_events(SocketManager *sm,
|
||||
SocketEvent *events, int max_events, struct pollfd *polled,
|
||||
int num_polled);
|
||||
SocketEvent *events, EventRegister *reg);
|
||||
|
||||
typedef enum {
|
||||
CONNECT_TARGET_NAME,
|
||||
@@ -295,7 +305,7 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
|
||||
int socket_send(SocketManager *sm, SocketHandle handle,
|
||||
char *src, int len);
|
||||
|
||||
void socket_close(SocketManager *sm, SocketHandle handle);
|
||||
int socket_close(SocketManager *sm, SocketHandle handle);
|
||||
|
||||
// Returns -1 on error, 0 if the socket was accepted
|
||||
// from the plaintext listener, or 1 if it was accepted
|
||||
@@ -303,4 +313,4 @@ void socket_close(SocketManager *sm, SocketHandle handle);
|
||||
int socket_is_secure(SocketManager *sm, SocketHandle handle);
|
||||
|
||||
// Set the user pointer of a socket
|
||||
void socket_set_user(SocketManager *sm, SocketHandle handle, void *user);
|
||||
int socket_set_user(SocketManager *sm, SocketHandle handle, void *user);
|
||||
|
||||
Reference in New Issue
Block a user