more network stuff
authorJann Horn <jann@thejh.net>
Fri, 21 Mar 2014 12:54:05 +0000 (13:54 +0100)
committerJann Horn <jann@thejh.net>
Fri, 21 Mar 2014 12:54:05 +0000 (13:54 +0100)
bufchain.c [new file with mode: 0644]
bufio.h [new file with mode: 0644]
compile.sh
header.h
net.c
netconn.c [new file with mode: 0644]

diff --git a/bufchain.c b/bufchain.c
new file mode 100644 (file)
index 0000000..55a1fed
--- /dev/null
@@ -0,0 +1,51 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <ev.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..3ad0861
--- /dev/null
+++ b/bufio.h
@@ -0,0 +1,100 @@
+#ifndef _BUFIO_HEADER
+#define _BUFIO_HEADER
+
+#include <ev.h>
+
+/* 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
index 4558464..6e3b1c6 100755 (executable)
@@ -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'
 # 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
 
 # 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
 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")"
 for source_file in $(ls|grep '\.c$'); do
   echo "extracting header data from $source_file..." >&2
   source_name="$(sed 's|\.c$||' <<< "$source_file")"
index faf6756..957788f 100644 (file)
--- a/header.h
+++ b/header.h
@@ -1,3 +1,5 @@
+#include <ev.h>
+
 /***** COMPILER-SPECIFIC STUFF *****/
 #ifdef __GNUC__
   #define JH_ATTR_NORETURN __attribute__ ((noreturn))
 /***** COMPILER-SPECIFIC STUFF *****/
 #ifdef __GNUC__
   #define JH_ATTR_NORETURN __attribute__ ((noreturn))
diff --git a/net.c b/net.c
index a5d5b50..8fe5980 100644 (file)
--- a/net.c
+++ b/net.c
@@ -79,3 +79,24 @@ err_socket:
   freeaddrinfo(addrs);
   return EAI_SYSTEM;
 }
   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 (file)
index 0000000..f4c78d6
--- /dev/null
+++ b/netconn.c
@@ -0,0 +1,82 @@
+#include <sys/types.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <ev.h>
+
+#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;
+}