From aa41ea1643fc4d43652e9e515d0c597e02b1b803 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 16 Jan 2008 22:31:54 +0100 Subject: [PATCH] Added a timeout watcher to TCP class - probably isn't working properly yet. Added ebb_client_write to complete the abstraction from TCP. --- README | 4 +++- ebb.c | 40 +++++++++++++++++++++++----------------- ebb.h | 5 +++-- ebb_test.c | 12 ++++++------ ruby_binding/ebb_ext.c | 5 ++--- tcp.c | 42 ++++++++++++++++++++++++++++++++++++------ tcp.h | 7 ++++++- 7 files changed, 79 insertions(+), 36 deletions(-) diff --git a/README b/README index fec7273..68cf0dd 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ Ebb Web Server = Design The design is similar to the Evented Mongrel web server; except instead of -using EventMachine (a ruby binding to libevent) the Ebb web server is written +using EventMachine (a ruby binding to libevent), the Ebb web server is written in C and uses the libev event loop library (http://software.schmorp.de/pkg/libev.html). Connections are processed as follows: @@ -35,6 +35,8 @@ wc -l ebb.c ebb.h tcp.c tcp.h ruby_binding/ebb_ext.c * Bug fixing, unit testing, solidifying. * Fix Ruby looping problem (see ruby_binding/ebb.rb:22) * Python binding +* Statically allocate all request objects at compile-time in both TCP and Ebb. +* >> TCP writer callback << = License diff --git a/ebb.c b/ebb.c index bd23650..85f9860 100644 --- a/ebb.c +++ b/ebb.c @@ -39,8 +39,8 @@ void ebb_on_read(char *buffer, int length, void *_client) { ebb_client *client = (ebb_client*)(_client); + assert(client->socket->open); assert(!http_parser_is_finished(client->parser)); - assert(client); g_string_append_len(client->buffer, buffer, length); @@ -56,22 +56,15 @@ void ebb_on_read(char *buffer, int length, void *_client) pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); int rc = pthread_create(&thread, &attr, ebb_handle_request, client); - // ebb_handle_request(client); - if(rc < 0) { - ebb_error("Could not create thread. Killing client."); - ebb_client_free(client); - return; - } - return; + if(rc < 0) + ebb_error("Could not create thread."); } - - return; } const char *ebb_input = "ebb.input"; const char *server_name = "SERVER_NAME"; const char *server_port = "SERVER_PORT"; -/* User is responsible for closing and freeing the client */ +/* User is responsible for freeing the client */ void* ebb_handle_request(void *_client) { ebb_client *client = (ebb_client*)(_client); @@ -86,6 +79,9 @@ void* ebb_handle_request(void *_client) client->server->socket->port_s)); client->server->request_cb(client, client->server->request_cb_data); + /* Cannot access client beyond this point because it's possible that the + * user has freed it. + */ pthread_exit(NULL); return NULL; @@ -97,16 +93,19 @@ void ebb_on_request(tcp_client *socket, void *data) ebb_server *server = (ebb_server*)(data); ebb_client *client = ebb_client_new(server, socket); + assert(client->socket->open); + assert(server->socket->open); + socket->read_cb = ebb_on_read; socket->read_cb_data = client; } -void ebb_server_start(ebb_server *server - , char *host - , int port - , ebb_request_cb_t request_cb - , void *request_cb_data - ) +void ebb_server_start( ebb_server *server + , char *host + , int port + , ebb_request_cb_t request_cb + , void *request_cb_data + ) { server->request_cb = request_cb; server->request_cb_data = request_cb_data; @@ -172,6 +171,13 @@ void ebb_client_free(ebb_client *client) //g_debug("ebb client freed"); } +// writes to the client +int ebb_client_write(ebb_client *this, const char *data, int length) +{ + return tcp_client_write(this->socket, data, length); +} + + ebb_env_pair* ebb_env_pair_new(const char *field, size_t flen, const char *value, size_t vlen) { ebb_env_pair *pair = g_new(ebb_env_pair, 1); diff --git a/ebb.h b/ebb.h index 32a43ee..5881cf7 100644 --- a/ebb.h +++ b/ebb.h @@ -36,8 +36,9 @@ struct ebb_server { /*** Ebb Client ***/ ebb_client* ebb_client_new(ebb_server *, tcp_client *); -void ebb_client_free(ebb_client *); -void ebb_client_close(ebb_client *client); +void ebb_client_free(ebb_client*); +void ebb_client_close(ebb_client*); +int ebb_client_write(ebb_client*, const char *data, int length); struct ebb_client { ebb_server *server; diff --git a/ebb_test.c b/ebb_test.c index 70c5f24..95b9fa7 100644 --- a/ebb_test.c +++ b/ebb_test.c @@ -9,17 +9,17 @@ void request_cb(ebb_client *client, void *data) { ebb_env_pair *pair; //g_message("Request"); - tcp_client_write(client->socket, header, strlen(header)); + ebb_client_write(client, header, strlen(header)); while((pair = g_queue_pop_head(client->env))) { - tcp_client_write(client->socket, pair->field, pair->flen); - tcp_client_write(client->socket, "\r\n", 2); - tcp_client_write(client->socket, pair->value, pair->vlen); - tcp_client_write(client->socket, "\r\n\r\n", 4); + ebb_client_write(client, pair->field, pair->flen); + ebb_client_write(client, "\r\n", 2); + ebb_client_write(client, pair->value, pair->vlen); + ebb_client_write(client, "\r\n\r\n", 4); ebb_env_pair_free(pair); } - tcp_client_write(client->socket, "Hello.\r\n\r\n", 6); + ebb_client_write(client, "Hello.\r\n\r\n", 6); ebb_client_free(client); } diff --git a/ruby_binding/ebb_ext.c b/ruby_binding/ebb_ext.c index 6e0d6b6..448c99f 100644 --- a/ruby_binding/ebb_ext.c +++ b/ruby_binding/ebb_ext.c @@ -5,7 +5,6 @@ #include #include -#include static VALUE cServer; static VALUE cClient; @@ -64,7 +63,7 @@ VALUE client_write(VALUE client, VALUE string) int written; Data_Get_Struct(client, ebb_client, _client); - written = tcp_client_write(_client->socket, RSTRING_PTR(string), RSTRING_LEN(string)); + written = ebb_client_write(_client, RSTRING_PTR(string), RSTRING_LEN(string)); return INT2FIX(written); } @@ -87,7 +86,7 @@ VALUE client_env(VALUE client) VALUE client_close(VALUE client) { ebb_client *_client; - + Data_Get_Struct(client, ebb_client, _client); ebb_client_close(_client); return Qnil; diff --git a/tcp.c b/tcp.c index c86f1a8..ee7705f 100644 --- a/tcp.c +++ b/tcp.c @@ -30,16 +30,36 @@ void tcp_client_stop_read_watcher(tcp_client *client); /* Returns the number of bytes remaining to write */ int tcp_client_write(tcp_client *client, const char *data, int length) { + if(!client->open) { + tcp_warning("Trying to write to a client that isn't open."); + return 0; + } assert(client->open); int sent = send(client->fd, data, length, 0); if(sent < 0) { - tcp_error("Error writing: %s", strerror(errno)); + tcp_warning("Error writing: %s", strerror(errno)); tcp_client_close(client); return 0; } + ev_timer_again(client->parent->loop, client->timeout_watcher); + return sent; } +void tcp_client_on_timeout( struct ev_loop *loop + , struct ev_timer *watcher + , int revents + ) +{ + tcp_client *client = (tcp_client*)(watcher->data); + + assert(client->parent->loop == loop); + assert(client->timeout_watcher == watcher); + + tcp_client_close(client); + tcp_info("client timed out"); +} + void tcp_client_on_readable( struct ev_loop *loop , struct ev_io *watcher , int revents @@ -67,7 +87,6 @@ void tcp_client_on_readable( struct ev_loop *loop g_debug("zero length read? what to do? killing read watcher"); tcp_client_stop_read_watcher(client); return; - //goto error; } else if(length < 0) { if(errno == EBADF || errno == ECONNRESET) g_debug("errno says Connection reset by peer"); @@ -76,6 +95,7 @@ void tcp_client_on_readable( struct ev_loop *loop goto error; } + ev_timer_again(loop, client->timeout_watcher); // g_debug("Read %d bytes", length); client->read_cb(client->read_buffer, length, client->read_cb_data); @@ -114,9 +134,14 @@ tcp_client* tcp_client_new(tcp_server *server) client->read_watcher = g_new0(struct ev_io, 1); client->read_watcher->data = client; - ev_init (client->read_watcher, tcp_client_on_readable); - ev_io_set (client->read_watcher, client->fd, EV_READ | EV_ERROR); - ev_io_start (server->loop, client->read_watcher); + ev_init(client->read_watcher, tcp_client_on_readable); + ev_io_set(client->read_watcher, client->fd, EV_READ | EV_ERROR); + ev_io_start(server->loop, client->read_watcher); + + client->timeout_watcher = g_new0(struct ev_timer, 1); + client->timeout_watcher->data = client; + ev_timer_init(client->timeout_watcher, tcp_client_on_timeout, 60., 60.); + ev_timer_start(server->loop, client->timeout_watcher); return client; @@ -139,7 +164,6 @@ void tcp_client_stop_read_watcher(tcp_client *client) } } -/* PRIVATE! */ void tcp_client_free(tcp_client *client) { tcp_client_close(client); @@ -152,6 +176,11 @@ void tcp_client_close(tcp_client *client) { if(client->open) { tcp_client_stop_read_watcher(client); + + ev_timer_stop(client->parent->loop, client->timeout_watcher); + free(client->timeout_watcher); + client->timeout_watcher = NULL; + close(client->fd); client->open = FALSE; //g_debug("tcp client closed"); @@ -208,6 +237,7 @@ void tcp_server_free(tcp_server *server) void tcp_server_close(tcp_server *server) { + printf("closeserver\n"); assert(server->open); tcp_client *client; diff --git a/tcp.h b/tcp.h index bcb86e7..a58e194 100644 --- a/tcp.h +++ b/tcp.h @@ -18,7 +18,11 @@ typedef struct tcp_server tcp_server; #define TCP_LOG_DOMAIN "TCP" #define tcp_error(str, ...) \ g_log(TCP_LOG_DOMAIN, G_LOG_LEVEL_ERROR, str, ## __VA_ARGS__); - +#define tcp_warning(str, ...) \ + g_log(TCP_LOG_DOMAIN, G_LOG_LEVEL_WARNING, str, ## __VA_ARGS__); +#define tcp_info(str, ...) \ + g_log(TCP_LOG_DOMAIN, G_LOG_LEVEL_INFO, str, ## __VA_ARGS__); + #define TCP_COMMON \ int fd; \ struct sockaddr_in sockaddr; \ @@ -70,6 +74,7 @@ struct tcp_client { tcp_client_read_cb_t read_cb; char *read_buffer; ev_io *read_watcher; + ev_timer *timeout_watcher; }; #endif tcp_h \ No newline at end of file -- 2.11.4.GIT