From: Jann Horn Date: Fri, 21 Mar 2014 12:54:05 +0000 (+0100) Subject: more network stuff X-Git-Url: http://git.thejh.net/?p=libjh.git;a=commitdiff_plain;h=6d9303c441fe1d8b473d24f40421d70058e2d2e5;hp=64999b98e2c98ab92d60ac625fc3ea3a1ec630ec more network stuff --- diff --git a/bufchain.c b/bufchain.c new file mode 100644 index 0000000..55a1fed --- /dev/null +++ b/bufchain.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "bufio.h" + +int bufio_chain_append(bufio_chain *bc, void *buf, size_t len) { + bufio_chain_entry *e = calloc(1, sizeof(*e)); + if (e != NULL) return -1; + e->next = NULL; + e->buf = buf; + e->len = len; + if (bc->head != NULL) { + bc->tail->next = e; + } else { + bc->head = e; + } + bc->tail = e; + return 0; +} + +int bufio_chain_flush(bufio_chain *bc, int fd) { + while (bc->head != NULL) { + bufio_chain_entry *e = bc->head; + int res = write(fd, e->buf+e->used, e->len-e->used); + if (res < 0) return res; + assert(e->used == e->len || res != 0); + e->used += res; + if (e->used == e->len) { + bc->head = e->next; + free(e->buf); + free(e); + } + } + bc->tail = NULL; + return 0; +} + +void bufio_chain_clear(bufio_chain *bc) { + while (bc->head != NULL) { + bufio_chain_entry *e = bc->head; + free(e->buf); + bc->head = e->next; + free(e); + } + bc->tail = NULL; +} diff --git a/bufio.h b/bufio.h new file mode 100644 index 0000000..3ad0861 --- /dev/null +++ b/bufio.h @@ -0,0 +1,100 @@ +#ifndef _BUFIO_HEADER +#define _BUFIO_HEADER + +#include + +/* BUFFER CHAIN */ +typedef struct bufio_chain_entry bufio_chain_entry; + +struct bufio_chain_entry { + bufio_chain_entry *next; + void *buf; + size_t len; + off_t used; +}; + +typedef struct { + bufio_chain_entry *head, *tail; +} bufio_chain; + +/** + * Enqueues the given buffer for writing. After writing, it will be freed. + * Returns -1 if calloc failed. + */ +int bufio_chain_append(bufio_chain *bc, void *buf, size_t len); + +/** + * Flush as much data as possible into the given FD. + * Returns -1 for errors, 0 for ok. errno is left + * intact on errors, so you might want to check for + * EAGAIN or so. + */ +int bufio_chain_flush(bufio_chain *bc, int fd); + +/** + * Clear the buffer chain without sending any data. + */ +void bufio_chain_clear(bufio_chain *bc); + + + +/* CONNECTION */ +typedef struct bufio_connection bufio_connection; + +typedef void (*bufio_connection_error_cb_t)(bufio_connection *con); +typedef void (*bufio_connection_data_cb_t)(bufio_connection *con); + +struct bufio_connection { + ev_io rw; /* readonly */ + ev_io ww; /* readonly */ + struct ev_loop *loop; /* readonly */ + void *inbuf; /* readonly */ + size_t inbuf_size; /* readonly */ + size_t inbuf_used; /* readonly */ + bufio_chain outbuf; /* readonly */ + + /** + * Called when an error has occured. You should react to it by destroying + * the connection. This property can be changed manually at any time. + */ + bufio_connection_error_cb_t err_cb; + + /** + * Called when your read buffer has been filled completely. When this is + * called, nothing will be read anymore until you set a new read buffer. + * This property can be changed manually at any time. + */ + bufio_connection_data_cb_t data_cb; + + /** + * Not touched by this library. + */ + void *data; +}; + +/** + * Creates a bufio connection around an existing file descriptor. + * This call already activates the watcher without initializing some + * important stuff, so set it before returning to the eventloop/entering + * it: + * + * - the input buffer – set it using `bufio_connection_set_read_buffer` + * - the callbacks (`err_cb` and `data_cb`) – set them directly + */ +bufio_connection *bufio_connection_create(struct ev_loop *loop, int fd); + +void bufio_connection_set_read_buffer(bufio_connection *con, void *buf, size_t size); + +/** + * This stops our watcher and clears and frees the output buffers and the + * connection struct, but it doesn't touch the input buffer or the fd. + */ +void bufio_connection_destroy(bufio_connection *con); + +/** + * This enqueues a chunk of data to be written. You are not allowed to + * touch the buffer anymore after calling this function! + */ +int bufio_connection_write(bufio_connection *con, void *buf, size_t len); + +#endif diff --git a/compile.sh b/compile.sh index 4558464..6e3b1c6 100755 --- a/compile.sh +++ b/compile.sh @@ -11,7 +11,7 @@ set -f -u -e -o pipefail # flags for the build - adjust for your needs # delete all the generated stuff afterwards (with `rm -r gen`) CC='gcc' -CFLAGS='-O3 -Wall -Werror -fPIC -std=c99 -march=native' +CFLAGS='-O3 -Wall -Werror -Wno-error=strict-aliasing -fPIC -std=c99 -march=native' # create build environment if it doesn't exist yet mkdir -p gen # contains all generated files @@ -24,7 +24,9 @@ echo "welcome. your friendly compiler will be \"$CC\" today." >&2 echo "going ahead with CFLAGS=\"$CFLAGS\"..." >&2 # generate header -cat header.h > gen/jh.h +set +f +cat *.h > gen/jh.h +set -f for source_file in $(ls|grep '\.c$'); do echo "extracting header data from $source_file..." >&2 source_name="$(sed 's|\.c$||' <<< "$source_file")" diff --git a/header.h b/header.h index faf6756..957788f 100644 --- a/header.h +++ b/header.h @@ -1,3 +1,5 @@ +#include + /***** COMPILER-SPECIFIC STUFF *****/ #ifdef __GNUC__ #define JH_ATTR_NORETURN __attribute__ ((noreturn)) diff --git a/net.c b/net.c index a5d5b50..8fe5980 100644 --- a/net.c +++ b/net.c @@ -79,3 +79,24 @@ err_socket: freeaddrinfo(addrs); return EAI_SYSTEM; } + +/********************** SERVER SOCKET STUFF **********************/ +HEADER typedef void (*jh_ev_io_ssock_cb)(EV_P_ ev_io *w, int socket); +static void ev_io_ssock_cb(EV_P_ ev_io *w, int revents) { + jh_ev_io_ssock_cb cb = (jh_ev_io_ssock_cb)w->data; + if (revents == EV_ERROR) { + cb(EV_A_ w, -1); + return; + } + int fd = accept(w->fd, NULL, NULL); + if (fd == -1) { + fprintf(stderr, "\nLIBJH ERROR: ACCEPT() FAILED - SLEEPING 1s\n"); + sleep(1); + return; + } + cb(EV_A_ w, fd); +} +PUBLIC_FN void jh_ev_io_ssock_init(ev_io *w, jh_ev_io_ssock_cb cb, int fd) { + ev_io_init(w, ev_io_ssock_cb, fd, EV_READ); + w->data = cb; +} diff --git a/netconn.c b/netconn.c new file mode 100644 index 0000000..f4c78d6 --- /dev/null +++ b/netconn.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "bufio.h" + +static void connection_waiting_cb(struct ev_loop *loop, ev_io *w, int revents) { + bufio_connection *con = (bufio_connection *)w->data; + if (revents & EV_READ) { + assert(con->inbuf_used != con->inbuf_size); + int res = read(w->fd, con->inbuf+con->inbuf_used, con->inbuf_size-con->inbuf_used); + if (res == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + con->err_cb(con); + } + return; + } + if (res == 0) { + con->err_cb(con); + return; + } + con->inbuf_used += res; + if (con->inbuf_size == con->inbuf_used) { + // The buffer is filled, so stop reading data until we have a new one. + ev_io_stop(loop, w); + + // Call the callback AFTERWARDS (after it has finished executing, we might + // already have a new read buffer). + con->data_cb(con); + } + } + + if (revents & EV_WRITE) { + if (bufio_chain_flush(&con->outbuf, w->fd) == 0) { + ev_io_stop(loop, w); + } + } +} + +bufio_connection *bufio_connection_create(struct ev_loop *loop, int fd) { + bufio_connection *con = calloc(1, sizeof(*con)); + if (con == NULL) return NULL; + con->loop = loop; + ev_io_init(&con->rw, connection_waiting_cb, fd, EV_READ); + ev_io_init(&con->ww, connection_waiting_cb, fd, EV_WRITE); + con->rw.data = con; + con->ww.data = con; + return con; +} + +void bufio_connection_set_read_buffer(bufio_connection *con, void *buf, size_t size) { + assert(size > 0); + con->inbuf = buf; + con->inbuf_size = size; + con->inbuf_used = 0; + + ev_io_start(con->loop, &con->rw); +} + +void bufio_connection_destroy(bufio_connection *con) { + bufio_chain_clear(&con->outbuf); + ev_io_stop(con->loop, &con->rw); + ev_io_stop(con->loop, &con->ww); + free(con); +} + +int bufio_connection_write(bufio_connection *con, void *buf, size_t len) { + int res; + bool chain_was_empty = (con->outbuf.head == NULL); + res = bufio_chain_append(&con->outbuf, buf, len); + if (res == -1) return -1; + if (chain_was_empty) { + if (bufio_chain_flush(&con->outbuf, con->ww.fd)) { + ev_io_start(con->loop, &con->ww); + } + } + return 0; +}