Browse Source

[multiple] gw_backends config_plugin_values_init()

personal/stbuehler/ci-build
Glenn Strauss 2 years ago
parent
commit
4a6fe83837
  1. 261
      src/gw_backend.c
  2. 45
      src/gw_backend.h
  3. 202
      src/mod_fastcgi.c
  4. 593
      src/mod_proxy.c
  5. 242
      src/mod_scgi.c
  6. 204
      src/mod_sockproxy.c
  7. 315
      src/mod_wstunnel.c

261
src/gw_backend.c

@ -158,7 +158,7 @@ static void gw_host_free(gw_host *h) {
gw_proc_free(h->first);
gw_proc_free(h->unused_procs);
for (size_t i = 0; i < h->args.used; ++i) free(h->args.ptr[i]);
for (uint32_t i = 0; i < h->args.used; ++i) free(h->args.ptr[i]);
free(h->args.ptr);
free(h);
}
@ -171,9 +171,9 @@ static gw_exts *gw_extensions_init(void) {
static void gw_extensions_free(gw_exts *f) {
if (!f) return;
for (size_t i = 0; i < f->used; ++i) {
for (uint32_t i = 0; i < f->used; ++i) {
gw_extension *fe = f->exts[i];
for (size_t j = 0; j < fe->used; ++j) {
for (uint32_t j = 0; j < fe->used; ++j) {
gw_host_free(fe->hosts[j]);
}
buffer_free(fe->key);
@ -186,7 +186,7 @@ static void gw_extensions_free(gw_exts *f) {
static int gw_extension_insert(gw_exts *ext, buffer *key, gw_host *fh) {
gw_extension *fe = NULL;
for (size_t i = 0; i < ext->used; ++i) {
for (uint32_t i = 0; i < ext->used; ++i) {
if (buffer_is_equal(key, ext->exts[i]->key)) {
fe = ext->exts[i];
break;
@ -423,7 +423,7 @@ static int env_add(char_array *env, const char *key, size_t key_len, const char
dst[key_len] = '=';
memcpy(dst + key_len + 1, val, val_len + 1); /* add the \0 from the value */
for (size_t i = 0; i < env->used; ++i) {
for (uint32_t i = 0; i < env->used; ++i) {
if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
free(env->ptr[i]);
env->ptr[i] = dst;
@ -476,7 +476,7 @@ static int gw_spawn_connection(server *srv, gw_host *host, gw_proc *proc, int de
if (-1 == status) {
/* server is not up, spawn it */
char_array env;
size_t i;
uint32_t i;
int dfd = -1;
/* reopen socket */
@ -701,13 +701,28 @@ static void gw_proc_kill(server *srv, gw_host *host, gw_proc *proc) {
--host->num_procs;
}
static gw_host * unixsocket_is_dup(gw_plugin_data *p, size_t used, buffer *unixsocket) {
for (size_t i = 0; i < used; ++i) {
gw_exts *exts = p->config_storage[i]->exts;
if (NULL == exts) continue;
for (size_t j = 0; j < exts->used; ++j) {
static gw_host * unixsocket_is_dup(gw_plugin_data *p, buffer *unixsocket) {
if (NULL == p->cvlist) return NULL;
/* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
gw_plugin_config *conf = NULL;
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0: /* xxxxx.server */
if (cpv->vtype == T_CONFIG_LOCAL) conf = cpv->v.v;
break;
default:
break;
}
}
if (NULL == conf || NULL == conf->exts) continue;
gw_exts *exts = conf->exts;
for (uint32_t j = 0; j < exts->used; ++j) {
gw_extension *ex = exts->exts[j];
for (size_t n = 0; n < ex->used; ++n) {
for (uint32_t n = 0; n < ex->used; ++n) {
gw_host *host = ex->hosts[n];
if (!buffer_string_is_empty(host->unixsocket)
&& buffer_is_equal(host->unixsocket, unixsocket)
@ -778,7 +793,7 @@ static gw_host * gw_host_get(server *srv, connection *con, gw_extension *extensi
unsigned long last_max = ULONG_MAX;
int max_usage = INT_MAX;
int ndx = -1;
size_t k;
uint32_t k;
if (extension->used <= 1) {
if (1 == extension->used && extension->hosts[0]->active_procs > 0) {
@ -1122,9 +1137,9 @@ void * gw_init(void) {
void gw_plugin_config_free(gw_plugin_config *s) {
gw_exts *exts = s->exts;
if (exts) {
for (size_t j = 0; j < exts->used; ++j) {
for (uint32_t j = 0; j < exts->used; ++j) {
gw_extension *ex = exts->exts[j];
for (size_t n = 0; n < ex->used; ++n) {
for (uint32_t n = 0; n < ex->used; ++n) {
gw_proc *proc;
gw_host *host = ex->hosts[n];
@ -1155,40 +1170,46 @@ void gw_plugin_config_free(gw_plugin_config *s) {
gw_extensions_free(s->exts_auth);
gw_extensions_free(s->exts_resp);
}
array_free(s->ext_mapping);
free(s);
}
handler_t gw_free(server *srv, void *p_d) {
gw_plugin_data *p = p_d;
if (p->config_storage) {
for (size_t i = 0; i < srv->config_context->used; ++i) {
gw_plugin_config *s = p->config_storage[i];
if (NULL == s) continue;
gw_plugin_config_free(s);
gw_plugin_data * const p = p_d;
if (!p) return HANDLER_GO_ON;
UNUSED(srv);
if (NULL == p->cvlist) { free(p); return HANDLER_GO_ON; }
/* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0: /* xxxxx.server */
if (cpv->vtype == T_CONFIG_LOCAL)
gw_plugin_config_free(cpv->v.v);
break;
default:
break;
}
}
free(p->config_storage);
}
free(p->cvlist);
free(p);
return HANDLER_GO_ON;
}
int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du, size_t i, int sh_exec) {
int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const array *a, gw_plugin_config *s, int sh_exec, const char *cpkkey) {
/* per-module plugin_config MUST have common "base class" gw_plugin_config*/
/* per-module plugin_data MUST have pointer-compatible common "base class"
* with gw_plugin_data (stemming from gw_plugin_config compatibility) */
const data_array *da = (const data_array *)du;
gw_plugin_config *s = p->config_storage[i];
buffer *gw_mode;
gw_host *host = NULL;
if (NULL == da) return 1;
if (da->type != TYPE_ARRAY || !array_is_kvarray(&da->value)) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for xxxxx.server; expected "
"( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
if (!array_is_kvarray(a)) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for %s; expected "
"( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))",
cpkkey);
return 0;
}
@ -1206,8 +1227,8 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
* "<ext>" => ( ... ) )
*/
for (size_t j = 0; j < da->value.used; ++j) {
data_array *da_ext = (data_array *)da->value.data[j];
for (uint32_t j = 0; j < a->used; ++j) {
data_array *da_ext = (data_array *)a->data[j];
/*
* da_ext->key == name of the extension
@ -1221,7 +1242,7 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
* "<ext>" => ... )
*/
for (size_t n = 0; n < da_ext->value.used; ++n) {
for (uint32_t n = 0; n < da_ext->value.used; ++n) {
data_array *da_host = (data_array *)da_ext->value.data[n];
config_values_t fcv[] = {
@ -1231,7 +1252,7 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
{ "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
{ "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
{ "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
{ "check-local", NULL, T_CONFIG_BOOL, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
{ "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
{ "min-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
{ "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
@ -1242,15 +1263,15 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
{ "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
{ "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
{ "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
{ "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
{ "broken-scriptfilename", NULL, T_CONFIG_BOOL, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
{ "allow-x-send-file", NULL, T_CONFIG_BOOL, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
{ "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
{ "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */
{ "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 18 */
{ "fix-root-scriptname", NULL, T_CONFIG_BOOL, T_CONFIG_SCOPE_CONNECTION }, /* 18 */
{ "listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 19 */
{ "x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 20 */
{ "x-sendfile", NULL, T_CONFIG_BOOL, T_CONFIG_SCOPE_CONNECTION }, /* 20 */
{ "x-sendfile-docroot",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 21 */
{ "tcp-fin-propagate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 22 */
{ "tcp-fin-propagate", NULL, T_CONFIG_BOOL, T_CONFIG_SCOPE_CONNECTION }, /* 22 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
@ -1312,7 +1333,7 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
goto error;
}
for (size_t m = 0; m < da_host->value.used; ++m) {
for (uint32_t m = 0; m < da_host->value.used; ++m) {
if (NULL != strchr(da_host->value.data[m]->key.ptr, '_')) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"incorrect directive contains underscore ('_') instead of dash ('-'):",
@ -1322,9 +1343,9 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
if ((!buffer_string_is_empty(host->host) || host->port)
&& !buffer_string_is_empty(host->unixsocket)) {
log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
log_error_write(srv, __FILE__, __LINE__, "sssbsbs",
"either host/port or socket have to be set in:",
&da->key, "= (",
cpkkey, "= (",
&da_ext->key, " => (",
&da_host->key, " ( ...");
@ -1341,9 +1362,9 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
struct sockaddr_un un;
if (buffer_string_length(host->unixsocket) + 1 > sizeof(un.sun_path) - 2) {
log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
log_error_write(srv, __FILE__, __LINE__, "sssbsbs",
"unixsocket is too long in:",
&da->key, "= (",
cpkkey, "= (",
&da_ext->key, " => (",
&da_host->key, " ( ...");
@ -1351,7 +1372,7 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
}
if (!buffer_string_is_empty(host->bin_path)) {
gw_host *duplicate = unixsocket_is_dup(p, i+1, host->unixsocket);
gw_host *duplicate = unixsocket_is_dup(p, host->unixsocket);
if (NULL != duplicate) {
if (!buffer_is_equal(host->bin_path, duplicate->bin_path)) {
log_error_write(srv, __FILE__, __LINE__, "sb",
@ -1371,9 +1392,9 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
if (buffer_string_is_empty(host->host) &&
buffer_string_is_empty(host->bin_path)) {
log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
log_error_write(srv, __FILE__, __LINE__, "sssbsbs",
"host or bin-path have to be set in:",
&da->key, "= (",
cpkkey, "= (",
&da_ext->key, " => (",
&da_host->key, " ( ...");
@ -1410,7 +1431,7 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
/*(preserve prior behavior for SCGI exec of command)*/
/*(admin should really prefer to put
* any complex command into a script)*/
for (size_t m = 0; m < host->args.used; ++m)
for (uint32_t m = 0; m < host->args.used; ++m)
free(host->args.ptr[m]);
free(host->args.ptr);
@ -1457,7 +1478,7 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
"\n\tmax-procs:", host->max_procs);
}
for (size_t pno = 0; pno < host->min_procs; ++pno) {
for (uint32_t pno = 0; pno < host->min_procs; ++pno) {
gw_proc *proc = gw_proc_init();
proc->id = host->num_procs++;
host->max_id++;
@ -1536,7 +1557,7 @@ int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du
}
if (host->xsendfile_docroot->used) {
size_t k;
uint32_t k;
for (k = 0; k < host->xsendfile_docroot->used; ++k) {
data_string *ds = (data_string *)host->xsendfile_docroot->data[k];
if (ds->type != TYPE_STRING) {
@ -1590,39 +1611,28 @@ error:
return 0;
}
int gw_set_defaults_balance(server *srv, gw_plugin_config *s, const data_unset *du) {
const buffer *b;
if (NULL == du) {
b = NULL;
} else if (du->type == TYPE_STRING) {
b = &((const data_string *)du)->value;
} else {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected type for xxxxx.balance; expected string");
return 0;
}
if (buffer_string_is_empty(b)) {
s->balance = GW_BALANCE_LEAST_CONNECTION;
} else if (buffer_is_equal_string(b, CONST_STR_LEN("fair"))) {
s->balance = GW_BALANCE_LEAST_CONNECTION;
} else if (buffer_is_equal_string(b, CONST_STR_LEN("least-connection"))) {
s->balance = GW_BALANCE_LEAST_CONNECTION;
} else if (buffer_is_equal_string(b, CONST_STR_LEN("round-robin"))) {
s->balance = GW_BALANCE_RR;
} else if (buffer_is_equal_string(b, CONST_STR_LEN("hash"))) {
s->balance = GW_BALANCE_HASH;
} else if (buffer_is_equal_string(b, CONST_STR_LEN("sticky"))) {
s->balance = GW_BALANCE_STICKY;
} else {
log_error_write(srv, __FILE__, __LINE__, "sb",
"xxxxx.balance has to be one of: "
"least-connection, round-robin, hash, sticky, but not:",
b);
return 0;
}
return 1;
int gw_get_defaults_balance(server *srv, const buffer *b) {
if (buffer_string_is_empty(b))
return GW_BALANCE_LEAST_CONNECTION;
if (buffer_eq_slen(b, CONST_STR_LEN("fair")))
return GW_BALANCE_LEAST_CONNECTION;
if (buffer_eq_slen(b, CONST_STR_LEN("least-connection")))
return GW_BALANCE_LEAST_CONNECTION;
if (buffer_eq_slen(b, CONST_STR_LEN("round-robin")))
return GW_BALANCE_RR;
if (buffer_eq_slen(b, CONST_STR_LEN("hash")))
return GW_BALANCE_HASH;
if (buffer_eq_slen(b, CONST_STR_LEN("sticky")))
return GW_BALANCE_STICKY;
log_error_write(srv, __FILE__, __LINE__, "sb",
"xxxxx.balance has to be one of: "
"least-connection, round-robin, hash, sticky, but not:",
b);
return GW_BALANCE_LEAST_CONNECTION;
}
static void gw_set_state(server *srv, gw_handler_ctx *hctx, gw_connection_state_t state) {
hctx->state = state;
hctx->state_timestamp = srv->cur_ts;
@ -2258,7 +2268,7 @@ handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, in
if (NULL != ds) {
/* found a mapping */
/* check if we know the extension */
size_t k;
uint32_t k;
for (k = 0; k < exts->used; ++k) {
extension = exts->exts[k];
@ -2278,7 +2288,7 @@ handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, in
size_t uri_path_len = buffer_string_length(con->uri.path);
/* check if extension matches */
for (size_t k = 0; k < exts->used; ++k) {
for (uint32_t k = 0; k < exts->used; ++k) {
gw_extension *ext = exts->exts[k];
size_t ct_len = buffer_string_length(ext->key);
@ -2489,18 +2499,18 @@ static void gw_handle_trigger_host(server *srv, gw_host *host, int debug) {
}
static void gw_handle_trigger_exts(server *srv, gw_exts *exts, int debug) {
for (size_t j = 0; j < exts->used; ++j) {
for (uint32_t j = 0; j < exts->used; ++j) {
gw_extension *ex = exts->exts[j];
for (size_t n = 0; n < ex->used; ++n) {
for (uint32_t n = 0; n < ex->used; ++n) {
gw_handle_trigger_host(srv, ex->hosts[n], debug);
}
}
}
static void gw_handle_trigger_exts_wkr(server *srv, gw_exts *exts) {
for (size_t j = 0; j < exts->used; ++j) {
for (uint32_t j = 0; j < exts->used; ++j) {
gw_extension * const ex = exts->exts[j];
for (size_t n = 0; n < ex->used; ++n) {
for (uint32_t n = 0; n < ex->used; ++n) {
gw_host * const host = ex->hosts[n];
for (gw_proc *proc = host->first; proc; proc = proc->next) {
if (proc->state == PROC_STATE_OVERLOADED)
@ -2511,35 +2521,78 @@ static void gw_handle_trigger_exts_wkr(server *srv, gw_exts *exts) {
}
handler_t gw_handle_trigger(server *srv, void *p_d) {
gw_plugin_data *p = p_d;
gw_plugin_data * const p = p_d;
int wkr = (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid);
int global_debug = 0;
if (NULL == p->cvlist) return HANDLER_GO_ON;
/* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
gw_plugin_config *conf = NULL;
int debug = global_debug;
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0: /* xxxxx.server */
if (cpv->vtype == T_CONFIG_LOCAL) conf = cpv->v.v;
break;
case 2: /* xxxxx.debug */
debug = (int)cpv->v.u;
if (0 == i) global_debug = (int)cpv->v.u;
default:
break;
}
}
for (size_t i = 0; i < srv->config_context->used; i++) {
gw_plugin_config *conf = p->config_storage[i];
gw_exts *exts = conf->exts;
int debug = conf->debug ? conf->debug : p->config_storage[0]->debug;
if (NULL == exts) continue;
if (NULL == conf || NULL == conf->exts) continue;
/* (debug flag is only active if set in same scope as xxxxx.server
* or global scope (for convenience))
* (unable to use p->defaults.debug since gw_plugin_config
* might be part of a larger plugin_config) */
wkr
? gw_handle_trigger_exts_wkr(srv, exts)
: gw_handle_trigger_exts(srv, exts, debug);
? gw_handle_trigger_exts_wkr(srv, conf->exts)
: gw_handle_trigger_exts(srv, conf->exts, debug);
}
return HANDLER_GO_ON;
}
handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
gw_plugin_data *p = p_d;
gw_plugin_data * const p = p_d;
if (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid)
return HANDLER_GO_ON;
int global_debug = 0;
if (NULL == p->cvlist) return HANDLER_GO_ON;
/* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
gw_plugin_config *conf = NULL;
int debug = global_debug;
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0: /* xxxxx.server */
if (cpv->vtype == T_CONFIG_LOCAL) conf = cpv->v.v;
break;
case 2: /* xxxxx.debug */
debug = (int)cpv->v.u;
if (0 == i) global_debug = (int)cpv->v.u;
default:
break;
}
}
if (NULL == conf || NULL == conf->exts) continue;
for (size_t i = 0; i < srv->config_context->used; ++i) {
gw_plugin_config *conf = p->config_storage[i];
/* (debug flag is only active if set in same scope as xxxxx.server
* or global scope (for convenience))
* (unable to use p->defaults.debug since gw_plugin_config
* might be part of a larger plugin_config) */
gw_exts *exts = conf->exts;
int debug = conf->debug ? conf->debug : p->config_storage[0]->debug;
if (NULL == exts) continue;
for (size_t j = 0; j < exts->used; ++j) {
for (uint32_t j = 0; j < exts->used; ++j) {
gw_extension *ex = exts->exts[j];
for (size_t n = 0; n < ex->used; ++n) {
for (uint32_t n = 0; n < ex->used; ++n) {
gw_host *host = ex->hosts[n];
gw_proc *proc;
for (proc = host->first; proc; proc = proc->next) {

45
src/gw_backend.h

@ -12,14 +12,14 @@
typedef struct {
char **ptr;
size_t size;
size_t used;
uint32_t size;
uint32_t used;
} char_array;
typedef struct gw_proc {
size_t id; /* id will be between 1 and max_procs */
uint32_t id; /* id will be between 1 and max_procs */
unsigned short port; /* config.port + pno */
buffer *unixsocket; /* config.socket + "-" + id */
unsigned port; /* config.port + pno */
socklen_t saddrlen;
struct sockaddr *saddr;
@ -28,13 +28,11 @@ typedef struct gw_proc {
pid_t pid; /* PID of the spawned process (0 if not spawned locally) */
uint32_t load; /* number of requests waiting on this process */
size_t load; /* number of requests waiting on this process */
time_t last_used; /* see idle_timeout */
size_t requests; /* see max_requests */
struct gw_proc *prev, *next; /* see first */
time_t last_used; /* see idle_timeout */
time_t disabled_until; /* proc disabled until given time */
int is_local;
@ -74,8 +72,8 @@ typedef struct {
unsigned short min_procs;
unsigned short max_procs;
size_t num_procs; /* how many procs are started */
size_t active_procs; /* how many procs in state PROC_STATE_RUNNING */
uint32_t num_procs; /* how many procs are started */
uint32_t active_procs; /* how many procs in state PROC_STATE_RUNNING */
unsigned short max_load_per_proc;
@ -103,7 +101,7 @@ typedef struct {
* process after a number of handled requests.
*
*/
size_t max_requests_per_proc;
uint32_t max_requests_per_proc;
/* config */
@ -197,9 +195,9 @@ typedef struct {
unsigned short xsendfile_allow;
array *xsendfile_docroot;
ssize_t load;
int32_t load;
size_t max_id; /* corresponds most of the time to num_procs */
uint32_t max_id; /* corresponds most of the time to num_procs */
buffer *strip_request_uri;
@ -242,15 +240,15 @@ typedef struct {
gw_host **hosts;
size_t used;
size_t size;
uint32_t used;
uint32_t size;
} gw_extension;
typedef struct {
gw_extension **exts;
size_t used;
size_t size;
uint32_t used;
uint32_t size;
} gw_exts;
@ -265,9 +263,7 @@ typedef struct gw_plugin_config {
gw_exts *exts;
gw_exts *exts_auth;
gw_exts *exts_resp;
array *ext_mapping;
const array *ext_mapping;
int balance;
int proto;
int debug;
@ -276,10 +272,9 @@ typedef struct gw_plugin_config {
/* generic plugin data, shared between all connections */
typedef struct gw_plugin_data {
PLUGIN_DATA;
gw_plugin_config **config_storage;
pid_t srv_pid; /* must precede gw_plugin_config for mods w/ larger struct */
gw_plugin_config conf; /* used only as long as no gw_handler_ctx is setup */
pid_t srv_pid;
gw_plugin_config defaults;/*(must not be used by gw_backend.c: lg struct) */
} gw_plugin_data;
/* connection specific data */
@ -335,8 +330,8 @@ typedef struct gw_handler_ctx {
void * gw_init(void);
void gw_plugin_config_free(gw_plugin_config *s);
handler_t gw_free(server *srv, void *p_d);
int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const data_unset *du, size_t i, int sh_exec);
int gw_set_defaults_balance(server *srv, gw_plugin_config *s, const data_unset *du);
int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const array *a, gw_plugin_config *s, int sh_exec, const char *cpkkey);
int gw_get_defaults_balance(server *srv, const buffer *b);
handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, int uri_path_handler, size_t hctx_sz);
handler_t gw_connection_reset(server *srv, connection *con, void *p_d);
handler_t gw_handle_subrequest(server *srv, connection *con, void *p_d);

202
src/mod_fastcgi.c

@ -1,7 +1,6 @@
#include "first.h"
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
@ -37,59 +36,120 @@ typedef gw_handler_ctx handler_ctx;
#error "mismatched defines: (GW_FILTER != FCGI_FILTER)"
#endif
SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
plugin_data *p = p_d;
const data_unset *du;
size_t i = 0;
config_values_t cv[] = {
{ "fastcgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
{ "fastcgi.debug", NULL, T_CONFIG_INT , T_CONFIG_SCOPE_CONNECTION }, /* 1 */
{ "fastcgi.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
{ "fastcgi.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
force_assert(p->config_storage);
for (i = 0; i < srv->config_context->used; i++) {
data_config const* config = (data_config const*)srv->config_context->data[i];
plugin_config *s;
s = calloc(1, sizeof(plugin_config));
force_assert(s);
s->exts = NULL;
s->exts_auth = NULL;
s->exts_resp = NULL;
s->debug = 0;
s->ext_mapping = array_init();
cv[0].destination = s->exts; /* not used; T_CONFIG_LOCAL */
cv[1].destination = &(s->debug);
cv[2].destination = s->ext_mapping;
cv[3].destination = NULL; /* not used; T_CONFIG_LOCAL */
p->config_storage[i] = s;
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
return HANDLER_ERROR;
}
static void mod_fastcgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
case 0: /* fastcgi.server */
if (cpv->vtype == T_CONFIG_LOCAL) {
gw_plugin_config * const gw = cpv->v.v;
pconf->exts = gw->exts;
pconf->exts_auth = gw->exts_auth;
pconf->exts_resp = gw->exts_resp;
}
break;
case 1: /* fastcgi.balance */
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
pconf->balance = (int)cpv->v.u;
break;
case 2: /* fastcgi.debug */
pconf->debug = (int)cpv->v.u;
break;
case 3: /* fastcgi.map-extensions */
pconf->ext_mapping = cpv->v.a;
break;
default:/* should not happen */
return;
}
}
du = array_get_element_klen(config->value, CONST_STR_LEN("fastcgi.server"));
if (!gw_set_defaults_backend(srv, p, du, i, 0)) {
return HANDLER_ERROR;
}
static void mod_fastcgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
do {
mod_fastcgi_merge_config_cpv(pconf, cpv);
} while ((++cpv)->k_id != -1);
}
du = array_get_element_klen(config->value, CONST_STR_LEN("fastcgi.balance"));
if (!gw_set_defaults_balance(srv, s, du)) {
return HANDLER_ERROR;
}
}
static void mod_fastcgi_patch_config(connection * const con, plugin_data * const p) {
memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
for (int i = 1, used = p->nconfig; i < used; ++i) {
if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id))
mod_fastcgi_merge_config(&p->conf,p->cvlist + p->cvlist[i].v.u2[0]);
}
}
return HANDLER_GO_ON;
SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
static const config_plugin_keys_t cpk[] = {
{ CONST_STR_LEN("fastcgi.server"),
T_CONFIG_ARRAY,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("fastcgi.balance"),
T_CONFIG_STRING,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("fastcgi.debug"),
T_CONFIG_INT,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("fastcgi.map-extensions"),
T_CONFIG_ARRAY,
T_CONFIG_SCOPE_CONNECTION }
,{ NULL, 0,
T_CONFIG_UNSET,
T_CONFIG_SCOPE_UNSET }
};
plugin_data * const p = p_d;
if (!config_plugin_values_init(srv, p, cpk, "mod_fastcgi"))
return HANDLER_ERROR;
/* process and validate config directives
* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0:{/* fastcgi.server */
gw_plugin_config *gw = calloc(1, sizeof(gw_plugin_config));
force_assert(gw);
if (!gw_set_defaults_backend(srv, p, cpv->v.a, gw, 0,
cpk[cpv->k_id].k)) {
gw_plugin_config_free(gw);
return HANDLER_ERROR;
}
cpv->v.v = gw;
cpv->vtype = T_CONFIG_LOCAL;
break;
}
case 1: /* fastcgi.balance */
cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b);
break;
case 2: /* fastcgi.debug */
break;
case 3: /* fastcgi.map-extensions */
if (!array_is_kvstring(cpv->v.a)) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for %s; "
"expected list of \"suffix\" => \"subst\"",
cpk[cpv->k_id].k);
return HANDLER_ERROR;
}
break;
default:/* should not happen */
break;
}
}
}
/* default is 0 */
/*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/
/* initialize p->defaults from global config context */
if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
if (-1 != cpv->k_id)
mod_fastcgi_merge_config(&p->defaults, cpv);
}
return HANDLER_GO_ON;
}
static int fcgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
buffer *env = venv;
size_t len;
@ -437,55 +497,13 @@ static handler_t fcgi_recv_parse(server *srv, connection *con, struct http_respo
return 0 == fin ? HANDLER_GO_ON : HANDLER_FINISHED;
}
#define PATCH(x) \
p->conf.x = s->x;
static int fcgi_patch_connection(server *srv, connection *con, plugin_data *p) {
size_t i, j;
plugin_config *s = p->config_storage[0];
PATCH(exts);
PATCH(exts_auth);
PATCH(exts_resp);
PATCH(debug);
PATCH(balance);
PATCH(ext_mapping);
/* skip the first, the global context */
for (i = 1; i < srv->config_context->used; i++) {
if (!config_check_cond(con, i)) continue; /* condition not matched */
data_config *dc = (data_config *)srv->config_context->data[i];
s = p->config_storage[i];
/* 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("fastcgi.server"))) {
PATCH(exts);
PATCH(exts_auth);
PATCH(exts_resp);
} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("fastcgi.debug"))) {
PATCH(debug);
} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("fastcgi.balance"))) {
PATCH(balance);
} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("fastcgi.map-extensions"))) {
PATCH(ext_mapping);
}
}
}
return 0;
}
#undef PATCH
static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) {
plugin_data *p = p_d;
handler_t rc;
if (con->mode != DIRECT) return HANDLER_GO_ON;
fcgi_patch_connection(srv, con, p);
mod_fastcgi_patch_config(con, p);
if (NULL == p->conf.exts) return HANDLER_GO_ON;
rc = gw_check_extension(srv, con, p, uri_path_handler, 0);

593
src/mod_proxy.c

@ -45,20 +45,17 @@ typedef enum {
} proxy_forwarded_t;
typedef struct {
gw_plugin_config gw;
array *forwarded_params;
array *header_params;
unsigned short replace_http_host;
unsigned int forwarded;
http_header_remap_opts header;
gw_plugin_config gw; /* start must match layout of gw_plugin_config */
unsigned int replace_http_host;
unsigned int forwarded;
http_header_remap_opts header;
} plugin_config;
typedef struct {
PLUGIN_DATA;
plugin_config **config_storage;
plugin_config conf;
PLUGIN_DATA;
pid_t srv_pid; /* must match layout of gw_plugin_data through conf member */
plugin_config conf;
plugin_config defaults;
} plugin_data;
static int proxy_check_extforward;
@ -71,218 +68,356 @@ typedef struct {
INIT_FUNC(mod_proxy_init) {
plugin_data *p;
return calloc(1, sizeof(plugin_data));
}
p = calloc(1, sizeof(*p));
return p;
static void mod_proxy_free_config(plugin_data * const p)
{
if (NULL == p->cvlist) return;
/* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 5: /* proxy.header */
if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v);
break;
default:
break;
}
}
}
}
FREE_FUNC(mod_proxy_free) {
plugin_data *p = p_d;
plugin_data *p = p_d;
if (!p) return HANDLER_GO_ON;
UNUSED(srv);
mod_proxy_free_config(p);
if (p->config_storage) {
size_t i;
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s = p->config_storage[i];
return gw_free(srv, p);
}
if (NULL == s) continue;
static void mod_proxy_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv)
{
switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
case 0: /* proxy.server */
if (cpv->vtype == T_CONFIG_LOCAL) {
gw_plugin_config * const gw = cpv->v.v;
pconf->gw.exts = gw->exts;
pconf->gw.exts_auth = gw->exts_auth;
pconf->gw.exts_resp = gw->exts_resp;
}
break;
case 1: /* proxy.balance */
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
pconf->gw.balance = (int)cpv->v.u;
break;
case 2: /* proxy.debug */
pconf->gw.debug = (int)cpv->v.u;
break;
case 3: /* proxy.map-extensions */
pconf->gw.ext_mapping = cpv->v.a;
break;
case 4: /* proxy.forwarded */
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
pconf->forwarded = cpv->v.u;
break;
case 5: /* proxy.header */
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
pconf->header = *(http_header_remap_opts *)cpv->v.v; /*(copies struct)*/
break;
case 6: /* proxy.replace-http-host */
pconf->replace_http_host = cpv->v.u;
break;
default:/* should not happen */
return;
}
}
array_free(s->forwarded_params);
array_free(s->header_params);
/*assert(0 == offsetof(s->gw));*/
gw_plugin_config_free(&s->gw);
/*free(s);*//*free'd by gw_plugin_config_free()*/
}
free(p->config_storage);
}
static void mod_proxy_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv)
{
do {
mod_proxy_merge_config_cpv(pconf, cpv);
} while ((++cpv)->k_id != -1);
}
free(p);
return HANDLER_GO_ON;
static void mod_proxy_patch_config(connection * const con, plugin_data * const p)
{
memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
for (int i = 1, used = p->nconfig; i < used; ++i) {
if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id))
mod_proxy_merge_config(&p->conf, p->cvlist+p->cvlist[i].v.u2[0]);
}
}
SETDEFAULTS_FUNC(mod_proxy_set_defaults) {
plugin_data *p = p_d;
const data_unset *du;
size_t i = 0;
config_values_t cv[] = {
{ "proxy.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
{ "proxy.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
{ "proxy.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
{ "proxy.replace-http-host", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
{ "proxy.forwarded", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
{ "proxy.header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
{ "proxy.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
for (i = 0; i < srv->config_context->used; i++) {
data_config const* config = (data_config const*)srv->config_context->data[i];
plugin_config *s;
s = calloc(1, sizeof(plugin_config));
s->gw.debug = 0;
s->replace_http_host = 0;
s->forwarded_params = array_init();
s->forwarded = PROXY_FORWARDED_NONE;
s->header_params = array_init();
s->gw.ext_mapping = array_init();
cv[0].destination = NULL; /* T_CONFIG_LOCAL */
cv[1].destination = &(s->gw.debug);
cv[2].destination = NULL; /* T_CONFIG_LOCAL */
cv[3].destination = &(s->replace_http_host);
cv[4].destination = s->forwarded_params;
cv[5].destination = s->header_params;
cv[6].destination = s->gw.ext_mapping;
p->config_storage[i] = s;
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
return HANDLER_ERROR;
}
du = array_get_element_klen(config->value, CONST_STR_LEN("proxy.server"));
if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, du, i, 0)) {
return HANDLER_ERROR;
}
static unsigned int mod_proxy_parse_forwarded(server *srv, const array *a)
{
unsigned int forwarded = 0;
for (uint32_t j = 0, used = a->used; j < used; ++j) {
proxy_forwarded_t param;
data_unset *du = a->data[j];
if (buffer_eq_slen(&du->key, CONST_STR_LEN("by")))
param = PROXY_FORWARDED_BY;
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for")))
param = PROXY_FORWARDED_FOR;
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("host")))
param = PROXY_FORWARDED_HOST;
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto")))
param = PROXY_FORWARDED_PROTO;
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user")))
param = PROXY_FORWARDED_REMOTE_USER;
else {
log_error(srv->errh, __FILE__, __LINE__,
"proxy.forwarded keys must be one of: "
"by, for, host, proto, remote_user, but not: %s", du->key.ptr);
return UINT_MAX;
}
if (du->type == TYPE_STRING) {
data_string *ds = (data_string *)du;
if (buffer_eq_slen(&ds->value, CONST_STR_LEN("enable")))
forwarded |= param;
else if (!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))) {
log_error(srv->errh, __FILE__, __LINE__,
"proxy.forwarded values must be one of: "
"0, 1, enable, disable; error for key: %s", du->key.ptr);
return UINT_MAX;
}
}
else if (du->type == TYPE_INTEGER) {
data_integer *di = (data_integer *)du;
if (di->value) forwarded |= param;
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"proxy.forwarded values must be one of: "
"0, 1, enable, disable; error for key: %s", du->key.ptr);
return UINT_MAX;
}
}
return forwarded;
}
du = array_get_element_klen(config->value, CONST_STR_LEN("proxy.balance"));
if (!gw_set_defaults_balance(srv, &s->gw, du)) {
return HANDLER_ERROR;
}
/* disable check-local for all exts (default enabled) */
if (s->gw.exts) { /*(check after gw_set_defaults_backend())*/
for (size_t j = 0; j < s->gw.exts->used; ++j) {
gw_extension *ex = s->gw.exts->exts[j];
for (size_t n = 0; n < ex->used; ++n) {
ex->hosts[n]->check_local = 0;
}
}
}
static http_header_remap_opts * mod_proxy_parse_header_opts(server *srv, const array *a)
{
http_header_remap_opts header;
memset(&header, 0, sizeof(header));
for (uint32_t j = 0, used = a->used; j < used; ++j) {
data_array *da = (data_array *)a->data[j];
if (buffer_eq_slen(&da->key, CONST_STR_LEN("https-remap"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for proxy.header; "
"expected \"enable\" or \"disable\" for https-remap");
return NULL;
}
header.https_remap =
!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))
&& !buffer_eq_slen(&ds->value, CONST_STR_LEN("0"));
continue;
}
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("upgrade"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for proxy.header; "
"expected \"upgrade\" => \"enable\" or \"disable\"");
return NULL;
}
header.upgrade =
!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))
&& !buffer_eq_slen(&ds->value, CONST_STR_LEN("0"));
continue;
}
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("connect"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for proxy.header; "
"expected \"connect\" => \"enable\" or \"disable\"");
return NULL;
}
header.connect_method =
!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))
&& !buffer_eq_slen(&ds->value, CONST_STR_LEN("0"));
continue;
}
if (da->type != TYPE_ARRAY || !array_is_kvstring(&da->value)) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for proxy.header; "
"expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s",
da->key.ptr);
return NULL;
}
if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-urlpath"))) {
header.urlpaths = &da->value;
}
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-request"))) {
header.hosts_request = &da->value;
}
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-response"))) {
header.hosts_response = &da->value;
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected key for proxy.header; "
"expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s",
da->key.ptr);
return NULL;
}
}
if (!array_is_kvany(s->forwarded_params)) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for proxy.forwarded; expected ( \"param\" => \"value\" )");
return HANDLER_ERROR;
}
for (size_t j = 0, used = s->forwarded_params->used; j < used; ++j) {
proxy_forwarded_t param;
du = s->forwarded_params->data[j];
if (buffer_is_equal_string(&du->key, CONST_STR_LEN("by"))) {
param = PROXY_FORWARDED_BY;
} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("for"))) {
param = PROXY_FORWARDED_FOR;
} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("host"))) {
param = PROXY_FORWARDED_HOST;
} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("proto"))) {
param = PROXY_FORWARDED_PROTO;
} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("remote_user"))) {
param = PROXY_FORWARDED_REMOTE_USER;
} else {
log_error_write(srv, __FILE__, __LINE__, "sb",
"proxy.forwarded keys must be one of: by, for, host, proto, remote_user, but not:", &du->key);
return HANDLER_ERROR;
}
if (du->type == TYPE_STRING) {
data_string *ds = (data_string *)du;
if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("enable"))) {
s->forwarded |= param;
} else if (!buffer_is_equal_string(&ds->value, CONST_STR_LEN("disable"))) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"proxy.forwarded values must be one of: 0, 1, enable, disable; error for key:", &du->key);
return HANDLER_ERROR;
}
} else if (du->type == TYPE_INTEGER) {
data_integer *di = (data_integer *)du;
if (di->value) s->forwarded |= param;
} else {
log_error_write(srv, __FILE__, __LINE__, "sb",
"proxy.forwarded values must be one of: 0, 1, enable, disable; error for key:", &du->key);
return HANDLER_ERROR;
}
}
http_header_remap_opts *opts = malloc(sizeof(header));
force_assert(opts);
memcpy(opts, &header, sizeof(header));
return opts;
}
if (!array_is_kvany(s->header_params)) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for proxy.header; expected ( \"param\" => ( \"key\" => \"value\" ) )");
return HANDLER_ERROR;
}
for (size_t j = 0, used = s->header_params->used; j < used; ++j) {
data_array *da = (data_array *)s->header_params->data[j];
if (buffer_is_equal_string(&da->key, CONST_STR_LEN("https-remap"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for proxy.header; expected \"enable\" or \"disable\" for https-remap");
return HANDLER_ERROR;
}
s->header.https_remap = !buffer_is_equal_string(&ds->value, CONST_STR_LEN("disable"))
&& !buffer_is_equal_string(&ds->value, CONST_STR_LEN("0"));
continue;
}
else if (buffer_is_equal_string(&da->key, CONST_STR_LEN("upgrade"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for proxy.header; expected \"upgrade\" => \"enable\" or \"disable\"");
return HANDLER_ERROR;
}
s->header.upgrade = !buffer_is_equal_string(&ds->value, CONST_STR_LEN("disable"))
&& !buffer_is_equal_string(&ds->value, CONST_STR_LEN("0"));
continue;
}
else if (buffer_is_equal_string(&da->key, CONST_STR_LEN("connect"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for proxy.header; expected \"connect\" => \"enable\" or \"disable\"");
return HANDLER_ERROR;
}
s->header.connect_method = !buffer_is_equal_string(&ds->value, CONST_STR_LEN("disable"))
&& !buffer_is_equal_string(&ds->value, CONST_STR_LEN("0"));
continue;
}
if (da->type != TYPE_ARRAY || !array_is_kvstring(&da->value)) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"unexpected value for proxy.header; expected ( \"param\" => ( \"key\" => \"value\" ) ) near key", &da->key);
return HANDLER_ERROR;
}
if (buffer_is_equal_string(&da->key, CONST_STR_LEN("map-urlpath"))) {
s->header.urlpaths = &da->value;
}
else if (buffer_is_equal_string(&da->key, CONST_STR_LEN("map-host-request"))) {
s->header.hosts_request = &da->value;
}
else if (buffer_is_equal_string(&da->key, CONST_STR_LEN("map-host-response"))) {
s->header.hosts_response = &da->value;
}
else {
log_error_write(srv, __FILE__, __LINE__, "sb",
"unexpected key for proxy.header; expected ( \"param\" => ( \"key\" => \"value\" ) ) near key", &da->key);
return HANDLER_ERROR;
}
}
}
for (i = 0; i < srv->srvconf.modules->used; i++) {
data_string *ds = (data_string *)srv->srvconf.modules->data[i];
if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_extforward"))) {
proxy_check_extforward = 1;
break;
}
}
SETDEFAULTS_FUNC(mod_proxy_set_defaults)
{
static const config_plugin_keys_t cpk[] = {
{ CONST_STR_LEN("proxy.server"),
T_CONFIG_ARRAY,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.balance"),
T_CONFIG_STRING,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.debug"),
T_CONFIG_INT,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.map-extensions"),
T_CONFIG_ARRAY,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.forwarded"),
T_CONFIG_ARRAY,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.header"),
T_CONFIG_ARRAY,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("proxy.replace-http-host"),
T_CONFIG_BOOL,
T_CONFIG_SCOPE_CONNECTION }
,{ NULL, 0,
T_CONFIG_UNSET,
T_CONFIG_SCOPE_UNSET }
};
plugin_data * const p = p_d;
if (!config_plugin_values_init(srv, p, cpk, "mod_proxy"))
return HANDLER_ERROR;
/* process and validate config directives
* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
gw_plugin_config *gw = NULL;
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0: /* proxy.server */
gw = calloc(1, sizeof(gw_plugin_config));
force_assert(gw);
if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, cpv->v.a,
gw, 0, cpk[cpv->k_id].k)) {
gw_plugin_config_free(gw);
return HANDLER_ERROR;