fix FastCGI, SCGI, proxy reconnect on failure

factor modules for consistent code flow for reconnect on failure
personal/stbuehler/mod-csrf
Glenn Strauss 7 years ago
parent 988ee80060
commit fa67918d3e

@ -1623,6 +1623,7 @@ static void fcgi_backend_close(server *srv, handler_ctx *hctx) {
fdevent_unregister(srv->ev, hctx->fd);
fdevent_sched_close(srv->ev, hctx->fd, 1);
hctx->fd = -1;
hctx->fde_ndx = -1;
}
if (hctx->host) {
@ -1643,6 +1644,53 @@ static void fcgi_backend_close(server *srv, handler_ctx *hctx) {
}
}
static fcgi_extension_host * fcgi_extension_host_get(server *srv, connection *con, plugin_data *p, fcgi_extension *extension) {
fcgi_extension_host *host;
int ndx = extension->last_used_ndx + 1;
if (ndx >= (int) extension->used || ndx < 0) ndx = 0;
UNUSED(p);
/* check if the next server has no load */
host = extension->hosts[ndx];
if (host->load > 0 || host->active_procs == 0) {
/* get backend with the least load */
size_t k;
int used = -1;
for (k = 0, ndx = -1; k < extension->used; k++) {
host = extension->hosts[k];
/* we should have at least one proc that can do something */
if (host->active_procs == 0) continue;
if (used == -1 || host->load < used) {
used = host->load;
ndx = k;
}
}
}
if (ndx == -1) {
/* all hosts are down */
/* sorry, we don't have a server alive for this ext */
con->http_status = 503; /* Service Unavailable */
con->mode = DIRECT;
/* only send the 'no handler' once */
if (!extension->note_is_sent) {
extension->note_is_sent = 1;
log_error_write(srv, __FILE__, __LINE__, "sBSbsbs",
"all handlers for", con->uri.path, "?", con->uri.query,
"on", extension->key,
"are down.");
}
}
/* found a server */
extension->last_used_ndx = ndx;
return extension->hosts[ndx];
}
static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
@ -1660,58 +1708,16 @@ static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
}
}
static int fcgi_reconnect(server *srv, handler_ctx *hctx) {
/* child died
*
* 1.
*
* connect was ok, connection was accepted
* but the php accept loop checks after the accept if it should die or not.
*
* if yes we can only detect it at a write()
*
* next step is resetting this attemp and setup a connection again
*
* if we have more than 5 reconnects for the same request, die
*
* 2.
*
* we have a connection but the child died by some other reason
*
*/
if (hctx->fd != -1) {
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
fdevent_unregister(srv->ev, hctx->fd);
fdevent_sched_close(srv->ev, hctx->fd, 1);
hctx->fd = -1;
}
static handler_t fcgi_reconnect(server *srv, handler_ctx *hctx) {
fcgi_backend_close(srv, hctx);
fcgi_set_state(srv, hctx, FCGI_STATE_INIT);
hctx->host = fcgi_extension_host_get(srv, hctx->remote_conn, hctx->plugin_data, hctx->ext);
if (NULL == hctx->host) return HANDLER_FINISHED;
fcgi_host_assign(srv, hctx, hctx->host);
hctx->request_id = 0;
hctx->reconnects++;
if (hctx->conf.debug > 2) {
if (hctx->proc) {
log_error_write(srv, __FILE__, __LINE__, "sdb",
"release proc for reconnect:",
hctx->proc->pid, hctx->proc->connection_name);
} else {
log_error_write(srv, __FILE__, __LINE__, "sb",
"release proc for reconnect:",
hctx->host->unixsocket);
}
}
if (hctx->proc && hctx->got_proc) {
fcgi_proc_load_dec(srv, hctx);
}
/* perhaps another host gives us more luck */
fcgi_host_reset(srv, hctx);
return 0;
fcgi_set_state(srv, hctx, FCGI_STATE_INIT);
return HANDLER_COMEBACK;
}
@ -2687,21 +2693,6 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) {
int ret;
/* sanity check:
* - host != NULL
* - either:
* - tcp socket (do not check host->host->uses, as it may be not set which means INADDR_LOOPBACK)
* - unix socket
*/
if (!host) {
log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: host = NULL");
return HANDLER_ERROR;
}
if ((!host->port && buffer_string_is_empty(host->unixsocket))) {
log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: neither host->port nor host->unixsocket is set");
return HANDLER_ERROR;
}
/* we can't handle this in the switch as we have to fall through in it */
if (hctx->state == FCGI_STATE_CONNECT_DELAYED) {
int socket_error;
@ -2953,66 +2944,9 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) {
/* might be called on fdevent after a connect() is delay too
* */
static handler_t fcgi_send_request(server *srv, handler_ctx *hctx) {
fcgi_extension_host *host;
handler_t rc;
/* we don't have a host yet, choose one
* -> this happens in the first round
* and when the host died and we have to select a new one */
if (hctx->host == NULL) {
size_t k;
int ndx, used = -1;
/* check if the next server has no load. */
ndx = hctx->ext->last_used_ndx + 1;
if(ndx >= (int) hctx->ext->used || ndx < 0) ndx = 0;
host = hctx->ext->hosts[ndx];
if (host->load > 0) {
/* get backend with the least load. */
for (k = 0, ndx = -1; k < hctx->ext->used; k++) {
host = hctx->ext->hosts[k];
/* we should have at least one proc that can do something */
if (host->active_procs == 0) continue;
if (used == -1 || host->load < used) {
used = host->load;
ndx = k;
}
}
}
/* found a server */
if (ndx == -1) {
/* all hosts are down */
fcgi_connection_close(srv, hctx);
return HANDLER_FINISHED;
}
hctx->ext->last_used_ndx = ndx;
host = hctx->ext->hosts[ndx];
/*
* if check-local is disabled, use the uri.path handler
*
*/
/* init handler-context */
/* we put a connection on this host, move the other new connections to other hosts
*
* as soon as hctx->host is unassigned, decrease the load again */
fcgi_host_assign(srv, hctx, host);
hctx->proc = NULL;
} else {
host = hctx->host;
}
/* ok, create the request */
rc = fcgi_write_request(srv, hctx);
fcgi_extension_host *host = hctx->host;
handler_t rc = fcgi_write_request(srv, hctx);
if (HANDLER_ERROR != rc) {
return rc;
} else {
@ -3024,10 +2958,8 @@ static handler_t fcgi_send_request(server *srv, handler_ctx *hctx) {
fcgi_restart_dead_procs(srv, p, host);
/* cleanup this request and let the request handler start this request again */
if (hctx->reconnects < 5) {
fcgi_reconnect(srv, hctx);
return HANDLER_COMEBACK;
if (hctx->reconnects++ < 5) {
return fcgi_reconnect(srv, hctx);
} else {
fcgi_connection_close(srv, hctx);
con->http_status = 503;
@ -3213,16 +3145,14 @@ static handler_t fcgi_recv_response(server *srv, handler_ctx *hctx) {
/* nothing has been sent out yet, try to use another child */
if (hctx->wb->bytes_out == 0 &&
hctx->reconnects < 5) {
hctx->reconnects++ < 5) {
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");
fcgi_reconnect(srv, hctx);
return HANDLER_COMEBACK;
return fcgi_reconnect(srv, hctx);
}
log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs",
@ -3462,34 +3392,8 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
}
/* check if we have at least one server for this extension up and running */
for (k = 0; k < extension->used; k++) {
fcgi_extension_host *h = extension->hosts[k];
/* we should have at least one proc that can do something */
if (h->active_procs == 0) {
continue;
}
/* we found one host that is alive */
host = h;
break;
}
if (!host) {
/* sorry, we don't have a server alive for this ext */
con->http_status = 500;
con->mode = DIRECT;
/* only send the 'no handler' once */
if (!extension->note_is_sent) {
extension->note_is_sent = 1;
log_error_write(srv, __FILE__, __LINE__, "sBSbsbs",
"all handlers for", con->uri.path, "?", con->uri.query,
"on", extension->key,
"are down.");
}
host = fcgi_extension_host_get(srv, con, p, extension);
if (NULL == host) {
return HANDLER_FINISHED;
}
@ -3558,6 +3462,7 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
hctx->plugin_data = p;
hctx->proc = NULL;
hctx->ext = extension;
fcgi_host_assign(srv, hctx, host);
hctx->fcgi_mode = fcgi_mode;
if (fcgi_mode == FCGI_AUTHORIZER) {

@ -102,12 +102,11 @@ typedef struct {
int fd; /* fd to the proxy process */
int fde_ndx; /* index into the fd-event buffer */
size_t path_info_offset; /* start of path_info in uri.path */
plugin_config conf;
connection *remote_conn; /* dump pointer */
plugin_data *plugin_data; /* dump pointer */
connection *remote_conn; /* dumb pointer */
plugin_data *plugin_data; /* dumb pointer */
data_array *ext;
} handler_ctx;
@ -333,23 +332,167 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) {
return HANDLER_GO_ON;
}
static void proxy_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
p = hctx->plugin_data;
con = hctx->remote_conn;
static void proxy_backend_close(server *srv, handler_ctx *hctx) {
if (hctx->fd != -1) {
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
fdevent_unregister(srv->ev, hctx->fd);
fdevent_sched_close(srv->ev, hctx->fd, 1);
hctx->fd = -1;
hctx->fde_ndx = -1;
}
if (hctx->host) {
hctx->host->usage--;
hctx->host = NULL;
}
}
static data_proxy * mod_proxy_extension_host_get(server *srv, connection *con, plugin_data *p, data_array *extension) {
unsigned long last_max = ULONG_MAX;
int max_usage = INT_MAX;
int ndx = -1;
size_t k;
if (extension->value->used == 1) {
if ( ((data_proxy *)extension->value->data[0])->is_disabled ) {
ndx = -1;
} else {
ndx = 0;
}
} else if (extension->value->used != 0) switch(p->conf.balance) {
case PROXY_BALANCE_HASH:
/* hash balancing */
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sd",
"proxy - used hash balancing, hosts:", extension->value->used);
}
for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->value->used; k++) {
data_proxy *host = (data_proxy *)extension->value->data[k];
unsigned long cur_max;
if (host->is_disabled) continue;
cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path)) +
generate_crc32c(CONST_BUF_LEN(host->host)) + /* we can cache this */
generate_crc32c(CONST_BUF_LEN(con->uri.authority));
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sbbbd",
"proxy - election:",
con->uri.path,
host->host,
con->uri.authority,
cur_max);
}
if ((last_max == ULONG_MAX) || /* first round */
(cur_max > last_max)) {
last_max = cur_max;
ndx = k;
}
}
break;
case PROXY_BALANCE_FAIR:
/* fair balancing */
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "s",
"proxy - used fair balancing");
}
for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->value->used; k++) {
data_proxy *host = (data_proxy *)extension->value->data[k];
if (host->is_disabled) continue;
if (host->usage < max_usage) {
max_usage = host->usage;
ndx = k;
}
}
break;
case PROXY_BALANCE_RR: {
data_proxy *host;
/* round robin */
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "s",
"proxy - used round-robin balancing");
}
/* just to be sure */
force_assert(extension->value->used < INT_MAX);
host = (data_proxy *)extension->value->data[0];
/* Use last_used_ndx from first host in list */
k = host->last_used_ndx;
ndx = k + 1; /* use next host after the last one */
if (ndx < 0) ndx = 0;
/* Search first active host after last_used_ndx */
while ( ndx < (int) extension->value->used
&& (host = (data_proxy *)extension->value->data[ndx])->is_disabled ) ndx++;
if (ndx >= (int) extension->value->used) {
/* didn't found a higher id, wrap to the start */
for (ndx = 0; ndx <= (int) k; ndx++) {
host = (data_proxy *)extension->value->data[ndx];
if (!host->is_disabled) break;
}
/* No active host found */
if (host->is_disabled) ndx = -1;
}
/* Save new index for next round */
((data_proxy *)extension->value->data[0])->last_used_ndx = ndx;
break;
}
default:
break;
}
/* found a server */
if (ndx != -1) {
data_proxy *host = (data_proxy *)extension->value->data[ndx];
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sbd",
"proxy - found a host",
host->host, host->port);
}
host->usage++;
return host;
} else {
/* no handler found */
con->http_status = 503; /* Service Unavailable */
con->mode = DIRECT;
log_error_write(srv, __FILE__, __LINE__, "sb",
"no proxy-handler found for:",
con->uri.path);
return NULL;
}
}
static void proxy_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
p = hctx->plugin_data;
con = hctx->remote_conn;
proxy_backend_close(srv, hctx);
handler_ctx_free(hctx);
con->plugin_ctx[p->id] = NULL;
@ -359,6 +502,16 @@ static void proxy_connection_close(server *srv, handler_ctx *hctx) {
}
}
static handler_t proxy_reconnect(server *srv, handler_ctx *hctx) {
proxy_backend_close(srv, hctx);
hctx->host = mod_proxy_extension_host_get(srv, hctx->remote_conn, hctx->plugin_data, hctx->ext);
if (NULL == hctx->host) return HANDLER_FINISHED;
hctx->state = PROXY_STATE_INIT;
return HANDLER_COMEBACK;
}
static int proxy_establish_connection(server *srv, handler_ctx *hctx) {
struct sockaddr *proxy_addr;
struct sockaddr_in proxy_addr_in;
@ -760,22 +913,11 @@ static handler_t proxy_write_request(server *srv, handler_ctx *hctx) {
int ret;
if (!host || buffer_string_is_empty(host->host) || !host->port) return HANDLER_ERROR;
switch(hctx->state) {
case PROXY_STATE_CONNECT:
/* wait for the connect() to finish */
/* connect failed ? */
if (-1 == hctx->fde_ndx) return HANDLER_ERROR;
/* wait */
return HANDLER_WAIT_FOR_EVENT;
case PROXY_STATE_INIT:
#if defined(HAVE_SYS_UN_H)
if (strstr(host->host->ptr,"/")) {
if (-1 == (hctx->fd = socket(AF_UNIX, SOCK_STREAM, 0))) {
if (-1 == (hctx->fd = fdevent_socket_nb_cloexec(AF_UNIX, SOCK_STREAM, 0))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno));
return HANDLER_ERROR;
}
@ -807,28 +949,54 @@ static handler_t proxy_write_request(server *srv, handler_ctx *hctx) {
return HANDLER_ERROR;
}
switch (proxy_establish_connection(srv, hctx)) {
case 1:
proxy_set_state(srv, hctx, PROXY_STATE_CONNECT);
/* fall through */
case PROXY_STATE_CONNECT:
if (hctx->state == PROXY_STATE_INIT) {
switch (proxy_establish_connection(srv, hctx)) {
case 1:
proxy_set_state(srv, hctx, PROXY_STATE_CONNECT);
/* connection is in progress, wait for an event and call getsockopt() below */
/* connection is in progress, wait for an event and call getsockopt() below */
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
return HANDLER_WAIT_FOR_EVENT;
case -1:
/* if ECONNREFUSED choose another connection */
hctx->fde_ndx = -1;
return HANDLER_WAIT_FOR_EVENT;
case -1:
/* if ECONNREFUSED choose another connection */
hctx->fde_ndx = -1;
return HANDLER_ERROR;
default:
/* everything is ok, go on */
proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE);
break;
return HANDLER_ERROR;
default:
/* everything is ok, go on */
break;
}
} else {
int socket_error;
socklen_t socket_error_len = sizeof(socket_error);
/* 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->host->port);
return HANDLER_ERROR;
}
if (hctx->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "s", "proxy - connect - delayed success");
}
}
/* fall through */
/* ok, we have the connection */
proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE);
/* fall through */
case PROXY_STATE_PREPARE_WRITE:
proxy_create_env(srv, hctx);
@ -923,8 +1091,6 @@ static handler_t proxy_send_request(server *srv, handler_ctx *hctx) {
return rc;
} else {
data_proxy *host = hctx->host;
connection *con = hctx->remote_conn;
plugin_data *p = hctx->plugin_data;
log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:",
host->host,
host->port,
@ -934,12 +1100,8 @@ static handler_t proxy_send_request(server *srv, handler_ctx *hctx) {
host->is_disabled = 1;
host->disable_ts = srv->cur_ts;
/* reset the enviroment and restart the sub-request */
con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
proxy_connection_close(srv, hctx);
con->mode = p->id;
return HANDLER_COMEBACK;
/* reset the environment and restart the sub-request */
return proxy_reconnect(srv, hctx);
}
}
@ -1019,92 +1181,23 @@ static handler_t proxy_recv_response(server *srv, handler_ctx *hctx) {
static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
handler_ctx *hctx = ctx;
connection *con = hctx->remote_conn;
plugin_data *p = hctx->plugin_data;
joblist_append(srv, con);
if (revents & FDEVENT_IN) {
if (hctx->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sd",
"proxy: fdevent-in", hctx->state);
}
{
handler_t rc = proxy_recv_response(srv,hctx);/*(might invalidate hctx)*/
if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
}
}
if (revents & FDEVENT_OUT) {
if (hctx->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sd",
"proxy: fdevent-out", hctx->state);
}
if (hctx->state == PROXY_STATE_CONNECT) {
int socket_error;
socklen_t socket_error_len = sizeof(socket_error);
/* 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_FINISHED;
}
if (socket_error != 0) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"establishing connection failed:", strerror(socket_error),
"port:", hctx->host->port);
return HANDLER_FINISHED;
}
if (hctx->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "s", "proxy - connect - delayed success");
}
proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE);
}
return proxy_send_request(srv, hctx); /*(might invalidate hctx)*/
}
/* perhaps this issue is already handled */
if (revents & FDEVENT_HUP) {
if (hctx->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sd",
"proxy: fdevent-hup", hctx->state);
}
if (hctx->state == PROXY_STATE_CONNECT) {
/* connect() -> EINPROGRESS -> HUP */
/**
* what is proxy is doing if it can't reach the next hop ?
*
*/
if (hctx->host) {
hctx->host->is_disabled = 1;
hctx->host->disable_ts = srv->cur_ts;
log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:",
hctx->host->host,
hctx->host->port,
hctx->fd);
/* disable this server */
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);
con->mode = p->id;
} else {
proxy_connection_close(srv, hctx);
con->http_status = 503;
}
proxy_send_request(srv, hctx); /*(might invalidate hctx)*/
} else if (con->file_started) {
/* drain any remaining data from kernel pipe buffers
* even if (con->conf.stream_response_body
@ -1132,13 +1225,10 @@ static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p_d) {
plugin_data *p = p_d;
size_t s_len;
unsigned long last_max = ULONG_MAX;
int max_usage = INT_MAX;
int ndx = -1;
size_t k;
buffer *fn;
data_array *extension = NULL;
size_t path_info_offset;
data_proxy *host;
if (con->mode != DIRECT) return HANDLER_GO_ON;
@ -1151,12 +1241,6 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p
if (buffer_string_is_empty(fn)) return HANDLER_ERROR;
s_len = buffer_string_length(fn);
path_info_offset = 0;
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "s", "proxy - start");
}
/* check if extension matches */
for (k = 0; k < p->conf.extensions->used; k++) {
data_array *ext = NULL;
@ -1173,13 +1257,6 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p
/* check extension in the form "/proxy_pattern" */
if (*(ext->key->ptr) == '/') {
if (strncmp(fn->ptr, ext->key->ptr, ct_len) == 0) {
if (s_len > ct_len + 1) {
char *pi_offset;
if (NULL != (pi_offset = strchr(fn->ptr + ct_len + 1, '/'))) {
path_info_offset = pi_offset - fn->ptr;
}
}
extension = ext;
break;
}
@ -1194,119 +1271,13 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p
return HANDLER_GO_ON;
}
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "s", "proxy - ext found");
}
if (extension->value->used == 1) {
if ( ((data_proxy *)extension->value->data[0])->is_disabled ) {
ndx = -1;
} else {
ndx = 0;
}
} else if (extension->value->used != 0) switch(p->conf.balance) {
case PROXY_BALANCE_HASH:
/* hash balancing */
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sd",
"proxy - used hash balancing, hosts:", extension->value->used);
}
for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->value->used; k++) {
data_proxy *host = (data_proxy *)extension->value->data[k];
unsigned long cur_max;
if (host->is_disabled) continue;
cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path)) +
generate_crc32c(CONST_BUF_LEN(host->host)) + /* we can cache this */
generate_crc32c(CONST_BUF_LEN(con->uri.authority));
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sbbbd",
"proxy - election:",
con->uri.path,
host->host,
con->uri.authority,
cur_max);
}
if ((last_max == ULONG_MAX) || /* first round */
(cur_max > last_max)) {
last_max = cur_max;
ndx = k;
}
}
break;
case PROXY_BALANCE_FAIR:
/* fair balancing */
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "s",
"proxy - used fair balancing");
}
for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->value->used; k++) {
data_proxy *host = (data_proxy *)extension->value->data[k];
if (host->is_disabled) continue;
if (host->usage < max_usage) {
max_usage = host->usage;
ndx = k;
}
}
break;
case PROXY_BALANCE_RR: {
data_proxy *host;
/* round robin */
if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "s",
"proxy - used round-robin balancing");
}
/* just to be sure */
force_assert(extension->value->used < INT_MAX);
host = (data_proxy *)extension->value->data[0];
/* Use last_used_ndx from first host in list */
k = host->last_used_ndx;
ndx = k + 1; /* use next host after the last one */
if (ndx < 0) ndx = 0;
/* Search first active host after last_used_ndx */
while ( ndx < (int) extension->value->used
&& (host = (data_proxy *)extension->value->data[ndx])->is_disabled ) ndx++;
if (ndx >= (int) extension->value->used) {
/* didn't found a higher id, wrap to the start */
for (ndx = 0; ndx <= (int) k; ndx++) {
host = (data_proxy *)extension->value->data[ndx];
if (!host->is_disabled) break;
}
/* No active host found */
if (host->is_disabled) ndx = -1;
}
/* Save new index for next round */
((data_proxy *)extension->value->data[0])->last_used_ndx = ndx;
break;
}
default:
break;
host = mod_proxy_extension_host_get(srv, con, p, extension);
if (NULL == host) {
return HANDLER_FINISHED;
}
/* found a server */
if (ndx != -1) {
data_proxy *host = (data_proxy *)extension->value->data[ndx];
{
/*
* if check-local is disabled, use the uri.path handler
@ -1317,17 +1288,14 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p
handler_ctx *hctx;
hctx = handler_ctx_init();
hctx->path_info_offset = path_info_offset;
hctx->remote_conn = con;
hctx->plugin_data = p;
hctx->host = host;
hctx->ext = extension;
hctx->conf.debug = p->conf.debug;
con->plugin_ctx[p->id] = hctx;
host->usage++;
con->mode = p->id;
if (p->conf.debug) {
@ -1337,17 +1305,7 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p
}
return HANDLER_GO_ON;
} else {
/* no handler found */
con->http_status = 500;
log_error_write(srv, __FILE__, __LINE__, "sb",
"no proxy-handler found for:",
fn);
return HANDLER_FINISHED;
}
return HANDLER_GO_ON;
}
static handler_t mod_proxy_connection_reset(server *srv, connection *con, void *p_d) {

@ -329,6 +329,7 @@ typedef struct {
connection *remote_conn; /* dumb pointer */
plugin_data *plugin_data; /* dumb pointer */
scgi_extension *ext;
} handler_ctx;
@ -1331,25 +1332,20 @@ static int scgi_set_state(server *srv, handler_ctx *hctx, scgi_connection_state_
}
static void scgi_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
p = hctx->plugin_data;
con = hctx->remote_conn;
static void scgi_backend_close(server *srv, handler_ctx *hctx) {
if (hctx->fd != -1) {
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
fdevent_unregister(srv->ev, hctx->fd);
fdevent_sched_close(srv->ev, hctx->fd, 1);
hctx->fd = -1;
hctx->fde_ndx = -1;
}
if (hctx->host && hctx->proc) {
hctx->host->load--;
if (hctx->got_proc) {
if (hctx->host) {
if (hctx->proc) {
/* after the connect the process gets a load */
hctx->proc->load--;
if (hctx->got_proc) hctx->proc->load--;
scgi_proclist_sort_down(srv, hctx->host, hctx->proc);
if (hctx->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sddb",
@ -1359,10 +1355,59 @@ static void scgi_connection_close(server *srv, handler_ctx *hctx) {
}
}
scgi_proclist_sort_down(srv, hctx->host, hctx->proc);
hctx->host->load--;
hctx->host = NULL;
}
}
static scgi_extension_host * scgi_extension_host_get(server *srv, connection *con, plugin_data *p, scgi_extension *extension) {
int used = -1;
scgi_extension_host *host = NULL;
UNUSED(p);
/* get best server */
for (size_t k = 0; k < extension->used; ++k) {
scgi_extension_host *h = extension->hosts[k];
/* we should have at least one proc that can do something */
if (h->active_procs == 0) {
continue;
}
if (used == -1 || h->load < used) {
used = h->load;
host = h;
}
}
if (!host) {
/* sorry, we don't have a server alive for this ext */
con->http_status = 503; /* Service Unavailable */
con->mode = DIRECT;
/* only send the 'no handler' once */
if (!extension->note_is_sent) {
extension->note_is_sent = 1;
log_error_write(srv, __FILE__, __LINE__, "sbsbs",
"all handlers for ", con->uri.path,
"on", extension->key,
"are down.");
}
}
return host;
}
static void scgi_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
p = hctx->plugin_data;
con = hctx->remote_conn;
scgi_backend_close(srv, hctx);
handler_ctx_free(hctx);
con->plugin_ctx[p->id] = NULL;
@ -1372,45 +1417,15 @@ static void scgi_connection_close(server *srv, handler_ctx *hctx) {
}
}
static int scgi_reconnect(server *srv, handler_ctx *hctx) {
/* child died
*
* 1.
*
* connect was ok, connection was accepted
* but the php accept loop checks after the accept if it should die or not.
*
* if yes we can only detect it at a write()
*
* next step is resetting this attemp and setup a connection again
*
* if we have more then 5 reconnects for the same request, die
*
* 2.
*
* we have a connection but the child died by some other reason
*
*/
static handler_t scgi_reconnect(server *srv, handler_ctx *hctx) {
scgi_backend_close(srv, hctx);
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
fdevent_unregister(srv->ev, hctx->fd);
fdevent_sched_close(srv->ev, hctx->fd, 1);
hctx->host = scgi_extension_host_get(srv, hctx->remote_conn, hctx->plugin_data, hctx->ext);
if (NULL == hctx->host) return HANDLER_FINISHED;
hctx->host->load++;
scgi_set_state(srv, hctx, FCGI_STATE_INIT);
hctx->reconnects++;
if (hctx->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sddb",
"release proc:",
hctx->fd,
hctx->proc->pid, hctx->proc->socket);
}
hctx->proc->load--;
scgi_proclist_sort_down(srv, hctx->host, hctx->proc);
return 0;
return HANDLER_COMEBACK;
}
@ -2141,22 +2156,6 @@ static handler_t scgi_write_request(server *srv, handler_ctx *hctx) {
int ret;
/* sanity check */
if (!host) {
log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: host = NULL");
return HANDLER_ERROR;
}
if (((buffer_string_is_empty(host->host) || !host->port) && buffer_string_is_empty(host->unixsocket))) {
log_error_write(srv, __FILE__, __LINE__, "sxddd",
"write-req: error",
host,
buffer_string_length(host->host),
host->port,
buffer_string_length(host->unixsocket));
return HANDLER_ERROR;
}
switch(hctx->state) {
case FCGI_STATE_INIT:
if (-1 == (hctx->fd = fdevent_socket_nb_cloexec(host->family, SOCK_STREAM, 0))) {
@ -2290,14 +2289,12 @@ static handler_t scgi_write_request(server *srv, handler_ctx *hctx) {
*
*/
if (hctx->wb->bytes_out == 0 &&
hctx->reconnects < 5) {
hctx->reconnects++ < 5) {
usleep(10000); /* take away the load of the webserver
* to let the php a chance to restart
*/
scgi_reconnect(srv, hctx);
return HANDLER_COMEBACK;
return scgi_reconnect(srv, hctx);
}
/* not reconnected ... why
@ -2408,11 +2405,7 @@ static handler_t scgi_send_request(server *srv, handler_ctx *hctx) {
}
scgi_restart_dead_procs(srv, p, host);
con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
scgi_connection_close(srv, hctx);
con->mode = p->id;
return HANDLER_COMEBACK;
return scgi_reconnect(srv, hctx);
} else {
scgi_connection_close(srv, hctx);
con->http_status = 503;
@ -2543,16 +2536,14 @@ static handler_t scgi_recv_response(server *srv, handler_ctx *hctx) {
/* nothing has been send out yet, try to use another child */
if (hctx->wb->bytes_out == 0 &&
hctx->reconnects < 5) {
hctx->reconnects++ < 5) {
log_error_write(srv, __FILE__, __LINE__, "ssdsd",
"response not sent, request not sent, reconnection.",
"connection-fd:", con->fd,
"fcgi-fd:", hctx->fd);
scgi_reconnect(srv, hctx);
return HANDLER_COMEBACK;
return scgi_reconnect(srv, hctx);
}
log_error_write(srv, __FILE__, __LINE__, "sosdsd",
@ -2679,7 +2670,6 @@ static int scgi_patch_connection(server *srv, connection *con, plugin_data *p) {
static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) {
plugin_data *p = p_d;
size_t s_len;
int used = -1;
size_t k;
buffer *fn;
scgi_extension *extension = NULL;
@ -2728,36 +2718,8 @@ static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, i
}
/* get best server */
for (k = 0; k < extension->used; k++) {
scgi_extension_host *h = extension->hosts[k];
/* we should have at least one proc that can do something */
if (h->active_procs == 0) {
continue;
}
if (used == -1 || h->load < used) {
used = h->load;
host = h;
}
}
if (!host) {
/* sorry, we don't have a server alive for this ext */
con->http_status = 500;
con->mode = DIRECT;
/* only send the 'no handler' once */
if (!extension->note_is_sent) {
extension->note_is_sent = 1;
log_error_write(srv, __FILE__, __LINE__, "sbsbs",
"all handlers for ", con->uri.path,
"on", extension->key,
"are down.");
}
host = scgi_extension_host_get(srv, con, p, extension);
if (NULL == host) {
return HANDLER_FINISHED;
}
@ -2793,8 +2755,9 @@ static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, i
hctx->plugin_data = p;
hctx->host = host;
hctx->proc = NULL;
hctx->ext = extension;
hctx->conf.exts = p->conf.exts;
/*hctx->conf.exts = p->conf.exts;*/
hctx->conf.debug = p->conf.debug;
con->plugin_ctx[p->id] = hctx;
@ -2849,8 +2812,9 @@ static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, i
hctx->plugin_data = p;
hctx->host = host;
hctx->proc = NULL;
hctx->ext = extension;
hctx->conf.exts = p->conf.exts;
/*hctx->conf.exts = p->conf.exts;*/
hctx->conf.debug = p->conf.debug;
con->plugin_ctx[p->id] = hctx;

Loading…
Cancel
Save