diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 13:19:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 13:19:22 +0000 |
commit | c21c3b0befeb46a51b6bf3758ffa30813bea0ff0 (patch) | |
tree | 9754ff1ca740f6346cf8483ec915d4054bc5da2d /web/server/h2o/libh2o/lib/handler/configurator | |
parent | Adding upstream version 1.43.2. (diff) | |
download | netdata-c21c3b0befeb46a51b6bf3758ffa30813bea0ff0.tar.xz netdata-c21c3b0befeb46a51b6bf3758ffa30813bea0ff0.zip |
Adding upstream version 1.44.3.upstream/1.44.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'web/server/h2o/libh2o/lib/handler/configurator')
15 files changed, 2375 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/lib/handler/configurator/access_log.c b/web/server/h2o/libh2o/lib/handler/configurator/access_log.c new file mode 100644 index 000000000..de6380882 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/access_log.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014 DeNA Co., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "h2o.h" +#include "h2o/configurator.h" + +typedef H2O_VECTOR(h2o_access_log_filehandle_t *) st_h2o_access_log_filehandle_vector_t; + +struct st_h2o_access_log_configurator_t { + h2o_configurator_t super; + st_h2o_access_log_filehandle_vector_t *handles; + st_h2o_access_log_filehandle_vector_t _handles_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static int on_config(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_access_log_configurator_t *self = (void *)cmd->configurator; + const char *path, *fmt = NULL; + int escape = H2O_LOGCONF_ESCAPE_APACHE; + h2o_access_log_filehandle_t *fh; + + switch (node->type) { + case YOML_TYPE_SCALAR: + path = node->data.scalar; + break; + case YOML_TYPE_MAPPING: { + yoml_t *t; + /* get path */ + if ((t = yoml_get(node, "path")) == NULL) { + h2o_configurator_errprintf(cmd, node, "could not find mandatory key `path`"); + return -1; + } + if (t->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, t, "`path` must be scalar"); + return -1; + } + path = t->data.scalar; + /* get format */ + if ((t = yoml_get(node, "format")) != NULL) { + if (t->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, t, "`format` must be a scalar"); + return -1; + } + fmt = t->data.scalar; + } + /* get escape */ + if ((t = yoml_get(node, "escape")) != NULL) { + switch (h2o_configurator_get_one_of(cmd, t, "apache,json")) { + case 0: + escape = H2O_LOGCONF_ESCAPE_APACHE; + break; + case 1: + escape = H2O_LOGCONF_ESCAPE_JSON; + break; + default: + return -1; + } + } + } break; + default: + h2o_configurator_errprintf(cmd, node, "node must be a scalar or a mapping"); + return -1; + } + + if (!ctx->dry_run) { + if ((fh = h2o_access_log_open_handle(path, fmt, escape)) == NULL) + return -1; + h2o_vector_reserve(NULL, self->handles, self->handles->size + 1); + self->handles->entries[self->handles->size++] = fh; + } + + return 0; +} + +static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_access_log_configurator_t *self = (void *)_self; + size_t i; + + /* push the stack pointer */ + ++self->handles; + + /* link the handles */ + memset(self->handles, 0, sizeof(*self->handles)); + h2o_vector_reserve(NULL, self->handles, self->handles[-1].size + 1); + for (i = 0; i != self->handles[-1].size; ++i) { + h2o_access_log_filehandle_t *fh = self->handles[-1].entries[i]; + self->handles[0].entries[self->handles[0].size++] = fh; + h2o_mem_addref_shared(fh); + } + + return 0; +} + +static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_access_log_configurator_t *self = (void *)_self; + size_t i; + + /* register all handles, and decref them */ + for (i = 0; i != self->handles->size; ++i) { + h2o_access_log_filehandle_t *fh = self->handles->entries[i]; + if (ctx->pathconf != NULL) + h2o_access_log_register(ctx->pathconf, fh); + h2o_mem_release_shared(fh); + } + /* free the vector */ + free(self->handles->entries); + + /* pop the stack pointer */ + --self->handles; + + return 0; +} + +void h2o_access_log_register_configurator(h2o_globalconf_t *conf) +{ + struct st_h2o_access_log_configurator_t *self = (void *)h2o_configurator_create(conf, sizeof(*self)); + + self->super.enter = on_config_enter; + self->super.exit = on_config_exit; + self->handles = self->_handles_stack; + + h2o_configurator_define_command(&self->super, "access-log", H2O_CONFIGURATOR_FLAG_ALL_LEVELS, on_config); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/compress.c b/web/server/h2o/libh2o/lib/handler/configurator/compress.c new file mode 100644 index 000000000..c023dd5cf --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/compress.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2015 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "h2o.h" +#include "h2o/configurator.h" + +#define DEFAULT_GZIP_QUALITY 1 +#define DEFAULT_BROTLI_QUALITY 1 + +struct compress_configurator_t { + h2o_configurator_t super; + h2o_compress_args_t *vars, _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static const h2o_compress_args_t all_off = {0, {-1}, {-1}}, all_on = {100, {DEFAULT_GZIP_QUALITY}, {DEFAULT_BROTLI_QUALITY}}; + +static int on_config_gzip(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct compress_configurator_t *self = (void *)cmd->configurator; + int mode; + + if ((mode = (int)h2o_configurator_get_one_of(cmd, node, "OFF,ON")) == -1) + return -1; + + *self->vars = all_off; + if (mode != 0) + self->vars->gzip.quality = DEFAULT_GZIP_QUALITY; + + return 0; +} + +static int obtain_quality(yoml_t *node, int min_quality, int max_quality, int default_quality, int *slot) +{ + int tmp; + if (node->type != YOML_TYPE_SCALAR) + return -1; + if (strcasecmp(node->data.scalar, "OFF") == 0) { + *slot = -1; + return 0; + } + if (strcasecmp(node->data.scalar, "ON") == 0) { + *slot = default_quality; + return 0; + } + if (sscanf(node->data.scalar, "%d", &tmp) == 1 && (min_quality <= tmp && tmp <= max_quality)) { + *slot = tmp; + return 0; + } + return -1; +} + +static int on_config_compress_min_size(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct compress_configurator_t *self = (void *)cmd->configurator; + return h2o_configurator_scanf(cmd, node, "%zu", &self->vars->min_size); +} + +static int on_config_compress(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct compress_configurator_t *self = (void *)cmd->configurator; + size_t i; + + switch (node->type) { + case YOML_TYPE_SCALAR: + if (strcasecmp(node->data.scalar, "OFF") == 0) { + *self->vars = all_off; + } else if (strcasecmp(node->data.scalar, "ON") == 0) { + *self->vars = all_on; + } else { + h2o_configurator_errprintf(cmd, node, "scalar argument must be either of: `OFF`, `ON`"); + return -1; + } + break; + case YOML_TYPE_SEQUENCE: + *self->vars = all_off; + for (i = 0; i != node->data.sequence.size; ++i) { + yoml_t *element = node->data.sequence.elements[i]; + if (element->type == YOML_TYPE_SCALAR && strcasecmp(element->data.scalar, "gzip") == 0) { + self->vars->gzip.quality = DEFAULT_GZIP_QUALITY; + } else if (element->type == YOML_TYPE_SCALAR && strcasecmp(element->data.scalar, "br") == 0) { + self->vars->brotli.quality = DEFAULT_BROTLI_QUALITY; + } else { + h2o_configurator_errprintf(cmd, element, "element of the sequence must be either of: `gzip`, `br`"); + return -1; + } + } + break; + case YOML_TYPE_MAPPING: + *self->vars = all_off; + for (i = 0; i != node->data.mapping.size; ++i) { + yoml_t *key = node->data.mapping.elements[i].key; + yoml_t *value = node->data.mapping.elements[i].value; + if (key->type == YOML_TYPE_SCALAR && strcasecmp(key->data.scalar, "gzip") == 0) { + if (obtain_quality(value, 1, 9, DEFAULT_GZIP_QUALITY, &self->vars->gzip.quality) != 0) { + h2o_configurator_errprintf( + cmd, value, "value of gzip attribute must be either of `OFF`, `ON` or an integer value between 1 and 9"); + return -1; + } + } else if (key->type == YOML_TYPE_SCALAR && strcasecmp(key->data.scalar, "br") == 0) { + if (obtain_quality(value, 0, 11, DEFAULT_BROTLI_QUALITY, &self->vars->brotli.quality) != 0) { + h2o_configurator_errprintf( + cmd, value, "value of br attribute must be either of `OFF`, `ON` or an integer between 0 and 11"); + return -1; + } + } else { + h2o_configurator_errprintf(cmd, key, "key must be either of: `gzip`, `br`"); + return -1; + } + } + break; + default: + h2o_fatal("unexpected node type"); + break; + } + + return 0; +} + +static int on_config_enter(h2o_configurator_t *configurator, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct compress_configurator_t *self = (void *)configurator; + + ++self->vars; + self->vars[0] = self->vars[-1]; + return 0; +} + +static int on_config_exit(h2o_configurator_t *configurator, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct compress_configurator_t *self = (void *)configurator; + + if (ctx->pathconf != NULL && (self->vars->gzip.quality != -1 || self->vars->brotli.quality != -1)) + h2o_compress_register(ctx->pathconf, self->vars); + + --self->vars; + return 0; +} + +void h2o_compress_register_configurator(h2o_globalconf_t *conf) +{ + struct compress_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + c->super.enter = on_config_enter; + c->super.exit = on_config_exit; + h2o_configurator_define_command(&c->super, "compress", H2O_CONFIGURATOR_FLAG_ALL_LEVELS, on_config_compress); + h2o_configurator_define_command(&c->super, "compress-minimum-size", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_compress_min_size); + h2o_configurator_define_command(&c->super, "gzip", H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_gzip); + c->vars = c->_vars_stack; + c->vars->gzip.quality = -1; + c->vars->brotli.quality = -1; +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/errordoc.c b/web/server/h2o/libh2o/lib/handler/configurator/errordoc.c new file mode 100644 index 000000000..ab24923ee --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/errordoc.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2015-2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "h2o.h" +#include "h2o/configurator.h" + +struct errordoc_configurator_t { + h2o_configurator_t super; + h2o_mem_pool_t pool; + H2O_VECTOR(h2o_errordoc_t) * vars, _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static int scan_and_check_status(h2o_configurator_command_t *cmd, yoml_t *value, int *slot) +{ + if (value->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, value, "status must be must be either of: scalar, sequence of scalar"); + return -1; + } + if (h2o_configurator_scanf(cmd, value, "%d", slot) != 0) + return -1; + if (!(400 <= *slot && *slot <= 599)) { + h2o_configurator_errprintf(cmd, value, "status must be within range of 400 to 599"); + return -1; + } + return 0; +} + +static int register_errordoc(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *hash) +{ + struct errordoc_configurator_t *self = (void *)cmd->configurator; + int status[200]; + size_t status_len = 0; + int parsed; + const char *url = NULL; + size_t i, j, k; + yoml_t *key, *value; + + for (i = 0; i != hash->data.mapping.size; ++i) { + key = hash->data.mapping.elements[i].key; + value = hash->data.mapping.elements[i].value; + if (key->type != YOML_TYPE_SCALAR) + goto UnknownKeyError; + if (strcmp(key->data.scalar, "status") == 0) { + if (status_len != 0) + goto KeyAlreadyDefinedError; + + if (value->type == YOML_TYPE_SEQUENCE) { + if (value->data.sequence.size == 0) { + h2o_configurator_errprintf(cmd, value, "status sequence must not be empty"); + return -1; + } + for (j = 0; j != value->data.sequence.size; ++j) { + if (scan_and_check_status(cmd, value->data.sequence.elements[j], &parsed) != 0) + return -1; + /* check the scanned status hasn't already appeared */ + for (k = 0; k != status_len; ++k) { + if (status[k] == parsed) { + h2o_configurator_errprintf(cmd, value, "status %d appears multiple times", status[k]); + return -1; + } + } + status[status_len++] = parsed; + } + } else { + if (scan_and_check_status(cmd, value, &parsed) != 0) + return -1; + status[status_len++] = parsed; + } + + } else if (strcmp(key->data.scalar, "url") == 0) { + if (url != NULL) + goto KeyAlreadyDefinedError; + if (value->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, value, "URL must be a scalar"); + return -1; + } + url = value->data.scalar; + } else { + goto UnknownKeyError; + } + } + + if (status_len == 0) { + h2o_configurator_errprintf(cmd, hash, "mandatory key `status` is not defined"); + return -1; + } + if (url == NULL) { + h2o_configurator_errprintf(cmd, hash, "mandatory key `url` is not defined"); + return -1; + } + + h2o_iovec_t _url = h2o_strdup(&self->pool, url, SIZE_MAX); + for (i = 0; i != status_len; ++i){ + /* register */ + h2o_vector_reserve(&self->pool, self->vars, self->vars->size + 1); + h2o_errordoc_t *errordoc = self->vars->entries + self->vars->size++; + errordoc->status = status[i]; + errordoc->url = _url; + } + + return 0; + +UnknownKeyError: + h2o_configurator_errprintf(cmd, key, "key must be either of: `status`, `url`"); + return -1; +KeyAlreadyDefinedError: + h2o_configurator_errprintf(cmd, key, "the key cannot be defined more than once"); + return -1; +} + +static int on_config_errordoc(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + switch (node->type) { + case YOML_TYPE_SEQUENCE: { + size_t i; + for (i = 0; i != node->data.sequence.size; ++i) { + yoml_t *e = node->data.sequence.elements[i]; + if (e->type != YOML_TYPE_MAPPING) { + h2o_configurator_errprintf(cmd, e, "element must be a mapping"); + return -1; + } + if (register_errordoc(cmd, ctx, e) != 0) + return -1; + } + return 0; + } + case YOML_TYPE_MAPPING: + return register_errordoc(cmd, ctx, node); + default: + break; + } + + h2o_configurator_errprintf(cmd, node, "argument must be either of: sequence, mapping"); + return -1; +} + +static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct errordoc_configurator_t *self = (void *)_self; + + if (self->vars == self->_vars_stack) { + /* entering global level */ + h2o_mem_init_pool(&self->pool); + } + + /* copy vars */ + memset(&self->vars[1], 0, sizeof(self->vars[1])); + h2o_vector_reserve(&self->pool, &self->vars[1], self->vars[0].size); + h2o_memcpy(self->vars[1].entries, self->vars[0].entries, sizeof(self->vars[0].entries[0]) * self->vars[0].size); + self->vars[1].size = self->vars[0].size; + + ++self->vars; + return 0; +} + +static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct errordoc_configurator_t *self = (void *)_self; + + if (ctx->pathconf != NULL && self->vars->size != 0) + h2o_errordoc_register(ctx->pathconf, self->vars->entries, self->vars->size); + + --self->vars; + if (self->vars == self->_vars_stack) { + /* exitting global level */ + h2o_mem_clear_pool(&self->pool); + } + + return 0; +} + +void h2o_errordoc_register_configurator(h2o_globalconf_t *conf) +{ + struct errordoc_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + /* set default vars */ + c->vars = c->_vars_stack; + + /* setup handlers */ + c->super.enter = on_config_enter; + c->super.exit = on_config_exit; + + /* reproxy: ON | OFF */ + h2o_configurator_define_command(&c->super, "error-doc", H2O_CONFIGURATOR_FLAG_ALL_LEVELS, on_config_errordoc); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/expires.c b/web/server/h2o/libh2o/lib/handler/configurator/expires.c new file mode 100644 index 000000000..01d404dd5 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/expires.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2014 DeNA Co., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include "h2o.h" +#include "h2o/configurator.h" + +struct expires_configurator_t { + h2o_configurator_t super; + h2o_expires_args_t **args; + h2o_expires_args_t *_args_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static int on_config_expires(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct expires_configurator_t *self = (void *)cmd->configurator; + uint64_t value; + char unit[32]; + + if (strcasecmp(node->data.scalar, "OFF") == 0) { + free(*self->args); + *self->args = NULL; + } else if (sscanf(node->data.scalar, "%" SCNu64 " %31s", &value, unit) == 2) { + /* convert value to seconds depending on the unit */ + if (strncasecmp(unit, H2O_STRLIT("second")) == 0) { + /* ok */ + } else if (strncasecmp(unit, H2O_STRLIT("minute")) == 0) { + value *= 60; + } else if (strncasecmp(unit, H2O_STRLIT("hour")) == 0) { + value *= 60 * 60; + } else if (strncasecmp(unit, H2O_STRLIT("day")) == 0) { + value *= 24 * 60 * 60; + } else if (strncasecmp(unit, H2O_STRLIT("month")) == 0) { + value *= 30 * 24 * 60 * 60; + } else if (strncasecmp(unit, H2O_STRLIT("year")) == 0) { + value *= 365 * 30 * 24 * 60 * 60; + } else { + /* TODO add support for H2O_EXPIRES_MODE_MAX_ABSOLUTE that sets the Expires header? */ + h2o_configurator_errprintf(cmd, node, "unknown unit:`%s` (see --help)", unit); + return -1; + } + /* save the value */ + if (*self->args == NULL) + *self->args = h2o_mem_alloc(sizeof(**self->args)); + (*self->args)->mode = H2O_EXPIRES_MODE_MAX_AGE; + (*self->args)->data.max_age = value; + } else { + h2o_configurator_errprintf(cmd, node, + "failed to parse the value, should be in form of: `<number> <unit>` or `OFF` (see --help)"); + return -1; + } + + return 0; +} + +static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct expires_configurator_t *self = (void *)_self; + + if (self->args[0] != NULL) { + /* duplicate */ + assert(self->args[0]->mode == H2O_EXPIRES_MODE_MAX_AGE); + self->args[1] = h2o_mem_alloc(sizeof(**self->args)); + *self->args[1] = *self->args[0]; + } else { + self->args[1] = NULL; + } + ++self->args; + return 0; +} + +static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct expires_configurator_t *self = (void *)_self; + + if (*self->args != NULL) { + /* setup */ + if (ctx->pathconf != NULL) { + h2o_expires_register(ctx->pathconf, *self->args); + } + /* destruct */ + assert((*self->args)->mode == H2O_EXPIRES_MODE_MAX_AGE); + free(*self->args); + *self->args = NULL; + } + + --self->args; + return 0; +} + +void h2o_expires_register_configurator(h2o_globalconf_t *conf) +{ + struct expires_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + /* set default vars */ + c->args = c->_args_stack; + + /* setup handlers */ + c->super.enter = on_config_enter; + c->super.exit = on_config_exit; + h2o_configurator_define_command(&c->super, "expires", H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_expires); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/fastcgi.c b/web/server/h2o/libh2o/lib/handler/configurator/fastcgi.c new file mode 100644 index 000000000..bf89b7b26 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/fastcgi.c @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2015 DeNA Co., Ltd. Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <pwd.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/un.h> +#include "h2o.h" +#include "h2o/configurator.h" +#include "h2o/serverutil.h" + +struct fastcgi_configurator_t { + h2o_configurator_t super; + h2o_fastcgi_config_vars_t *vars; + h2o_fastcgi_config_vars_t _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static int on_config_timeout_io(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct fastcgi_configurator_t *self = (void *)cmd->configurator; + return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->io_timeout); +} + +static int on_config_timeout_keepalive(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct fastcgi_configurator_t *self = (void *)cmd->configurator; + return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->keepalive_timeout); +} + +static int on_config_document_root(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct fastcgi_configurator_t *self = (void *)cmd->configurator; + + if (node->data.scalar[0] == '\0') { + /* unset */ + self->vars->document_root = h2o_iovec_init(NULL, 0); + } else if (node->data.scalar[0] == '/') { + /* set */ + self->vars->document_root = h2o_iovec_init(node->data.scalar, strlen(node->data.scalar)); + } else { + h2o_configurator_errprintf(cmd, node, "value does not start from `/`"); + return -1; + } + return 0; +} + +static int on_config_send_delegated_uri(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct fastcgi_configurator_t *self = (void *)cmd->configurator; + ssize_t v; + + if ((v = h2o_configurator_get_one_of(cmd, node, "OFF,ON")) == -1) + return -1; + self->vars->send_delegated_uri = (int)v; + return 0; +} + +static int on_config_connect(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct fastcgi_configurator_t *self = (void *)cmd->configurator; + const char *hostname = "127.0.0.1", *servname = NULL, *type = "tcp"; + + /* fetch servname (and hostname) */ + switch (node->type) { + case YOML_TYPE_SCALAR: + servname = node->data.scalar; + break; + case YOML_TYPE_MAPPING: { + yoml_t *t; + if ((t = yoml_get(node, "host")) != NULL) { + if (t->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, t, "`host` is not a string"); + return -1; + } + hostname = t->data.scalar; + } + if ((t = yoml_get(node, "port")) == NULL) { + h2o_configurator_errprintf(cmd, node, "cannot find mandatory property `port`"); + return -1; + } + if (t->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, node, "`port` is not a string"); + return -1; + } + servname = t->data.scalar; + if ((t = yoml_get(node, "type")) != NULL) { + if (t->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, t, "`type` is not a string"); + return -1; + } + type = t->data.scalar; + } + } break; + default: + h2o_configurator_errprintf(cmd, node, + "value must be a string or a mapping (with keys: `port` and optionally `host` and `type`)"); + return -1; + } + + if (strcmp(type, "unix") == 0) { + /* unix socket */ + struct sockaddr_un sa; + memset(&sa, 0, sizeof(sa)); + if (strlen(servname) >= sizeof(sa.sun_path)) { + h2o_configurator_errprintf(cmd, node, "path:%s is too long as a unix socket name", servname); + return -1; + } + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, servname); + h2o_fastcgi_register_by_address(ctx->pathconf, (void *)&sa, sizeof(sa), self->vars); + } else if (strcmp(type, "tcp") == 0) { + /* tcp socket */ + uint16_t port; + if (sscanf(servname, "%" SCNu16, &port) != 1) { + h2o_configurator_errprintf(cmd, node, "invalid port number:%s", servname); + return -1; + } + h2o_fastcgi_register_by_hostport(ctx->pathconf, hostname, port, self->vars); + } else { + h2o_configurator_errprintf(cmd, node, "unknown listen type: %s", type); + return -1; + } + + return 0; +} + +static int create_spawnproc(h2o_configurator_command_t *cmd, yoml_t *node, const char *dirname, char *const *argv, + struct sockaddr_un *sa, struct passwd *pw) +{ + int ret, listen_fd = -1, pipe_fds[2] = {-1, -1}; + + /* build socket path */ + sa->sun_family = AF_UNIX; + ret = snprintf(sa->sun_path, sizeof(sa->sun_path), "%s/_", dirname); + if (ret < 0 || ret >= sizeof(sa->sun_path)) { + h2o_configurator_errprintf(cmd, node, "unix socket path too long: %s", dirname); + goto Error; + } + + /* create socket */ + if ((listen_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + h2o_configurator_errprintf(cmd, node, "socket(2) failed: %s", strerror(errno)); + goto Error; + } + if (bind(listen_fd, (void *)sa, sizeof(*sa)) != 0) { + h2o_configurator_errprintf(cmd, node, "bind(2) failed: %s", strerror(errno)); + goto Error; + } + if (listen(listen_fd, H2O_SOMAXCONN) != 0) { + h2o_configurator_errprintf(cmd, node, "listen(2) failed: %s", strerror(errno)); + goto Error; + } + /* change ownership of socket */ + if (pw != NULL && chown(sa->sun_path, pw->pw_uid, pw->pw_gid) != 0) { + h2o_configurator_errprintf(cmd, node, "chown(2) failed to change ownership of socket:%s:%s", sa->sun_path, strerror(errno)); + goto Error; + } + + /* create pipe which is used to notify the termination of the server */ + if (pipe(pipe_fds) != 0) { + h2o_configurator_errprintf(cmd, node, "pipe(2) failed: %s", strerror(errno)); + pipe_fds[0] = -1; + pipe_fds[1] = -1; + goto Error; + } + if (fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC) < 0) + goto Error; + + /* spawn */ + int mapped_fds[] = {listen_fd, 0, /* listen_fd to 0 */ + pipe_fds[0], 5, /* pipe_fds[0] to 5 */ + -1}; + pid_t pid = h2o_spawnp(argv[0], argv, mapped_fds, 0); + if (pid == -1) { + fprintf(stderr, "[lib/handler/fastcgi.c] failed to launch helper program %s:%s\n", argv[0], strerror(errno)); + goto Error; + } + + close(listen_fd); + listen_fd = -1; + close(pipe_fds[0]); + pipe_fds[0] = -1; + + return pipe_fds[1]; + +Error: + if (pipe_fds[0] != -1) + close(pipe_fds[0]); + if (pipe_fds[1]) + close(pipe_fds[1]); + if (listen_fd != -1) + close(listen_fd); + unlink(sa->sun_path); + return -1; +} + +static void spawnproc_on_dispose(h2o_fastcgi_handler_t *handler, void *data) +{ + int pipe_fd = (int)((char *)data - (char *)NULL); + close(pipe_fd); +} + +static int on_config_spawn(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct fastcgi_configurator_t *self = (void *)cmd->configurator; + char *spawn_user = NULL, *spawn_cmd; + char *kill_on_close_cmd_path = NULL, *setuidgid_cmd_path = NULL; + char dirname[] = "/tmp/h2o.fcgisock.XXXXXX"; + char *argv[10]; + int spawner_fd; + struct sockaddr_un sa; + h2o_fastcgi_config_vars_t config_vars; + int ret = -1; + struct passwd h2o_user_pwbuf, *h2o_user_pw; + char h2o_user_buf[65536]; + + memset(&sa, 0, sizeof(sa)); + + switch (node->type) { + case YOML_TYPE_SCALAR: + spawn_user = ctx->globalconf->user; + spawn_cmd = node->data.scalar; + break; + case YOML_TYPE_MAPPING: { + yoml_t *t; + if ((t = yoml_get(node, "command")) == NULL) { + h2o_configurator_errprintf(cmd, node, "mandatory attribute `command` does not exist"); + return -1; + } + if (t->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, node, "attribute `command` must be scalar"); + return -1; + } + spawn_cmd = t->data.scalar; + spawn_user = ctx->globalconf->user; + if ((t = yoml_get(node, "user")) != NULL) { + if (t->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, node, "attribute `user` must be scalar"); + return -1; + } + spawn_user = t->data.scalar; + } + } break; + default: + h2o_configurator_errprintf(cmd, node, "argument must be scalar or mapping"); + return -1; + } + + /* obtain uid & gid of the client that connects to the FastCGI daemon (i.e. H2O after dropping privileges) */ + if (ctx->globalconf->user != NULL) { + /* change ownership of temporary directory */ + if (getpwnam_r(ctx->globalconf->user, &h2o_user_pwbuf, h2o_user_buf, sizeof(h2o_user_buf), &h2o_user_pw) != 0 || + h2o_user_pw == NULL) { + h2o_configurator_errprintf(cmd, node, "getpwnam_r(3) failed to obtain uid of user:%s", ctx->globalconf->user); + goto Exit; + } + } else { + h2o_user_pw = NULL; + } + + { /* build args */ + size_t i = 0; + argv[i++] = kill_on_close_cmd_path = h2o_configurator_get_cmd_path("share/h2o/kill-on-close"); + argv[i++] = "--rm"; + argv[i++] = dirname; + argv[i++] = "--"; + if (spawn_user != NULL) { + argv[i++] = setuidgid_cmd_path = h2o_configurator_get_cmd_path("share/h2o/setuidgid"); + argv[i++] = spawn_user; + } + argv[i++] = "/bin/sh"; + argv[i++] = "-c"; + argv[i++] = spawn_cmd; + argv[i++] = NULL; + assert(i <= sizeof(argv) / sizeof(argv[0])); + } + + if (ctx->dry_run) { + dirname[0] = '\0'; + spawner_fd = -1; + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, "/dry-run.nonexistent"); + } else { + /* create temporary directory */ + if (mkdtemp(dirname) == NULL) { + h2o_configurator_errprintf(cmd, node, "mkdtemp(3) failed to create temporary directory:%s:%s", dirname, + strerror(errno)); + dirname[0] = '\0'; + goto Exit; + } + /* change ownership of temporary directory */ + if (h2o_user_pw != NULL && chown(dirname, h2o_user_pw->pw_uid, h2o_user_pw->pw_gid) != 0) { + h2o_configurator_errprintf(cmd, node, "chown(2) failed to change ownership of temporary directory:%s:%s", dirname, + strerror(errno)); + goto Exit; + } + /* launch spawnfcgi command */ + if ((spawner_fd = create_spawnproc(cmd, node, dirname, argv, &sa, h2o_user_pw)) == -1) { + goto Exit; + } + } + + config_vars = *self->vars; + config_vars.callbacks.dispose = spawnproc_on_dispose; + config_vars.callbacks.data = (char *)NULL + spawner_fd; + h2o_fastcgi_register_by_address(ctx->pathconf, (void *)&sa, sizeof(sa), &config_vars); + + ret = 0; +Exit: + if (dirname[0] != '\0') + unlink(dirname); + free(kill_on_close_cmd_path); + free(setuidgid_cmd_path); + return ret; +} + +static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct fastcgi_configurator_t *self = (void *)_self; + + memcpy(self->vars + 1, self->vars, sizeof(*self->vars)); + ++self->vars; + return 0; +} + +static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct fastcgi_configurator_t *self = (void *)_self; + + --self->vars; + return 0; +} + +void h2o_fastcgi_register_configurator(h2o_globalconf_t *conf) +{ + struct fastcgi_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + /* set default vars */ + c->vars = c->_vars_stack; + c->vars->io_timeout = H2O_DEFAULT_FASTCGI_IO_TIMEOUT; + c->vars->keepalive_timeout = 0; + + /* setup handlers */ + c->super.enter = on_config_enter; + c->super.exit = on_config_exit; + + h2o_configurator_define_command(&c->super, "fastcgi.connect", + H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_EXTENSION | H2O_CONFIGURATOR_FLAG_DEFERRED, + on_config_connect); + h2o_configurator_define_command(&c->super, "fastcgi.spawn", + H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_EXTENSION | H2O_CONFIGURATOR_FLAG_DEFERRED, + on_config_spawn); + h2o_configurator_define_command(&c->super, "fastcgi.timeout.io", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_timeout_io); + h2o_configurator_define_command(&c->super, "fastcgi.timeout.keepalive", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_timeout_keepalive); + h2o_configurator_define_command(&c->super, "fastcgi.document_root", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_document_root); + h2o_configurator_define_command(&c->super, "fastcgi.send-delegated-uri", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_send_delegated_uri); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/file.c b/web/server/h2o/libh2o/lib/handler/configurator/file.c new file mode 100644 index 000000000..c1c779c68 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/file.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014 DeNA Co., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "h2o.h" +#include "h2o/configurator.h" + +struct st_h2o_file_config_vars_t { + const char **index_files; + int flags; +}; + +struct st_h2o_file_configurator_t { + h2o_configurator_t super; + struct st_h2o_file_config_vars_t *vars; + struct st_h2o_file_config_vars_t _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static int on_config_dir(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_file_configurator_t *self = (void *)cmd->configurator; + + h2o_file_register(ctx->pathconf, node->data.scalar, self->vars->index_files, *ctx->mimemap, self->vars->flags); + return 0; +} + +static int on_config_file(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_file_configurator_t *self = (void *)cmd->configurator; + h2o_mimemap_type_t *mime_type = + h2o_mimemap_get_type_by_extension(*ctx->mimemap, h2o_get_filext(node->data.scalar, strlen(node->data.scalar))); + h2o_file_register_file(ctx->pathconf, node->data.scalar, mime_type, self->vars->flags); + return 0; +} + +static int on_config_index(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_file_configurator_t *self = (void *)cmd->configurator; + size_t i; + + free(self->vars->index_files); + self->vars->index_files = h2o_mem_alloc(sizeof(self->vars->index_files[0]) * (node->data.sequence.size + 1)); + for (i = 0; i != node->data.sequence.size; ++i) { + yoml_t *element = node->data.sequence.elements[i]; + if (element->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, element, "argument must be a sequence of scalars"); + return -1; + } + self->vars->index_files[i] = element->data.scalar; + } + self->vars->index_files[i] = NULL; + + return 0; +} + +static int on_config_etag(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_file_configurator_t *self = (void *)cmd->configurator; + + switch (h2o_configurator_get_one_of(cmd, node, "OFF,ON")) { + case 0: /* off */ + self->vars->flags |= H2O_FILE_FLAG_NO_ETAG; + break; + case 1: /* on */ + self->vars->flags &= ~H2O_FILE_FLAG_NO_ETAG; + break; + default: /* error */ + return -1; + } + + return 0; +} + +static int on_config_send_compressed(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_file_configurator_t *self = (void *)cmd->configurator; + + switch (h2o_configurator_get_one_of(cmd, node, "OFF,ON,gunzip")) { + case 0: /* off */ + self->vars->flags &= ~H2O_FILE_FLAG_SEND_COMPRESSED; + break; + case 1: /* on */ + self->vars->flags |= H2O_FILE_FLAG_SEND_COMPRESSED; + break; + case 2: /* gunzip */ + self->vars->flags |= (H2O_FILE_FLAG_SEND_COMPRESSED | H2O_FILE_FLAG_GUNZIP); + break; + default: /* error */ + return -1; + } + + return 0; +} + +static int on_config_dir_listing(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_file_configurator_t *self = (void *)cmd->configurator; + + switch (h2o_configurator_get_one_of(cmd, node, "OFF,ON")) { + case 0: /* off */ + self->vars->flags &= ~H2O_FILE_FLAG_DIR_LISTING; + break; + case 1: /* on */ + self->vars->flags |= H2O_FILE_FLAG_DIR_LISTING; + break; + default: /* error */ + return -1; + } + + return 0; +} + +static const char **dup_strlist(const char **s) +{ + size_t i; + const char **ret; + + for (i = 0; s[i] != NULL; ++i) + ; + ret = h2o_mem_alloc(sizeof(*ret) * (i + 1)); + for (i = 0; s[i] != NULL; ++i) + ret[i] = s[i]; + ret[i] = NULL; + + return ret; +} + +static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_file_configurator_t *self = (void *)_self; + ++self->vars; + self->vars[0].index_files = dup_strlist(self->vars[-1].index_files); + self->vars[0].flags = self->vars[-1].flags; + return 0; +} + +static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_h2o_file_configurator_t *self = (void *)_self; + free(self->vars->index_files); + --self->vars; + return 0; +} + +void h2o_file_register_configurator(h2o_globalconf_t *globalconf) +{ + struct st_h2o_file_configurator_t *self = (void *)h2o_configurator_create(globalconf, sizeof(*self)); + + self->super.enter = on_config_enter; + self->super.exit = on_config_exit; + self->vars = self->_vars_stack; + self->vars->index_files = h2o_file_default_index_files; + + h2o_configurator_define_command(&self->super, "file.dir", H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR | + H2O_CONFIGURATOR_FLAG_DEFERRED, + on_config_dir); + h2o_configurator_define_command(&self->super, "file.file", H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR | + H2O_CONFIGURATOR_FLAG_DEFERRED, + on_config_file); + h2o_configurator_define_command(&self->super, "file.index", + (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) | + H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE, + on_config_index); + h2o_configurator_define_command(&self->super, "file.etag", + (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) | + H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_etag); + h2o_configurator_define_command(&self->super, "file.send-compressed", + (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) | + H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_send_compressed); + h2o_configurator_define_command(&self->super, "file.send-gzip", + (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) | + H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_send_compressed); + h2o_configurator_define_command(&self->super, "file.dirlisting", + (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) | + H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_dir_listing); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/headers.c b/web/server/h2o/libh2o/lib/handler/configurator/headers.c new file mode 100644 index 000000000..68536c052 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/headers.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include <string.h> +#include "h2o.h" +#include "h2o/configurator.h" + +struct headers_configurator_t { + h2o_configurator_t super; + h2o_headers_command_t **cmds, *_cmd_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct headers_configurator_t *self = (void *)_self; + + self->cmds[1] = self->cmds[0]; + if (self->cmds[1] != NULL) + h2o_mem_addref_shared(self->cmds[1]); + + ++self->cmds; + return 0; +} + +static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct headers_configurator_t *self = (void *)_self; + + if (ctx->pathconf != NULL && *self->cmds != NULL) { + if (*self->cmds != NULL) + h2o_mem_addref_shared(*self->cmds); + h2o_headers_register(ctx->pathconf, *self->cmds); + } + + if (*self->cmds != NULL) + h2o_mem_release_shared(*self->cmds); + --self->cmds; + return 0; +} + +static h2o_headers_command_t **get_headers_commands(h2o_configurator_t *_self) +{ + struct headers_configurator_t *self = (void *)_self; + return self->cmds; +} + +void h2o_headers_register_configurator(h2o_globalconf_t *conf) +{ + struct headers_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + c->super.enter = on_config_enter; + c->super.exit = on_config_exit; + + h2o_configurator_define_headers_commands(conf, &c->super, "header", get_headers_commands); + c->cmds = c->_cmd_stack; +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/headers_util.c b/web/server/h2o/libh2o/lib/handler/configurator/headers_util.c new file mode 100644 index 000000000..c05b9b7c2 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/headers_util.c @@ -0,0 +1,143 @@ +#include "h2o.h" +#include "h2o/configurator.h" + +struct headers_util_configurator_t { + h2o_configurator_t super; + h2o_configurator_t *child; + h2o_configurator_get_headers_commands_cb get_commands; +}; + +static int extract_name(const char *src, size_t len, h2o_iovec_t **_name) +{ + h2o_iovec_t name; + const h2o_token_t *name_token; + + name = h2o_str_stripws(src, len); + if (name.len == 0) + return -1; + + name = h2o_strdup(NULL, name.base, name.len); + h2o_strtolower(name.base, name.len); + + if ((name_token = h2o_lookup_token(name.base, name.len)) != NULL) { + *_name = (h2o_iovec_t *)&name_token->buf; + free(name.base); + } else { + *_name = h2o_mem_alloc(sizeof(**_name)); + **_name = name; + } + + return 0; +} + +static int extract_name_value(const char *src, h2o_iovec_t **name, h2o_iovec_t *value) +{ + const char *colon = strchr(src, ':'); + + if (colon == NULL) + return -1; + + if (extract_name(src, colon - src, name) != 0) + return -1; + *value = h2o_str_stripws(colon + 1, strlen(colon + 1)); + *value = h2o_strdup(NULL, value->base, value->len); + + return 0; +} + +static int add_cmd(h2o_configurator_command_t *cmd, yoml_t *node, int cmd_id, h2o_iovec_t *name, h2o_iovec_t value, + h2o_headers_command_t **cmds) +{ + if (h2o_iovec_is_token(name)) { + const h2o_token_t *token = (void *)name; + if (h2o_headers_is_prohibited_name(token)) { + h2o_configurator_errprintf(cmd, node, "the named header cannot be rewritten"); + return -1; + } + } + + h2o_headers_append_command(cmds, cmd_id, name, value); + return 0; +} + +static int on_config_header_2arg(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, int cmd_id, yoml_t *node, + h2o_headers_command_t **headers_cmds) +{ + h2o_iovec_t *name, value; + + if (extract_name_value(node->data.scalar, &name, &value) != 0) { + h2o_configurator_errprintf(cmd, node, "failed to parse the value; should be in form of `name: value`"); + return -1; + } + if (add_cmd(cmd, node, cmd_id, name, value, headers_cmds) != 0) { + if (!h2o_iovec_is_token(name)) + free(name->base); + free(value.base); + return -1; + } + return 0; +} + +static int on_config_header_unset(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + h2o_iovec_t *name; + struct headers_util_configurator_t *self = (void *)cmd->configurator; + + if (extract_name(node->data.scalar, strlen(node->data.scalar), &name) != 0) { + h2o_configurator_errprintf(cmd, node, "invalid header name"); + return -1; + } + if (add_cmd(cmd, node, H2O_HEADERS_CMD_UNSET, name, (h2o_iovec_t){NULL}, self->get_commands(self->child)) != 0) { + if (!h2o_iovec_is_token(name)) + free(name->base); + return -1; + } + return 0; +} + +#define DEFINE_2ARG(fn, cmd_id) \ + static int fn(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) \ + { \ + struct headers_util_configurator_t *self = (void *)cmd->configurator; \ + return on_config_header_2arg(cmd, ctx, cmd_id, node, self->get_commands(self->child)); \ + } + +DEFINE_2ARG(on_config_header_add, H2O_HEADERS_CMD_ADD) +DEFINE_2ARG(on_config_header_append, H2O_HEADERS_CMD_APPEND) +DEFINE_2ARG(on_config_header_merge, H2O_HEADERS_CMD_MERGE) +DEFINE_2ARG(on_config_header_set, H2O_HEADERS_CMD_SET) +DEFINE_2ARG(on_config_header_setifempty, H2O_HEADERS_CMD_SETIFEMPTY) + +#undef DEFINE_2ARG + +void h2o_configurator_define_headers_commands(h2o_globalconf_t *global_conf, h2o_configurator_t *conf, const char *prefix, + h2o_configurator_get_headers_commands_cb get_commands) +{ + struct headers_util_configurator_t *c = (void *)h2o_configurator_create(global_conf, sizeof(*c)); + c->child = conf; + c->get_commands = get_commands; + size_t prefix_len = strlen(prefix); + +#define DEFINE_CMD_NAME(name, suffix) \ + char *name = h2o_mem_alloc(prefix_len + sizeof(suffix)); \ + memcpy(name, prefix, prefix_len); \ + memcpy(name + prefix_len, suffix, sizeof(suffix)) + + DEFINE_CMD_NAME(add_directive, ".add"); + DEFINE_CMD_NAME(append_directive, ".append"); + DEFINE_CMD_NAME(merge_directive, ".merge"); + DEFINE_CMD_NAME(set_directive, ".set"); + DEFINE_CMD_NAME(setifempty_directive, ".setifempty"); + DEFINE_CMD_NAME(unset_directive, ".unset"); +#undef DEFINE_CMD_NAME + +#define DEFINE_CMD(name, cb) \ + h2o_configurator_define_command(&c->super, name, H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, cb) + DEFINE_CMD(add_directive, on_config_header_add); + DEFINE_CMD(append_directive, on_config_header_append); + DEFINE_CMD(merge_directive, on_config_header_merge); + DEFINE_CMD(set_directive, on_config_header_set); + DEFINE_CMD(setifempty_directive, on_config_header_setifempty); + DEFINE_CMD(unset_directive, on_config_header_unset); +#undef DEFINE_CMD +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/http2_debug_state.c b/web/server/h2o/libh2o/lib/handler/configurator/http2_debug_state.c new file mode 100644 index 000000000..419da9de8 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/http2_debug_state.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Ichito Nagata + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "h2o.h" +#include "h2o/configurator.h" + +static int on_config_debug_state(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + switch (h2o_configurator_get_one_of(cmd, node, "minimum,hpack")) { + case 0: /* minimum */ + h2o_http2_debug_state_register(ctx->hostconf, 0); + return 0; + case 1: /* with hpack state*/ + h2o_http2_debug_state_register(ctx->hostconf, 1); + return 0; + default: /* error */ + return -1; + } +} + +void h2o_http2_debug_state_register_configurator(h2o_globalconf_t *conf) +{ + struct st_h2o_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + h2o_configurator_define_command(c, "http2-debug-state", H2O_CONFIGURATOR_FLAG_HOST | H2O_CONFIGURATOR_FLAG_DEFERRED | + H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_debug_state); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/mruby.c b/web/server/h2o/libh2o/lib/handler/configurator/mruby.c new file mode 100644 index 000000000..1cef8f499 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/mruby.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2015 DeNA Co., Ltd., Kazuho Oku, Ryosuke Matsumoto + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include "h2o.h" +#include "h2o/configurator.h" +#include "h2o/mruby_.h" + +struct mruby_configurator_t { + h2o_configurator_t super; + h2o_mruby_config_vars_t *vars; + h2o_mruby_config_vars_t _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; + mrb_state *mrb; /* will be lazily initialized */ +}; + +static int compile_test(mrb_state *mrb, h2o_mruby_config_vars_t *config, char *errbuf) +{ + mrb_value result = h2o_mruby_compile_code(mrb, config, errbuf); + int ok = !mrb_nil_p(result); + return ok; +} + +static mrb_state *get_mrb(struct mruby_configurator_t *self) +{ + if (self->mrb == NULL) { + self->mrb = mrb_open(); + if (self->mrb == NULL) { + fprintf(stderr, "%s: no memory\n", H2O_MRUBY_MODULE_NAME); + abort(); + } + h2o_mruby_setup_globals(self->mrb); + } + return self->mrb; +} + +static int on_config_mruby_handler(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct mruby_configurator_t *self = (void *)cmd->configurator; + + /* set source */ + self->vars->source = h2o_strdup(NULL, node->data.scalar, SIZE_MAX); + self->vars->path = node->filename; + self->vars->lineno = (int)node->line + 1; + + /* check if there is any error in source */ + char errbuf[1024]; + if (!compile_test(get_mrb(self), self->vars, errbuf)) { + h2o_configurator_errprintf(cmd, node, "ruby compile error:%s", errbuf); + return -1; + } + + /* register */ + h2o_mruby_register(ctx->pathconf, self->vars); + + return 0; +} + +static int on_config_mruby_handler_file(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct mruby_configurator_t *self = (void *)cmd->configurator; + FILE *fp = NULL; + h2o_iovec_t buf = {NULL}; + int ret = -1; + + /* open and read file */ + if ((fp = fopen(node->data.scalar, "rt")) == NULL) { + h2o_configurator_errprintf(cmd, node, "failed to open file: %s:%s", node->data.scalar, strerror(errno)); + goto Exit; + } + while (!feof(fp)) { + buf.base = h2o_mem_realloc(buf.base, buf.len + 65536); + buf.len += fread(buf.base + buf.len, 1, 65536, fp); + if (ferror(fp)) { + h2o_configurator_errprintf(cmd, node, "I/O error occurred while reading file:%s:%s", node->data.scalar, + strerror(errno)); + goto Exit; + } + } + + /* set source */ + self->vars->source = buf; + buf.base = NULL; + self->vars->path = node->data.scalar; /* the value is retained until the end of the configuration phase */ + self->vars->lineno = 0; + + /* check if there is any error in source */ + char errbuf[1024]; + if (!compile_test(get_mrb(self), self->vars, errbuf)) { + h2o_configurator_errprintf(cmd, node, "failed to compile file:%s:%s", node->data.scalar, errbuf); + goto Exit; + } + + /* register */ + h2o_mruby_register(ctx->pathconf, self->vars); + + ret = 0; + +Exit: + if (fp != NULL) + fclose(fp); + if (buf.base != NULL) + free(buf.base); + return ret; +} + +static int on_config_mruby_handler_path(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + h2o_configurator_errprintf(cmd, node, "the command has been removed; see https://github.com/h2o/h2o/pull/467"); + return -1; +} + +static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct mruby_configurator_t *self = (void *)_self; + + memcpy(self->vars + 1, self->vars, sizeof(*self->vars)); + ++self->vars; + return 0; +} + +static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct mruby_configurator_t *self = (void *)_self; + + /* free if the to-be-exitted frame level contains a different source */ + if (self->vars[-1].source.base != self->vars[0].source.base) + free(self->vars->source.base); + + --self->vars; + + /* release mrb only when global configuration exited */ + if (self->mrb != NULL && ctx->parent == NULL) { + mrb_close(self->mrb); + self->mrb = NULL; + } + + return 0; +} + +void h2o_mruby_register_configurator(h2o_globalconf_t *conf) +{ + struct mruby_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + c->vars = c->_vars_stack; + c->super.enter = on_config_enter; + c->super.exit = on_config_exit; + + h2o_configurator_define_command(&c->super, "mruby.handler", H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_DEFERRED | + H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_mruby_handler); + h2o_configurator_define_command(&c->super, "mruby.handler-file", H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_DEFERRED | + H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_mruby_handler_file); + h2o_configurator_define_command(&c->super, "mruby.handler_path", H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_DEFERRED, + on_config_mruby_handler_path); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/proxy.c b/web/server/h2o/libh2o/lib/handler/configurator/proxy.c new file mode 100644 index 000000000..cfc9cbf40 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/proxy.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2014 DeNA Co., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include <inttypes.h> +#include <errno.h> +#include <stdio.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include "h2o.h" +#include "h2o/configurator.h" + +struct proxy_configurator_t { + h2o_configurator_t super; + h2o_proxy_config_vars_t *vars; + h2o_proxy_config_vars_t _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static int on_config_timeout_io(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->io_timeout); +} + +static int on_config_timeout_keepalive(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->keepalive_timeout); +} + +static int on_config_preserve_host(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); + if (ret == -1) + return -1; + self->vars->preserve_host = (int)ret; + return 0; +} + +static int on_config_proxy_protocol(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); + if (ret == -1) + return -1; + self->vars->use_proxy_protocol = (int)ret; + return 0; +} + +static int on_config_websocket_timeout(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->websocket.timeout); +} + +static int on_config_websocket(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); + if (ret == -1) + return -1; + self->vars->websocket.enabled = (int)ret; + return 0; +} + +static SSL_CTX *create_ssl_ctx(void) +{ + SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method()); + SSL_CTX_set_options(ctx, SSL_CTX_get_options(ctx) | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + return ctx; +} + +static h2o_cache_t *create_ssl_session_cache(size_t capacity, uint64_t duration) +{ + return h2o_cache_create(H2O_CACHE_FLAG_MULTITHREADED, capacity, duration, h2o_socket_ssl_destroy_session_cache_entry); +} + +static void update_ssl_ctx(SSL_CTX **ctx, X509_STORE *cert_store, int verify_mode, h2o_cache_t **session_cache) +{ + assert(*ctx != NULL); + + /* inherit the properties that weren't specified */ + if (cert_store == NULL) + cert_store = SSL_CTX_get_cert_store(*ctx); + X509_STORE_up_ref(cert_store); + if (verify_mode == -1) + verify_mode = SSL_CTX_get_verify_mode(*ctx); + h2o_cache_t *new_session_cache; + if (session_cache == NULL) { + h2o_cache_t *current = h2o_socket_ssl_get_session_cache(*ctx); + new_session_cache = + current == NULL ? NULL : create_ssl_session_cache(h2o_cache_get_capacity(current), h2o_cache_get_duration(current)); + } else { + new_session_cache = *session_cache; + } + + /* free the existing context */ + if (*ctx != NULL) + SSL_CTX_free(*ctx); + + /* create new ctx */ + *ctx = create_ssl_ctx(); + SSL_CTX_set_cert_store(*ctx, cert_store); + SSL_CTX_set_verify(*ctx, verify_mode, NULL); + if (new_session_cache != NULL) + h2o_socket_ssl_set_session_cache(*ctx, new_session_cache); +} + +static int on_config_ssl_verify_peer(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); + if (ret == -1) + return -1; + + update_ssl_ctx(&self->vars->ssl_ctx, NULL, ret != 0 ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, + NULL); + + return 0; +} + +static int on_config_ssl_cafile(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + X509_STORE *store = X509_STORE_new(); + int ret = -1; + + if (X509_STORE_load_locations(store, node->data.scalar, NULL) == 1) { + update_ssl_ctx(&self->vars->ssl_ctx, store, -1, NULL); + ret = 0; + } else { + h2o_configurator_errprintf(cmd, node, "failed to load certificates file:%s", node->data.scalar); + ERR_print_errors_fp(stderr); + } + + X509_STORE_free(store); + return ret; +} + +static int on_config_ssl_session_cache(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + size_t capacity = 0; + uint64_t duration = 0; + h2o_cache_t *current_cache = h2o_socket_ssl_get_session_cache(self->vars->ssl_ctx); + + switch (node->type) { + case YOML_TYPE_SCALAR: + if (strcasecmp(node->data.scalar, "OFF") == 0) { + if (current_cache != NULL) { + /* set the cache NULL */ + h2o_cache_t *empty_cache = NULL; + update_ssl_ctx(&self->vars->ssl_ctx, NULL, -1, &empty_cache); + } + return 0; + } else if (strcasecmp(node->data.scalar, "ON") == 0) { + /* use default values */ + capacity = H2O_DEFAULT_PROXY_SSL_SESSION_CACHE_CAPACITY; + duration = H2O_DEFAULT_PROXY_SSL_SESSION_CACHE_DURATION; + } else { + h2o_configurator_errprintf(cmd, node, "scalar argument must be either of: `OFF`, `ON`"); + return -1; + } + break; + case YOML_TYPE_MAPPING: { + size_t i; + for (i = 0; i != node->data.mapping.size; ++i) { + yoml_t *key = node->data.mapping.elements[i].key; + yoml_t *value = node->data.mapping.elements[i].value; + if (key->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, key, "key must be a scalar"); + return -1; + } + if (strcasecmp(key->data.scalar, "capacity") == 0) { + if (h2o_configurator_scanf(cmd, value, "%zu", &capacity) != 0) + return -1; + if (capacity == 0) { + h2o_configurator_errprintf(cmd, key, "capacity must be greater than zero"); + return -1; + } + } else if (strcasecmp(key->data.scalar, "lifetime") == 0) { + unsigned lifetime = 0; + if (h2o_configurator_scanf(cmd, value, "%u", &lifetime) != 0) + return -1; + if (lifetime == 0) { + h2o_configurator_errprintf(cmd, key, "lifetime must be greater than zero"); + return -1; + } + duration = (uint64_t)lifetime * 1000; + } else { + h2o_configurator_errprintf(cmd, key, "key must be either of: `capacity`, `lifetime`"); + return -1; + } + } + if (capacity == 0 || duration == 0) { + h2o_configurator_errprintf(cmd, node, "`capacity` and `lifetime` are required"); + return -1; + } + } break; + default: + h2o_configurator_errprintf(cmd, node, "node must be a scalar or a mapping"); + return -1; + } + + if (current_cache != NULL) { + size_t current_capacity = h2o_cache_get_capacity(current_cache); + uint64_t current_duration = h2o_cache_get_duration(current_cache); + if (capacity == current_capacity && duration == current_duration) { + /* parameters aren't changed, so reuse it */ + return 0; + } + } + + h2o_cache_t *new_cache = create_ssl_session_cache(capacity, duration); + update_ssl_ctx(&self->vars->ssl_ctx, NULL, -1, &new_cache); + return 0; +} + +static int on_config_reverse_url(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)cmd->configurator; + h2o_url_t parsed; + + if (h2o_url_parse(node->data.scalar, SIZE_MAX, &parsed) != 0) { + h2o_configurator_errprintf(cmd, node, "failed to parse URL: %s\n", node->data.scalar); + return -1; + } + if (self->vars->keepalive_timeout != 0 && self->vars->use_proxy_protocol) { + h2o_configurator_errprintf(cmd, node, "please either set `proxy.use-proxy-protocol` to `OFF` or disable keep-alive by " + "setting `proxy.timeout.keepalive` to zero; the features are mutually exclusive"); + return -1; + } + + if (self->vars->headers_cmds != NULL) + h2o_mem_addref_shared(self->vars->headers_cmds); + + /* register */ + h2o_proxy_register_reverse_proxy(ctx->pathconf, &parsed, self->vars); + + return 0; +} + +static int on_config_emit_x_forwarded_headers(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); + if (ret == -1) + return -1; + ctx->globalconf->proxy.emit_x_forwarded_headers = (int)ret; + return 0; +} + +static int on_config_emit_via_header(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); + if (ret == -1) + return -1; + ctx->globalconf->proxy.emit_via_header = (int)ret; + return 0; +} + +static int on_config_preserve_x_forwarded_proto(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); + if (ret == -1) + return -1; + ctx->globalconf->proxy.preserve_x_forwarded_proto = (int)ret; + return 0; +} + +static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)_self; + + memcpy(self->vars + 1, self->vars, sizeof(*self->vars)); + if (self->vars[1].headers_cmds != NULL) + h2o_mem_addref_shared(self->vars[1].headers_cmds); + ++self->vars; + + if (ctx->pathconf == NULL && ctx->hostconf == NULL) { + /* is global conf, setup the default SSL context */ + self->vars->ssl_ctx = create_ssl_ctx(); + char *ca_bundle = h2o_configurator_get_cmd_path("share/h2o/ca-bundle.crt"); + if (SSL_CTX_load_verify_locations(self->vars->ssl_ctx, ca_bundle, NULL) != 1) + fprintf(stderr, "Warning: failed to load the default certificates file at %s. Proxying to HTTPS servers may fail.\n", + ca_bundle); + free(ca_bundle); + SSL_CTX_set_verify(self->vars->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + h2o_cache_t *ssl_session_cache = + create_ssl_session_cache(H2O_DEFAULT_PROXY_SSL_SESSION_CACHE_CAPACITY, H2O_DEFAULT_PROXY_SSL_SESSION_CACHE_DURATION); + h2o_socket_ssl_set_session_cache(self->vars->ssl_ctx, ssl_session_cache); + } else { + SSL_CTX_up_ref(self->vars->ssl_ctx); + } + + return 0; +} + +static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct proxy_configurator_t *self = (void *)_self; + + if (ctx->pathconf == NULL && ctx->hostconf == NULL) { + /* is global conf */ + ctx->globalconf->proxy.io_timeout = self->vars->io_timeout; + ctx->globalconf->proxy.ssl_ctx = self->vars->ssl_ctx; + } else { + SSL_CTX_free(self->vars->ssl_ctx); + } + + if (self->vars->headers_cmds != NULL) + h2o_mem_release_shared(self->vars->headers_cmds); + + --self->vars; + return 0; +} + +static h2o_headers_command_t **get_headers_commands(h2o_configurator_t *_self) +{ + struct proxy_configurator_t *self = (void *)_self; + return &self->vars->headers_cmds; +} + +void h2o_proxy_register_configurator(h2o_globalconf_t *conf) +{ + struct proxy_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + /* set default vars */ + c->vars = c->_vars_stack; + c->vars->io_timeout = H2O_DEFAULT_PROXY_IO_TIMEOUT; + c->vars->keepalive_timeout = 2000; + c->vars->websocket.enabled = 0; /* have websocket proxying disabled by default; until it becomes non-experimental */ + c->vars->websocket.timeout = H2O_DEFAULT_PROXY_WEBSOCKET_TIMEOUT; + + /* setup handlers */ + c->super.enter = on_config_enter; + c->super.exit = on_config_exit; + h2o_configurator_define_command( + &c->super, "proxy.reverse.url", + H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR | H2O_CONFIGURATOR_FLAG_DEFERRED, on_config_reverse_url); + h2o_configurator_define_command(&c->super, "proxy.preserve-host", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_preserve_host); + h2o_configurator_define_command(&c->super, "proxy.proxy-protocol", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_proxy_protocol); + h2o_configurator_define_command(&c->super, "proxy.timeout.io", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_timeout_io); + h2o_configurator_define_command(&c->super, "proxy.timeout.keepalive", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_timeout_keepalive); + h2o_configurator_define_command(&c->super, "proxy.websocket", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_websocket); + h2o_configurator_define_command(&c->super, "proxy.websocket.timeout", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_websocket_timeout); + h2o_configurator_define_command(&c->super, "proxy.ssl.verify-peer", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_ssl_verify_peer); + h2o_configurator_define_command(&c->super, "proxy.ssl.cafile", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_ssl_cafile); + h2o_configurator_define_command(&c->super, "proxy.ssl.session-cache", H2O_CONFIGURATOR_FLAG_ALL_LEVELS, + on_config_ssl_session_cache); + h2o_configurator_define_command(&c->super, "proxy.preserve-x-forwarded-proto", + H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_preserve_x_forwarded_proto); + h2o_configurator_define_command(&c->super, "proxy.emit-x-forwarded-headers", + H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_emit_x_forwarded_headers); + h2o_configurator_define_command(&c->super, "proxy.emit-via-header", + H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_emit_via_header); + h2o_configurator_define_headers_commands(conf, &c->super, "proxy.header", get_headers_commands); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/redirect.c b/web/server/h2o/libh2o/lib/handler/configurator/redirect.c new file mode 100644 index 000000000..4ebbb99c0 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/redirect.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "h2o.h" +#include "h2o/configurator.h" + +static int on_config(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + const char *dest; + int status = 302; /* default is temporary redirect */ + int internal = 0; /* default is external redirect */ + yoml_t *t; + + switch (node->type) { + case YOML_TYPE_SCALAR: + dest = node->data.scalar; + break; + case YOML_TYPE_MAPPING: + if ((t = yoml_get(node, "url")) == NULL) { + h2o_configurator_errprintf(cmd, node, "mandatory property `url` is missing"); + return -1; + } + if (t->type != YOML_TYPE_SCALAR) { + h2o_configurator_errprintf(cmd, t, "property `url` must be a string"); + return -1; + } + dest = t->data.scalar; + if ((t = yoml_get(node, "status")) == NULL) { + h2o_configurator_errprintf(cmd, node, "mandatory property `status` is missing"); + return -1; + } + if (h2o_configurator_scanf(cmd, t, "%d", &status) != 0) + return -1; + if (!(300 <= status && status <= 399)) { + h2o_configurator_errprintf(cmd, t, "value of property `status` should be within 300 to 399"); + return -1; + } + if ((t = yoml_get(node, "internal")) != NULL) { + if ((internal = (int)h2o_configurator_get_one_of(cmd, t, "NO,YES")) == -1) + return -1; + } + break; + default: + h2o_configurator_errprintf(cmd, node, "value must be a string or a mapping"); + return -1; + } + + h2o_redirect_register(ctx->pathconf, internal, status, dest); + + return 0; +} + +void h2o_redirect_register_configurator(h2o_globalconf_t *conf) +{ + h2o_configurator_t *c = h2o_configurator_create(conf, sizeof(*c)); + + h2o_configurator_define_command(c, "redirect", H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_DEFERRED, on_config); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/reproxy.c b/web/server/h2o/libh2o/lib/handler/configurator/reproxy.c new file mode 100644 index 000000000..21650e4fb --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/reproxy.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015 Daisuke Maki, DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include <inttypes.h> +#include "h2o.h" +#include "h2o/configurator.h" + +struct config_t { + int enabled; +}; + +struct reproxy_configurator_t { + h2o_configurator_t super; + struct config_t *vars, _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static int on_config_reproxy(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct reproxy_configurator_t *self = (void *)cmd->configurator; + + ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); + if (ret == -1) + return -1; + self->vars->enabled = (int)ret; + + return 0; +} + +static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct reproxy_configurator_t *self = (void *)_self; + + self->vars[1] = self->vars[0]; + ++self->vars; + return 0; +} + +static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct reproxy_configurator_t *self = (void *)_self; + + if (ctx->pathconf != NULL && self->vars->enabled != 0) + h2o_reproxy_register(ctx->pathconf); + + --self->vars; + return 0; +} + +void h2o_reproxy_register_configurator(h2o_globalconf_t *conf) +{ + struct reproxy_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + /* set default vars */ + c->vars = c->_vars_stack; + + /* setup handlers */ + c->super.enter = on_config_enter; + c->super.exit = on_config_exit; + + /* reproxy: ON | OFF */ + h2o_configurator_define_command(&c->super, "reproxy", H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_reproxy); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/status.c b/web/server/h2o/libh2o/lib/handler/configurator/status.c new file mode 100644 index 000000000..8645aa33d --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/status.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "h2o.h" +#include "h2o/configurator.h" + +static int on_config_status(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + switch (h2o_configurator_get_one_of(cmd, node, "OFF,ON")) { + case 0: /* OFF */ + return 0; + case 1: /* ON */ + h2o_status_register(ctx->pathconf); + return 0; + default: /* error */ + return -1; + } +} + +struct st_status_configurator { + h2o_configurator_t super; + int stack; + int duration_stats; +}; + +static int on_config_duration_stats(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_status_configurator *c = (void *)cmd->configurator; + ssize_t ret; + switch (ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON")) { + case 0: /* OFF */ + case 1: /* ON */ + c->duration_stats = (int)ret; + return 0; + default: /* error */ + return -1; + } +} + +int on_enter_status(h2o_configurator_t *_conf, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_status_configurator *c = (void *)_conf; + c->stack++; + return 0; +} + +int on_exit_status(h2o_configurator_t *_conf, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct st_status_configurator *c = (void *)_conf; + c->stack--; + if (!c->stack && c->duration_stats) { + h2o_duration_stats_register(ctx->globalconf); + } + return 0; +} + +void h2o_status_register_configurator(h2o_globalconf_t *conf) +{ + struct st_status_configurator *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + c->super.enter = on_enter_status; + c->super.exit = on_exit_status; + + h2o_configurator_define_command(&c->super, "status", H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_DEFERRED | + H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_status); + + h2o_configurator_define_command(&c->super, "duration-stats", H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_duration_stats); +} diff --git a/web/server/h2o/libh2o/lib/handler/configurator/throttle_resp.c b/web/server/h2o/libh2o/lib/handler/configurator/throttle_resp.c new file mode 100644 index 000000000..61431f756 --- /dev/null +++ b/web/server/h2o/libh2o/lib/handler/configurator/throttle_resp.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Justin Zhu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "h2o.h" +#include "h2o/configurator.h" + +struct throttle_resp_config_vars_t { + int on; +}; + +struct throttle_resp_configurator_t { + h2o_configurator_t super; + struct throttle_resp_config_vars_t *vars, _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; +}; + +static int on_config_throttle_resp(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct throttle_resp_configurator_t *self = (void *)cmd->configurator; + + if ((self->vars->on = (int)h2o_configurator_get_one_of(cmd, node, "OFF,ON")) == -1) + return -1; + return 0; +} + +static int on_config_enter(h2o_configurator_t *configurator, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct throttle_resp_configurator_t *self = (void *)configurator; + + ++self->vars; + self->vars[0] = self->vars[-1]; + return 0; +} + +static int on_config_exit(h2o_configurator_t *configurator, h2o_configurator_context_t *ctx, yoml_t *node) +{ + struct throttle_resp_configurator_t *self = (void *)configurator; + + if (ctx->pathconf != NULL && self->vars->on) + h2o_throttle_resp_register(ctx->pathconf); + + --self->vars; + return 0; +} + +void h2o_throttle_resp_register_configurator(h2o_globalconf_t *conf) +{ + struct throttle_resp_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); + + c->super.enter = on_config_enter; + c->super.exit = on_config_exit; + h2o_configurator_define_command(&c->super, "throttle-response", + H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, + on_config_throttle_resp); + c->vars = c->_vars_stack; +} |