more network stuff
[libjh.git] / netconn.c
1 #include <sys/types.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <unistd.h>
5 #include <stdbool.h>
6 #include <assert.h>
7 #include <ev.h>
8
9 #include "bufio.h"
10
11 static void connection_waiting_cb(struct ev_loop *loop, ev_io *w, int revents) {
12   bufio_connection *con = (bufio_connection *)w->data;
13   if (revents & EV_READ) {
14     assert(con->inbuf_used != con->inbuf_size);
15     int res = read(w->fd, con->inbuf+con->inbuf_used, con->inbuf_size-con->inbuf_used);
16     if (res == -1) {
17       if (errno != EAGAIN && errno != EWOULDBLOCK) {
18         con->err_cb(con);
19       }
20       return;
21     }
22     if (res == 0) {
23       con->err_cb(con);
24       return;
25     }
26     con->inbuf_used += res;
27     if (con->inbuf_size == con->inbuf_used) {
28       // The buffer is filled, so stop reading data until we have a new one.
29       ev_io_stop(loop, w);
30
31       // Call the callback AFTERWARDS (after it has finished executing, we might
32       // already have a new read buffer).
33       con->data_cb(con);
34     }
35   }
36
37   if (revents & EV_WRITE) {
38     if (bufio_chain_flush(&con->outbuf, w->fd) == 0) {
39       ev_io_stop(loop, w);
40     }
41   }
42 }
43
44 bufio_connection *bufio_connection_create(struct ev_loop *loop, int fd) {
45   bufio_connection *con = calloc(1, sizeof(*con));
46   if (con == NULL) return NULL;
47   con->loop = loop;
48   ev_io_init(&con->rw, connection_waiting_cb, fd, EV_READ);
49   ev_io_init(&con->ww, connection_waiting_cb, fd, EV_WRITE);
50   con->rw.data = con;
51   con->ww.data = con;
52   return con;
53 }
54
55 void bufio_connection_set_read_buffer(bufio_connection *con, void *buf, size_t size) {
56   assert(size > 0);
57   con->inbuf = buf;
58   con->inbuf_size = size;
59   con->inbuf_used = 0;
60
61   ev_io_start(con->loop, &con->rw);
62 }
63
64 void bufio_connection_destroy(bufio_connection *con) {
65   bufio_chain_clear(&con->outbuf);
66   ev_io_stop(con->loop, &con->rw);
67   ev_io_stop(con->loop, &con->ww);
68   free(con);
69 }
70
71 int bufio_connection_write(bufio_connection *con, void *buf, size_t len) {
72   int res;
73   bool chain_was_empty = (con->outbuf.head == NULL);
74   res = bufio_chain_append(&con->outbuf, buf, len);
75   if (res == -1) return -1;
76   if (chain_was_empty) {
77     if (bufio_chain_flush(&con->outbuf, con->ww.fd)) {
78       ev_io_start(con->loop, &con->ww);
79     }
80   }
81   return 0;
82 }