/* Copyright (c) 2008,2009 Ryan Dahl * * evcom_queue comes from Nginx, ngx_queue.h * Copyright (C) 2002-2009 Igor Sysoev * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include /* offsetof() */ #ifndef evcom_h #define evcom_h #ifdef __cplusplus extern "C" { #endif #ifndef EVCOM_HAVE_GNUTLS # define EVCOM_HAVE_GNUTLS 0 #endif #if EVCOM_HAVE_GNUTLS # include #endif /* The maximum evcom_stream will try to read in one callback */ #ifndef EVCOM_CHUNKSIZE # define EVCOM_CHUNKSIZE (8*1024) #endif /* flags for stream and server */ #define EVCOM_ATTACHED 0x0001 #define EVCOM_LISTENING 0x0002 #define EVCOM_CONNECTED 0x0004 #define EVCOM_SECURE 0x0008 #define EVCOM_GOT_HALF_CLOSE 0x0010 #define EVCOM_GOT_FULL_CLOSE 0x0020 #define EVCOM_PAUSED 0x0040 #define EVCOM_READABLE 0x0080 #define EVCOM_WRITABLE 0x0100 #define EVCOM_GOT_WRITE_EVENT 0x0200 enum evcom_stream_state { EVCOM_INITIALIZED , EVCOM_CONNECTING , EVCOM_CONNECTED_RW /* read write */ , EVCOM_CONNECTED_RO /* read only */ , EVCOM_CONNECTED_WO /* write only */ , EVCOM_CLOSING , EVCOM_CLOSED }; typedef struct evcom_queue { struct evcom_queue *prev; struct evcom_queue *next; } evcom_queue; typedef struct evcom_buf { /* public */ char *base; size_t len; void (*release) (struct evcom_buf *); /* called when oi is done with the object */ void *data; /* private */ size_t written; evcom_queue queue; } evcom_buf; #if EV_MULTIPLICITY # define EVCOM_LOOP struct ev_loop *loop; #else # define EVCOM_LOOP #endif #define EVCOM_DESCRIPTOR(type) \ unsigned int flags; /* private */ \ int (*action) (struct evcom_descriptor*); /* private */ \ int errorno; /* read-only */ \ int fd; /* read-only */ \ EVCOM_LOOP /* read-only */ \ void *data; /* public */ \ void (*on_close) (struct type*); /* public */ typedef struct evcom_descriptor { EVCOM_DESCRIPTOR(evcom_descriptor) } evcom_descriptor; typedef struct evcom_server { EVCOM_DESCRIPTOR(evcom_server) /* PRIVATE */ ev_io watcher; /* PUBLIC */ struct evcom_stream* (*on_connection)(struct evcom_server *, struct sockaddr *remote_addr); } evcom_server; typedef struct evcom_stream { EVCOM_DESCRIPTOR(evcom_stream) /* PRIVATE */ ev_io write_watcher; ev_io read_watcher; ev_timer timeout_watcher; #if EVCOM_HAVE_GNUTLS gnutls_session_t session; #endif /* READ-ONLY */ struct evcom_server *server; evcom_queue out; #if EVCOM_HAVE_GNUTLS int gnutls_errorno; #endif /* PUBLIC */ void (*on_connect) (struct evcom_stream *); void (*on_read) (struct evcom_stream *, const void *buf, size_t count); void (*on_drain) (struct evcom_stream *); void (*on_timeout) (struct evcom_stream *); } evcom_stream; void evcom_server_init (evcom_server *); int evcom_server_listen (evcom_server *, struct sockaddr *address, int backlog); void evcom_server_attach (EV_P_ evcom_server *); void evcom_server_detach (evcom_server *); void evcom_server_close (evcom_server *); // synchronous void evcom_stream_init (evcom_stream *, float timeout); int evcom_stream_connect (evcom_stream *, struct sockaddr *address); void evcom_stream_attach (EV_P_ evcom_stream *); void evcom_stream_detach (evcom_stream *); void evcom_stream_read_resume (evcom_stream *); void evcom_stream_read_pause (evcom_stream *); /* Resets the timeout to stay alive for another stream->timeout seconds */ void evcom_stream_reset_timeout (evcom_stream *); /* Writes a buffer to the stream. */ void evcom_stream_write (evcom_stream *, evcom_buf *); void evcom_stream_write_simple (evcom_stream *, const char *str, size_t len); /* Once the write buffer is drained, evcom_stream_close will shutdown the * writing end of the stream and will close the read end once the server * replies with an EOF. */ void evcom_stream_close (evcom_stream *); /* Do not wait for the server to reply with EOF. This will only be called * once the write buffer is drained. * Warning: For TCP stream, the OS kernel may (should) reply with RST * packets if this is called when data is still being received from the * server. */ void evcom_stream_full_close (evcom_stream *); /* The most extreme measure. * Will not wait for the write queue to complete. */ void evcom_stream_force_close (evcom_stream *); #if EVCOM_HAVE_GNUTLS /* Tells the stream to use transport layer security (SSL). evcom_stream does * not want to make any decisions about security requirements, so the * majoirty of GnuTLS configuration is left to the user. Only the transport * layer of GnuTLS is controlled by evcom_stream. That is, do not use * gnutls_transport_* functions. Do use the rest of GnuTLS's API. */ void evcom_stream_set_secure_session (evcom_stream *, gnutls_session_t); #endif enum evcom_stream_state evcom_stream_state (evcom_stream *stream); evcom_buf * evcom_buf_new (const char* base, size_t len); evcom_buf * evcom_buf_new2 (size_t len); void evcom_buf_destroy (evcom_buf *); EV_INLINE void evcom_queue_init (evcom_queue *q) { q->prev = q; q->next = q; } EV_INLINE void evcom_queue_insert_head (evcom_queue *h, evcom_queue *x) { (x)->next = (h)->next; (x)->next->prev = x; (x)->prev = h; (h)->next = x; } EV_INLINE void evcom_queue_remove (evcom_queue *x) { (x)->next->prev = (x)->prev; (x)->prev->next = (x)->next; #ifndef NDEBUG (x)->prev = NULL; (x)->next = NULL; #endif } #define evcom_queue_empty(h) (h == (h)->prev) #define evcom_queue_head(h) (h)->next #define evcom_queue_last(h) (h)->prev #define evcom_queue_data(q, type, link) \ (type *) ((unsigned char *) q - offsetof(type, link)) #ifdef __cplusplus } #endif #endif /* evcom_h */