untangle overly complex control flow logic

for dynamic handlers mod_cgi, mod_fastcgi, mod_scgi, mod_proxy
(mod_cgi control flow logic simplification began in a prior commit)

- connection state machine calls the subrequest handler
- subrequest handler sets up the connection to the backend
  and registers fdevent handler to handle backend events
- fdevent handler handles backend events and then schedules
  a call to connection state machine
- when retrying an alternate backend, backend state is reset
  and then response state is reset so that the connection state
  machine will call back into the subrequest handler to retry

Dynamic handlers no longer directly modify connection state
(calls to connection_set_state() from dynamic handlers were removed)

Dynamic handlers no longer reset con->physical.path, and they
preserve con->mode when retrying alternate backends.  This is done
to skip repeated processing in response.c:http_response_prepare()

While this patch increases consistency in control flow handling,
there is more work to be done that can further improve upon this.

x-ref:
  "handle-req time too long"
  https://redmine.lighttpd.net/issues/1149
personal/stbuehler/mod-csrf-old
Glenn Strauss 2016-04-06 20:46:43 -04:00
parent 77bd45121c
commit bbbbfb3de0
4 changed files with 245 additions and 341 deletions

View File

@ -35,10 +35,6 @@
#include <stdio.h>
#include <fcntl.h>
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#include "version.h"
enum {EOL_UNSET, EOL_N, EOL_RN};
@ -92,6 +88,7 @@ static handler_ctx * cgi_handler_ctx_init(void) {
hctx->response = buffer_init();
hctx->response_header = buffer_init();
hctx->fd = -1;
return hctx;
}
@ -385,7 +382,6 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) {
/* send final chunk */
http_chunk_close(srv, con);
joblist_append(srv, con);
return FDEVENT_HANDLED_FINISHED;
}
@ -473,7 +469,6 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) {
}
http_chunk_append_buffer(srv, con, hctx->response_header);
joblist_append(srv, con);
} else {
const char *bstart;
size_t blen;
@ -507,7 +502,6 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) {
if (blen > 0) {
http_chunk_append_mem(srv, con, bstart, blen);
joblist_append(srv, con);
}
}
@ -515,7 +509,6 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) {
}
} else {
http_chunk_append_buffer(srv, con, hctx->response);
joblist_append(srv, con);
}
#if 0
@ -526,7 +519,7 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) {
return FDEVENT_HANDLED_NOT_FINISHED;
}
static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) {
static void cgi_connection_close(server *srv, handler_ctx *hctx) {
int status;
pid_t pid;
plugin_data *p = hctx->plugin_data;
@ -612,7 +605,8 @@ static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) {
}
#endif
if (con->state == CON_STATE_HANDLE_REQUEST) {
/* finish response (if not already finished) */
if (con->mode == p->id && con->state == CON_STATE_HANDLE_REQUEST) {
/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
* i.e. not called from cgi_connection_close_callback()) */
@ -625,18 +619,14 @@ static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) {
con->file_finished = 1;
}
}
return HANDLER_GO_ON;
}
static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) {
plugin_data *p = p_d;
handler_ctx *hctx = con->plugin_ctx[p->id];
if (hctx) cgi_connection_close(srv, hctx);
if (con->mode != p->id) return HANDLER_GO_ON;
if (NULL == hctx) return HANDLER_GO_ON;
return cgi_connection_close(srv, hctx);
return HANDLER_GO_ON;
}
@ -805,7 +795,7 @@ static int cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunk
return 0;
}
static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) {
static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) {
pid_t pid;
#ifdef HAVE_IPV6
@ -1103,8 +1093,7 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *
close(to_cgi_fds[1]);
return -1;
default: {
handler_ctx *hctx;
/* parent proces */
/* parent process */
close(from_cgi_fds[1]);
close(to_cgi_fds[0]);
@ -1180,19 +1169,11 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *
close(to_cgi_fds[1]);
/* register PID and wait for them asyncronously */
con->mode = p->id;
buffer_reset(con->physical.path);
hctx = cgi_handler_ctx_init();
hctx->remote_conn = con;
hctx->plugin_data = p;
hctx->pid = pid;
hctx->fd = from_cgi_fds[0];
hctx->fde_ndx = -1;
con->plugin_ctx[p->id] = hctx;
fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
@ -1212,6 +1193,23 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *
#endif
}
static buffer * cgi_get_handler(array *a, buffer *fn) {
size_t k, s_len = buffer_string_length(fn);
for (k = 0; k < a->used; ++k) {
data_string *ds = (data_string *)a->data[k];
size_t ct_len = buffer_string_length(ds->key);
if (buffer_is_empty(ds->key)) continue;
if (s_len < ct_len) continue;
if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
return ds->value;
}
}
return NULL;
}
#define PATCH(x) \
p->conf.x = s->x;
static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
@ -1246,7 +1244,6 @@ static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p
#undef PATCH
URIHANDLER_FUNC(cgi_is_handled) {
size_t k, s_len;
plugin_data *p = p_d;
buffer *fn = con->physical.path;
stat_cache_entry *sce = NULL;
@ -1261,26 +1258,12 @@ URIHANDLER_FUNC(cgi_is_handled) {
if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
s_len = buffer_string_length(fn);
for (k = 0; k < p->conf.cgi->used; k++) {
data_string *ds = (data_string *)p->conf.cgi->data[k];
size_t ct_len = buffer_string_length(ds->key);
if (buffer_is_empty(ds->key)) continue;
if (s_len < ct_len) continue;
if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
if (cgi_create_env(srv, con, p, ds->value)) {
con->mode = DIRECT;
con->http_status = 500;
buffer_reset(con->physical.path);
return HANDLER_FINISHED;
}
/* one handler is enough for the request */
break;
}
if (NULL != cgi_get_handler(p->conf.cgi, fn)) {
handler_ctx *hctx = cgi_handler_ctx_init();
hctx->remote_conn = con;
hctx->plugin_data = p;
con->plugin_ctx[p->id] = hctx;
con->mode = p->id;
}
return HANDLER_GO_ON;
@ -1351,11 +1334,21 @@ TRIGGER_FUNC(cgi_trigger) {
SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
plugin_data *p = p_d;
handler_ctx *hctx = con->plugin_ctx[p->id];
UNUSED(srv);
if (con->mode != p->id) return HANDLER_GO_ON;
if (NULL == hctx) return HANDLER_GO_ON;
if (-1 == hctx->fd) {
buffer *handler = cgi_get_handler(p->conf.cgi, con->physical.path);
if (!handler) return HANDLER_GO_ON; /*(should not happen; checked in cgi_is_handled())*/
if (cgi_create_env(srv, con, p, hctx, handler)) {
con->http_status = 500;
con->mode = DIRECT;
return HANDLER_FINISHED;
}
}
#if 0
log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
#endif

View File

@ -39,10 +39,6 @@
#include <stdio.h>
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#include "sys-socket.h"
#ifdef HAVE_SYS_UIO_H
@ -325,7 +321,6 @@ typedef struct {
/* connection specific data */
typedef enum {
FCGI_STATE_UNSET,
FCGI_STATE_INIT,
FCGI_STATE_CONNECT_DELAYED,
FCGI_STATE_PREPARE_WRITE,
@ -1089,7 +1084,7 @@ static int fcgi_spawn_connection(server *srv,
/* log_error_write(srv, __FILE__, __LINE__, "sbs",
"execve failed for:", host->bin_path, strerror(errno)); */
exit(errno);
_exit(errno);
break;
}
@ -1517,8 +1512,6 @@ static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
if (NULL == hctx) return;
p = hctx->plugin_data;
con = hctx->remote_conn;
@ -1547,6 +1540,22 @@ static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
handler_ctx_free(srv, hctx);
con->plugin_ctx[p->id] = NULL;
/* finish response (if not already finished) */
if (con->mode == p->id && con->state == CON_STATE_HANDLE_REQUEST) {
/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
* i.e. not called from fcgi_connection_reset()) */
/* Send an error if we haven't sent any data yet */
if (0 == con->file_started) {
con->http_status = 500;
con->mode = DIRECT;
}
else if (!con->file_finished) {
http_chunk_close(srv, con);
con->file_finished = 1;
}
}
}
static int fcgi_reconnect(server *srv, handler_ctx *hctx) {
@ -1609,8 +1618,8 @@ static int fcgi_reconnect(server *srv, handler_ctx *hctx) {
static handler_t fcgi_connection_reset(server *srv, connection *con, void *p_d) {
plugin_data *p = p_d;
fcgi_connection_close(srv, con->plugin_ctx[p->id]);
handler_ctx *hctx = con->plugin_ctx[p->id];
if (hctx) fcgi_connection_close(srv, hctx);
return HANDLER_GO_ON;
}
@ -2521,7 +2530,6 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) {
con->parsed_response &= ~HTTP_CONTENT_LENGTH;
con->response.content_length = -1;
hctx->send_content_body = 0; /* ignore the content */
joblist_append(srv, con);
} else {
log_error_write(srv, __FILE__, __LINE__, "sb",
"send-file error: couldn't get stat_cache entry for:",
@ -2542,7 +2550,6 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) {
}
http_chunk_append_buffer(srv, con, packet.b);
joblist_append(srv, con);
}
} else if (hctx->send_content_body && !buffer_string_is_empty(packet.b)) {
if (con->request.http_version == HTTP_VERSION_1_1 &&
@ -2552,7 +2559,6 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) {
}
http_chunk_append_buffer(srv, con, packet.b);
joblist_append(srv, con);
}
break;
case FCGI_STDERR:
@ -2570,7 +2576,6 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) {
con->http_status == 200)) {
/* send chunk-end if necessary */
http_chunk_close(srv, con);
joblist_append(srv, con);
}
fin = 1;
@ -2789,7 +2794,7 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) {
switch(hctx->state) {
case FCGI_STATE_CONNECT_DELAYED:
/* should never happen */
break;
return HANDLER_WAIT_FOR_EVENT;
case FCGI_STATE_INIT:
/* do we have a running process for this host (max-procs) ? */
hctx->proc = NULL;
@ -2972,34 +2977,24 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) {
} else {
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
return HANDLER_WAIT_FOR_EVENT;
}
break;
return HANDLER_WAIT_FOR_EVENT;
case FCGI_STATE_READ:
/* waiting for a response */
break;
return HANDLER_WAIT_FOR_EVENT;
default:
log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
return HANDLER_ERROR;
}
return HANDLER_WAIT_FOR_EVENT;
}
/* might be called on fdevent after a connect() is delay too
* */
SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
plugin_data *p = p_d;
handler_ctx *hctx = con->plugin_ctx[p->id];
static handler_t fcgi_send_request(server *srv, handler_ctx *hctx) {
fcgi_extension_host *host;
if (NULL == hctx) return HANDLER_GO_ON;
/* not my job */
if (con->mode != p->id) return HANDLER_GO_ON;
handler_t rc;
/* we don't have a host yet, choose one
* -> this happens in the first round
@ -3034,9 +3029,6 @@ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
fcgi_connection_close(srv, hctx);
con->http_status = 500;
con->mode = DIRECT;
return HANDLER_FINISHED;
}
@ -3060,8 +3052,13 @@ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
}
/* ok, create the request */
switch(fcgi_write_request(srv, hctx)) {
case HANDLER_ERROR:
rc = fcgi_write_request(srv, hctx);
if (HANDLER_ERROR != rc) {
return rc;
} else {
plugin_data *p = hctx->plugin_data;
connection *con = hctx->remote_conn;
if (hctx->state == FCGI_STATE_INIT ||
hctx->state == FCGI_STATE_CONNECT_DELAYED) {
fcgi_restart_dead_procs(srv, p, host);
@ -3069,43 +3066,40 @@ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
/* cleanup this request and let the request handler start this request again */
if (hctx->reconnects < 5) {
fcgi_reconnect(srv, hctx);
joblist_append(srv, con); /* in case we come from the event-handler */
return HANDLER_WAIT_FOR_FD;
return HANDLER_COMEBACK;
} else {
fcgi_connection_close(srv, hctx);
buffer_reset(con->physical.path);
con->mode = DIRECT;
con->http_status = 503;
joblist_append(srv, con); /* in case we come from the event-handler */
return HANDLER_FINISHED;
}
} else {
int status = con->http_status;
fcgi_connection_close(srv, hctx);
buffer_reset(con->physical.path);
con->mode = DIRECT;
if (con->http_status != 400) con->http_status = 503;
joblist_append(srv, con); /* really ? */
con->http_status = (status == 400) ? 400 : 503; /* see FCGI_ENV_ADD_CHECK() for 400 error */
return HANDLER_FINISHED;
}
case HANDLER_WAIT_FOR_EVENT:
if (con->file_started == 1) {
return HANDLER_FINISHED;
} else {
return HANDLER_WAIT_FOR_EVENT;
}
case HANDLER_WAIT_FOR_FD:
return HANDLER_WAIT_FOR_FD;
default:
log_error_write(srv, __FILE__, __LINE__, "s", "subrequest write-req default");
return HANDLER_ERROR;
}
}
SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
plugin_data *p = p_d;
handler_ctx *hctx = con->plugin_ctx[p->id];
if (NULL == hctx) return HANDLER_GO_ON;
/* not my job */
if (con->mode != p->id) return HANDLER_GO_ON;
return (hctx->state != FCGI_STATE_READ)
? fcgi_send_request(srv, hctx)
: HANDLER_WAIT_FOR_EVENT;
}
static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
handler_ctx *hctx = ctx;
connection *con = hctx->remote_conn;
@ -3114,6 +3108,8 @@ static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
fcgi_proc *proc = hctx->proc;
fcgi_extension_host *host= hctx->host;
joblist_append(srv, con);
if ((revents & FDEVENT_IN) &&
hctx->state == FCGI_STATE_READ) {
switch (fcgi_demux_response(srv, hctx)) {
@ -3135,9 +3131,9 @@ static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
buffer_copy_buffer(con->physical.path, host->docroot);
buffer_append_string_buffer(con->physical.path, con->uri.path);
fcgi_connection_close(srv, hctx);
con->mode = DIRECT;
con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
fcgi_connection_close(srv, hctx);
con->http_status = 0;
con->file_started = 1; /* fcgi_extension won't touch the request afterwards */
} else {
@ -3145,7 +3141,6 @@ static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
fcgi_connection_close(srv, hctx);
}
joblist_append(srv, con);
return HANDLER_FINISHED;
case -1:
if (proc->pid && proc->state != PROC_STATE_DIED) {
@ -3199,14 +3194,15 @@ static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
if (hctx->wb->bytes_out == 0 &&
hctx->reconnects < 5) {
fcgi_reconnect(srv, hctx);
log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
"response not received, request not sent",
"on socket:", proc->connection_name,
"for", con->uri.path, "?", con->uri.query, ", reconnecting");
return HANDLER_WAIT_FOR_FD;
fcgi_reconnect(srv, hctx);
return HANDLER_COMEBACK;
}
log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs",
@ -3215,27 +3211,19 @@ static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
"for", con->uri.path, "?", con->uri.query, ", closing connection");
fcgi_connection_close(srv, hctx);
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
buffer_reset(con->physical.path);
con->http_status = 500;
con->mode = DIRECT;
} else {
/* response might have been already started, kill the connection */
fcgi_connection_close(srv, hctx);
log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
"response already sent out, but backend returned error",
"on socket:", proc->connection_name,
"for", con->uri.path, "?", con->uri.query, ", terminating connection");
connection_set_state(srv, con, CON_STATE_ERROR);
con->keep_alive = 0;
con->file_finished = 1;
con->mode = DIRECT; /*(avoid sending final chunked block)*/
fcgi_connection_close(srv, hctx);
}
/* */
joblist_append(srv, con);
return HANDLER_FINISHED;
}
}
@ -3248,7 +3236,7 @@ static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
* 1. in an unfinished connect() call
* 2. in an unfinished write() call (long POST request)
*/
return mod_fastcgi_handle_subrequest(srv, con, p);
return fcgi_send_request(srv, hctx); /*(might invalidate hctx)*/
} else {
log_error_write(srv, __FILE__, __LINE__, "sd",
"got a FDEVENT_OUT and didn't know why:",
@ -3268,7 +3256,7 @@ static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
* FIXME: as it is a bit ugly.
*
*/
return mod_fastcgi_handle_subrequest(srv, con, p);
fcgi_send_request(srv, hctx);
} else if (hctx->state == FCGI_STATE_READ &&
hctx->proc->port == 0) {
/* FIXME:
@ -3283,23 +3271,23 @@ static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
"(no fastcgi process on socket:", proc->connection_name, "?)",
hctx->state);
connection_set_state(srv, con, CON_STATE_ERROR);
fcgi_connection_close(srv, hctx);
joblist_append(srv, con);
}
} else if (revents & FDEVENT_ERR) {
log_error_write(srv, __FILE__, __LINE__, "s",
"fcgi: got a FDEVENT_ERR. Don't know why.");
/* kill all connections to the fastcgi process */
connection_set_state(srv, con, CON_STATE_ERROR);
if (con->file_started) {
con->keep_alive = 0;
con->file_finished = 1;
con->mode = DIRECT; /*(avoid sending final chunked block)*/
}
fcgi_connection_close(srv, hctx);
joblist_append(srv, con);
}
return HANDLER_FINISHED;
}
#define PATCH(x) \
p->conf.x = s->x;
static int fcgi_patch_connection(server *srv, connection *con, plugin_data *p) {
@ -3445,8 +3433,8 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
if (!host) {
/* sorry, we don't have a server alive for this ext */
buffer_reset(con->physical.path);
con->http_status = 500;
con->mode = DIRECT;
/* only send the 'no handler' once */
if (!extension->note_is_sent) {
@ -3602,14 +3590,6 @@ JOBLIST_FUNC(mod_fastcgi_handle_joblist) {
}
static handler_t fcgi_connection_close_callback(server *srv, connection *con, void *p_d) {
plugin_data *p = p_d;
fcgi_connection_close(srv, con->plugin_ctx[p->id]);
return HANDLER_GO_ON;
}
TRIGGER_FUNC(mod_fastcgi_handle_trigger) {
plugin_data *p = p_d;
size_t i, j, n;
@ -3714,7 +3694,7 @@ int mod_fastcgi_plugin_init(plugin *p) {
p->cleanup = mod_fastcgi_free;
p->set_defaults = mod_fastcgi_set_defaults;
p->connection_reset = fcgi_connection_reset;
p->handle_connection_close = fcgi_connection_close_callback;
p->handle_connection_close = fcgi_connection_reset;
p->handle_uri_clean = fcgi_check_extension_1;
p->handle_subrequest_start = fcgi_check_extension_2;
p->handle_subrequest = mod_fastcgi_handle_subrequest;

View File

@ -28,10 +28,6 @@
#include <stdio.h>
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#include "sys-socket.h"
#define data_proxy data_fastcgi
@ -86,8 +82,7 @@ typedef enum {
PROXY_STATE_CONNECT,
PROXY_STATE_PREPARE_WRITE,
PROXY_STATE_WRITE,
PROXY_STATE_READ,
PROXY_STATE_ERROR
PROXY_STATE_READ
} proxy_connection_state_t;
enum { PROXY_STDOUT, PROXY_END_REQUEST };
@ -338,8 +333,6 @@ static void proxy_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
if (NULL == hctx) return;
p = hctx->plugin_data;
con = hctx->remote_conn;
@ -357,6 +350,22 @@ static void proxy_connection_close(server *srv, handler_ctx *hctx) {
handler_ctx_free(hctx);
con->plugin_ctx[p->id] = NULL;
/* finish response (if not already finished) */
if (con->mode == p->id && con->state == CON_STATE_HANDLE_REQUEST) {
/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
* i.e. not called from proxy_connection_reset()) */
/* Send an error if we haven't sent any data yet */
if (0 == con->file_started) {
con->http_status = 500;
con->mode = DIRECT;
}
else if (!con->file_finished) {
http_chunk_close(srv, con);
con->file_finished = 1;
}
}
}
static int proxy_establish_connection(server *srv, handler_ctx *hctx) {
@ -692,11 +701,9 @@ static int proxy_demux_response(server *srv, handler_ctx *hctx) {
con->file_started = 1;
if (blen > 0) http_chunk_append_mem(srv, con, c + 4, blen);
buffer_reset(hctx->response);
joblist_append(srv, con);
}
} else {
http_chunk_append_buffer(srv, con, hctx->response);
joblist_append(srv, con);
buffer_reset(hctx->response);
}
@ -705,7 +712,6 @@ static int proxy_demux_response(server *srv, handler_ctx *hctx) {
con->file_finished = 1;
http_chunk_close(srv, con);
joblist_append(srv, con);
fin = 1;
}
@ -720,7 +726,7 @@ static handler_t proxy_write_request(server *srv, handler_ctx *hctx) {
int ret;
if (!host || buffer_string_is_empty(host->host) || !host->port) return -1;
if (!host || buffer_string_is_empty(host->host) || !host->port) return HANDLER_ERROR;
switch(hctx->state) {
case PROXY_STATE_CONNECT:
@ -732,8 +738,6 @@ static handler_t proxy_write_request(server *srv, handler_ctx *hctx) {
/* wait */
return HANDLER_WAIT_FOR_EVENT;
break;
case PROXY_STATE_INIT:
#if defined(HAVE_SYS_UN_H)
if (strstr(host->host->ptr,"/")) {
@ -819,8 +823,6 @@ static handler_t proxy_write_request(server *srv, handler_ctx *hctx) {
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
} else {
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
return HANDLER_WAIT_FOR_EVENT;
}
return HANDLER_WAIT_FOR_EVENT;
@ -831,8 +833,6 @@ static handler_t proxy_write_request(server *srv, handler_ctx *hctx) {
log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
return HANDLER_ERROR;
}
return HANDLER_GO_ON;
}
#define PATCH(x) \
@ -871,24 +871,14 @@ static int mod_proxy_patch_connection(server *srv, connection *con, plugin_data
}
#undef PATCH
SUBREQUEST_FUNC(mod_proxy_handle_subrequest) {
plugin_data *p = p_d;
handler_ctx *hctx = con->plugin_ctx[p->id];
data_proxy *host;
if (NULL == hctx) return HANDLER_GO_ON;
mod_proxy_patch_connection(srv, con, p);
host = hctx->host;
/* not my job */
if (con->mode != p->id) return HANDLER_GO_ON;
static handler_t proxy_send_request(server *srv, handler_ctx *hctx) {
/* ok, create the request */
switch(proxy_write_request(srv, hctx)) {
case HANDLER_ERROR:
handler_t rc = proxy_write_request(srv, hctx);
if (HANDLER_ERROR != rc) {
return rc;
} else {
data_proxy *host = hctx->host;
connection *con = hctx->remote_conn;
log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:",
host->host,
host->port,
@ -898,33 +888,28 @@ SUBREQUEST_FUNC(mod_proxy_handle_subrequest) {
host->is_disabled = 1;
host->disable_ts = srv->cur_ts;
proxy_connection_close(srv, hctx);
/* reset the enviroment and restart the sub-request */
buffer_reset(con->physical.path);
con->mode = DIRECT;
con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
proxy_connection_close(srv, hctx);
con->mode = hctx->plugin_data->id; /* p->id */
joblist_append(srv, con);
/* mis-using HANDLER_WAIT_FOR_FD to break out of the loop
* and hope that the childs will be restarted
*
*/
return HANDLER_WAIT_FOR_FD;
case HANDLER_WAIT_FOR_EVENT:
break;
case HANDLER_WAIT_FOR_FD:
return HANDLER_WAIT_FOR_FD;
default:
break;
return HANDLER_COMEBACK;
}
}
if (con->file_started == 1) {
return HANDLER_FINISHED;
} else {
return HANDLER_WAIT_FOR_EVENT;
}
SUBREQUEST_FUNC(mod_proxy_handle_subrequest) {
plugin_data *p = p_d;
handler_ctx *hctx = con->plugin_ctx[p->id];
if (NULL == hctx) return HANDLER_GO_ON;
/* not my job */
if (con->mode != p->id) return HANDLER_GO_ON;
return (hctx->state != PROXY_STATE_READ)
? proxy_send_request(srv, hctx)
: HANDLER_WAIT_FOR_EVENT;
}
static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
@ -932,6 +917,7 @@ static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
connection *con = hctx->remote_conn;
plugin_data *p = hctx->plugin_data;
joblist_append(srv, con);
if ((revents & FDEVENT_IN) &&
hctx->state == PROXY_STATE_READ) {
@ -948,20 +934,19 @@ static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
/* we are done */
proxy_connection_close(srv, hctx);
joblist_append(srv, con);
return HANDLER_FINISHED;
case -1:
if (con->file_started == 0) {
/* nothing has been send out yet, send a 500 */
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
con->http_status = 500;
con->mode = DIRECT;
/* reading response headers failed */
} else {
/* response might have been already started, kill the connection */
connection_set_state(srv, con, CON_STATE_ERROR);
con->keep_alive = 0;
con->file_finished = 1;
con->mode = DIRECT; /*(avoid sending final chunked block)*/
}
joblist_append(srv, con);
proxy_connection_close(srv, hctx);
return HANDLER_FINISHED;
}
}
@ -985,7 +970,6 @@ static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"getsockopt failed:", strerror(errno));
joblist_append(srv, con);
return HANDLER_FINISHED;
}
if (socket_error != 0) {
@ -993,7 +977,6 @@ static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
"establishing connection failed:", strerror(socket_error),
"port:", hctx->host->port);
joblist_append(srv, con);
return HANDLER_FINISHED;
}
if (p->conf.debug) {
@ -1010,7 +993,7 @@ static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
* 1. after a just finished connect() call
* 2. in a unfinished write() call (long POST request)
*/
return mod_proxy_handle_subrequest(srv, con, p);
return proxy_send_request(srv, hctx); /*(might invalidate hctx)*/
} else {
log_error_write(srv, __FILE__, __LINE__, "sd",
"proxy: out", hctx->state);
@ -1044,38 +1027,25 @@ static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
hctx->host->is_disabled = 1;
hctx->host->disable_ts = srv->cur_ts;
/* reset the environment and restart the sub-request */
con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
proxy_connection_close(srv, hctx);
/* reset the enviroment and restart the sub-request */
buffer_reset(con->physical.path);
con->mode = DIRECT;
joblist_append(srv, con);
con->mode = p->id;
} else {
proxy_connection_close(srv, hctx);
joblist_append(srv, con);
con->mode = DIRECT;
con->http_status = 503;
}
return HANDLER_FINISHED;
} else {
proxy_connection_close(srv, hctx);
}
if (!con->file_finished) {
http_chunk_close(srv, con);
}
con->file_finished = 1;
proxy_connection_close(srv, hctx);
joblist_append(srv, con);
} else if (revents & FDEVENT_ERR) {
/* kill all connections to the proxy process */
log_error_write(srv, __FILE__, __LINE__, "sd", "proxy-FDEVENT_ERR, but no HUP", revents);
con->file_finished = 1;
joblist_append(srv, con);
if (con->file_started) {
con->keep_alive = 0;
con->file_finished = 1;
con->mode = DIRECT; /*(avoid sending final chunked block)*/
}
proxy_connection_close(srv, hctx);
}
@ -1301,10 +1271,10 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p
return HANDLER_GO_ON;
}
static handler_t mod_proxy_connection_close_callback(server *srv, connection *con, void *p_d) {
static handler_t mod_proxy_connection_reset(server *srv, connection *con, void *p_d) {
plugin_data *p = p_d;
proxy_connection_close(srv, con->plugin_ctx[p->id]);
handler_ctx *hctx = con->plugin_ctx[p->id];
if (hctx) proxy_connection_close(srv, hctx);
return HANDLER_GO_ON;
}
@ -1359,8 +1329,8 @@ int mod_proxy_plugin_init(plugin *p) {
p->init = mod_proxy_init;
p->cleanup = mod_proxy_free;
p->set_defaults = mod_proxy_set_defaults;
p->connection_reset = mod_proxy_connection_close_callback; /* end of req-resp cycle */
p->handle_connection_close = mod_proxy_connection_close_callback; /* end of client connection */
p->connection_reset = mod_proxy_connection_reset; /* end of req-resp cycle */
p->handle_connection_close = mod_proxy_connection_reset; /* end of client connection */
p->handle_uri_clean = mod_proxy_check_extension;
p->handle_subrequest = mod_proxy_handle_subrequest;
p->handle_trigger = mod_proxy_trigger;

View File

@ -27,10 +27,6 @@
#include <stdio.h>
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#include "sys-socket.h"
#ifdef HAVE_SYS_UIO_H
@ -869,7 +865,7 @@ static int scgi_spawn_connection(server *srv,
log_error_write(srv, __FILE__, __LINE__, "sbs",
"execl failed for:", host->bin_path, strerror(errno));
exit(errno);
_exit(errno);
break;
}
@ -1253,12 +1249,10 @@ static int scgi_set_state(server *srv, handler_ctx *hctx, scgi_connection_state_
}
static void scgi_connection_cleanup(server *srv, handler_ctx *hctx) {
static void scgi_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
if (NULL == hctx) return;
p = hctx->plugin_data;
con = hctx->remote_conn;
@ -1290,6 +1284,22 @@ static void scgi_connection_cleanup(server *srv, handler_ctx *hctx) {
handler_ctx_free(hctx);
con->plugin_ctx[p->id] = NULL;
/* finish response (if not already finished) */
if (con->mode == p->id && con->state == CON_STATE_HANDLE_REQUEST) {
/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
* i.e. not called from scgi_connection_reset()) */
/* Send an error if we haven't sent any data yet */
if (0 == con->file_started) {
con->http_status = 500;
con->mode = DIRECT;
}
else if (!con->file_finished) {
http_chunk_close(srv, con);
con->file_finished = 1;
}
}
}
static int scgi_reconnect(server *srv, handler_ctx *hctx) {
@ -1340,8 +1350,8 @@ static int scgi_reconnect(server *srv, handler_ctx *hctx) {
static handler_t scgi_connection_reset(server *srv, connection *con, void *p_d) {
plugin_data *p = p_d;
scgi_connection_cleanup(srv, con->plugin_ctx[p->id]);
handler_ctx *hctx = con->plugin_ctx[p->id];
if (hctx) scgi_connection_close(srv, hctx);
return HANDLER_GO_ON;
}
@ -1813,7 +1823,6 @@ static int scgi_demux_response(server *srv, handler_ctx *hctx) {
/* send final chunk */
http_chunk_close(srv, con);
joblist_append(srv, con);
return 1;
}
@ -1890,7 +1899,6 @@ static int scgi_demux_response(server *srv, handler_ctx *hctx) {
}
http_chunk_append_buffer(srv, con, hctx->response_header);
joblist_append(srv, con);
} else {
size_t blen = buffer_string_length(hctx->response_header) - hlen;
@ -1908,7 +1916,6 @@ static int scgi_demux_response(server *srv, handler_ctx *hctx) {
if (blen > 0) {
http_chunk_append_mem(srv, con, hctx->response_header->ptr + hlen, blen);
joblist_append(srv, con);
}
}
@ -1916,7 +1923,6 @@ static int scgi_demux_response(server *srv, handler_ctx *hctx) {
}
} else {
http_chunk_append_buffer(srv, con, hctx->response);
joblist_append(srv, con);
}
#if 0
@ -2320,7 +2326,7 @@ static handler_t scgi_write_request(server *srv, handler_ctx *hctx) {
scgi_reconnect(srv, hctx);
return HANDLER_WAIT_FOR_FD;
return HANDLER_COMEBACK;
}
/* not reconnected ... why
@ -2352,38 +2358,28 @@ static handler_t scgi_write_request(server *srv, handler_ctx *hctx) {
} else {
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
return HANDLER_WAIT_FOR_EVENT;
}
break;
return HANDLER_WAIT_FOR_EVENT;
case FCGI_STATE_READ:
/* waiting for a response */
break;
return HANDLER_WAIT_FOR_EVENT;
default:
log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
return HANDLER_ERROR;
}
return HANDLER_WAIT_FOR_EVENT;
}
SUBREQUEST_FUNC(mod_scgi_handle_subrequest) {
plugin_data *p = p_d;
handler_ctx *hctx = con->plugin_ctx[p->id];
scgi_proc *proc;
scgi_extension_host *host;
if (NULL == hctx) return HANDLER_GO_ON;
/* not my job */
if (con->mode != p->id) return HANDLER_GO_ON;
static handler_t scgi_send_request(server *srv, handler_ctx *hctx) {
/* ok, create the request */
switch(scgi_write_request(srv, hctx)) {
case HANDLER_ERROR:
proc = hctx->proc;
host = hctx->host;
handler_t rc = scgi_write_request(srv, hctx);
if (HANDLER_ERROR != rc) {
return rc;
} else {
scgi_proc *proc = hctx->proc;
scgi_extension_host *host = hctx->host;
plugin_data *p = hctx->plugin_data;
connection *con = hctx->remote_conn;
if (proc &&
0 == proc->is_local &&
@ -2432,55 +2428,33 @@ SUBREQUEST_FUNC(mod_scgi_handle_subrequest) {
}
scgi_restart_dead_procs(srv, p, host);
scgi_connection_cleanup(srv, hctx);
con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
scgi_connection_close(srv, hctx);
con->mode = p->id;
buffer_reset(con->physical.path);
con->mode = DIRECT;
joblist_append(srv, con);
/* mis-using HANDLER_WAIT_FOR_FD to break out of the loop
* and hope that the childs will be restarted
*
*/
return HANDLER_WAIT_FOR_FD;
return HANDLER_COMEBACK;
} else {
scgi_connection_cleanup(srv, hctx);
buffer_reset(con->physical.path);
con->mode = DIRECT;
scgi_connection_close(srv, hctx);
con->http_status = 503;
return HANDLER_FINISHED;
}
case HANDLER_WAIT_FOR_EVENT:
if (con->file_started == 1) {
return HANDLER_FINISHED;
} else {
return HANDLER_WAIT_FOR_EVENT;
}
case HANDLER_WAIT_FOR_FD:
return HANDLER_WAIT_FOR_FD;
default:
log_error_write(srv, __FILE__, __LINE__, "s", "subrequest write-req default");
return HANDLER_ERROR;
}
}
static handler_t scgi_connection_close(server *srv, handler_ctx *hctx) {
connection *con;
SUBREQUEST_FUNC(mod_scgi_handle_subrequest) {
plugin_data *p = p_d;
handler_ctx *hctx = con->plugin_ctx[p->id];
if (NULL == hctx) return HANDLER_GO_ON;
con = hctx->remote_conn;
/* not my job */
if (con->mode != p->id) return HANDLER_GO_ON;
log_error_write(srv, __FILE__, __LINE__, "ssdsd",
"emergency exit: scgi:",
"connection-fd:", con->fd,
"fcgi-fd:", hctx->fd);
scgi_connection_cleanup(srv, hctx);
return HANDLER_FINISHED;
return (hctx->state != FCGI_STATE_READ)
? scgi_send_request(srv, hctx)
: HANDLER_WAIT_FOR_EVENT;
}
@ -2492,6 +2466,8 @@ static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
scgi_proc *proc = hctx->proc;
scgi_extension_host *host= hctx->host;
joblist_append(srv, con);
if ((revents & FDEVENT_IN) &&
hctx->state == FCGI_STATE_READ) {
switch (scgi_demux_response(srv, hctx)) {
@ -2499,9 +2475,8 @@ static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
break;
case 1:
/* we are done */
scgi_connection_cleanup(srv, hctx);
scgi_connection_close(srv, hctx);
joblist_append(srv, con);
return HANDLER_FINISHED;
case -1:
if (proc->pid && proc->state != PROC_STATE_DIED) {
@ -2555,14 +2530,15 @@ static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
if (hctx->wb->bytes_out == 0 &&
hctx->reconnects < 5) {
scgi_reconnect(srv, hctx);
log_error_write(srv, __FILE__, __LINE__, "ssdsd",
"response not sent, request not sent, reconnection.",
"connection-fd:", con->fd,
"fcgi-fd:", hctx->fd);
return HANDLER_WAIT_FOR_FD;
scgi_reconnect(srv, hctx);
return HANDLER_COMEBACK;
}
log_error_write(srv, __FILE__, __LINE__, "sosdsd",
@ -2570,12 +2546,7 @@ static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
"connection-fd:", con->fd,
"fcgi-fd:", hctx->fd);
scgi_connection_cleanup(srv, hctx);
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
buffer_reset(con->physical.path);
con->http_status = 500;
con->mode = DIRECT;
scgi_connection_close(srv, hctx);
} else {
/* response might have been already started, kill the connection */
log_error_write(srv, __FILE__, __LINE__, "ssdsd",
@ -2583,15 +2554,12 @@ static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
"connection-fd:", con->fd,
"fcgi-fd:", hctx->fd);
scgi_connection_cleanup(srv, hctx);
connection_set_state(srv, con, CON_STATE_ERROR);
con->keep_alive = 0;
con->file_finished = 1;
con->mode = DIRECT; /*(avoid sending final chunked block)*/
scgi_connection_close(srv, hctx);
}
/* */
joblist_append(srv, con);
return HANDLER_FINISHED;
}
}
@ -2604,7 +2572,7 @@ static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
* 1. in a unfinished connect() call
* 2. in a unfinished write() call (long POST request)
*/
return mod_scgi_handle_subrequest(srv, con, p);
return scgi_send_request(srv, hctx); /*(might invalidate hctx)*/
} else {
log_error_write(srv, __FILE__, __LINE__, "sd",
"got a FDEVENT_OUT and didn't know why:",
@ -2624,7 +2592,7 @@ static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
* FIXME: as it is a bit ugly.
*
*/
return mod_scgi_handle_subrequest(srv, con, p);
scgi_send_request(srv, hctx);
} else if (hctx->state == FCGI_STATE_READ &&
hctx->proc->port == 0) {
/* FIXME:
@ -2643,19 +2611,18 @@ static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
" ?)",
hctx->state);
connection_set_state(srv, con, CON_STATE_ERROR);
scgi_connection_close(srv, hctx);
joblist_append(srv, con);
}
} else if (revents & FDEVENT_ERR) {
log_error_write(srv, __FILE__, __LINE__, "s",
"fcgi: got a FDEVENT_ERR. Don't know why.");
/* kill all connections to the scgi process */
connection_set_state(srv, con, CON_STATE_ERROR);
if (con->file_started) {
con->keep_alive = 0;
con->file_finished = 1;
con->mode = DIRECT; /*(avoid sending final chunked block)*/
}
scgi_connection_close(srv, hctx);
joblist_append(srv, con);
}
return HANDLER_FINISHED;
@ -2763,8 +2730,8 @@ static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, i
if (!host) {
/* sorry, we don't have a server alive for this ext */
buffer_reset(con->physical.path);
con->http_status = 500;
con->mode = DIRECT;
/* only send the 'no handler' once */
if (!extension->note_is_sent) {
@ -2913,12 +2880,6 @@ JOBLIST_FUNC(mod_scgi_handle_joblist) {
}
static handler_t scgi_connection_close_callback(server *srv, connection *con, void *p_d) {
plugin_data *p = p_d;