Created
October 30, 2009 05:48
-
-
Save ry/222164 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright (c) 2009 Ryan Dahl <ry@tinyclouds.org> | |
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. | |
*/ | |
#ifndef EVC_H_ | |
#define EVC_H_ | |
/* Compile with HAVE_GNUTLS=1 to use GnuTLS encryption. */ | |
#ifndef HAVE_GNUTLS | |
# define HAVE_GNUTLS 0 | |
#endif | |
#if HAVE_GNUTLS | |
# include <gnutls/gnutls.h> | |
#endif | |
enum evc_error | |
/* No error. */ | |
{ EVC_ERROR_OK = 0 | |
/* A system error occurred. May indicate a bug in EVC. */ | |
, EVC_ERROR_SYS | |
/* Connection reset by peer. */ | |
, EVC_ERROR_CONNRESET | |
/* For Unix domain streams, permission denied. */ | |
, EVC_ERROR_ACCESS | |
/* GnuTLS error. Use evc_stream_tls_error() to access the error code. */ | |
, EVC_ERROR_TLS | |
}; | |
enum evc_transport_status | |
{ EVC_TRANSPORT_NEEDS_READ = 0x0001 | |
, EVC_TRANSPORT_NEEDS_WRITE = 0x0002 | |
, EVC_TRANSPORT_DONE = 0x0004 | |
}; | |
/* | |
An evc_transport has many evc_streams and evc_servers. It provides | |
efficient idle-timeouts for streams as well as a callback for interfacing | |
with the event-loop. | |
*/ | |
struct evc_transport; | |
typedef struct evc_transport evc_transport; | |
/* | |
One of these callbacks is accoiated with each evc_transport. | |
- 'data' is pointer given as the last argument to evc_transport_new() | |
- 'status' has one of the following values: | |
EVC_TRANSPORT_NEEDS_READ | |
EVC_TRANSPORT_NEEDS_WRITE | |
EVC_TRANSPORT_NEEDS_READ | EVC_TRANSPORT_NEEDS_WRITE | |
EVC_TRANSPORT_DONE | |
- of the last two arguments, only one will be filled, but not both. | |
The user needs to wait for the obj1 or obj2 to become either readable or | |
writeable before continuing. | |
*/ | |
typedef void (*evc_transport_cb)(void *data, | |
int status, | |
evc_server * server, | |
evc_stream * stream); | |
/* | |
Create a new transport. | |
The first argument is the timeout. The timeout argument specifies the idle | |
timeout for all evc_streams in the transport, in seconds. A non-positive | |
value for timeout will disable idle timeouts in this transport. | |
The second argument is a callback. The third is a pointer which will be | |
passed to the callback. | |
*/ | |
evc_transport * evc_transport_new (double timeout, | |
evc_transport_cb cb, | |
void * data); | |
/* | |
Processes timeouts for evc_streams, invoking EVC_STREAM_TIMEOUT events for | |
streams which have reached their idle limit. | |
Returns the time in seconds until the next evc_stream times out. The user | |
should call this function again after that many seconds. | |
A negative return value means zero active evc_stream are on the transport. | |
*/ | |
double evc_transport_timeout (evc_transport *); | |
/* | |
Destroys a transport, frees memory associated with it. Will close all | |
streams belonging to this transport. | |
*/ | |
void evc_transport_destroy (evc_transport *); | |
/* | |
This callback is made each time a new connection appears on the server. | |
The user is expected to examine the remote address and decide if the | |
connection is allowed. If so, the user must allocate and initialize (with | |
evc_stream_init()) an evc_stream object and return a pointer to it. | |
Returning NULL from this callback rejects the connection. | |
This returned evc_stream will be filled with the connection information | |
and attached to its transport during this call. It will begin receiving data | |
immediately after. The EVC_STREAM_CONNECTED event will be issued for the | |
given stream directly following this callback. | |
*/ | |
typedef evc_stream * (*evc_server_cb)(evc_server *, | |
struct sockaddr *addr, | |
socklen_t addrlen); | |
struct evc_server { | |
/* public */ | |
evc_server_cb cb; | |
void * data; | |
/* read-only */ | |
int fd; | |
evc_transport * transport; | |
/* private */ | |
int connection_fd; | |
}; | |
typedef evc_server evc_server; | |
void evc_server_init(evc_server *, | |
evc_transport *transport, | |
evc_server_cb cb, | |
void * data); | |
void evc_server_process(evc_server *); | |
/* | |
Destroy all references to this object immediately. No more callbacks will | |
be issued. If open, the server will stop accepting connections. You may | |
free the memory associated with the server immediately following a call to | |
this method. | |
*/ | |
void evc_server_release(evc_server *); | |
/* Listen and bind to the given address. */ | |
int evc_server_listen(evc_server *, | |
struct sockaddr *addr, | |
socklen_t addrlen, | |
int backlog); | |
/* | |
Stops listening for new connections, closes the file descriptor associated | |
with the server. The server will be inactive after this call. To reuse | |
the object, evc_server_init() must be called again. It is okay to free the | |
memory associated with the server after this call. | |
*/ | |
void evc_server_close(evc_server *); | |
enum evc_stream_event | |
/* | |
The EVC_STREAM_ACTIVE event occurs when the stream becomes first | |
attached to the event loop. In the case of a client-side stream, this | |
happens when evc_stream_connect() is called; for a server-side stream, | |
this event is emitted, directly after connection is accepted. The | |
object's memory cannot be freed until EVC_STREAM_INACTIVE is emitted or | |
evc_stream_release() is called. | |
*/ | |
{ EVC_STREAM_ACTIVE, | |
/* | |
EVC_STREAM_CONNECTED is emitted once the stream becomes initially | |
usable. For a server-side stream, this happens immediately after | |
evc_server_accept(). For client-side streams, it may take some time to | |
establish the connection and this event is only emitted once the 3-way | |
TCP handshake is complete. | |
This event is a good time to issue a evc_stream_start_tls() command. | |
*/ | |
EVC_STREAM_CONNECTED, | |
/* | |
HANDSHAKE_COMPLETE is emitted only after evc_stream_start_tls() has | |
been called. This is useful to examine TLS credentials before | |
continuing to communicate. | |
*/ | |
EVC_STREAM_HANDSHAKE_COMPLETE, | |
/* | |
EVC_STREAM_READABLE is emitted whenever incoming data is present. It is | |
edge-triggered; that is, it will only be called once when the incoming | |
data buffer's state changes from 'empty' to 'non-empty'. If you fail to | |
process all of the incoming data in this event, no more | |
EVC_STREAM_READABLE events will be emitted. It is expected that you | |
will drain the incoming data buffer. | |
Use evc_stream_read() to retrieve incoming data. | |
Use evc_is_readable() to efficiently check if data is present. | |
*/ | |
EVC_STREAM_READABLE, | |
/* | |
This event is made when a stream becomes writable. It is edge-triggered; | |
that is, it will only be called once when the stream changes state from | |
unwritable to writable. This event is necessary because | |
evc_stream_write() may not be able to send all of the data given to it. | |
In that case you must queue up data until this event is made and then | |
call evc_stream_write() again. Use evc_is_writable() to efficiently | |
check at any point if a stream is writable. | |
*/ | |
EVC_STREAM_WRITABLE, | |
/* | |
This event is issued when a TCP FIN packet or an EOF for UNIX streams | |
is received. This event will be called exactly once per stream. Writing | |
to the stream is still allowed, but you should be sure the other end | |
knows how to consume input after having sent an FIN packet. | |
*/ | |
EVC_STREAM_EOF, | |
/* | |
Emitted when the idle timeout is reached. The idle timeout is defined by | |
the evc_transport that the evc_stream belongs to. | |
*/ | |
EVC_STREAM_TIMEOUT, | |
/* | |
This event occurs when the stream will no longer emit any events. It is | |
emitted exactly once per stream. It is safe to destroy the memory | |
associated with the stream during the callback issuing this event. Any | |
open stream which becomes inactive is closed. The stream may become | |
inactive for any number of reasons, including errors. | |
If a stream is in a half-closed state (readable but not writable) and | |
receives a FIN packet from the peer, the stream will be closed and this | |
event emitted as an indication that the connection is completely dead. | |
Use evc_stream_error() and evc_stream_tls_error() to check errors. | |
*/ | |
EVC_STREAM_INACTIVE }; | |
typedef void (*evc_stream_cb)(evc_stream *, enum evc_stream_event); | |
struct evc_stream { | |
/* public */ | |
evc_stream_cb cb; | |
void * data; | |
/* read-only */ | |
int fd; | |
double last_activity; | |
evc_transport * transport; | |
/* private */ | |
evc_stream * prev; | |
evc_stream * next; | |
long flags; | |
enum evc_error errorno; | |
#if HAVE_GNUTLS | |
int gnutls_errorno; | |
gnutls_session_t tls_session; | |
#endif | |
}; | |
typedef struct evc_stream evc_stream; | |
/* | |
Constructor. This must be called before any other methods are called on | |
the object. In particular, this must be called before the object is passed | |
to evc_server_accept(). | |
The second parameter specifies a transport to add the stream to - the | |
transport defines the idle timeout a stream will have. | |
Calling this function does not open any connection or attach anything to | |
the event loop. | |
*/ | |
void evc_stream_init(evc_stream *stream, | |
evc_transport *transport, | |
evc_stream_cb cb, | |
void * data); | |
void evc_stream_process(evc_stream *, int got_read, int got_write); | |
/* | |
Destructor. Call this method at any time to tell evc to never access the | |
stream's memory again. EVC will close the associated file descriptor if it | |
is open. EVC_STREAM_INACTIVE will be emitted during the call of this | |
method. | |
*/ | |
void evc_stream_release(evc_stream *); | |
/* | |
The three methods | |
evc_stream_open_addr(), | |
evc_stream_open_read(), | |
evc_stream_open_write() | |
activate an initialized evc_stream to a transport. | |
*/ | |
/* Connects the stream to the specified TCP or UNIX address. */ | |
int evc_stream_open_addr(evc_stream *, | |
struct sockaddr * address, | |
socklen_t addrlen); | |
int evc_stream_open_read(evc_stream *, int fd); | |
int evc_stream_open_write(evc_stream *, int fd); | |
/* | |
Read incoming data from a stream. The second argument is a buffer to read | |
into, the third argument is the size of that buffer. A successful read | |
will return the number of bytes read. | |
If no data is available this function will return 0. The user should wait | |
until a EVC_STREAM_READABLE event occurs to try again. The | |
EVC_STREAM_READABLE event will not be triggered until this function is | |
called and returns 0. The user is encouraged to drain the incoming data on | |
each EVC_STREAM_READABLE event. | |
IMPORTANT NOTE: Unlike POSIX read(), this function does not indicate EOF | |
by returning 0. EOF is indicated by the EVC_STREAM_EOF event. | |
*/ | |
size_t evc_stream_read(evc_stream *, const char * buf, size_t * len); | |
ssize_t evc_stream_write(evc_stream *, const char * buf, size_t len); | |
/* | |
Sends a FIN packet for TCP stream or an EOF on a UNIX stream. The | |
connection is half-closed after calling this. Further writing is not | |
allowed, but further reading is allowed until the EVC_STREAM_EOF event is | |
fired indicating that the peer has also closed the connection. | |
*/ | |
void evc_stream_close(evc_stream *); | |
/* | |
Start a TLS session with the given argument. This method can only be | |
called after EVC_STREAM_CONNECTED is emitted. | |
EVC_STREAM_HANDSHAKE_COMPLETE is emitted after the TLS handshake is | |
complete, and evc_stream_tls_error() can be used to check for any TLS | |
errors after EVC_STREAM_INACTIVE. | |
*/ | |
void evc_stream_start_tls(evc_stream *, gnutls_session_t session); | |
static inline enum evc_error evc_stream_error (evc_stream * stream) { | |
return stream->errorno; | |
} | |
static inline int evc_stream_tls_error (evc_stream * stream) { | |
return stream->gnutls_errorno; | |
} | |
enum evc_flags /* Internal use. */ | |
{ EVC_ACTIVE = 0x0001 | |
, EVC_READABLE = 0x0002 | |
, EVC_WRITABLE = 0x0004 | |
, EVC_CONNECTED = 0x0010 | |
}; | |
#define evc_is_active(s) ((s)->flags & EVC_ACTIVE) | |
#define evc_is_readable(s) ((s)->flags & EVC_READABLE) | |
#define evc_is_writable(s) ((s)->flags & EVC_WRITABLE) | |
#define evc_is_connected(s) ((s)->flags & EVC_CONNECTED) | |
#endif /* EVC_H_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment