Feature #2060 ยป mod-connect.patch
lighttpd-1.4.x/src/Makefile.am 2009-09-01 11:22:58.000000000 +0200 | ||
---|---|---|
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
|
||
... | ... | |
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
|
||
... | ... | |
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)";
|
||
}
|
||
}
|
||
... | ... | |
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
|
||
... | ... | |
}
|
||
break;
|
||
case CON_STATE_READ_CONTINUOUS:
|
||
break;
|
||
default: break;
|
||
}
|
||
... | ... | |
}
|
||
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);
|
||
}
|
||
... | ... | |
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) {
|
||
... | ... | |
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
|
||
... | ... | |
#include <sys/types.h>
|
||
#include <unistd.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <ctype.h>
|
||
#include <assert.h>
|
||
#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 <fnmatch.h>
|
||
#include <stdio.h>
|
||
#ifdef HAVE_SYS_FILIO_H
|
||
# include <sys/filio.h>
|
||
#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 = ( "<regex>" => "<host>", ...)
|
||
*/
|
||
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
|
||
... | ... | |
PLUGIN_FUNC_INIT,
|
||
PLUGIN_FUNC_CLEANUP,
|
||
PLUGIN_FUNC_SET_DEFAULTS,
|
||
PLUGIN_FUNC_READ_CONTINUOUS,
|
||
PLUGIN_FUNC_SIZEOF
|
||
} plugin_t;
|
||
... | ... | |
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
|
||
... | ... | |
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
|
||
... | ... | |
#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
|
||
... | ... | |
*/
|
||
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 */
|
||
... | ... | |
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);
|