diff --git a/Makefile b/Makefile index c5aadeb..dde7518 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ chttp.c chttp.h: $(wildcard src/*.c src/*.h) misc/amalg.py Makefile python misc/amalg.py example: main.c chttp.c chttp.h - gcc main.c chttp.c -o example.exe -Wfatal-errors + gcc main.c chttp.c -o example -Wfatal-errors clean: rm chttp.c chttp.h diff --git a/chttp.c b/chttp.c index 024ab41..f3c8284 100644 --- a/chttp.c +++ b/chttp.c @@ -1582,7 +1582,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); @@ -1683,7 +1683,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; @@ -1698,8 +1698,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; @@ -1966,38 +1966,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++; } } @@ -2012,28 +2015,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; @@ -2052,14 +2052,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 @@ -2075,7 +2075,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) { @@ -2083,7 +2083,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; @@ -2106,12 +2106,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); } } @@ -2122,14 +2124,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 }; @@ -2142,7 +2141,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) { @@ -2157,18 +2156,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, @@ -2180,7 +2177,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; @@ -2188,8 +2185,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; @@ -2206,7 +2209,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++; } @@ -2237,6 +2240,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) { @@ -2273,10 +2278,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) { @@ -2354,14 +2378,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; @@ -2402,19 +2426,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) @@ -2440,11 +2470,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; @@ -2673,6 +2706,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) @@ -2874,6 +2947,16 @@ int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN, // src/client.c //////////////////////////////////////////////////////////////////////////////////////// +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; @@ -2910,22 +2993,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 } @@ -2936,18 +3023,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++) { @@ -2967,9 +3055,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]; @@ -2996,6 +3084,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); } @@ -3012,8 +3101,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; @@ -3049,7 +3140,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; @@ -3057,8 +3148,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) @@ -3083,9 +3174,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 @@ -3133,27 +3226,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; @@ -3236,6 +3313,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 @@ -3252,17 +3345,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) @@ -3282,7 +3444,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) @@ -3291,17 +3453,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) diff --git a/chttp.h b/chttp.h index 57dda4f..32c8773 100644 --- a/chttp.h +++ b/chttp.h @@ -19,6 +19,8 @@ #include #include #else +#include +#include #include #include #include @@ -457,7 +459,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 @@ -467,8 +469,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 @@ -485,22 +487,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, @@ -531,7 +543,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 @@ -539,7 +551,7 @@ 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); //////////////////////////////////////////////////////////////////////////////////////// // src/byte_queue.h @@ -663,6 +675,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); @@ -724,6 +743,7 @@ typedef struct { HTTP_ClientConnState state; ByteQueue input; ByteQueue output; + HTTP_Response response; } HTTP_ClientConn; // Fields of this struct are private @@ -781,31 +801,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 @@ -875,6 +896,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. @@ -890,6 +922,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 { @@ -944,7 +989,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 @@ -962,17 +1007,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; @@ -1000,7 +1045,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); diff --git a/src/byte_queue.c b/src/byte_queue.c index ff16aad..23035a7 100644 --- a/src/byte_queue.c +++ b/src/byte_queue.c @@ -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) diff --git a/src/byte_queue.h b/src/byte_queue.h index b685713..2df19cd 100644 --- a/src/byte_queue.h +++ b/src/byte_queue.h @@ -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); diff --git a/src/client.c b/src/client.c index 8ec236c..bf306e5 100644 --- a/src/client.c +++ b/src/client.c @@ -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]; diff --git a/src/client.h b/src/client.h index ac42d2e..2d90e71 100644 --- a/src/client.h +++ b/src/client.h @@ -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 diff --git a/src/includes.h b/src/includes.h index 304be8d..0e68396 100644 --- a/src/includes.h +++ b/src/includes.h @@ -11,6 +11,8 @@ #include #include #else +#include +#include #include #include #include diff --git a/src/server.c b/src/server.c index e160909..a8bc760 100644 --- a/src/server.c +++ b/src/server.c @@ -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) diff --git a/src/server.h b/src/server.h index 2d16d56..be25990 100644 --- a/src/server.h +++ b/src/server.h @@ -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); diff --git a/src/socket.c b/src/socket.c index 817cbc7..89520cb 100644 --- a/src/socket.c +++ b/src/socket.c @@ -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; diff --git a/src/socket.h b/src/socket.h index ec656c2..7a8864f 100644 --- a/src/socket.h +++ b/src/socket.h @@ -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);