Example config: connect.server = ( # finger kernel.org "/FINGER" => ( "host" => "204.152.191.37", "port" => 79 ), # connect to local echo server "/ECHO1" => ( "host" => "127.0.0.1", "port" => 7 ), # connect to local echo server, omit 200 response # the idea is that the server (echo doesn't of course) # will generate the 200 response to have control over it. "/ECHO2" => ( "host" => "127.0.0.1", "port" => 7, "noresponse" => 1 ), # connect to sipsolutions.net web server "/SIPS" => ( "host" => "83.246.72.84", "port" => 80 ), # w/o port: treat as http connection "83.246.72.84" => ( "port" => 80 ), # with any port: allow "83.246.72.84:*" => (), # allow connections anywhere to port 443 "*:443" => (), ) --- src/Makefile.am | 5 src/base.h | 3 src/connections-glue.c | 2 src/connections.c | 14 src/mod_connect.c | 850 +++++++++++++++++++++++++++++++++++++++++++++++++ src/plugin.c | 3 src/plugin.h | 3 7 files changed, 878 insertions(+), 2 deletions(-) --- lighttpd-1.4.x.orig/src/Makefile.am 2009-09-01 11:22:55.000000000 +0200 +++ lighttpd-1.4.x/src/Makefile.am 2009-09-01 11:22:58.000000000 +0200 @@ -189,6 +189,11 @@ mod_proxy_la_SOURCES = mod_proxy.c mod_proxy_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_proxy_la_LIBADD = $(common_libadd) +lib_LTLIBRARIES += mod_connect.la +mod_connect_la_SOURCES = mod_connect.c +mod_connect_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_connect_la_LIBADD = $(common_libadd) + lib_LTLIBRARIES += mod_ssi.la mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c mod_ssi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined --- lighttpd-1.4.x.orig/src/base.h 2009-09-01 11:22:55.000000000 +0200 +++ lighttpd-1.4.x/src/base.h 2009-09-01 11:22:58.000000000 +0200 @@ -323,7 +323,8 @@ typedef enum { CON_STATE_WRITE, CON_STATE_RESPONSE_END, CON_STATE_ERROR, - CON_STATE_CLOSE + CON_STATE_CLOSE, + CON_STATE_READ_CONTINUOUS, } connection_state_t; typedef enum { COND_RESULT_UNSET, COND_RESULT_FALSE, COND_RESULT_TRUE } cond_result_t; --- lighttpd-1.4.x.orig/src/connections-glue.c 2009-09-01 11:22:55.000000000 +0200 +++ lighttpd-1.4.x/src/connections-glue.c 2009-09-01 11:22:58.000000000 +0200 @@ -14,6 +14,7 @@ const char *connection_get_state(connect case CON_STATE_REQUEST_END: return "req-end"; case CON_STATE_RESPONSE_START: return "resp-start"; case CON_STATE_RESPONSE_END: return "resp-end"; + case CON_STATE_READ_CONTINUOUS: return "read-continuous"; default: return "(unknown)"; } } @@ -31,6 +32,7 @@ const char *connection_get_short_state(c case CON_STATE_REQUEST_END: return "Q"; case CON_STATE_RESPONSE_START: return "s"; case CON_STATE_RESPONSE_END: return "S"; + case CON_STATE_READ_CONTINUOUS: return "F"; default: return "x"; } } --- lighttpd-1.4.x.orig/src/connections.c 2009-09-01 11:22:55.000000000 +0200 +++ lighttpd-1.4.x/src/connections.c 2009-09-01 11:22:58.000000000 +0200 @@ -1145,6 +1145,8 @@ static int connection_handle_read_state( } break; + case CON_STATE_READ_CONTINUOUS: + break; default: break; } @@ -1214,7 +1216,8 @@ static handler_t connection_handle_fdeve } if (con->state == CON_STATE_READ || - con->state == CON_STATE_READ_POST) { + con->state == CON_STATE_READ_POST || + con->state == CON_STATE_READ_CONTINUOUS) { connection_handle_read_state(srv, con); } @@ -1627,12 +1630,18 @@ int connection_state_machine(server *srv break; case CON_STATE_READ_POST: case CON_STATE_READ: + case CON_STATE_READ_CONTINUOUS: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } connection_handle_read_state(srv, con); + + if (con->state == CON_STATE_READ_CONTINUOUS) { + plugins_call_read_continuous(srv, con); + } + break; case CON_STATE_WRITE: if (srv->srvconf.log_state_handling) { @@ -1797,6 +1806,9 @@ int connection_state_machine(server *srv fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); } break; + case CON_STATE_READ_CONTINUOUS: + /* leave up to plugins */ + break; default: fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); break; --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ lighttpd-1.4.x/src/mod_connect.c 2009-09-01 11:35:04.000000000 +0200 @@ -0,0 +1,850 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "buffer.h" +#include "server.h" +#include "keyvalue.h" +#include "log.h" + +#include "http_chunk.h" +#include "fdevent.h" +#include "connections.h" +#include "response.h" +#include "joblist.h" +#include "network.h" +#include "plugin.h" + +#include "inet_ntop_cache.h" +#include "crc32.h" +#include + +#include + +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#include "sys-socket.h" + +#define data_connect data_fastcgi +#define data_connect_init data_fastcgi_init + +#define CONNECT_RETRY_TIMEOUT 60 + +/** + * + * The connect module is based on the proxy module, + * which in turn is based on the fastcgi module. + */ + +typedef struct { + array *extensions; + unsigned int debug; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *parse_response; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +typedef enum { + CONNECT_STATE_INIT, + CONNECT_STATE_CONNECT, + CONNECT_STATE_CONNECTED, +} connect_connection_state_t; + +typedef struct { + connect_connection_state_t state; + time_t state_timestamp; + + buffer *host; + unsigned short port; + + chunkqueue *toclient; + + buffer *response; + + int fd; /* fd to the connect process */ + int fd_server_ndx; /* index into the fd-event buffer */ + + int client_closed, server_closed; + + int noresp; + int path; /* handle as /path based CONNECT */ + + size_t path_info_offset; /* start of path_info in uri.path */ + + connection *remote_conn; /* dump pointer */ + plugin_data *plugin_data; /* dump pointer */ +} handler_ctx; + + +/* ok, we need a prototype */ +static handler_t connect_handle_fdevent(void *s, void *ctx, int revents); + +static handler_ctx *handler_ctx_init(void) +{ + handler_ctx *hctx = calloc(1, sizeof(*hctx)); + + hctx->state = CONNECT_STATE_INIT; + hctx->host = buffer_init(); + + hctx->response = buffer_init(); + + hctx->toclient = chunkqueue_init(); + + hctx->fd = -1; + hctx->fd_server_ndx = -1; + hctx->path = 0; + + return hctx; +} + +static void handler_ctx_free(handler_ctx *hctx) +{ + buffer_free(hctx->host); + buffer_free(hctx->response); + chunkqueue_free(hctx->toclient); + + free(hctx); +} + +INIT_FUNC(mod_connect_init) +{ + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->parse_response = buffer_init(); + + return p; +} + + +FREE_FUNC(mod_connect_free) +{ + plugin_data *p = p_d; + + UNUSED(srv); + + buffer_free(p->parse_response); + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (s) { + + array_free(s->extensions); + + free(s); + } + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_connect_set_defaults) +{ + plugin_data *p = p_d; + data_unset *du; + size_t i = 0; + + config_values_t cv[] = { + { "connect.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, + { "connect.debug", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s; + array *ca; + + s = malloc(sizeof(plugin_config)); + s->extensions = array_init(); + s->debug = 0; + + cv[0].destination = s->extensions; + cv[1].destination = &(s->debug); + + p->config_storage[i] = s; + ca = ((data_config *)srv->config_context->data[i])->value; + + if (config_insert_values_global(srv, ca, cv)) + return HANDLER_ERROR; + + du = array_get_element(ca, "connect.server"); + if (du) { + size_t j; + data_array *da = (data_array *)du; + + if (du->type != TYPE_ARRAY) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "unexpected type for key: ", "connect.server", "array of strings"); + + return HANDLER_ERROR; + } + + /* + * connect.server = ( "" => "", ...) + */ + + for (j = 0; j < da->value->used; j++) { + data_array *da_ext = (data_array *)da->value->data[j]; + data_array *dca; + data_connect *dc; + + if (da_ext->type != TYPE_ARRAY) { + log_error_write(srv, __FILE__, __LINE__, "sssbs", + "unexpected type for key: ", "connect.server", + "[", da->value->data[j]->key, "](string)"); + + return HANDLER_ERROR; + } + + config_values_t pcv[] = { + { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "port", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, + { "noresponse", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + dc = data_connect_init(); + + buffer_copy_string_buffer(dc->key, da_ext->key); + pcv[0].destination = dc->host; + pcv[1].destination = &(dc->port); + pcv[2].destination = &(dc->usage); // hack + + if (config_insert_values_internal(srv, da_ext->value, pcv)) + return HANDLER_ERROR; + + dca = (data_array *)array_get_element(s->extensions, da_ext->key->ptr); + if (!dca) { + dca = data_array_init(); + + buffer_copy_string_buffer(dca->key, da_ext->key); + + array_insert_unique(s->extensions, (data_unset *)dca); + } + + if (s->debug) + log_error_write(srv, __FILE__, __LINE__, "ssssd", + da_ext->key->ptr, "=>", dc->host->ptr, ":", dc->port); + + array_insert_unique(dca->value, (data_unset *)dc); + } + } + } + + return HANDLER_GO_ON; +} + +static void connect_connection_close(server *srv, handler_ctx *hctx) +{ + plugin_data *p; + connection *con; + + if (!hctx) + return; + + p = hctx->plugin_data; + con = hctx->remote_conn; + + if (hctx->fd != -1) { + fdevent_event_del(srv->ev, &(hctx->fd_server_ndx), hctx->fd); + fdevent_unregister(srv->ev, hctx->fd); + + close(hctx->fd); + srv->cur_fds--; + } + + handler_ctx_free(hctx); + con->plugin_ctx[p->id] = NULL; +} + +static int connect_establish_connection(server *srv, handler_ctx *hctx) +{ + struct sockaddr *connect_addr; + struct sockaddr_in connect_addr_in; +#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) + struct sockaddr_in6 connect_addr_in6; +#endif + socklen_t servlen; + + plugin_data *p = hctx->plugin_data; + int connect_fd = hctx->fd; + +#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) + if (strstr(hctx->host->ptr, ":")) { + memset(&connect_addr_in6, 0, sizeof(connect_addr_in6)); + connect_addr_in6.sin6_family = AF_INET6; + inet_pton(AF_INET6, hctx->host->ptr, (char *) &connect_addr_in6.sin6_addr); + connect_addr_in6.sin6_port = htons(hctx->port); + servlen = sizeof(connect_addr_in6); + connect_addr = (struct sockaddr *) &connect_addr_in6; + } else +#endif + { + memset(&connect_addr_in, 0, sizeof(connect_addr_in)); + connect_addr_in.sin_family = AF_INET; + connect_addr_in.sin_addr.s_addr = inet_addr(hctx->host->ptr); + connect_addr_in.sin_port = htons(hctx->port); + servlen = sizeof(connect_addr_in); + connect_addr = (struct sockaddr *) &connect_addr_in; + } + + + if (-1 == connect(connect_fd, connect_addr, servlen)) { + if (errno == EINPROGRESS || errno == EALREADY) { + if (p->conf.debug) + log_error_write(srv, __FILE__, __LINE__, "sd", + "connect delayed:", connect_fd); + + return 1; + } else { + + log_error_write(srv, __FILE__, __LINE__, "sdsd", + "connect failed:", connect_fd, strerror(errno), errno); + + return -1; + } + } + if (p->conf.debug) + log_error_write(srv, __FILE__, __LINE__, "sd", + "connect succeeded: ", connect_fd); + + return 0; +} + +static int connect_set_state(server *srv, handler_ctx *hctx, connect_connection_state_t state) +{ + hctx->state = state; + hctx->state_timestamp = srv->cur_ts; + + return 0; +} + +static handler_t connect_write_request(server *srv, handler_ctx *hctx) +{ + plugin_data *p = hctx->plugin_data; + connection *con = hctx->remote_conn; + + int ret; + + switch(hctx->state) { + case CONNECT_STATE_INIT: +#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) + if (strstr(hctx->host->ptr,":")) { + if (-1 == (hctx->fd = socket(AF_INET6, SOCK_STREAM, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); + return HANDLER_ERROR; + } + } else +#endif + { + if (-1 == (hctx->fd = socket(AF_INET, SOCK_STREAM, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); + return HANDLER_ERROR; + } + } + hctx->fd_server_ndx = -1; + + srv->cur_fds++; + + fdevent_register(srv->ev, hctx->fd, connect_handle_fdevent, hctx); + + if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); + + return HANDLER_ERROR; + } + + /* fall through */ + + case CONNECT_STATE_CONNECT: + /* try to finish the connect() */ + if (hctx->state == CONNECT_STATE_INIT) { + /* first round */ + switch (connect_establish_connection(srv, hctx)) { + case 1: + connect_set_state(srv, hctx, CONNECT_STATE_CONNECT); + + /* connection is in progress, wait for an event and call getsockopt() below */ + + fdevent_event_add(srv->ev, &(hctx->fd_server_ndx), hctx->fd, FDEVENT_OUT); + + return HANDLER_WAIT_FOR_EVENT; + case -1: + hctx->fd_server_ndx = -1; + + return HANDLER_ERROR; + default: + /* everything is ok, go on */ + break; + } + } else { + int socket_error; + socklen_t socket_error_len = sizeof(socket_error); + + /* we don't need it anymore */ + fdevent_event_del(srv->ev, &(hctx->fd_server_ndx), hctx->fd); + + /* try to finish the connect() */ + if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "getsockopt failed:", strerror(errno)); + + return HANDLER_ERROR; + } + if (socket_error != 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "establishing connection failed:", strerror(socket_error), + "port:", hctx->port); + + con->http_status = 503; + con->mode = DIRECT; + return HANDLER_FINISHED; + } + if (p->conf.debug) + log_error_write(srv, __FILE__, __LINE__, "s", "connect - connect - delayed success"); + } + + connect_set_state(srv, hctx, CONNECT_STATE_CONNECTED); + + if (!hctx->noresp) { + struct { + const char *b; size_t l; + } strs[] = { + { CONST_STR_LEN("HTTP/1.0 200 Connection established\r\n") }, + { CONST_STR_LEN("\r\n") }, + }; + int i; + buffer *b = chunkqueue_get_append_buffer(hctx->toclient); + for (i = 0; i < (int)(sizeof(strs)/sizeof(strs[0])); i++) + buffer_append_string_len(b, strs[i].b, strs[i].l); + } + + connection_set_state(srv, con, CON_STATE_READ_CONTINUOUS); + + /* fall through */ + case CONNECT_STATE_CONNECTED: + if (!hctx->server_closed) { + ret = srv->network_backend_write(srv, con, hctx->fd, con->read_queue); + chunkqueue_remove_finished_chunks(con->read_queue); + + if (-1 == ret) { /* error on our side */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno); + } else if (-2 == ret) { /* remote close */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed, remote connection close:", strerror(errno), errno); + hctx->server_closed = 1; + } + } + + if (!hctx->client_closed) { + con->keep_alive = 1; + ret = network_write_chunkqueue(srv, con, hctx->toclient); + con->keep_alive = 0; + chunkqueue_remove_finished_chunks(hctx->toclient); + if (-1 == ret) { /* error on our side */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno); + } else if (-2 == ret) { /* remote close */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed, remote connection close:", strerror(errno), errno); + hctx->client_closed = 1; + } + } + + if (!chunkqueue_is_empty(con->read_queue)) { + fdevent_event_del(srv->ev, &(hctx->fd_server_ndx), hctx->fd); + fdevent_event_add(srv->ev, &(hctx->fd_server_ndx), hctx->fd, FDEVENT_OUT); + } else { + fdevent_event_del(srv->ev, &(hctx->fd_server_ndx), hctx->fd); + if (hctx->client_closed) + connect_connection_close(srv, hctx); + else if (chunkqueue_is_empty(hctx->toclient)) + fdevent_event_add(srv->ev, &(hctx->fd_server_ndx), hctx->fd, FDEVENT_IN); + } + + if (!chunkqueue_is_empty(hctx->toclient)) { + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT); + } else { + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + if (hctx->server_closed) { + if (!hctx->client_closed) { + connection_set_state(srv, con, CON_STATE_RESPONSE_END); + hctx->client_closed = 1; + } + } else if (chunkqueue_is_empty(con->read_queue)) + fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN); + } + + return HANDLER_WAIT_FOR_EVENT; + default: + log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state"); + return HANDLER_ERROR; + } +} + +static int mod_connect_patch_connection(server *srv, connection *con, plugin_data *p) +{ + size_t i, j; + plugin_config *s = p->config_storage[0]; + +#define PATCH(x) do { p->conf.x = s->x; } while (0) + + PATCH(extensions); + PATCH(debug); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) + continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("connect.server"))) + PATCH(extensions); + else if (buffer_is_equal_string(du->key, CONST_STR_LEN("connect.debug"))) + PATCH(debug); + } + } + +#undef PATCH + + return 0; +} + +SUBREQUEST_FUNC(mod_connect_handle_subrequest) +{ + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + handler_t h; + + if (!hctx) + return HANDLER_GO_ON; + + mod_connect_patch_connection(srv, con, p); + + /* not my job */ + if (con->mode != p->id) + return HANDLER_GO_ON; + + /* ok, create the request */ + h = connect_write_request(srv, hctx); + switch (h) { + case HANDLER_ERROR: + log_error_write(srv, __FILE__, __LINE__, "sbdd", "connect-server disabled:", + hctx->host, hctx->port, hctx->fd); + + connect_connection_close(srv, hctx); + + /* reset the enviroment and restart the sub-request */ + buffer_reset(con->physical.path); + con->mode = DIRECT; + + joblist_append(srv, con); + + return HANDLER_ERROR; + default: + return h; + } +} + +static handler_t connect_handle_fdevent(void *s, void *ctx, int revents) +{ + server *srv = (server *)s; + handler_ctx *hctx = ctx; + connection *con = hctx->remote_conn; + plugin_data *p = hctx->plugin_data; + int b; + ssize_t r; + buffer *buf; + int fd = hctx->fd; + char readbuf[4096]; + + + if ((revents & FDEVENT_IN) && + hctx->state == CONNECT_STATE_CONNECTED && + chunkqueue_is_empty(hctx->toclient)) { + /* check how much we have to read */ + if (ioctl(fd, FIONREAD, &b)) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "ioctl failed: ", fd); + goto disconnect; + } + + if (!b || b > (int)sizeof(readbuf)) + b = sizeof(readbuf); + + errno = 0; + r = read(fd, readbuf, b); + if (r > 0) { + buf = chunkqueue_get_append_buffer(hctx->toclient); + buffer_append_memory(buf, readbuf, r + 1); // XX ??? + } else if (errno != EAGAIN) + goto disconnect; + } + + if (revents & FDEVENT_OUT) { + if (p->conf.debug) + log_error_write(srv, __FILE__, __LINE__, "sd", + "connect: fdevent-out", hctx->state); + + if (hctx->state == CONNECT_STATE_CONNECT || + hctx->state == CONNECT_STATE_CONNECTED) { + /* unfinished connect call or data to send */ + return mod_connect_handle_subrequest(srv, con, p); + } else { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connect: out", hctx->state); + } + } + + /* perhaps this issue is already handled */ + if (revents & FDEVENT_HUP) { + if (p->conf.debug) + log_error_write(srv, __FILE__, __LINE__, "sd", + "connect: fdevent-hup", hctx->state); + + if (hctx->state == CONNECT_STATE_CONNECT) { + /* connect() -> EINPROGRESS -> HUP */ + + goto disconnect; + } + + con->file_finished = 1; + goto disconnect; + } else if (revents & FDEVENT_ERR) { + /* kill all connections to the connect process */ + log_error_write(srv, __FILE__, __LINE__, "sd", "connect-FDEVENT_ERR, but no HUP", revents); + goto disconnect; + } + + joblist_append(srv, con); + return HANDLER_FINISHED; + disconnect: + hctx->server_closed = 1; + joblist_append(srv, con); + connect_write_request(srv, hctx); + return HANDLER_FINISHED; +} + +static handler_t mod_connect_check(server *srv, connection *con, void *p_d) +{ + plugin_data *p = p_d; + size_t s_len; + size_t k; + buffer *fn; + data_array *extension = NULL; + size_t path_info_offset; + data_connect *host; + handler_ctx *hctx; + char *colon; + int path = 0; + + if (con->request.http_method != HTTP_METHOD_CONNECT) + return HANDLER_GO_ON; + + if (con->mode != DIRECT) + return HANDLER_GO_ON; + + /* Have we processed this request already? */ + if (con->file_started == 1) + return HANDLER_GO_ON; + + mod_connect_patch_connection(srv, con, p); + + fn = con->uri.path; + + if (fn->used == 0) + return HANDLER_ERROR; + + s_len = fn->used - 1; + + path_info_offset = 0; + + if (p->conf.debug) + log_error_write(srv, __FILE__, __LINE__, "s", "connect - start"); + + /* check if prefix or host matches */ + for (k = 0; k < p->conf.extensions->used; k++) { + data_array *ext = NULL; + size_t ct_len; + + ext = (data_array *)p->conf.extensions->data[k]; + + if (ext->key->used == 0) + continue; + + ct_len = ext->key->used - 1; + + if (s_len < ct_len) + continue; + + /* check host/prefix in the form "/path" */ + if (*(ext->key->ptr) == '/') { + if (strncmp(fn->ptr, ext->key->ptr, ct_len) == 0) { + if (s_len > ct_len + 1) { + char *pi_offset; + + pi_offset = strchr(fn->ptr + ct_len + 1, '/'); + if (pi_offset) + path_info_offset = pi_offset - fn->ptr; + } + extension = ext; + path = 1; + break; + } + } else if (fnmatch(ext->key->ptr, fn->ptr + 1, FNM_NOESCAPE|FNM_CASEFOLD) == 0) { + /* check extension in the form ".fcg" */ + extension = ext; + break; + } + } + + if (!extension) + return HANDLER_GO_ON; + + if (p->conf.debug) + log_error_write(srv, __FILE__, __LINE__, "s", "connect - ext found"); + + host = (data_connect *)extension->value->data[0]; + + /* + * if check-local is disabled, use the uri.path handler + * + */ + + /* init handler-context */ + hctx = handler_ctx_init(); + + hctx->path_info_offset = path_info_offset; + hctx->remote_conn = con; + hctx->plugin_data = p; + if (!host->host) { + colon = strrchr(fn->ptr + 1, ':'); + if (!colon) { + buffer_append_string(hctx->host, fn->ptr + 1); + hctx->port = host->port ? host->port : 443; + } else { + hctx->port = atoi(colon + 1); + buffer_append_string_len(hctx->host, fn->ptr + 1, colon - fn->ptr - 1); + } + } else { + buffer_append_string(hctx->host, host->host->ptr); + hctx->port = host->port; + } + hctx->noresp = host->usage; // XXX + hctx->path = path; + + con->plugin_ctx[p->id] = hctx; + + con->mode = p->id; + + if (p->conf.debug) + log_error_write(srv, __FILE__, __LINE__, "sbd", + "connect - found a host", + hctx->host, hctx->port); + + return HANDLER_GO_ON; +} + +static handler_t mod_connect_connection_close(server *srv, connection *con, void *p_d) +{ + plugin_data *p = p_d; + + connect_connection_close(srv, con->plugin_ctx[p->id]); + + return HANDLER_GO_ON; +} + +/** + * + * the trigger re-enables the disabled connections after the timeout is over + * + */ + +TRIGGER_FUNC(mod_connect_trigger) +{ + plugin_data *p = p_d; + + if (p->config_storage) { + size_t i, n, k; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) + continue; + + /* get the extensions for all configs */ + + for (k = 0; k < s->extensions->used; k++) { + data_array *extension = (data_array *)s->extensions->data[k]; + + /* get all hosts */ + for (n = 0; n < extension->value->used; n++) { + data_connect *host = (data_connect *)extension->value->data[n]; + + if (!host->is_disabled || + srv->cur_ts - host->disable_ts < 5) + continue; + + log_error_write(srv, __FILE__, __LINE__, "sbd", + "connect - re-enabled:", + host->host, host->port); + + host->is_disabled = 0; + } + } + } + } + + return HANDLER_GO_ON; +} + + +int mod_connect_plugin_init(plugin *p) +{ + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("connect"); + + p->init = mod_connect_init; + p->cleanup = mod_connect_free; + p->set_defaults = mod_connect_set_defaults; + p->connection_reset = mod_connect_connection_close; + p->handle_connection_close = mod_connect_connection_close; + p->handle_uri_clean = mod_connect_check; + p->handle_subrequest = mod_connect_handle_subrequest; + p->handle_trigger = mod_connect_trigger; + p->read_continuous = mod_connect_handle_subrequest; + + p->data = NULL; + + return 0; +} --- lighttpd-1.4.x.orig/src/plugin.c 2009-09-01 11:22:55.000000000 +0200 +++ lighttpd-1.4.x/src/plugin.c 2009-09-01 11:22:58.000000000 +0200 @@ -47,6 +47,7 @@ typedef enum { PLUGIN_FUNC_INIT, PLUGIN_FUNC_CLEANUP, PLUGIN_FUNC_SET_DEFAULTS, + PLUGIN_FUNC_READ_CONTINUOUS, PLUGIN_FUNC_SIZEOF } plugin_t; @@ -270,6 +271,7 @@ PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIS PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical) PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset) +PLUGIN_TO_SLOT(PLUGIN_FUNC_READ_CONTINUOUS, read_continuous) #undef PLUGIN_TO_SLOT @@ -399,6 +401,7 @@ handler_t plugins_call_init(server *srv) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot); PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical); PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset); + PLUGIN_TO_SLOT(PLUGIN_FUNC_READ_CONTINUOUS, read_continuous) PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup); PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults); #undef PLUGIN_TO_SLOT --- lighttpd-1.4.x.orig/src/plugin.h 2009-09-01 11:22:55.000000000 +0200 +++ lighttpd-1.4.x/src/plugin.h 2009-09-01 11:22:58.000000000 +0200 @@ -23,6 +23,7 @@ #define PHYSICALPATH_FUNC CONNECTION_FUNC #define REQUESTDONE_FUNC CONNECTION_FUNC #define URIHANDLER_FUNC CONNECTION_FUNC +#define READ_CONT_FUNC CONNECTION_FUNC #define PLUGIN_DATA size_t id @@ -55,6 +56,7 @@ typedef struct { */ handler_t (* handle_subrequest) (server *srv, connection *con, void *p_d); /* */ handler_t (* connection_reset) (server *srv, connection *con, void *p_d); /* */ + handler_t (* read_continuous) (server *srv, connection *con, void *p_d); /* */ void *data; /* dlopen handle */ @@ -74,6 +76,7 @@ handler_t plugins_call_handle_physical(s handler_t plugins_call_handle_connection_close(server *srv, connection *con); handler_t plugins_call_handle_joblist(server *srv, connection *con); handler_t plugins_call_connection_reset(server *srv, connection *con); +handler_t plugins_call_read_continuous(server *srv, connection *con); handler_t plugins_call_handle_trigger(server *srv); handler_t plugins_call_handle_sighup(server *srv);