Skip to content

Commit

Permalink
bugfixes, extensions, and other minor updates
Browse files Browse the repository at this point in the history
  • Loading branch information
barnii77 committed Jan 1, 2025
1 parent ca01fdd commit 9edd35e
Show file tree
Hide file tree
Showing 23 changed files with 320 additions and 154 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ set(NET77_SOURCES
lib/mcfss.c
lib/linked_list.c
lib/thread_pool.c
lib/string_utils.c
include/net77/request.h
include/net77/string_utils.h
include/net77/utils.h
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# net77
A tiny networking library written in C for educational purposes. Has HTTP parsing/construction features, can sendAllData requests HTTP/1.0 style and has a helpers for hosting HTTP servers. Should in the future support https and http 2.0
A tiny networking library written in C for educational purposes. Has HTTP parsing/construction features, can send requests HTTP/1.0 style and has a helpers for hosting HTTP servers. Should in the future support https and http 2.0
13 changes: 10 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
- [X] HTTP 1.0 support
- [X] basic TCP client/server implementation
- [X] functions for basic HTTP 1.0 requests (can communicate with actual websites)
- [ ] more flexible TCP server implementation
- [X] more flexible TCP server implementation
- [X] ErrStatus and SuccessStatus typedefs (they're ints) with macros ecIsErr, ecIsOk, scIsOk, scIsErr to encode whether 0 is no error and 1 is error or 1 is success and 0 no success...
- [ ] Make everything more stable (some of the tests don't always pass if you run them 1000 times)
- [ ] testServerBigData1 seems to sometimes fail if I turn up the size of the messages enough AND run it very often in a loop with no pause in between
- [ ] the tests which make real requests to www.example.com seem to be unable to connect sometimes (but only if I run all the other tests as well....)
- [ ] HTTP 1.1 support
- [ ] HTTP 2.0 support
- [ ] HTTP 3.0 support
- [ ] Some utilities for hosting HTTP/S servers (i.e. a tcp server handler wrapper)
- [ ] ALPN (application layer protocol negotiation)
- [ ] TLS 1.2 support (custom TLS implementation)
- [ ] TLS 1.3 support (custom TLS implementation)
- [ ] TLS session resumption
- [ ] HTTPS support
- [ ] proper error codes (not just 0, 1, -1 but actual error enums)
- [ ] proper error codes (not just 0, 1, -1 but actual error enums)
- [ ] HTTP 3.0 support
- [ ] QUIC support
12 changes: 12 additions & 0 deletions include/net77/error_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef NET77_ERROR_MANAGEMENT_H
#define NET77_ERROR_MANAGEMENT_H

typedef int ErrorStatus;
typedef int SuccessStatus;

#define errStatusIsOk(err_status) ((err_status) == 0)
#define errStatusIsErr(err_status) ((err_status) != 0)
#define successStatusIsOk(success_status) ((success_status) == 0)
#define successStatusIsErr(success_status) ((success_status) != 0)

#endif
4 changes: 3 additions & 1 deletion include/net77/init.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#ifndef NET77_INIT_H
#define NET77_INIT_H

int socketInit();
#include "net77/error_utils.h"

ErrorStatus socketInit();

void socketCleanup();

Expand Down
4 changes: 2 additions & 2 deletions include/net77/logging.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <stdio.h>

#ifndef NET77_LOGGING_H
#define NET77_LOGGING_H

#include <stdio.h>

#ifdef NET77_ENABLE_LOGGING
#define LOG_MSG(msg, ...) {printf(msg, ##__VA_ARGS__); fflush(stdout);}
#else
Expand Down
6 changes: 3 additions & 3 deletions include/net77/mcfss.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* associated items. \n For example: \n
* ```c \n
* struct pollfds pfds[] = {pfd1, pfd2, pfd3}; \n
* int pfd_time_to_live[] = {99, 42, 69}; \n
* int pfd_time_to_live[] = {99, 42, 1337}; \n
* ``` \n
* You have two arrays where items at the same index i in those arrays belong together conceptually, e.g. 42 is the time
* to live of pfd2. With this type, you can add, remove and modify these pairs almost as if they were packed into a
Expand All @@ -34,13 +34,13 @@ MultiCategoryFixedSizeSet newMcfsSet(size_t cap, size_t *item_sizes, size_t n_ca

/**
* Adds to a set.
* returns: 0 if item was added or was already present, 1 if item couldn't be added because the capacity was already maxed out
* @return: 0 if item was added or was already present, 1 if item couldn't be added because the capacity was already maxed out
*/
int mcfsSetAdd(MultiCategoryFixedSizeSet *set, const char **items, size_t exist_check_category);

/**
* Removes from a set.
* returns: 0 if item was removed from set, 1 if it wasn't there in the first place
* @return: 0 if item was removed from set, 1 if it wasn't there in the first place
*/
int mcfsSetRemove(MultiCategoryFixedSizeSet *set, const char *item, size_t item_category);

Expand Down
9 changes: 5 additions & 4 deletions include/net77/net_includes.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
#include "net77/init.h"
#include "net77/int_includes.h"
#include "net77/string_utils.h"
#include "net77/error_utils.h"

int setSendRecvTimeout(size_t fd, ssize_t timeout_usec);
ErrorStatus setSendRecvTimeout(size_t fd, ssize_t timeout_usec);

/**
* @param socket_fd the socket file descriptor
Expand All @@ -15,7 +16,7 @@ int setSendRecvTimeout(size_t fd, ssize_t timeout_usec);
* @param timeout_usec the timeout in microseconds
* @return 0 on success, x != 0 on error: returns 1 on normal error and -1 on error that lead to the socket being closed.
*/
int sendAllData(size_t socket_fd, const char *buf, size_t len, ssize_t timeout_usec);
ErrorStatus sendAllData(size_t socket_fd, const char *buf, size_t len, ssize_t timeout_usec);

/**
* @param socket_fd the socket file descriptor
Expand All @@ -25,7 +26,7 @@ int sendAllData(size_t socket_fd, const char *buf, size_t len, ssize_t timeout_u
* @param out_bytes_received if not NULL, will be set to the number of bytes received
* @return 0 on success, x != 0 on error: returns 1 on normal error and -1 on error that lead to the socket being closed.
*/
int recvAllData(size_t socket_fd, char *buf, size_t len, ssize_t timeout_usec, size_t *out_bytes_received);
ErrorStatus recvAllData(size_t socket_fd, char *buf, size_t len, ssize_t timeout_usec, size_t *out_bytes_received);

/**
* @param socket_fd the socket file descriptor
Expand All @@ -38,7 +39,7 @@ int recvAllData(size_t socket_fd, char *buf, size_t len, ssize_t timeout_usec, s
* @return 0 on success, x != 0 on error: returns 1 on normal error and -1 on error that lead to the socket being closed.
* @note It is the callers responsibility to free the StringBuilder
*/
int recvAllDataSb(size_t socket_fd, StringBuilder *builder, ssize_t max_len, ssize_t timeout_usec, ssize_t sb_min_cap);
ErrorStatus recvAllDataSb(size_t socket_fd, StringBuilder *builder, ssize_t max_len, ssize_t timeout_usec, ssize_t sb_min_cap);

#if defined(_WIN32) || defined(_WIN64)
// Include Windows-specific headers
Expand Down
7 changes: 4 additions & 3 deletions include/net77/request.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@

#include "net77/serde.h"
#include "net77/int_includes.h"
#include "net77/error_utils.h"

int
ErrorStatus
request(const char *host, int port, Request *req, String *out, size_t max_response_size, ssize_t connect_timeout_usec,
ssize_t response_timeout_usec);

int rawRequest(const char *host, int port, StringRef req, String *out, size_t max_response_size,
ssize_t connect_timeout_usec, ssize_t response_timeout_usec);
ErrorStatus rawRequest(const char *host, int port, StringRef req, String *out, size_t max_response_size,
ssize_t connect_timeout_usec, ssize_t response_timeout_usec);

#endif
10 changes: 6 additions & 4 deletions include/net77/serde.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

#include <stddef.h>
#include "net77/string_utils.h"
#include "net77/error_utils.h"

typedef enum Version {
VERSION_HTTP09,
VERSION_HTTP10,
VERSION_HTTP11,
} Version;
Expand Down Expand Up @@ -66,13 +68,13 @@ typedef struct Response {

int isValidRequest(Request *req);

int parseRequest(StringRef str, Request *req, int parse_header_into_structs);
ErrorStatus parseRequest(StringRef str, Request *req, int parse_header_into_structs);

int parseResponse(StringRef str, Response *resp, int parse_header_into_structs);
ErrorStatus parseResponse(StringRef str, Response *resp, int parse_header_into_structs);

int serializeRequest(Request *req, StringBuilder *builder);
ErrorStatus serializeRequest(Request *req, StringBuilder *builder);

int serializeResponse(Response *resp, StringBuilder *builder);
ErrorStatus serializeResponse(Response *resp, StringBuilder *builder);

void freeRequest(Request *req);

Expand Down
54 changes: 30 additions & 24 deletions include/net77/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

#include <stddef.h>
#include "net77/thread_pool.h"
#include "net77/mcfss.h"
#include "net77/error_utils.h"

#define SERVER_BUF_SIZE (32768)
#define DEFAULT_SERVER_BUF_SIZE (32768)
#define DEFAULT_MAX_ACCEPTED_CONNECTIONS (16)

typedef struct ConnState {
Expand All @@ -22,42 +24,46 @@ typedef struct ServerHandlerArgs {
int heap_allocated;
} ServerHandlerArgs;

typedef void (*ServerHandler)(void *server_handler_args);

// TODO I could add a callback parameter that gets called with poll_ret (ie occupation data), number of connections, etc.
// which gets called every server main loop iteration. This could allow monitoring and all derivatives of that.
// TODO this function may accept a ton of requests from a single client and then block other clients from connecting
// the above could be fixed by taking in a callback that determines whether to accept a connection or not or
// by having a max number of requests that can be accepted from a single client. Probably the latter is better.
// TODO handle SIGPIPE and kill the connection if it is received... not quite sure how to implement that
// TODO conditional timeouts: the handler should be able to decide on a per-connection basis what the timeout for this
// connection should be. This is so that you can easily support keep-alive connections while still providing
// timeouts for other connections.
typedef void (*ServerHandler)(void *callback_args);

typedef void (*ServerWatcherCallback)(size_t n_open_conns, int new_conn_attempt, int poll_ret,
MultiCategoryFixedSizeSet *conn_states, void *callback_args);

/**
* helper function for hosting a tcp server
* @param callback_args some opaque data all callbacks are passed that may point to some state which is controlled by another thread
* @param thread_pool pointer to thread pool with handler that takes (some_opaque_data, socket_fd, sock_is_closed, data_received, data_received_size, conn_state)
* @param handler_data some opaque data the handler function is passed that may point to some state which is controlled by another thread
* @param host the host address to serve under, eg "127.0.0.1" (may also be IPv6). If NULL, then accepts requests from any local address (127.0.0.1, 192.168.0.1, 10.1.1.xy, ...)
* @param port the port to serve under
* @param max_concurrent_connections the maximum number of connections (sockets) that may simultaneously be open.
* If -1 is passed, use default of 16
* If x < 0 is passed, use default of 16. For unlimited, just pass 9999999 :)... \n WARNING: this parameter immediately
* pre-allocates an array of exactly that number of elements, so don't pass ridiculously high values.
* @param max_conns_per_ip the maximum number of connections that a single ip can maintain concurrently to the server.
* If x < 0 is passed, use default of 4. For unlimited, just pass 9999999 :)
* @param server_buf_size the init size of the buffer that the received data is written to before it is realloc-ed to expand.
* If -1 is passed, use default of 1kB
* If x < 0 is passed, use default of 1kB
* @param recv_timeout_usec after how many microseconds a recv times out and received data is processed
* @param max_request_size maximum size (in bytes) that the data of a request sent to the server must have before it is discarded and ignored. 0 means no limit.
* @param connection_timeout_usec after how many microseconds a request times out
* @param default_connection_timeout_usec after how many microseconds a request times out
* @param server_killed pointer to int. server terminates gracefully when the value pointed to turns non-zero.
* @param kill_ack set to one when the server has been killed, all conns have been closed and all buffers freed.
* @param enable_delaying_sockets if non-zero, nagle's algorithm will *not* be disabled. This may introduce latency.
* @param server_watcher_callback nullable callback (function) that gets called every server main loop iteration. May
* potentially intervene with the server directly by modifying connection states, so "watcher" here refers to either
* passive watching and monitoring of the server or active modification of connection states. It gets called after
* receiving all the new data from the connections and passing it to the handler thread pool, but before the connection
* timeout checks.
* @return err (0 means success)
*/
int runServer(ThreadPool *thread_pool, void *handler_data, const char *host, int port, int max_concurrent_connections,
int server_buf_size, ssize_t recv_timeout_usec, size_t max_request_size, ssize_t connection_timeout_usec,
const UnsafeSignal *server_killed, UnsafeSignal *kill_ack, int enable_delaying_sockets);

size_t launchServerOnThread(ThreadPool *thread_pool, void *handler_data, const char *host, int port,
int max_concurrent_connections, int server_buf_size, ssize_t recv_timeout_usec,
size_t max_request_size, ssize_t connection_timeout_usec, const UnsafeSignal *server_killed,
UnsafeSignal *kill_ack, int enable_delaying_sockets, UnsafeSignal *has_started_signal);
ErrorStatus runServer(void *callback_args, ThreadPool *thread_pool, const char *host, int port, int max_concurrent_connections,
int max_conns_per_ip, int server_buf_size, ssize_t recv_timeout_usec, size_t max_request_size,
ssize_t default_connection_timeout_usec, const UnsafeSignal *server_killed, UnsafeSignal *kill_ack,
int enable_delaying_sockets, ServerWatcherCallback watcher_callback);

size_t launchServerOnThread(void *callback_args, ThreadPool *thread_pool, const char *host, int port,
int max_concurrent_connections, int max_conns_per_ip, int server_buf_size,
ssize_t recv_timeout_usec, size_t max_request_size, ssize_t default_connection_timeout_usec,
const UnsafeSignal *server_killed, UnsafeSignal *kill_ack, int enable_delaying_sockets,
ServerWatcherCallback watcher_callback, UnsafeSignal *has_started_signal);

#endif
4 changes: 2 additions & 2 deletions include/net77/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#define NET77_SOCK_H

#include "net77/string_utils.h"
#include "net77/error_utils.h"

// TODO handle SIGPIPE and return an error if it is received (ec = -1)
// TODO I should return some more detailed error codes (eg to tell caller whether server failed to respond or client failed to connect, etc)
/**
* open a socket, connect to the host, sendAllData the data, receive response and close the socket
Expand All @@ -22,7 +22,7 @@
* returns an error
* @return err (0 means success)
*/
int newSocketSendReceiveClose(const char *host, int port, StringRef data, String *out, int client_buf_size,
ErrorStatus newSocketSendReceiveClose(const char *host, int port, StringRef data, String *out, int client_buf_size,
ssize_t server_connect_timeout_usec, ssize_t server_response_timeout_usec,
size_t max_response_size, ssize_t response_done_timeout_usec,
int enable_delaying_sockets);
Expand Down
2 changes: 2 additions & 0 deletions include/net77/string_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ String stringBuilderBuildAndDestroy(StringBuilder *builder);

void stringBuilderDestroy(StringBuilder *builder);

const char *findAsciiSubstringCaseInsensitive(const char *haystack, size_t haystack_len, const char *needle, size_t needle_len);

#endif
6 changes: 3 additions & 3 deletions include/net77/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ StringRef charPtrToStringRef(const char *data);
size_t getTimeInUSecs();

/// Cross-platform way to make a file descriptor non-delaying (deactivate Nagle's algorithm to reduce latency)
int setSocketNoDelay(size_t fd);
ErrorStatus setSocketNoDelay(size_t fd);

/// Cross-platform way to make a file descriptor non-blocking (recv and sendAllData will return immediately)
int makeSocketNonBlocking(size_t fd);
ErrorStatus makeSocketNonBlocking(size_t fd);

/// Cross-platform way to yield the CPU to another thread to avoid busy waiting
/// Cross-platform way to yield the CPU to another thread to avoid busy waiting. Does not guarantee yielding.
void schedYield();

#endif
5 changes: 3 additions & 2 deletions lib/init.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#include "net77/net_includes.h"
#include "net77/error_utils.h"

#if defined(_WIN32) || defined(_WIN64)
int socketInit() {
ErrorStatus socketInit() {
WSADATA wsaData;
return WSAStartup(MAKEWORD(2, 2), &wsaData);
}
Expand All @@ -11,7 +12,7 @@ void socketCleanup() {
}
#else

int socketInit() {
ErrorStatus socketInit() {
return 0;
}

Expand Down
Loading

0 comments on commit 9edd35e

Please sign in to comment.