void evcom_reader_close (evcom_reader *reader) { ev_io_start(D_LOOP_(reader) &reader->read_watcher); ev_feed_event(D_LOOP_(reader) &reader->read_watcher, EV_READ); close_asap((evcom_descriptor*)reader); }
/** * Stops the server. Will not accept new connections. Does not drop * existing connections. */ void evcom_server_close (evcom_server *server) { ev_io_start(D_LOOP_(server) &server->watcher); ev_feed_event(D_LOOP_(server) &server->watcher, EV_READ); close_asap((evcom_descriptor*)server); }
void evcom_stream_detach (evcom_stream *stream) { ev_io_stop(D_LOOP_(stream) &stream->write_watcher); ev_io_stop(D_LOOP_(stream) &stream->read_watcher); ev_timer_stop(D_LOOP_(stream) &stream->timeout_watcher); D_LOOP_SET(stream, NULL); stream->flags &= ~EVCOM_ATTACHED; }
void evcom_stream_read_pause (evcom_stream *stream) { stream->flags |= EVCOM_PAUSED; ev_timer_stop(D_LOOP_(stream) &stream->timeout_watcher); if (stream->recv_action == stream_recv__data) { ev_io_stop(D_LOOP_(stream) &stream->read_watcher); stream->recv_action = stream_recv__wait_for_resume; } }
void evcom_stream_read_resume (evcom_stream *stream) { stream->flags &= ~EVCOM_PAUSED; ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); if (stream->recv_action == stream_recv__wait_for_resume) { stream->recv_action = stream_recv__data; } if (ATTACHED(stream)) { ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); if (READABLE(stream)) ev_io_start(D_LOOP_(stream) &stream->read_watcher); } }
static int close_writer_asap (evcom_writer *writer) { release_write_buffer(writer); ev_feed_event(D_LOOP_(writer) &writer->write_watcher, EV_WRITE); return close_asap((evcom_descriptor*)writer); }
void evcom_server_detach (evcom_server *server) { ev_io_stop (D_LOOP_(server) &server->watcher); D_LOOP_SET(server, NULL); server->flags &= ~EVCOM_ATTACHED; }
/* Internal callback. called by stream->timeout_watcher */ static void on_timeout (EV_P_ ev_timer *watcher, int revents) { evcom_stream *stream = watcher->data; #if EV_MULTIPLICITY assert(stream->loop == loop); #endif assert(revents == EV_TIMEOUT); assert(watcher == &stream->timeout_watcher); if (PAUSED(stream)) { ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); return; } if (stream->on_timeout) stream->on_timeout(stream); // Hack to get error in Node on 'close' event. // should probably be made into a proper error code. stream->errorno = 1; ev_timer_stop(EV_A_ watcher); evcom_stream_force_close(stream); if (stream->on_close) stream->on_close(stream); }
static inline void stream__set_recv_closed (evcom_stream *stream) { stream->flags &= ~EVCOM_READABLE; stream->recvfd = -1; stream->recv_action = NULL; ev_io_stop(D_LOOP_(stream) &stream->read_watcher); }
static int stream_recv__wait_for_resume (evcom_stream *stream) { stream->flags |= EVCOM_PAUSED; ev_io_stop(D_LOOP_(stream) &stream->read_watcher); assert(stream->recv_action == stream_recv__wait_for_resume); return AGAIN; }
void evcom_stream_reset_timeout (evcom_stream *stream, float timeout) { stream->timeout_watcher.repeat = timeout; if (ATTACHED(stream)) { ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); } }
static inline void stream__set_send_closed (evcom_stream *stream) { release_write_buffer(stream); stream->flags &= ~EVCOM_WRITABLE; stream->sendfd = -1; stream->send_action = NULL; ev_io_stop(D_LOOP_(stream) &stream->write_watcher); }
void evcom_stream_close (evcom_stream *stream) { stream->flags |= EVCOM_GOT_CLOSE; if (ATTACHED(stream)) { // start the watchers if attached. evcom_stream_attach(D_LOOP_(stream) stream); } }
static int stream_send__wait_for_eof (evcom_stream *stream) { if (READABLE(stream)) { ev_io_stop(D_LOOP_(stream) &stream->write_watcher); assert(stream->send_action == stream_send__wait_for_eof); return AGAIN; } stream->send_action = stream_send__close_one; return OKAY; }
static int stream_recv__wait_for_close (evcom_stream *stream) { assert(!READABLE(stream)); if (!WRITABLE(stream)) { stream->recv_action = stream_recv__close; return OKAY; } ev_io_stop(D_LOOP_(stream) &stream->read_watcher); return AGAIN; }
/* Internal callback * Called by server->watcher. */ static int accept_connections (evcom_descriptor *d) { evcom_server *server = (evcom_server *)d; assert(LISTENING(server)); /* accept as many connections as possible */ evcom_stream *stream; while (server->fd >= 0 && (stream = accept_connection(server))) { evcom_stream_attach(D_LOOP_(server) stream); } return AGAIN; }
static int stream__connection_established (evcom_stream *stream) { assert(!CONNECTED(stream)); #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { stream->send_action = stream__handshake; stream->recv_action = stream__handshake; } else #endif { stream->flags |= EVCOM_CONNECTED; if (stream->on_connect) stream->on_connect(stream); stream->send_action = stream_send__data; stream->recv_action = stream_recv__data; } ev_io_start(D_LOOP_(stream) &stream->write_watcher); ev_io_start(D_LOOP_(stream) &stream->read_watcher); return OKAY; }
static int stream_send__wait_for_buf (evcom_stream *stream) { if (evcom_queue_empty(&stream->out)) { if (GOT_CLOSE(stream)) { stream->send_action = stream_send__drain; return OKAY; } ev_io_stop(D_LOOP_(stream) &stream->write_watcher); return AGAIN; } stream->send_action = stream_send__data; return OKAY; }
static int writer_send (evcom_descriptor *d) { evcom_writer* writer = (evcom_writer*) d; assert(writer->fd >= 0); while (!evcom_queue_empty(&writer->out)) { evcom_queue *q = evcom_queue_last(&writer->out); evcom_buf *buf = evcom_queue_data(q, evcom_buf, queue); ssize_t sent = write(writer->fd, buf->base + buf->written, buf->len - buf->written); if (sent < 0) { switch (errno) { case ECONNRESET: case EPIPE: return close_writer_asap(writer); case EINTR: case EAGAIN: sent = 0; return AGAIN; default: writer->errorno = errno; evcom_perror("send()", writer->errorno); return close_writer_asap(writer); } } assert(sent >= 0); buf->written += sent; if (buf->written == buf->len) { evcom_queue_remove(q); if (buf->release) buf->release(buf); } } if (GOT_CLOSE(writer)) { assert(evcom_queue_empty(&writer->out)); return close_writer_asap(writer); } else { ev_io_stop(D_LOOP_(writer) &writer->write_watcher); return AGAIN; } }
void evcom_writer_write (evcom_writer* writer, const char* buf, size_t len) { assert(writer->fd >= 0); ssize_t sent = 0; if (evcom_queue_empty(&writer->out)) { sent = write(writer->fd, buf, len); if (sent < 0) { switch (errno) { case ECONNRESET: case EPIPE: goto close; case EINTR: case EAGAIN: sent = 0; break; default: writer->errorno = errno; evcom_perror("send()", writer->errorno); goto close; } } } /* TODO else { memcpy to last buffer on head } */ assert(sent >= 0); if ((size_t)sent == len) return; /* sent the whole buffer */ len -= sent; buf += sent; evcom_buf *b = evcom_buf_new(buf, len); evcom_queue_insert_head(&writer->out, &b->queue); b->written = 0; assert(writer->fd >= 0); ev_io_start(D_LOOP_(writer) &writer->write_watcher); return; close: close_writer_asap(writer); }
static int stream__handshake (evcom_stream *stream) { assert(SECURE(stream)); int r = gnutls_handshake(stream->session); if (gnutls_error_is_fatal(r)) { stream->gnutls_errorno = r; stream->send_action = stream_send__close; stream->recv_action = stream_recv__close; return OKAY; } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); if (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) { if (0 == gnutls_record_get_direction((stream)->session)) { ev_io_start(D_LOOP_(stream) &(stream)->read_watcher); ev_io_stop(D_LOOP_(stream) &(stream)->write_watcher); } else { ev_io_stop(D_LOOP_(stream) &(stream)->read_watcher); ev_io_start(D_LOOP_(stream) &(stream)->write_watcher); } assert(stream->recv_action == stream__handshake); assert(stream->send_action == stream__handshake); return AGAIN; } assert(!CONNECTED(stream)); stream->flags |= EVCOM_CONNECTED; if (stream->on_connect) stream->on_connect(stream); /* evcom_stream_force_close might have been called. */ if (stream->recvfd >= 0 && stream->sendfd >= 0) { ev_io_start(D_LOOP_(stream) &stream->read_watcher); ev_io_start(D_LOOP_(stream) &stream->write_watcher); stream->send_action = stream_send__data; stream->recv_action = stream_recv__data; } return OKAY; }
/* Internal callback. called by stream->timeout_watcher */ static void on_timeout (EV_P_ ev_timer *watcher, int revents) { evcom_stream *stream = watcher->data; #if EV_MULTIPLICITY assert(stream->loop == loop); #endif assert(revents == EV_TIMEOUT); assert(watcher == &stream->timeout_watcher); if (PAUSED(stream)) { ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); return; } if (stream->on_timeout) stream->on_timeout(stream); evcom_stream_force_close(stream); if (stream->on_close) stream->on_close(stream); }
/* Returns the number of bytes flushed to the buffer */ ssize_t evcom_stream_write (evcom_stream *stream, const char *str, size_t len) { if (!WRITABLE(stream) || GOT_CLOSE(stream)) { assert(0 && "Do not write to a closed stream"); return -1; } ssize_t sent = 0; if ( stream->send_action == stream_send__wait_for_buf && evcom_queue_empty(&stream->out) ) { assert(CONNECTED(stream)); #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { sent = gnutls_record_send(stream->session, str, len); if (gnutls_error_is_fatal(sent)) { stream->gnutls_errorno = sent; goto close; } } else #endif // EVCOM_HAVE_GNUTLS { /* TODO use writev() here? */ sent = nosigpipe_stream_send(stream, str, len); } if (sent < 0) { switch (errno) { case EPIPE: goto close; case EINTR: case EAGAIN: sent = 0; break; default: stream->errorno = errno; evcom_perror("send()", stream->errorno); goto close; } } } /* TODO else { memcpy to last buffer on head } */ assert(sent >= 0); if ((size_t)sent == len) return sent; /* sent the whole buffer */ len -= sent; str += sent; evcom_buf *b = evcom_buf_new(str, len); evcom_queue_insert_head(&stream->out, &b->queue); b->written = 0; assert(stream->sendfd >= 0); if (ATTACHED(stream)) { ev_io_start(D_LOOP_(stream) &stream->write_watcher); } return sent; close: stream->send_action = stream_send__close; stream->recv_action = stream_recv__close; if (ATTACHED(stream)) { ev_io_start(D_LOOP_(stream) &stream->write_watcher); } return -1; }
static int stream_send__data (evcom_stream *stream) { ssize_t sent; while (!evcom_queue_empty(&stream->out)) { assert(WRITABLE(stream)); evcom_queue *q = evcom_queue_last(&stream->out); evcom_buf *buf = evcom_queue_data(q, evcom_buf, queue); #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { sent = gnutls_record_send(stream->session, buf->base + buf->written, buf->len - buf->written); if (sent == GNUTLS_E_INTERRUPTED || sent == GNUTLS_E_AGAIN) { if (0 == gnutls_record_get_direction((stream)->session)) { fprintf(stderr, "(evcom) gnutls send: unexpected switch direction!\n"); ev_io_start(D_LOOP_(stream) &(stream)->read_watcher); ev_io_stop(D_LOOP_(stream) &(stream)->write_watcher); } return AGAIN; } if (gnutls_error_is_fatal(sent)) { stream->gnutls_errorno = sent; stream->send_action = stream_send__close; return OKAY; } } else #endif // EVCOM_HAVE_GNUTLS { /* TODO use writev() here? */ sent = nosigpipe_stream_send(stream, buf->base + buf->written, buf->len - buf->written); } if (sent <= 0) { switch (errno) { case EAGAIN: case EINTR: assert(stream->send_action == stream_send__data); return AGAIN; default: stream->errorno = errno; evcom_perror("send()", errno); /* pass through */ case EPIPE: stream->send_action = stream_send__close; return OKAY; } } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); assert(sent >= 0); buf->written += sent; if (buf->written == buf->len) { evcom_queue_remove(q); if (buf->release) buf->release(buf); } } assert(evcom_queue_empty(&stream->out)); stream->send_action = stream_send__drain; return OKAY; }
static int stream_recv__data (evcom_stream *stream) { char buf[EVCOM_CHUNKSIZE]; size_t buf_size = EVCOM_CHUNKSIZE; ssize_t recved; while (READABLE(stream)) { assert(CONNECTED(stream)); if (PAUSED(stream)) { stream->recv_action = stream_recv__wait_for_resume; return OKAY; } #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { recved = gnutls_record_recv(stream->session, buf, buf_size); if (gnutls_error_is_fatal(recved)) { stream->gnutls_errorno = recved; stream->recv_action = stream_recv__close; return OKAY; } if (recved == GNUTLS_E_INTERRUPTED || recved == GNUTLS_E_AGAIN) { if (1 == gnutls_record_get_direction((stream)->session)) { fprintf(stderr, "(evcom) gnutls recv: unexpected switch direction!\n"); ev_io_stop(D_LOOP_(stream) &(stream)->read_watcher); ev_io_start(D_LOOP_(stream) &(stream)->write_watcher); } return AGAIN; } /* A server may also receive GNUTLS_E_REHANDSHAKE when a client has * initiated a andshake. In that case the server can only initiate a * handshake or terminate the connection. */ if (recved == GNUTLS_E_REHANDSHAKE) { assert(WRITABLE(stream)); stream->recv_action = stream__handshake; stream->send_action = stream__handshake; return OKAY; } } else #endif /* EVCOM_HAVE_GNUTLS */ { recved = read(stream->recvfd, buf, buf_size); } if (recved < 0) { if (errno == EAGAIN || errno == EINTR) { assert(stream->recv_action == stream_recv__data); return AGAIN; } if (errno != ECONNRESET) { evcom_perror("recv()", stream->errorno); } stream->errorno = errno; stream->recv_action = stream_recv__close; return OKAY; } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); assert(recved >= 0); if (recved == 0) { stream->flags &= ~EVCOM_READABLE; ev_io_stop(D_LOOP_(stream) &stream->read_watcher); stream->recv_action = stream_recv__wait_for_close; } /* NOTE: EOF is signaled with recved == 0 on callback */ if (stream->on_read) stream->on_read(stream, buf, recved); if (recved == 0) { return OKAY; } } return AGAIN; }
void evcom_reader_detach (evcom_reader *reader) { ev_io_stop(D_LOOP_(reader) &reader->read_watcher); D_LOOP_SET(reader, NULL); }
void evcom_writer_detach (evcom_writer* writer) { ev_io_stop(D_LOOP_(writer) &writer->write_watcher); D_LOOP_SET(writer, NULL); }