From 4a6fe83837907674450a24914e6b863bcc84d0a5 Mon Sep 17 00:00:00 2001 From: Glenn Strauss Date: Wed, 13 Nov 2019 00:40:28 -0500 Subject: [PATCH] [multiple] gw_backends config_plugin_values_init() --- src/gw_backend.c | 261 ++++++++++++-------- src/gw_backend.h | 45 ++-- src/mod_fastcgi.c | 208 +++++++++------- src/mod_proxy.c | 589 +++++++++++++++++++++++++------------------- src/mod_scgi.c | 236 +++++++++--------- src/mod_sockproxy.c | 192 ++++++++------- src/mod_wstunnel.c | 319 ++++++++++++------------ 7 files changed, 1016 insertions(+), 834 deletions(-) diff --git a/src/gw_backend.c b/src/gw_backend.c index 4f2e9e14..cc288478 100644 --- a/src/gw_backend.c +++ b/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 * "" => ( ... ) ) */ - 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 * "" => ... ) */ - 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; - 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 == 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; + + /* (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; - for (size_t i = 0; i < srv->config_context->used; ++i) { - gw_plugin_config *conf = p->config_storage[i]; + 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; + + /* (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) { diff --git a/src/gw_backend.h b/src/gw_backend.h index c84035ef..d5909c86 100644 --- a/src/gw_backend.h +++ b/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); diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index 544750c7..3615a2ae 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -1,7 +1,6 @@ #include "first.h" #include -#include #include #include @@ -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; - } - - 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; - } - - du = array_get_element_klen(config->value, CONST_STR_LEN("fastcgi.balance")); - if (!gw_set_defaults_balance(srv, s, du)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; +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; + } } +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); +} + +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]); + } +} + +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); diff --git a/src/mod_proxy.c b/src/mod_proxy.c index b74ead44..69d226fe 100644 --- a/src/mod_proxy.c +++ b/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]; - - if (NULL == s) continue; - - 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); - } - - free(p); - - return HANDLER_GO_ON; + return gw_free(srv, p); } -SETDEFAULTS_FUNC(mod_proxy_set_defaults) { - plugin_data *p = p_d; - const data_unset *du; - size_t i = 0; +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; + } +} - 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 *)); +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); +} - 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(); +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]); + } +} - 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; +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; +} - 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 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; + } + } - du = array_get_element_klen(config->value, CONST_STR_LEN("proxy.balance")); - if (!gw_set_defaults_balance(srv, &s->gw, du)) { - return HANDLER_ERROR; - } + http_header_remap_opts *opts = malloc(sizeof(header)); + force_assert(opts); + memcpy(opts, &header, sizeof(header)); + return opts; +} - /* 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; - } - } - } - 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; - } - } +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 } + }; - 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; - } - } - } + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_proxy")) + 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; - } - } + /* 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; + } + /* error if "mode" = "authorizer"; + * proxy can not act as authorizer */ + /*(check after gw_set_defaults_backend())*/ + if (gw->exts_auth && gw->exts_auth->used) { + log_error(srv->errh, __FILE__, __LINE__, + "%s must not define any hosts with " + "attribute \"mode\" = \"authorizer\"", 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: /* proxy.balance */ + cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b); + break; + case 2: /* proxy.debug */ + break; + case 3: /* proxy.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; + case 4: /* proxy.forwarded */ + if (!array_is_kvany(cpv->v.a)) { + log_error(srv->errh, __FILE__, __LINE__, + "unexpected value for %s; " + "expected ( \"param\" => \"value\" )", + cpk[cpv->k_id].k); + return HANDLER_ERROR; + } + cpv->v.u = mod_proxy_parse_forwarded(srv, cpv->v.a); + if (UINT_MAX == cpv->v.u) return HANDLER_ERROR; + cpv->vtype = T_CONFIG_LOCAL; + break; + case 5: /* proxy.header */ + if (!array_is_kvany(cpv->v.a)) { + log_error(srv->errh, __FILE__, __LINE__, + "unexpected value for %s; " + "expected ( \"param\" => ( \"key\" => \"value\" ) )", + cpk[cpv->k_id].k); + return HANDLER_ERROR; + } + cpv->v.v = mod_proxy_parse_header_opts(srv, cpv->v.a); + if (NULL == cpv->v.v) return HANDLER_ERROR; + cpv->vtype = T_CONFIG_LOCAL; + break; + case 6: /* proxy.replace-http-host */ + break; + default:/* should not happen */ + break; + } + } - return HANDLER_GO_ON; + /* disable check-local for all exts (default enabled) */ + if (gw && gw->exts) { /*(check after gw_set_defaults_backend())*/ + for (uint32_t j = 0; j < gw->exts->used; ++j) { + gw_extension *ex = gw->exts->exts[j]; + for (uint32_t n = 0; n < ex->used; ++n) { + ex->hosts[n]->check_local = 0; + } + } + } + } + + /* 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_proxy_merge_config(&p->defaults, cpv); + } + + /* special-case behavior if mod_extforward is loaded */ + for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { + buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value; + if (buffer_eq_slen(m, CONST_STR_LEN("mod_extforward"))) { + proxy_check_extforward = 1; + break; + } + } + + return HANDLER_GO_ON; } @@ -870,60 +1005,6 @@ static handler_t proxy_create_env_connect(server *srv, gw_handler_ctx *gwhctx) { } -#define PATCH(x) \ - p->conf.x = s->x; -#define PATCH_GW(x) \ - p->conf.gw.x = s->gw.x; -static int mod_proxy_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH_GW(exts); - PATCH_GW(exts_auth); - PATCH_GW(exts_resp); - PATCH_GW(debug); - PATCH_GW(ext_mapping); - PATCH_GW(balance); - PATCH(replace_http_host); - PATCH(forwarded); - PATCH(header); /*(copies struct)*/ - - /* 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("proxy.server"))) { - PATCH_GW(exts); - PATCH_GW(exts_auth); - PATCH_GW(exts_resp); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("proxy.debug"))) { - PATCH_GW(debug); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("proxy.balance"))) { - PATCH_GW(balance); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("proxy.map-extensions"))) { - PATCH_GW(ext_mapping); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("proxy.replace-http-host"))) { - PATCH(replace_http_host); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("proxy.forwarded"))) { - PATCH(forwarded); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("proxy.header"))) { - PATCH(header); /*(copies struct)*/ - } - } - } - - return 0; -} -#undef PATCH_GW -#undef PATCH - static handler_t proxy_response_headers(server *srv, connection *con, struct http_response_opts_t *opts) { /* response headers just completed */ handler_ctx *hctx = (handler_ctx *)opts->pdata; @@ -974,7 +1055,7 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p if (con->mode != DIRECT) return HANDLER_GO_ON; - mod_proxy_patch_connection(srv, con, p); + mod_proxy_patch_config(con, p); if (NULL == p->conf.gw.exts) return HANDLER_GO_ON; rc = gw_check_extension(srv, con, (gw_plugin_data *)p, 1, sizeof(handler_ctx)); diff --git a/src/mod_scgi.c b/src/mod_scgi.c index 13e33a43..8e5cd12a 100644 --- a/src/mod_scgi.c +++ b/src/mod_scgi.c @@ -20,76 +20,137 @@ typedef gw_handler_ctx handler_ctx; enum { LI_PROTOCOL_SCGI, LI_PROTOCOL_UWSGI }; +static void mod_scgi_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: /* scgi.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: /* scgi.balance */ + /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ + pconf->balance = (int)cpv->v.u; + break; + case 2: /* scgi.debug */ + pconf->debug = (int)cpv->v.u; + break; + case 3: /* scgi.map-extensions */ + pconf->ext_mapping = cpv->v.a; + break; + case 4: /* scgi.protocol */ + /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ + pconf->proto = (int)cpv->v.u; + break; + default:/* should not happen */ + return; + } +} + +static void mod_scgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_scgi_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} + +static void mod_scgi_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_scgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); + } +} + SETDEFAULTS_FUNC(mod_scgi_set_defaults) { - plugin_data *p = p_d; - const data_unset *du; - size_t i = 0; + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("scgi.server"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("scgi.balance"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("scgi.debug"), + T_CONFIG_INT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("scgi.map-extensions"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("scgi.protocol"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; - config_values_t cv[] = { - { "scgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { "scgi.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { "scgi.protocol", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { "scgi.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ - { "scgi.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_scgi")) + return HANDLER_ERROR; - p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); - force_assert(p->config_storage); + /* 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:{/* scgi.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, 1, + 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: /* scgi.balance */ + cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b); + break; + case 2: /* scgi.debug */ + break; + case 3: /* scgi.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; + case 4: /* scgi.protocol */ + if (buffer_eq_slen(cpv->v.b, CONST_STR_LEN("scgi"))) + cpv->v.u = LI_PROTOCOL_SCGI; + else if (buffer_eq_slen(cpv->v.b, CONST_STR_LEN("uwsgi"))) + cpv->v.u = LI_PROTOCOL_UWSGI; + else { + log_error(srv->errh, __FILE__, __LINE__, + "unexpected type for key: %s" + "expected \"scgi\" or \"uwsgi\"", cpk[cpv->k_id].k); + return HANDLER_ERROR; + } + break; + default:/* should not happen */ + break; + } + } + } - for (i = 0; i < srv->config_context->used; i++) { - data_config const* config = (data_config const*)srv->config_context->data[i]; - plugin_config *s; + /* default is 0 */ + /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/ + /*p->defaults.proto = LI_PROTOCOL_SCGI;*//*(default)*/ - s = calloc(1, sizeof(plugin_config)); - force_assert(s); - s->exts = NULL; - s->exts_auth = NULL; - s->exts_resp = NULL; - s->debug = 0; - s->proto = LI_PROTOCOL_SCGI; - s->ext_mapping = array_init(); + /* 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_scgi_merge_config(&p->defaults, cpv); + } - cv[0].destination = s->exts; /* not used; T_CONFIG_LOCAL */ - cv[1].destination = &(s->debug); - cv[2].destination = NULL; /* not used; T_CONFIG_LOCAL */ - cv[3].destination = s->ext_mapping; - cv[4].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; - } - - du = array_get_element_klen(config->value, CONST_STR_LEN("scgi.server")); - if (!gw_set_defaults_backend(srv, p, du, i, 1)) { - return HANDLER_ERROR; - } - - du = array_get_element_klen(config->value, CONST_STR_LEN("scgi.balance")); - if (!gw_set_defaults_balance(srv, s, du)) { - return HANDLER_ERROR; - } - - if (NULL != (du = array_get_element_klen(config->value, CONST_STR_LEN("scgi.protocol")))) { - const data_string *ds = (const data_string *)du; - if (du->type == TYPE_STRING - && buffer_is_equal_string(&ds->value, CONST_STR_LEN("scgi"))) { - s->proto = LI_PROTOCOL_SCGI; - } else if (du->type == TYPE_STRING - && buffer_is_equal_string(&ds->value, CONST_STR_LEN("uwsgi"))) { - s->proto = LI_PROTOCOL_UWSGI; - } else { - log_error_write(srv, __FILE__, __LINE__, "sss", - "unexpected type for key: ", "scgi.protocol", "expected \"scgi\" or \"uwsgi\""); - - return HANDLER_ERROR; - } - } - } - - return HANDLER_GO_ON; + return HANDLER_GO_ON; } static int scgi_env_add_scgi(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { @@ -225,51 +286,6 @@ static handler_t scgi_create_env(server *srv, handler_ctx *hctx) { return HANDLER_GO_ON; } -#define PATCH(x) \ - p->conf.x = s->x; -static int scgi_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(proto); - 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("scgi.server"))) { - PATCH(exts); - PATCH(exts_auth); - PATCH(exts_resp); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("scgi.protocol"))) { - PATCH(proto); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("scgi.balance"))) { - PATCH(balance); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("scgi.debug"))) { - PATCH(debug); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("scgi.map-extensions"))) { - PATCH(ext_mapping); - } - } - } - - return 0; -} -#undef PATCH - static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) { plugin_data *p = p_d; @@ -277,7 +293,7 @@ static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, i if (con->mode != DIRECT) return HANDLER_GO_ON; - scgi_patch_connection(srv, con, p); + mod_scgi_patch_config(con, p); if (NULL == p->conf.exts) return HANDLER_GO_ON; rc = gw_check_extension(srv, con, p, uri_path_handler, 0); diff --git a/src/mod_sockproxy.c b/src/mod_sockproxy.c index f365babe..ae6247b6 100644 --- a/src/mod_sockproxy.c +++ b/src/mod_sockproxy.c @@ -20,64 +20,112 @@ typedef gw_handler_ctx handler_ctx; * */ +static void mod_sockproxy_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: /* sockproxy.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: /* sockproxy.balance */ + /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ + pconf->balance = (int)cpv->v.u; + break; + case 2: /* sockproxy.debug */ + pconf->debug = (int)cpv->v.u; + break; + default:/* should not happen */ + return; + } +} + +static void mod_sockproxy_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_sockproxy_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} + +static void mod_sockproxy_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_sockproxy_merge_config(&p->conf,p->cvlist+p->cvlist[i].v.u2[0]); + } +} + SETDEFAULTS_FUNC(mod_sockproxy_set_defaults) { - plugin_data *p = p_d; - const data_unset *du; - size_t i = 0; + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("sockproxy.server"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("sockproxy.balance"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("sockproxy.debug"), + T_CONFIG_INT, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; - config_values_t cv[] = { - { "sockproxy.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { "sockproxy.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { "sockproxy.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_sockproxy")) + return HANDLER_ERROR; - p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); - force_assert(p->config_storage); + /* 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: /* sockproxy.server */ + 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: /* sockproxy.balance */ + cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b); + break; + case 2: /* sockproxy.debug */ + break; + default:/* should not happen */ + break; + } + } - for (i = 0; i < srv->config_context->used; i++) { - data_config const* config = (data_config const*)srv->config_context->data[i]; - plugin_config *s; + /* disable check-local for all exts (default enabled) */ + if (gw && gw->exts) { /*(check after gw_set_defaults_backend())*/ + for (uint32_t j = 0; j < gw->exts->used; ++j) { + gw_extension *ex = gw->exts->exts[j]; + for (uint32_t n = 0; n < ex->used; ++n) { + ex->hosts[n]->check_local = 0; + } + } + } + } - s = calloc(1, sizeof(plugin_config)); - force_assert(s); - s->exts = NULL; - s->exts_auth = NULL; - s->exts_resp = NULL; - s->debug = 0; + /* default is 0 */ + /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/ - cv[0].destination = NULL; /* T_CONFIG_LOCAL */ - cv[1].destination = &(s->debug); - cv[2].destination = NULL; /* T_CONFIG_LOCAL */ + /* 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_sockproxy_merge_config(&p->defaults, cpv); + } - 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("sockproxy.server")); - if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, du, i, 0)) { - return HANDLER_ERROR; - } - - du = array_get_element_klen(config->value, CONST_STR_LEN("sockproxy.balance")); - if (!gw_set_defaults_balance(srv, s, du)) { - return HANDLER_ERROR; - } - - /* disable check-local for all exts (default enabled) */ - if (s->exts) { /*(check after gw_set_defaults_backend())*/ - for (size_t j = 0; j < s->exts->used; ++j) { - gw_extension *ex = s->exts->exts[j]; - for (size_t n = 0; n < ex->used; ++n) { - ex->hosts[n]->check_local = 0; - } - } - } - } - - return HANDLER_GO_ON; + return HANDLER_GO_ON; } @@ -92,53 +140,13 @@ static handler_t sockproxy_create_env_connect(server *srv, handler_ctx *hctx) { } -#define PATCH(x) \ - p->conf.x = s->x; -static int mod_sockproxy_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(ext_mapping); - PATCH(balance); - - /* 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("sockproxy.server"))) { - PATCH(exts); - PATCH(exts_auth); - PATCH(exts_resp); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("sockproxy.debug"))) { - PATCH(debug); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("sockproxy.balance"))) { - PATCH(balance); - } - } - } - - return 0; -} -#undef PATCH - static handler_t mod_sockproxy_connection_accept(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; handler_t rc; if (con->mode != DIRECT) return HANDLER_GO_ON; - mod_sockproxy_patch_connection(srv, con, p); + mod_sockproxy_patch_config(con, p); if (NULL == p->conf.exts) return HANDLER_GO_ON; /*(fake con->uri.path for matching purposes in gw_check_extension())*/ diff --git a/src/mod_wstunnel.c b/src/mod_wstunnel.c index a84c3b6c..e41a193a 100644 --- a/src/mod_wstunnel.c +++ b/src/mod_wstunnel.c @@ -104,16 +104,17 @@ } typedef struct { - gw_plugin_config gw; - buffer *frame_type; - array *origins; + gw_plugin_config gw; /* start must match layout of gw_plugin_config */ + const array *origins; + unsigned int frame_type; unsigned short int ping_interval; } plugin_config; typedef struct plugin_data { PLUGIN_DATA; - plugin_config **config_storage; + pid_t srv_pid; /* must match layout of gw_plugin_data through conf member */ plugin_config conf; + plugin_config defaults; } plugin_data; typedef enum { @@ -182,116 +183,179 @@ INIT_FUNC(mod_wstunnel_init) { return calloc(1, sizeof(plugin_data)); } -FREE_FUNC(mod_wstunnel_free) { - plugin_data *p = p_d; - if (p->config_storage) { - for (size_t i = 0; i < srv->config_context->used; ++i) { - plugin_config *s = p->config_storage[i]; - if (NULL == s) continue; - buffer_free(s->frame_type); - array_free(s->origins); - /*assert(0 == offsetof(s->gw));*/ - gw_plugin_config_free(&s->gw); - /*free(s);*//*free'd by gw_plugin_config_free()*/ +static void mod_wstunnel_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: /* wstunnel.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; } - free(p->config_storage); + break; + case 1: /* wstunnel.balance */ + /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ + pconf->gw.balance = (int)cpv->v.u; + break; + case 2: /* wstunnel.debug */ + pconf->gw.debug = (int)cpv->v.u; + break; + case 3: /* wstunnel.map-extensions */ + pconf->gw.ext_mapping = cpv->v.a; + break; + case 4: /* wstunnel.frame-type */ + pconf->frame_type = cpv->v.u; + break; + case 5: /* wstunnel.origins */ + pconf->origins = cpv->v.a; + break; + case 6: /* wstunnel.ping-interval */ + pconf->ping_interval = cpv->v.shrt; + break; + default:/* should not happen */ + return; + } +} + +static void mod_wstunnel_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { + do { + mod_wstunnel_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} + +static void mod_wstunnel_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_wstunnel_merge_config(&p->conf, p->cvlist+p->cvlist[i].v.u2[0]); } - free(p); - return HANDLER_GO_ON; } SETDEFAULTS_FUNC(mod_wstunnel_set_defaults) { - plugin_data *p = p_d; - const data_unset *du; - config_values_t cv[] = { - { "wstunnel.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, - { "wstunnel.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { "wstunnel.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, - { "wstunnel.map-extensions",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, - { "wstunnel.frame-type", NULL, T_CONFIG_STRING,T_CONFIG_SCOPE_CONNECTION }, - { "wstunnel.origins", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, - { "wstunnel.ping-interval", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + static const config_plugin_keys_t cpk[] = { + { CONST_STR_LEN("wstunnel.server"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("wstunnel.balance"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("wstunnel.debug"), + T_CONFIG_INT, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("wstunnel.map-extensions"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("wstunnel.frame-type"), + T_CONFIG_STRING, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("wstunnel.origins"), + T_CONFIG_ARRAY, + T_CONFIG_SCOPE_CONNECTION } + ,{ CONST_STR_LEN("wstunnel.ping-interval"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } }; - p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); - force_assert(p->config_storage); - for (size_t i = 0; i < srv->config_context->used; ++i) { - array *ca = ((data_config *)(srv->config_context->data[i]))->value; - plugin_config *s = calloc(1, sizeof(plugin_config)); - force_assert(s); + plugin_data * const p = p_d; + if (!config_plugin_values_init(srv, p, cpk, "mod_wstunnel")) + return HANDLER_ERROR; - s->gw.debug = 0; /* MOD_WEBSOCKET_LOG_NONE */ - s->gw.ext_mapping = array_init(); - s->frame_type = buffer_init(); - s->origins = array_init(); - s->ping_interval = 0; /* do not send ping */ - - cv[0].destination = NULL; /* T_CONFIG_LOCAL */ - cv[1].destination = &(s->gw.debug); - cv[2].destination = NULL; /* T_CONFIG_LOCAL */ - cv[3].destination = s->gw.ext_mapping; - cv[4].destination = s->frame_type; - cv[5].destination = s->origins; - cv[6].destination = &(s->ping_interval); - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ca, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { - return HANDLER_ERROR; - } - - du = array_get_element_klen(ca, CONST_STR_LEN("wstunnel.server")); - if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, du, i, 0)) { - return HANDLER_ERROR; - } - - du = array_get_element_klen(ca, CONST_STR_LEN("wstunnel.balance")); - if (!gw_set_defaults_balance(srv, &s->gw, du)) { - 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: /* wstunnel.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; + } + /* error if "mode" = "authorizer"; + * wstunnel can not act as authorizer */ + /*(check after gw_set_defaults_backend())*/ + if (gw->exts_auth && gw->exts_auth->used) { + log_error(srv->errh, __FILE__, __LINE__, + "%s must not define any hosts with " + "attribute \"mode\" = \"authorizer\"", 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: /* wstunnel.balance */ + cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b); + break; + case 2: /* wstunnel.debug */ + break; + case 3: /* wstunnel.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; + case 4: /* wstunnel.frame-type */ + /*(default frame-type to "text" unless "binary" is specified)*/ + cpv->v.u = + buffer_eq_icase_slen(cpv->v.b, CONST_STR_LEN("binary")); + break; + case 5: /* wstunnel.origins */ + if (!array_is_vlist(cpv->v.a)) { + log_error(srv->errh, __FILE__, __LINE__, + "unexpected value for %s; " + "expected %s = ( \"...\", \"...\" )", + cpk[cpv->k_id].k, cpk[cpv->k_id].k); + return HANDLER_ERROR; + } + for (uint32_t j = 0; j < cpv->v.a->used; ++j) { + buffer *origin = &((data_string *)cpv->v.a->data[j])->value; + if (buffer_string_is_empty(origin)) { + log_error(srv->errh, __FILE__, __LINE__, + "unexpected empty string in %s", cpk[cpv->k_id].k); + return HANDLER_ERROR; + } + } + break; + case 6: /* wstunnel.ping-interval */ + break; + default:/* should not happen */ + break; + } } /* 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) { + if (gw && gw->exts) { /*(check after gw_set_defaults_backend())*/ + for (uint32_t j = 0; j < gw->exts->used; ++j) { + gw_extension *ex = gw->exts->exts[j]; + for (uint32_t n = 0; n < ex->used; ++n) { ex->hosts[n]->check_local = 0; } } } - - /* error if "mode" = "authorizer"; wstunnel can not act as authorizer */ - /*(check after gw_set_defaults_backend())*/ - if (s->gw.exts_auth && s->gw.exts_auth->used) { - log_error_write(srv, __FILE__, __LINE__, "s", - "wstunnel.server must not define any hosts " - "with attribute \"mode\" = \"authorizer\""); - return HANDLER_ERROR; - } - - /*(default frame-type to "text" unless "binary" is specified)*/ - if (!buffer_is_empty(s->frame_type) - && !buffer_is_equal_caseless_string(s->frame_type, - CONST_STR_LEN("binary"))) { - buffer_clear(s->frame_type); - } - - if (!array_is_vlist(s->origins)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "unexpected value for wstunnel.origins; expected wstunnel.origins = ( \"...\", \"...\" )"); - return HANDLER_ERROR; - } - for (size_t j = 0; j < s->origins->used; ++j) { - if (buffer_string_is_empty(&((data_string *)s->origins->data[j])->value)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "unexpected empty string in wstunnel.origins"); - return HANDLER_ERROR; - } - } } - /*assert(0 == offsetof(s->gw));*/ + /* default is 0 */ + /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/ + p->defaults.ping_interval = 0; /* do not send ping */ + + /* 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_wstunnel_merge_config(&p->defaults, cpv); + } + return HANDLER_GO_ON; } @@ -353,58 +417,6 @@ static handler_t wstunnel_recv_parse(server *srv, connection *con, http_response return HANDLER_GO_ON; } -#define PATCH(x) p->conf.x = s->x -#define PATCH_GW(x) p->conf.gw.x = s->gw.x -static void mod_wstunnel_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH_GW(exts); - PATCH_GW(exts_auth); - PATCH_GW(exts_resp); - PATCH_GW(debug); - PATCH_GW(balance); - PATCH_GW(ext_mapping); - PATCH(frame_type); - PATCH(origins); - PATCH(ping_interval); - - /* 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("wstunnel.server"))) { - PATCH_GW(exts); - /*(wstunnel can not act as authorizer, - * but p->conf.exts_auth must not be NULL)*/ - PATCH_GW(exts_auth); - PATCH_GW(exts_resp); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("wstunnel.debug"))) { - PATCH_GW(debug); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("wstunnel.balance"))) { - PATCH_GW(balance); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("wstunnel.map-extensions"))) { - PATCH_GW(ext_mapping); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("wstunnel.frame-type"))) { - PATCH(frame_type); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("wstunnel.origins"))) { - PATCH(origins); - } else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("wstunnel.ping-interval"))) { - PATCH(ping_interval); - } - } - } -} -#undef PATCH_GW -#undef PATCH - static int wstunnel_is_allowed_origin(connection *con, handler_ctx *hctx) { /* If allowed origins is set (and not empty list), fail closed if no match. * Note that origin provided in request header has not been normalized, so @@ -413,7 +425,7 @@ static int wstunnel_is_allowed_origin(connection *con, handler_ctx *hctx) { const buffer *origin = NULL; size_t olen; - if (0 == allowed_origins->used) { + if (NULL == allowed_origins || 0 == allowed_origins->used) { DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO, "s", "allowed origins not specified"); return 1; } @@ -486,7 +498,6 @@ static void wstunnel_handler_ctx_free(void *gwhctx) { static handler_t wstunnel_handler_setup (server *srv, connection *con, plugin_data *p) { handler_ctx *hctx = con->plugin_ctx[p->id]; - int binary; int hybivers; hctx->srv = srv; /*(for mod_wstunnel module-specific DEBUG_LOG() macro)*/ hctx->conf = p->conf; /*(copies struct)*/ @@ -513,7 +524,7 @@ static handler_t wstunnel_handler_setup (server *srv, connection *con, plugin_da hctx->frame.ctl.siz = 0; hctx->frame.payload = chunk_buffer_acquire(); - binary = !buffer_is_empty(hctx->conf.frame_type); /*("binary")*/ + unsigned int binary = hctx->conf.frame_type; /*(0 = "text"; 1 = "binary")*/ if (!binary) { const buffer *vb = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Sec-WebSocket-Protocol")); @@ -586,7 +597,7 @@ static handler_t mod_wstunnel_check_extension(server *srv, connection *con, void || !http_header_str_contains_token(CONST_BUF_LEN(vb), CONST_STR_LEN("upgrade"))) return HANDLER_GO_ON; - mod_wstunnel_patch_connection(srv, con, p); + mod_wstunnel_patch_config(con, p); if (NULL == p->conf.gw.exts) return HANDLER_GO_ON; rc = gw_check_extension(srv,con,(gw_plugin_data *)p,1,sizeof(handler_ctx)); @@ -640,7 +651,7 @@ int mod_wstunnel_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = "wstunnel"; p->init = mod_wstunnel_init; - p->cleanup = mod_wstunnel_free; + p->cleanup = gw_free; p->set_defaults = mod_wstunnel_set_defaults; p->connection_reset = gw_connection_reset; p->handle_uri_clean = mod_wstunnel_check_extension;