summaryrefslogtreecommitdiffstats
path: root/debian/vendor-h2o/lib/core
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/vendor-h2o/lib/core/config.c326
-rw-r--r--debian/vendor-h2o/lib/core/configurator.c1111
-rw-r--r--debian/vendor-h2o/lib/core/context.c203
-rw-r--r--debian/vendor-h2o/lib/core/headers.c155
-rw-r--r--debian/vendor-h2o/lib/core/logconf.c793
-rw-r--r--debian/vendor-h2o/lib/core/proxy.c610
-rw-r--r--debian/vendor-h2o/lib/core/request.c696
-rw-r--r--debian/vendor-h2o/lib/core/token.c28
-rw-r--r--debian/vendor-h2o/lib/core/token_table.h408
-rw-r--r--debian/vendor-h2o/lib/core/util.c562
10 files changed, 4892 insertions, 0 deletions
diff --git a/debian/vendor-h2o/lib/core/config.c b/debian/vendor-h2o/lib/core/config.c
new file mode 100644
index 0000000..08e43a6
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/config.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2014-2016 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 <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "h2o.h"
+#include "h2o/configurator.h"
+#include "h2o/http1.h"
+#include "h2o/http2.h"
+
+static h2o_hostconf_t *create_hostconf(h2o_globalconf_t *globalconf)
+{
+ h2o_hostconf_t *hostconf = h2o_mem_alloc(sizeof(*hostconf));
+ *hostconf = (h2o_hostconf_t){globalconf};
+ hostconf->http2.push_preload = 1; /* enabled by default */
+ h2o_config_init_pathconf(&hostconf->fallback_path, globalconf, NULL, globalconf->mimemap);
+ hostconf->mimemap = globalconf->mimemap;
+ h2o_mem_addref_shared(hostconf->mimemap);
+ return hostconf;
+}
+
+static void destroy_hostconf(h2o_hostconf_t *hostconf)
+{
+ size_t i;
+
+ if (hostconf->authority.hostport.base != hostconf->authority.host.base)
+ free(hostconf->authority.hostport.base);
+ free(hostconf->authority.host.base);
+ for (i = 0; i != hostconf->paths.size; ++i) {
+ h2o_pathconf_t *pathconf = hostconf->paths.entries + i;
+ h2o_config_dispose_pathconf(pathconf);
+ }
+ free(hostconf->paths.entries);
+ h2o_config_dispose_pathconf(&hostconf->fallback_path);
+ h2o_mem_release_shared(hostconf->mimemap);
+
+ free(hostconf);
+}
+
+static void on_dispose_envconf(void *_envconf)
+{
+ h2o_envconf_t *envconf = _envconf;
+ size_t i;
+
+ if (envconf->parent != NULL)
+ h2o_mem_release_shared(envconf->parent);
+
+ for (i = 0; i != envconf->unsets.size; ++i)
+ h2o_mem_release_shared(envconf->unsets.entries[i].base);
+ free(envconf->unsets.entries);
+ for (i = 0; i != envconf->sets.size; ++i)
+ h2o_mem_release_shared(envconf->sets.entries[i].base);
+ free(envconf->sets.entries);
+}
+
+h2o_envconf_t *h2o_config_create_envconf(h2o_envconf_t *parent)
+{
+ h2o_envconf_t *envconf = h2o_mem_alloc_shared(NULL, sizeof(*envconf), on_dispose_envconf);
+ *envconf = (h2o_envconf_t){NULL};
+
+ if (parent != NULL) {
+ envconf->parent = parent;
+ h2o_mem_addref_shared(parent);
+ }
+ return envconf;
+}
+
+void h2o_config_setenv(h2o_envconf_t *envconf, const char *name, const char *value)
+{
+ size_t name_len = strlen(name), i;
+ h2o_iovec_t *value_slot;
+
+ /* remove from the list of unsets */
+ for (i = 0; i != envconf->unsets.size; ++i) {
+ if (h2o_memis(envconf->unsets.entries[i].base, envconf->unsets.entries[i].len, name, name_len)) {
+ h2o_mem_release_shared(envconf->unsets.entries[i].base);
+ h2o_vector_erase(&envconf->unsets, i);
+ break;
+ }
+ }
+ /* find the slot */
+ for (i = 0; i != envconf->sets.size; i += 2) {
+ if (h2o_memis(envconf->sets.entries[i].base, envconf->sets.entries[i].len, name, name_len)) {
+ value_slot = envconf->sets.entries + i + 1;
+ h2o_mem_release_shared(value_slot->base);
+ goto SetValue;
+ }
+ }
+ /* name not found in existing sets */
+ h2o_vector_reserve(NULL, &envconf->sets, envconf->sets.size + 2);
+ envconf->sets.entries[envconf->sets.size++] = h2o_strdup_shared(NULL, name, name_len);
+ value_slot = envconf->sets.entries + envconf->sets.size++;
+SetValue:
+ *value_slot = h2o_strdup_shared(NULL, value, SIZE_MAX);
+}
+
+void h2o_config_unsetenv(h2o_envconf_t *envconf, const char *name)
+{
+ size_t i, name_len = strlen(name);
+
+ /* do nothing if already set */
+ for (i = 0; i != envconf->unsets.size; ++i)
+ if (h2o_memis(envconf->unsets.entries[i].base, envconf->unsets.entries[i].len, name, name_len))
+ return;
+ /* register */
+ h2o_vector_reserve(NULL, &envconf->unsets, envconf->unsets.size + 1);
+ envconf->unsets.entries[envconf->unsets.size++] = h2o_strdup_shared(NULL, name, name_len);
+}
+
+void h2o_config_init_pathconf(h2o_pathconf_t *pathconf, h2o_globalconf_t *globalconf, const char *path, h2o_mimemap_t *mimemap)
+{
+ memset(pathconf, 0, sizeof(*pathconf));
+ pathconf->global = globalconf;
+ h2o_chunked_register(pathconf);
+ if (path != NULL)
+ pathconf->path = h2o_strdup(NULL, path, SIZE_MAX);
+ h2o_mem_addref_shared(mimemap);
+ pathconf->mimemap = mimemap;
+ pathconf->error_log.emit_request_errors = 1;
+}
+
+void h2o_config_dispose_pathconf(h2o_pathconf_t *pathconf)
+{
+#define DESTROY_LIST(type, list) \
+ do { \
+ size_t i; \
+ for (i = 0; i != list.size; ++i) { \
+ type *e = list.entries[i]; \
+ if (e->dispose != NULL) \
+ e->dispose(e); \
+ free(e); \
+ } \
+ free(list.entries); \
+ } while (0)
+ DESTROY_LIST(h2o_handler_t, pathconf->handlers);
+ DESTROY_LIST(h2o_filter_t, pathconf->filters);
+ DESTROY_LIST(h2o_logger_t, pathconf->loggers);
+#undef DESTROY_LIST
+
+ free(pathconf->path.base);
+ if (pathconf->mimemap != NULL)
+ h2o_mem_release_shared(pathconf->mimemap);
+ if (pathconf->env != NULL)
+ h2o_mem_release_shared(pathconf->env);
+}
+
+void h2o_config_init(h2o_globalconf_t *config)
+{
+ memset(config, 0, sizeof(*config));
+ config->hosts = h2o_mem_alloc(sizeof(config->hosts[0]));
+ config->hosts[0] = NULL;
+ h2o_linklist_init_anchor(&config->configurators);
+ config->server_name = h2o_iovec_init(H2O_STRLIT("h2o/" H2O_VERSION));
+ config->max_request_entity_size = H2O_DEFAULT_MAX_REQUEST_ENTITY_SIZE;
+ config->max_delegations = H2O_DEFAULT_MAX_DELEGATIONS;
+ config->handshake_timeout = H2O_DEFAULT_HANDSHAKE_TIMEOUT;
+ config->http1.req_timeout = H2O_DEFAULT_HTTP1_REQ_TIMEOUT;
+ config->http1.upgrade_to_http2 = H2O_DEFAULT_HTTP1_UPGRADE_TO_HTTP2;
+ config->http1.callbacks = H2O_HTTP1_CALLBACKS;
+ config->http2.idle_timeout = H2O_DEFAULT_HTTP2_IDLE_TIMEOUT;
+ config->http2.graceful_shutdown_timeout = H2O_DEFAULT_HTTP2_GRACEFUL_SHUTDOWN_TIMEOUT;
+ config->proxy.io_timeout = H2O_DEFAULT_PROXY_IO_TIMEOUT;
+ config->proxy.emit_x_forwarded_headers = 1;
+ config->proxy.emit_via_header = 1;
+ config->http2.max_concurrent_requests_per_connection = H2O_HTTP2_SETTINGS_HOST.max_concurrent_streams;
+ config->http2.max_streams_for_priority = 16;
+ config->http2.latency_optimization.min_rtt = 50; // milliseconds
+ config->http2.latency_optimization.max_additional_delay = 10;
+ config->http2.latency_optimization.max_cwnd = 65535;
+ config->http2.dos_delay = 100; /* 100ms processing delay when observing suspicious behavior */
+ config->http2.callbacks = H2O_HTTP2_CALLBACKS;
+ config->mimemap = h2o_mimemap_create();
+
+ h2o_configurator__init_core(config);
+}
+
+h2o_pathconf_t *h2o_config_register_path(h2o_hostconf_t *hostconf, const char *path, int flags)
+{
+ h2o_pathconf_t *pathconf;
+
+ h2o_vector_reserve(NULL, &hostconf->paths, hostconf->paths.size + 1);
+ pathconf = hostconf->paths.entries + hostconf->paths.size++;
+
+ h2o_config_init_pathconf(pathconf, hostconf->global, path, hostconf->mimemap);
+
+ return pathconf;
+}
+
+void h2o_config_register_status_handler(h2o_globalconf_t *config, h2o_status_handler_t status_handler)
+{
+ h2o_vector_reserve(NULL, &config->statuses, config->statuses.size + 1);
+ config->statuses.entries[config->statuses.size++] = status_handler;
+}
+
+void h2o_config_register_simple_status_handler(h2o_globalconf_t *config, h2o_iovec_t name, final_status_handler_cb status_handler)
+{
+ h2o_status_handler_t *sh;
+
+ h2o_vector_reserve(NULL, &config->statuses, config->statuses.size + 1);
+ sh = &config->statuses.entries[config->statuses.size++];
+ memset(sh, 0, sizeof(*sh));
+ sh->name = h2o_strdup(NULL, name.base, name.len);
+ sh->final = status_handler;
+}
+
+h2o_hostconf_t *h2o_config_register_host(h2o_globalconf_t *config, h2o_iovec_t host, uint16_t port)
+{
+ h2o_hostconf_t *hostconf = NULL;
+ h2o_iovec_t host_lc;
+
+ assert(host.len != 0);
+
+ /* convert hostname to lowercase */
+ host_lc = h2o_strdup(NULL, host.base, host.len);
+ h2o_strtolower(host_lc.base, host_lc.len);
+
+ { /* return NULL if given authority is already registered */
+ h2o_hostconf_t **p;
+ for (p = config->hosts; *p != NULL; ++p)
+ if (h2o_memis((*p)->authority.host.base, (*p)->authority.host.len, host_lc.base, host_lc.len) &&
+ (*p)->authority.port == port)
+ goto Exit;
+ }
+
+ /* create hostconf */
+ hostconf = create_hostconf(config);
+ hostconf->authority.host = host_lc;
+ host_lc = (h2o_iovec_t){NULL};
+ hostconf->authority.port = port;
+ if (hostconf->authority.port == 65535) {
+ hostconf->authority.hostport = hostconf->authority.host;
+ } else {
+ hostconf->authority.hostport.base = h2o_mem_alloc(hostconf->authority.host.len + sizeof("[]:" H2O_UINT16_LONGEST_STR));
+ if (strchr(hostconf->authority.host.base, ':') != NULL) {
+ hostconf->authority.hostport.len =
+ sprintf(hostconf->authority.hostport.base, "[%s]:%" PRIu16, hostconf->authority.host.base, port);
+ } else {
+ hostconf->authority.hostport.len =
+ sprintf(hostconf->authority.hostport.base, "%s:%" PRIu16, hostconf->authority.host.base, port);
+ }
+ }
+
+ /* append to the list */
+ h2o_append_to_null_terminated_list((void *)&config->hosts, hostconf);
+
+Exit:
+ free(host_lc.base);
+ return hostconf;
+}
+
+void h2o_config_dispose(h2o_globalconf_t *config)
+{
+ size_t i;
+
+ for (i = 0; config->hosts[i] != NULL; ++i) {
+ h2o_hostconf_t *hostconf = config->hosts[i];
+ destroy_hostconf(hostconf);
+ }
+ free(config->hosts);
+
+ h2o_mem_release_shared(config->mimemap);
+ h2o_configurator__dispose_configurators(config);
+}
+
+h2o_handler_t *h2o_create_handler(h2o_pathconf_t *conf, size_t sz)
+{
+ h2o_handler_t *handler = h2o_mem_alloc(sz);
+
+ memset(handler, 0, sz);
+ handler->_config_slot = conf->global->_num_config_slots++;
+
+ h2o_vector_reserve(NULL, &conf->handlers, conf->handlers.size + 1);
+ conf->handlers.entries[conf->handlers.size++] = handler;
+
+ return handler;
+}
+
+h2o_filter_t *h2o_create_filter(h2o_pathconf_t *conf, size_t sz)
+{
+ h2o_filter_t *filter = h2o_mem_alloc(sz);
+
+ memset(filter, 0, sz);
+ filter->_config_slot = conf->global->_num_config_slots++;
+
+ h2o_vector_reserve(NULL, &conf->filters, conf->filters.size + 1);
+ memmove(conf->filters.entries + 1, conf->filters.entries, conf->filters.size * sizeof(conf->filters.entries[0]));
+ conf->filters.entries[0] = filter;
+ ++conf->filters.size;
+
+ return filter;
+}
+
+h2o_logger_t *h2o_create_logger(h2o_pathconf_t *conf, size_t sz)
+{
+ h2o_logger_t *logger = h2o_mem_alloc(sz);
+
+ memset(logger, 0, sz);
+ logger->_config_slot = conf->global->_num_config_slots++;
+
+ h2o_vector_reserve(NULL, &conf->loggers, conf->loggers.size + 1);
+ conf->loggers.entries[conf->loggers.size++] = logger;
+
+ return logger;
+}
diff --git a/debian/vendor-h2o/lib/core/configurator.c b/debian/vendor-h2o/lib/core/configurator.c
new file mode 100644
index 0000000..4731ba2
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/configurator.c
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku, Fastly, Inc.
+ *
+ * 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "h2o.h"
+#include "h2o/configurator.h"
+
+struct st_core_config_vars_t {
+ struct {
+ unsigned reprioritize_blocking_assets : 1;
+ unsigned push_preload : 1;
+ h2o_casper_conf_t casper;
+ } http2;
+ struct {
+ unsigned emit_request_errors : 1;
+ } error_log;
+};
+
+struct st_core_configurator_t {
+ h2o_configurator_t super;
+ struct st_core_config_vars_t *vars, _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1];
+};
+
+static h2o_configurator_context_t *create_context(h2o_configurator_context_t *parent, int is_custom_handler)
+{
+ h2o_configurator_context_t *ctx = h2o_mem_alloc(sizeof(*ctx));
+ if (parent == NULL) {
+ *ctx = (h2o_configurator_context_t){NULL};
+ return ctx;
+ }
+ *ctx = *parent;
+ if (ctx->env != NULL)
+ h2o_mem_addref_shared(ctx->env);
+ ctx->parent = parent;
+ return ctx;
+}
+
+static void destroy_context(h2o_configurator_context_t *ctx)
+{
+ if (ctx->env != NULL) {
+ if (ctx->pathconf != NULL)
+ ctx->pathconf->env = ctx->env;
+ else
+ h2o_mem_release_shared(ctx->env);
+ }
+ free(ctx);
+}
+
+static int on_core_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ struct st_core_configurator_t *self = (void *)_self;
+
+ ++self->vars;
+ self->vars[0] = self->vars[-1];
+ return 0;
+}
+
+static int on_core_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ struct st_core_configurator_t *self = (void *)_self;
+
+ if (ctx->hostconf != NULL && ctx->pathconf == NULL) {
+ /* exitting from host-level configuration */
+ ctx->hostconf->http2.reprioritize_blocking_assets = self->vars->http2.reprioritize_blocking_assets;
+ ctx->hostconf->http2.push_preload = self->vars->http2.push_preload;
+ ctx->hostconf->http2.casper = self->vars->http2.casper;
+ } else if (ctx->pathconf != NULL) {
+ /* exitting from path or extension-level configuration */
+ ctx->pathconf->error_log.emit_request_errors = self->vars->error_log.emit_request_errors;
+ }
+
+ --self->vars;
+ return 0;
+}
+
+static void destroy_configurator(h2o_configurator_t *configurator)
+{
+ if (configurator->dispose != NULL)
+ configurator->dispose(configurator);
+ free(configurator->commands.entries);
+ free(configurator);
+}
+
+static int setup_configurators(h2o_configurator_context_t *ctx, int is_enter, yoml_t *node)
+{
+ h2o_linklist_t *n;
+
+ for (n = ctx->globalconf->configurators.next; n != &ctx->globalconf->configurators; n = n->next) {
+ h2o_configurator_t *c = H2O_STRUCT_FROM_MEMBER(h2o_configurator_t, _link, n);
+ if (is_enter) {
+ if (c->enter != NULL && c->enter(c, ctx, node) != 0)
+ return -1;
+ } else {
+ if (c->exit != NULL && c->exit(c, ctx, node) != 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int config_timeout(h2o_configurator_command_t *cmd, yoml_t *node, uint64_t *slot)
+{
+ uint64_t timeout_in_secs;
+
+ if (h2o_configurator_scanf(cmd, node, "%" SCNu64, &timeout_in_secs) != 0)
+ return -1;
+
+ *slot = timeout_in_secs * 1000;
+ return 0;
+}
+
+int h2o_configurator_apply_commands(h2o_configurator_context_t *ctx, yoml_t *node, int flags_mask, const char **ignore_commands)
+{
+ struct st_cmd_value_t {
+ h2o_configurator_command_t *cmd;
+ yoml_t *value;
+ };
+ H2O_VECTOR(struct st_cmd_value_t) deferred = {NULL}, semi_deferred = {NULL};
+ int ret = -1;
+
+ if (node != NULL && node->type != YOML_TYPE_MAPPING) {
+ h2o_configurator_errprintf(NULL, node, "node must be a MAPPING");
+ goto Exit;
+ }
+
+ /* call on_enter of every configurator */
+ if (setup_configurators(ctx, 1, node) != 0)
+ goto Exit;
+
+ /* handle the configuration commands */
+ if (node != NULL) {
+ size_t i;
+ for (i = 0; i != node->data.mapping.size; ++i) {
+ yoml_t *key = node->data.mapping.elements[i].key, *value = node->data.mapping.elements[i].value;
+ h2o_configurator_command_t *cmd;
+ /* obtain the target command */
+ if (key->type != YOML_TYPE_SCALAR) {
+ h2o_configurator_errprintf(NULL, key, "command must be a string");
+ goto Exit;
+ }
+ if (ignore_commands != NULL) {
+ size_t i;
+ for (i = 0; ignore_commands[i] != NULL; ++i)
+ if (strcmp(ignore_commands[i], key->data.scalar) == 0)
+ goto SkipCommand;
+ }
+ if ((cmd = h2o_configurator_get_command(ctx->globalconf, key->data.scalar)) == NULL) {
+ h2o_configurator_errprintf(NULL, key, "unknown command: %s", key->data.scalar);
+ goto Exit;
+ }
+ if ((cmd->flags & flags_mask) == 0) {
+ h2o_configurator_errprintf(cmd, key, "the command cannot be used at this level");
+ goto Exit;
+ }
+ /* check value type */
+ if ((cmd->flags & (H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR | H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE |
+ H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING)) != 0) {
+ switch (value->type) {
+ case YOML_TYPE_SCALAR:
+ if ((cmd->flags & H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR) == 0) {
+ h2o_configurator_errprintf(cmd, value, "argument cannot be a scalar");
+ goto Exit;
+ }
+ break;
+ case YOML_TYPE_SEQUENCE:
+ if ((cmd->flags & H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE) == 0) {
+ h2o_configurator_errprintf(cmd, value, "argument cannot be a sequence");
+ goto Exit;
+ }
+ break;
+ case YOML_TYPE_MAPPING:
+ if ((cmd->flags & H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING) == 0) {
+ h2o_configurator_errprintf(cmd, value, "argument cannot be a mapping");
+ goto Exit;
+ }
+ break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
+ }
+ /* handle the command (or keep it for later execution) */
+ if ((cmd->flags & H2O_CONFIGURATOR_FLAG_SEMI_DEFERRED) != 0) {
+ h2o_vector_reserve(NULL, &semi_deferred, semi_deferred.size + 1);
+ semi_deferred.entries[semi_deferred.size++] = (struct st_cmd_value_t){cmd, value};
+ } else if ((cmd->flags & H2O_CONFIGURATOR_FLAG_DEFERRED) != 0) {
+ h2o_vector_reserve(NULL, &deferred, deferred.size + 1);
+ deferred.entries[deferred.size++] = (struct st_cmd_value_t){cmd, value};
+ } else {
+ if (cmd->cb(cmd, ctx, value) != 0)
+ goto Exit;
+ }
+ SkipCommand:;
+ }
+ for (i = 0; i != semi_deferred.size; ++i) {
+ struct st_cmd_value_t *pair = semi_deferred.entries + i;
+ if (pair->cmd->cb(pair->cmd, ctx, pair->value) != 0)
+ goto Exit;
+ }
+ for (i = 0; i != deferred.size; ++i) {
+ struct st_cmd_value_t *pair = deferred.entries + i;
+ if (pair->cmd->cb(pair->cmd, ctx, pair->value) != 0)
+ goto Exit;
+ }
+ }
+
+ /* call on_exit of every configurator */
+ if (setup_configurators(ctx, 0, node) != 0)
+ goto Exit;
+
+ ret = 0;
+Exit:
+ free(deferred.entries);
+ free(semi_deferred.entries);
+ return ret;
+}
+
+static int sort_from_longer_paths(const yoml_mapping_element_t *x, const yoml_mapping_element_t *y)
+{
+ size_t xlen = strlen(x->key->data.scalar), ylen = strlen(y->key->data.scalar);
+ if (xlen < ylen)
+ return 1;
+ else if (xlen > ylen)
+ return -1;
+ /* apply strcmp for stable sort */
+ return strcmp(x->key->data.scalar, y->key->data.scalar);
+}
+
+static yoml_t *convert_path_config_node(h2o_configurator_command_t *cmd, yoml_t *node)
+{
+ size_t i, j;
+
+ switch (node->type) {
+ case YOML_TYPE_MAPPING:
+ break;
+ case YOML_TYPE_SEQUENCE: {
+ /* convert to mapping */
+ yoml_t *map = h2o_mem_alloc(sizeof(yoml_t));
+ *map = (yoml_t){YOML_TYPE_MAPPING};
+ if (node->filename != NULL)
+ map->filename = h2o_strdup(NULL, node->filename, SIZE_MAX).base;
+ map->line = node->line;
+ map->column = node->column;
+ if (node->anchor != NULL)
+ map->anchor = h2o_strdup(NULL, node->anchor, SIZE_MAX).base;
+ map->_refcnt = 1;
+
+ for (i = 0; i != node->data.sequence.size; ++i) {
+ yoml_t *elem = node->data.sequence.elements[i];
+ if (elem->type != YOML_TYPE_MAPPING) {
+ yoml_free(map, NULL);
+ goto Error;
+ }
+ for (j = 0; j != elem->data.mapping.size; ++j) {
+ yoml_t *elemkey = elem->data.mapping.elements[j].key;
+ yoml_t *elemvalue = elem->data.mapping.elements[j].value;
+ map = h2o_mem_realloc(map, offsetof(yoml_t, data.mapping.elements) +
+ sizeof(yoml_mapping_element_t) * (map->data.mapping.size + 1));
+ map->data.mapping.elements[map->data.mapping.size].key = elemkey;
+ map->data.mapping.elements[map->data.mapping.size].value = elemvalue;
+ ++map->data.mapping.size;
+ ++elemkey->_refcnt;
+ ++elemvalue->_refcnt;
+ }
+ }
+ return map;
+ } break;
+ default:
+ Error:
+ h2o_configurator_errprintf(cmd, node, "value must be a mapping or sequence of mapping");
+ return NULL;
+ }
+
+ ++node->_refcnt;
+ return node;
+}
+
+static int config_path(h2o_configurator_context_t *parent_ctx, h2o_pathconf_t *pathconf, yoml_t *node)
+{
+ h2o_configurator_context_t *path_ctx = create_context(parent_ctx, 0);
+ path_ctx->pathconf = pathconf;
+ path_ctx->mimemap = &pathconf->mimemap;
+
+ int ret = h2o_configurator_apply_commands(path_ctx, node, H2O_CONFIGURATOR_FLAG_PATH, NULL);
+
+ destroy_context(path_ctx);
+ return ret;
+}
+
+static int on_config_paths(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ size_t i;
+
+ /* sort by the length of the path (descending) */
+ for (i = 0; i != node->data.mapping.size; ++i) {
+ yoml_t *key = node->data.mapping.elements[i].key;
+ if (key->type != YOML_TYPE_SCALAR) {
+ h2o_configurator_errprintf(cmd, key, "key (representing the virtual path) must be a string");
+ return -1;
+ }
+ }
+ qsort(node->data.mapping.elements, node->data.mapping.size, sizeof(node->data.mapping.elements[0]),
+ (int (*)(const void *, const void *))sort_from_longer_paths);
+
+ for (i = 0; i != node->data.mapping.size; ++i) {
+ yoml_t *key = node->data.mapping.elements[i].key, *value;
+ if ((value = convert_path_config_node(cmd, node->data.mapping.elements[i].value)) == NULL)
+ return -1;
+ h2o_pathconf_t *pathconf = h2o_config_register_path(ctx->hostconf, key->data.scalar, 0);
+ int cmd_ret = config_path(ctx, pathconf, value);
+ yoml_free(value, NULL);
+ if (cmd_ret != 0)
+ return cmd_ret;
+ }
+
+ /* configure fallback path along with ordinary paths */
+ return config_path(ctx, &ctx->hostconf->fallback_path, NULL);
+}
+
+static int on_config_hosts(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ size_t i;
+
+ if (node->data.mapping.size == 0) {
+ h2o_configurator_errprintf(cmd, node, "the mapping cannot be empty");
+ return -1;
+ }
+
+ 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;
+ h2o_iovec_t hostname;
+ uint16_t port;
+ if (key->type != YOML_TYPE_SCALAR) {
+ h2o_configurator_errprintf(cmd, key, "key (representing the hostname) must be a string");
+ return -1;
+ }
+ if (h2o_url_parse_hostport(key->data.scalar, strlen(key->data.scalar), &hostname, &port) == NULL) {
+ h2o_configurator_errprintf(cmd, key, "invalid key (must be either `host` or `host:port`)");
+ return -1;
+ }
+ assert(hostname.len != 0);
+ if ((hostname.base[0] == '*' && !(hostname.len == 1 || hostname.base[1] == '.')) ||
+ memchr(hostname.base + 1, '*', hostname.len - 1) != NULL) {
+ h2o_configurator_errprintf(cmd, key, "wildcard (*) can only be used at the start of the hostname");
+ return -1;
+ }
+ h2o_configurator_context_t *host_ctx = create_context(ctx, 0);
+ if ((host_ctx->hostconf = h2o_config_register_host(host_ctx->globalconf, hostname, port)) == NULL) {
+ h2o_configurator_errprintf(cmd, key, "duplicate host entry");
+ destroy_context(host_ctx);
+ return -1;
+ }
+ host_ctx->mimemap = &host_ctx->hostconf->mimemap;
+ int cmd_ret = h2o_configurator_apply_commands(host_ctx, value, H2O_CONFIGURATOR_FLAG_HOST, NULL);
+ destroy_context(host_ctx);
+ if (cmd_ret != 0)
+ return -1;
+ if (yoml_get(value, "paths") == NULL) {
+ h2o_configurator_errprintf(NULL, value, "mandatory configuration directive `paths` is missing");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int on_config_limit_request_body(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ return h2o_configurator_scanf(cmd, node, "%zu", &ctx->globalconf->max_request_entity_size);
+}
+
+static int on_config_max_delegations(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ return h2o_configurator_scanf(cmd, node, "%u", &ctx->globalconf->max_delegations);
+}
+
+static int on_config_handshake_timeout(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ return config_timeout(cmd, node, &ctx->globalconf->handshake_timeout);
+}
+
+static int on_config_http1_request_timeout(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ return config_timeout(cmd, node, &ctx->globalconf->http1.req_timeout);
+}
+
+static int on_config_http1_upgrade_to_http2(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->http1.upgrade_to_http2 = (int)ret;
+ return 0;
+}
+
+static int on_config_http2_idle_timeout(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ return config_timeout(cmd, node, &ctx->globalconf->http2.idle_timeout);
+}
+
+static int on_config_http2_graceful_shutdown_timeout(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ return config_timeout(cmd, node, &ctx->globalconf->http2.graceful_shutdown_timeout);
+}
+
+static int on_config_http2_max_concurrent_requests_per_connection(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx,
+ yoml_t *node)
+{
+ return h2o_configurator_scanf(cmd, node, "%zu", &ctx->globalconf->http2.max_concurrent_requests_per_connection);
+}
+
+static int on_config_http2_latency_optimization_min_rtt(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx,
+ yoml_t *node)
+{
+ return h2o_configurator_scanf(cmd, node, "%u", &ctx->globalconf->http2.latency_optimization.min_rtt);
+}
+
+static int on_config_http2_latency_optimization_max_additional_delay(h2o_configurator_command_t *cmd,
+ h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ double ratio;
+ if (h2o_configurator_scanf(cmd, node, "%lf", &ratio) != 0)
+ return -1;
+ if (!(0.0 < ratio)) {
+ h2o_configurator_errprintf(cmd, node, "ratio must be a positive number");
+ return -1;
+ }
+ ctx->globalconf->http2.latency_optimization.max_additional_delay = 100 * ratio;
+ return 0;
+}
+
+static int on_config_http2_latency_optimization_max_cwnd(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx,
+ yoml_t *node)
+{
+ return h2o_configurator_scanf(cmd, node, "%u", &ctx->globalconf->http2.latency_optimization.max_cwnd);
+}
+
+static int on_config_http2_reprioritize_blocking_assets(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx,
+ yoml_t *node)
+{
+ struct st_core_configurator_t *self = (void *)cmd->configurator;
+ ssize_t on;
+
+ if ((on = h2o_configurator_get_one_of(cmd, node, "OFF,ON")) == -1)
+ return -1;
+ self->vars->http2.reprioritize_blocking_assets = (int)on;
+
+ return 0;
+}
+
+static int on_config_http2_push_preload(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ struct st_core_configurator_t *self = (void *)cmd->configurator;
+ ssize_t on;
+
+ if ((on = h2o_configurator_get_one_of(cmd, node, "OFF,ON")) == -1)
+ return -1;
+ self->vars->http2.push_preload = (int)on;
+
+ return 0;
+}
+
+static int on_config_http2_casper(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ static const h2o_casper_conf_t defaults = {
+ 13, /* casper_bits: default (2^13 ~= 100 assets * 1/0.01 collision probability) */
+ 0 /* track blocking assets only */
+ };
+
+ struct st_core_configurator_t *self = (void *)cmd->configurator;
+
+ switch (node->type) {
+ case YOML_TYPE_SCALAR:
+ if (strcasecmp(node->data.scalar, "OFF") == 0) {
+ self->vars->http2.casper = (h2o_casper_conf_t){0};
+ } else if (strcasecmp(node->data.scalar, "ON") == 0) {
+ self->vars->http2.casper = defaults;
+ }
+ break;
+ case YOML_TYPE_MAPPING: {
+ /* set to default */
+ self->vars->http2.casper = defaults;
+ /* override the attributes defined */
+ yoml_t *t;
+ if ((t = yoml_get(node, "capacity-bits")) != NULL) {
+ if (!(t->type == YOML_TYPE_SCALAR && sscanf(t->data.scalar, "%u", &self->vars->http2.casper.capacity_bits) == 1 &&
+ self->vars->http2.casper.capacity_bits < 16)) {
+ h2o_configurator_errprintf(cmd, t, "value of `capacity-bits` must be an integer between 0 to 15");
+ return -1;
+ }
+ }
+ if ((t = yoml_get(node, "tracking-types")) != NULL) {
+ if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "blocking-assets") == 0) {
+ self->vars->http2.casper.track_all_types = 0;
+ } else if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "all") == 0) {
+ self->vars->http2.casper.track_all_types = 1;
+ } else {
+ h2o_configurator_errprintf(cmd, t, "value of `tracking-types` must be either of: `blocking-assets` or `all`");
+ return -1;
+ }
+ }
+ } break;
+ default:
+ h2o_configurator_errprintf(cmd, node, "value must be `OFF`,`ON` or a mapping containing the necessary attributes");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int on_config_http2_dos_delay(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ return config_timeout(cmd, node, &ctx->globalconf->http2.dos_delay);
+}
+
+static int assert_is_mimetype(h2o_configurator_command_t *cmd, yoml_t *node)
+{
+ if (node->type != YOML_TYPE_SCALAR) {
+ h2o_configurator_errprintf(cmd, node, "expected a scalar (mime-type)");
+ return -1;
+ }
+ if (strchr(node->data.scalar, '/') == NULL) {
+ h2o_configurator_errprintf(cmd, node, "the string \"%s\" does not look like a mime-type", node->data.scalar);
+ return -1;
+ }
+ return 0;
+}
+
+static int assert_is_extension(h2o_configurator_command_t *cmd, yoml_t *node)
+{
+ if (node->type != YOML_TYPE_SCALAR) {
+ h2o_configurator_errprintf(cmd, node, "expected a scalar (extension)");
+ return -1;
+ }
+ if (node->data.scalar[0] != '.') {
+ h2o_configurator_errprintf(cmd, node, "given extension \"%s\" does not start with a \".\"", node->data.scalar);
+ return -1;
+ }
+ return 0;
+}
+
+static int set_mimetypes(h2o_configurator_command_t *cmd, h2o_mimemap_t *mimemap, yoml_t *node)
+{
+ size_t i, j;
+
+ assert(node->type == YOML_TYPE_MAPPING);
+
+ 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 (assert_is_mimetype(cmd, key) != 0)
+ return -1;
+ switch (value->type) {
+ case YOML_TYPE_SCALAR:
+ if (assert_is_extension(cmd, value) != 0)
+ return -1;
+ h2o_mimemap_define_mimetype(mimemap, value->data.scalar + 1, key->data.scalar, NULL);
+ break;
+ case YOML_TYPE_SEQUENCE:
+ for (j = 0; j != value->data.sequence.size; ++j) {
+ yoml_t *ext_node = value->data.sequence.elements[j];
+ if (assert_is_extension(cmd, ext_node) != 0)
+ return -1;
+ h2o_mimemap_define_mimetype(mimemap, ext_node->data.scalar + 1, key->data.scalar, NULL);
+ }
+ break;
+ case YOML_TYPE_MAPPING: {
+ yoml_t *t;
+ h2o_mime_attributes_t attr;
+ h2o_mimemap_get_default_attributes(key->data.scalar, &attr);
+ if ((t = yoml_get(value, "is_compressible")) != NULL) {
+ if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "YES") == 0) {
+ attr.is_compressible = 1;
+ } else if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "NO") == 0) {
+ attr.is_compressible = 0;
+ } else {
+ h2o_configurator_errprintf(cmd, t, "`is_compressible` attribute must be either of: `YES` or `NO`");
+ return -1;
+ }
+ }
+ if ((t = yoml_get(value, "priority")) != NULL) {
+ if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "normal") == 0) {
+ attr.priority = H2O_MIME_ATTRIBUTE_PRIORITY_NORMAL;
+ } else if (t->type == YOML_TYPE_SCALAR && strcasecmp(t->data.scalar, "highest") == 0) {
+ attr.priority = H2O_MIME_ATTRIBUTE_PRIORITY_HIGHEST;
+ } else {
+ h2o_configurator_errprintf(cmd, t, "`priority` attribute must be either of: `normal` or `highest`");
+ return -1;
+ }
+ }
+ if ((t = yoml_get(value, "extensions")) == NULL) {
+ h2o_configurator_errprintf(cmd, value, "cannot find mandatory attribute `extensions`");
+ return -1;
+ }
+ if (t->type != YOML_TYPE_SEQUENCE) {
+ h2o_configurator_errprintf(cmd, t, "`extensions` attribute must be a sequence of scalars");
+ return -1;
+ }
+ for (j = 0; j != t->data.sequence.size; ++j) {
+ yoml_t *ext_node = t->data.sequence.elements[j];
+ if (assert_is_extension(cmd, ext_node) != 0)
+ return -1;
+ h2o_mimemap_define_mimetype(mimemap, ext_node->data.scalar + 1, key->data.scalar, &attr);
+ }
+ } break;
+ default:
+ fprintf(stderr, "logic flaw at %s:%d\n", __FILE__, __LINE__);
+ abort();
+ }
+ }
+
+ return 0;
+}
+
+static int on_config_mime_settypes(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ h2o_mimemap_t *newmap = h2o_mimemap_create();
+ h2o_mimemap_clear_types(newmap);
+ h2o_mimemap_set_default_type(newmap, h2o_mimemap_get_default_type(*ctx->mimemap)->data.mimetype.base, NULL);
+ if (set_mimetypes(cmd, newmap, node) != 0) {
+ h2o_mem_release_shared(newmap);
+ return -1;
+ }
+
+ h2o_mem_release_shared(*ctx->mimemap);
+ *ctx->mimemap = newmap;
+ return 0;
+}
+
+static void clone_mimemap_if_clean(h2o_configurator_context_t *ctx)
+{
+ if (ctx->parent == NULL)
+ return;
+ if (*ctx->mimemap != *ctx->parent->mimemap)
+ return;
+ h2o_mem_release_shared(*ctx->mimemap);
+ /* even after release, ctx->mimemap is still retained by the parent and therefore we can use it as the argument to clone */
+ *ctx->mimemap = h2o_mimemap_clone(*ctx->mimemap);
+}
+
+static int on_config_mime_addtypes(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ clone_mimemap_if_clean(ctx);
+ return set_mimetypes(cmd, *ctx->mimemap, node);
+}
+
+static int on_config_mime_removetypes(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ size_t i;
+
+ clone_mimemap_if_clean(ctx);
+ for (i = 0; i != node->data.sequence.size; ++i) {
+ yoml_t *ext_node = node->data.sequence.elements[i];
+ if (assert_is_extension(cmd, ext_node) != 0)
+ return -1;
+ h2o_mimemap_remove_type(*ctx->mimemap, ext_node->data.scalar + 1);
+ }
+
+ return 0;
+}
+
+static int on_config_mime_setdefaulttype(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ if (assert_is_mimetype(cmd, node) != 0)
+ return -1;
+
+ clone_mimemap_if_clean(ctx);
+ h2o_mimemap_set_default_type(*ctx->mimemap, node->data.scalar, NULL);
+
+ return 0;
+}
+
+static int on_config_custom_handler(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ static const char *ignore_commands[] = {"extension", NULL};
+ yoml_t *ext_node;
+ const char **exts;
+ h2o_mimemap_type_t *type = NULL;
+
+ if (node->type != YOML_TYPE_MAPPING) {
+ h2o_configurator_errprintf(cmd, node, "argument must be a MAPPING");
+ return -1;
+ }
+ if ((ext_node = yoml_get(node, "extension")) == NULL) {
+ h2o_configurator_errprintf(cmd, node, "mandatory key `extension` is missing");
+ return -1;
+ }
+
+ /* create dynamic type */
+ switch (ext_node->type) {
+ case YOML_TYPE_SCALAR:
+ if (assert_is_extension(cmd, ext_node) != 0)
+ return -1;
+ exts = alloca(2 * sizeof(*exts));
+ exts[0] = ext_node->data.scalar + 1;
+ exts[1] = NULL;
+ break;
+ case YOML_TYPE_SEQUENCE: {
+ exts = alloca((ext_node->data.sequence.size + 1) * sizeof(*exts));
+ size_t i;
+ for (i = 0; i != ext_node->data.sequence.size; ++i) {
+ yoml_t *n = ext_node->data.sequence.elements[i];
+ if (assert_is_extension(cmd, n) != 0)
+ return -1;
+ exts[i] = n->data.scalar + 1;
+ }
+ exts[i] = NULL;
+ } break;
+ default:
+ h2o_configurator_errprintf(cmd, ext_node, "`extensions` must be a scalar or sequence of scalar");
+ return -1;
+ }
+ clone_mimemap_if_clean(ctx);
+ type = h2o_mimemap_define_dynamic(*ctx->mimemap, exts, ctx->globalconf);
+
+ /* apply the configuration commands */
+ h2o_configurator_context_t *ext_ctx = create_context(ctx, 1);
+ ext_ctx->pathconf = &type->data.dynamic.pathconf;
+ ext_ctx->mimemap = NULL;
+ int cmd_ret = h2o_configurator_apply_commands(ext_ctx, node, H2O_CONFIGURATOR_FLAG_EXTENSION, ignore_commands);
+ destroy_context(ext_ctx);
+ if (cmd_ret != 0)
+ return cmd_ret;
+ switch (type->data.dynamic.pathconf.handlers.size) {
+ case 1:
+ break;
+ case 0:
+ h2o_configurator_errprintf(cmd, node, "no handler declared for given extension");
+ return -1;
+ default:
+ h2o_configurator_errprintf(cmd, node, "cannot assign more than one handler for given extension");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void inherit_env_if_necessary(h2o_configurator_context_t *ctx)
+{
+ if (ctx->env == (ctx->parent != NULL ? ctx->parent->env : NULL))
+ ctx->env = h2o_config_create_envconf(ctx->env);
+}
+
+static int on_config_setenv(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ size_t i;
+
+ inherit_env_if_necessary(ctx);
+
+ for (i = 0; i != node->data.mapping.size; ++i) {
+ yoml_t *key = node->data.mapping.elements[i].key, *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 (value->type != YOML_TYPE_SCALAR) {
+ h2o_configurator_errprintf(cmd, value, "value must be a scalar");
+ return -1;
+ }
+ h2o_config_setenv(ctx->env, key->data.scalar, value->data.scalar);
+ }
+
+ return 0;
+}
+
+static int on_config_unsetenv(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ inherit_env_if_necessary(ctx);
+
+ switch (node->type) {
+ case YOML_TYPE_SCALAR:
+ h2o_config_unsetenv(ctx->env, node->data.scalar);
+ break;
+ case YOML_TYPE_SEQUENCE: {
+ size_t i;
+ 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, "element of a sequence passed to unsetenv must be a scalar");
+ return -1;
+ }
+ h2o_config_unsetenv(ctx->env, element->data.scalar);
+ }
+ } break;
+ default:
+ h2o_configurator_errprintf(cmd, node, "argument to unsetenv must be either a scalar or a sequence");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int on_config_server_name(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ ctx->globalconf->server_name = h2o_strdup(NULL, node->data.scalar, SIZE_MAX);
+ return 0;
+}
+
+static int on_config_send_server_name(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ switch(h2o_configurator_get_one_of(cmd, node, "OFF,ON,preserve")) {
+ case 0: /* off */
+ ctx->globalconf->server_name = h2o_iovec_init(H2O_STRLIT(""));
+ break;
+ case 1: /* on */
+ break;
+ case 2: /* preserve */
+ ctx->globalconf->server_name = h2o_iovec_init(H2O_STRLIT(""));
+ ctx->globalconf->proxy.preserve_server_header = 1;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static int on_config_error_log_emit_request_errors(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
+{
+ struct st_core_configurator_t *self = (void *)cmd->configurator;
+ ssize_t on;
+
+ if ((on = h2o_configurator_get_one_of(cmd, node, "OFF,ON")) == -1)
+ return -1;
+
+ self->vars->error_log.emit_request_errors = (int)on;
+ return 0;
+}
+
+void h2o_configurator__init_core(h2o_globalconf_t *conf)
+{
+ /* check if already initialized */
+ if (h2o_configurator_get_command(conf, "files") != NULL)
+ return;
+
+ { /* `hosts` and `paths` */
+ h2o_configurator_t *c = h2o_configurator_create(conf, sizeof(*c));
+ h2o_configurator_define_command(c, "hosts", H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING |
+ H2O_CONFIGURATOR_FLAG_DEFERRED,
+ on_config_hosts);
+ h2o_configurator_define_command(c, "paths", H2O_CONFIGURATOR_FLAG_HOST | H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING |
+ H2O_CONFIGURATOR_FLAG_DEFERRED,
+ on_config_paths);
+ };
+
+ { /* setup global configurators */
+ struct st_core_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c));
+ c->super.enter = on_core_enter;
+ c->super.exit = on_core_exit;
+ c->vars = c->_vars_stack;
+ c->vars->http2.reprioritize_blocking_assets = 1; /* defaults to ON */
+ c->vars->http2.push_preload = 1; /* defaults to ON */
+ c->vars->error_log.emit_request_errors = 1; /* defaults to ON */
+ h2o_configurator_define_command(&c->super, "limit-request-body",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_limit_request_body);
+ h2o_configurator_define_command(&c->super, "max-delegations",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_max_delegations);
+ h2o_configurator_define_command(&c->super, "handshake-timeout",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_handshake_timeout);
+ h2o_configurator_define_command(&c->super, "http1-request-timeout",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http1_request_timeout);
+ h2o_configurator_define_command(&c->super, "http1-upgrade-to-http2",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http1_upgrade_to_http2);
+ h2o_configurator_define_command(&c->super, "http2-idle-timeout",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http2_idle_timeout);
+ h2o_configurator_define_command(&c->super, "http2-graceful-shutdown-timeout",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http2_graceful_shutdown_timeout);
+ h2o_configurator_define_command(&c->super, "http2-max-concurrent-requests-per-connection",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http2_max_concurrent_requests_per_connection);
+ h2o_configurator_define_command(&c->super, "http2-latency-optimization-min-rtt",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http2_latency_optimization_min_rtt);
+ h2o_configurator_define_command(&c->super, "http2-latency-optimization-max-additional-delay",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http2_latency_optimization_max_additional_delay);
+ h2o_configurator_define_command(&c->super, "http2-latency-optimization-max-cwnd",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http2_latency_optimization_max_cwnd);
+ h2o_configurator_define_command(&c->super, "http2-reprioritize-blocking-assets",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_HOST |
+ H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http2_reprioritize_blocking_assets);
+ h2o_configurator_define_command(&c->super, "http2-push-preload", H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_HOST |
+ H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http2_push_preload);
+ h2o_configurator_define_command(&c->super, "http2-casper", H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_HOST,
+ on_config_http2_casper);
+ h2o_configurator_define_command(&c->super, "http2-dos-delay",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_http2_dos_delay);
+ h2o_configurator_define_command(&c->super, "file.mime.settypes",
+ (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) |
+ H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING,
+ on_config_mime_settypes);
+ h2o_configurator_define_command(&c->super, "file.mime.addtypes",
+ (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) |
+ H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING,
+ on_config_mime_addtypes);
+ h2o_configurator_define_command(&c->super, "file.mime.removetypes",
+ (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) |
+ H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE,
+ on_config_mime_removetypes);
+ h2o_configurator_define_command(&c->super, "file.mime.setdefaulttype",
+ (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) |
+ H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_mime_setdefaulttype);
+ h2o_configurator_define_command(&c->super, "file.custom-handler",
+ (H2O_CONFIGURATOR_FLAG_ALL_LEVELS & ~H2O_CONFIGURATOR_FLAG_EXTENSION) |
+ H2O_CONFIGURATOR_FLAG_SEMI_DEFERRED,
+ on_config_custom_handler);
+ h2o_configurator_define_command(&c->super, "setenv",
+ H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING, on_config_setenv);
+ h2o_configurator_define_command(&c->super, "unsetenv", H2O_CONFIGURATOR_FLAG_ALL_LEVELS, on_config_unsetenv);
+ h2o_configurator_define_command(&c->super, "server-name",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_server_name);
+ h2o_configurator_define_command(&c->super, "send-server-name",
+ H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR |
+ H2O_CONFIGURATOR_FLAG_DEFERRED,
+ on_config_send_server_name);
+ h2o_configurator_define_command(&c->super, "error-log.emit-request-errors",
+ H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
+ on_config_error_log_emit_request_errors);
+ }
+}
+
+void h2o_configurator__dispose_configurators(h2o_globalconf_t *conf)
+{
+ while (!h2o_linklist_is_empty(&conf->configurators)) {
+ h2o_configurator_t *c = H2O_STRUCT_FROM_MEMBER(h2o_configurator_t, _link, conf->configurators.next);
+ h2o_linklist_unlink(&c->_link);
+ if (c->dispose != NULL)
+ c->dispose(c);
+ destroy_configurator(c);
+ }
+}
+
+h2o_configurator_t *h2o_configurator_create(h2o_globalconf_t *conf, size_t sz)
+{
+ h2o_configurator_t *c;
+
+ assert(sz >= sizeof(*c));
+
+ c = h2o_mem_alloc(sz);
+ memset(c, 0, sz);
+ h2o_linklist_insert(&conf->configurators, &c->_link);
+
+ return c;
+}
+
+void h2o_configurator_define_command(h2o_configurator_t *configurator, const char *name, int flags, h2o_configurator_command_cb cb)
+{
+ h2o_configurator_command_t *cmd;
+
+ h2o_vector_reserve(NULL, &configurator->commands, configurator->commands.size + 1);
+ cmd = configurator->commands.entries + configurator->commands.size++;
+ cmd->configurator = configurator;
+ cmd->flags = flags;
+ cmd->name = name;
+ cmd->cb = cb;
+}
+
+h2o_configurator_command_t *h2o_configurator_get_command(h2o_globalconf_t *conf, const char *name)
+{
+ h2o_linklist_t *node;
+ size_t i;
+
+ for (node = conf->configurators.next; node != &conf->configurators; node = node->next) {
+ h2o_configurator_t *configurator = H2O_STRUCT_FROM_MEMBER(h2o_configurator_t, _link, node);
+ for (i = 0; i != configurator->commands.size; ++i) {
+ h2o_configurator_command_t *cmd = configurator->commands.entries + i;
+ if (strcmp(cmd->name, name) == 0) {
+ return cmd;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int h2o_configurator_apply(h2o_globalconf_t *config, yoml_t *node, int dry_run)
+{
+ h2o_configurator_context_t *ctx = create_context(NULL, 0);
+ ctx->globalconf = config;
+ ctx->mimemap = &ctx->globalconf->mimemap;
+ ctx->dry_run = dry_run;
+ int cmd_ret = h2o_configurator_apply_commands(ctx, node, H2O_CONFIGURATOR_FLAG_GLOBAL, NULL);
+ destroy_context(ctx);
+
+ if (cmd_ret != 0)
+ return cmd_ret;
+ if (config->hosts[0] == NULL) {
+ h2o_configurator_errprintf(NULL, node, "mandatory configuration directive `hosts` is missing");
+ return -1;
+ }
+ return 0;
+}
+
+void h2o_configurator_errprintf(h2o_configurator_command_t *cmd, yoml_t *node, const char *reason, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "[%s:%zu] ", node->filename ? node->filename : "-", node->line + 1);
+ if (cmd != NULL)
+ fprintf(stderr, "in command %s, ", cmd->name);
+ va_start(args, reason);
+ vfprintf(stderr, reason, args);
+ va_end(args);
+ fputc('\n', stderr);
+}
+
+int h2o_configurator_scanf(h2o_configurator_command_t *cmd, yoml_t *node, const char *fmt, ...)
+{
+ va_list args;
+ int sscan_ret;
+
+ if (node->type != YOML_TYPE_SCALAR)
+ goto Error;
+ va_start(args, fmt);
+ sscan_ret = vsscanf(node->data.scalar, fmt, args);
+ va_end(args);
+ if (sscan_ret != 1)
+ goto Error;
+
+ return 0;
+Error:
+ h2o_configurator_errprintf(cmd, node, "argument must match the format: %s", fmt);
+ return -1;
+}
+
+ssize_t h2o_configurator_get_one_of(h2o_configurator_command_t *cmd, yoml_t *node, const char *candidates)
+{
+ const char *config_str, *cand_str;
+ ssize_t config_str_len, cand_index;
+
+ if (node->type != YOML_TYPE_SCALAR)
+ goto Error;
+
+ config_str = node->data.scalar;
+ config_str_len = strlen(config_str);
+
+ cand_str = candidates;
+ for (cand_index = 0;; ++cand_index) {
+ if (strncasecmp(cand_str, config_str, config_str_len) == 0 &&
+ (cand_str[config_str_len] == '\0' || cand_str[config_str_len] == ',')) {
+ /* found */
+ return cand_index;
+ }
+ cand_str = strchr(cand_str, ',');
+ if (cand_str == NULL)
+ goto Error;
+ cand_str += 1; /* skip ',' */
+ }
+/* not reached */
+
+Error:
+ h2o_configurator_errprintf(cmd, node, "argument must be one of: %s", candidates);
+ return -1;
+}
+
+char *h2o_configurator_get_cmd_path(const char *cmd)
+{
+ char *root, *cmd_fullpath;
+
+ /* just return the cmd (being strdup'ed) in case we do not need to prefix the value */
+ if (cmd[0] == '/' || strchr(cmd, '/') == NULL)
+ goto ReturnOrig;
+
+ /* obtain root */
+ if ((root = getenv("H2O_ROOT")) == NULL) {
+ root = H2O_TO_STR(H2O_ROOT);
+ }
+
+ /* build full-path and return */
+ cmd_fullpath = h2o_mem_alloc(strlen(root) + strlen(cmd) + 2);
+ sprintf(cmd_fullpath, "%s/%s", root, cmd);
+ return cmd_fullpath;
+
+ReturnOrig:
+ return h2o_strdup(NULL, cmd, SIZE_MAX).base;
+}
diff --git a/debian/vendor-h2o/lib/core/context.c b/debian/vendor-h2o/lib/core/context.c
new file mode 100644
index 0000000..ac4b0aa
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/context.c
@@ -0,0 +1,203 @@
+/*
+ * 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 <stddef.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include "h2o.h"
+#include "h2o/memcached.h"
+
+void h2o_context_init_pathconf_context(h2o_context_t *ctx, h2o_pathconf_t *pathconf)
+{
+ /* add pathconf to the inited list (or return if already inited) */
+ size_t i;
+ for (i = 0; i != ctx->_pathconfs_inited.size; ++i)
+ if (ctx->_pathconfs_inited.entries[i] == pathconf)
+ return;
+ h2o_vector_reserve(NULL, &ctx->_pathconfs_inited, ctx->_pathconfs_inited.size + 1);
+ ctx->_pathconfs_inited.entries[ctx->_pathconfs_inited.size++] = pathconf;
+
+#define DOIT(type, list) \
+ do { \
+ size_t i; \
+ for (i = 0; i != pathconf->list.size; ++i) { \
+ type *o = pathconf->list.entries[i]; \
+ if (o->on_context_init != NULL) \
+ o->on_context_init(o, ctx); \
+ } \
+ } while (0)
+
+ DOIT(h2o_handler_t, handlers);
+ DOIT(h2o_filter_t, filters);
+ DOIT(h2o_logger_t, loggers);
+
+#undef DOIT
+}
+
+void h2o_context_dispose_pathconf_context(h2o_context_t *ctx, h2o_pathconf_t *pathconf)
+{
+ /* nullify pathconf in the inited list (or return if already disposed) */
+ size_t i;
+ for (i = 0; i != ctx->_pathconfs_inited.size; ++i)
+ if (ctx->_pathconfs_inited.entries[i] == pathconf)
+ break;
+ if (i == ctx->_pathconfs_inited.size)
+ return;
+ ctx->_pathconfs_inited.entries[i] = NULL;
+
+#define DOIT(type, list) \
+ do { \
+ size_t i; \
+ for (i = 0; i != pathconf->list.size; ++i) { \
+ type *o = pathconf->list.entries[i]; \
+ if (o->on_context_dispose != NULL) \
+ o->on_context_dispose(o, ctx); \
+ } \
+ } while (0)
+
+ DOIT(h2o_handler_t, handlers);
+ DOIT(h2o_filter_t, filters);
+ DOIT(h2o_logger_t, loggers);
+
+#undef DOIT
+}
+
+void h2o_context_init(h2o_context_t *ctx, h2o_loop_t *loop, h2o_globalconf_t *config)
+{
+ size_t i, j;
+
+ assert(config->hosts[0] != NULL);
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->loop = loop;
+ ctx->globalconf = config;
+ h2o_timeout_init(ctx->loop, &ctx->zero_timeout, 0);
+ h2o_timeout_init(ctx->loop, &ctx->one_sec_timeout, 1000);
+ h2o_timeout_init(ctx->loop, &ctx->hundred_ms_timeout, 100);
+ ctx->queue = h2o_multithread_create_queue(loop);
+ h2o_multithread_register_receiver(ctx->queue, &ctx->receivers.hostinfo_getaddr, h2o_hostinfo_getaddr_receiver);
+ ctx->filecache = h2o_filecache_create(config->filecache.capacity);
+
+ h2o_timeout_init(ctx->loop, &ctx->handshake_timeout, config->handshake_timeout);
+ h2o_timeout_init(ctx->loop, &ctx->http1.req_timeout, config->http1.req_timeout);
+ h2o_linklist_init_anchor(&ctx->http1._conns);
+ h2o_timeout_init(ctx->loop, &ctx->http2.idle_timeout, config->http2.idle_timeout);
+ h2o_timeout_init(ctx->loop, &ctx->http2.graceful_shutdown_timeout, config->http2.graceful_shutdown_timeout);
+ h2o_timeout_init(ctx->loop, &ctx->http2.dos_delay_timeout, config->http2.dos_delay);
+ h2o_linklist_init_anchor(&ctx->http2._conns);
+ ctx->proxy.client_ctx.loop = loop;
+ h2o_timeout_init(ctx->loop, &ctx->proxy.io_timeout, config->proxy.io_timeout);
+ ctx->proxy.client_ctx.getaddr_receiver = &ctx->receivers.hostinfo_getaddr;
+ ctx->proxy.client_ctx.io_timeout = &ctx->proxy.io_timeout;
+ ctx->proxy.client_ctx.ssl_ctx = config->proxy.ssl_ctx;
+
+ ctx->_module_configs = h2o_mem_alloc(sizeof(*ctx->_module_configs) * config->_num_config_slots);
+ memset(ctx->_module_configs, 0, sizeof(*ctx->_module_configs) * config->_num_config_slots);
+
+ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_lock(&mutex);
+ for (i = 0; config->hosts[i] != NULL; ++i) {
+ h2o_hostconf_t *hostconf = config->hosts[i];
+ for (j = 0; j != hostconf->paths.size; ++j) {
+ h2o_pathconf_t *pathconf = hostconf->paths.entries + j;
+ h2o_context_init_pathconf_context(ctx, pathconf);
+ }
+ h2o_context_init_pathconf_context(ctx, &hostconf->fallback_path);
+ }
+ pthread_mutex_unlock(&mutex);
+}
+
+void h2o_context_dispose(h2o_context_t *ctx)
+{
+ h2o_globalconf_t *config = ctx->globalconf;
+ size_t i, j;
+
+ for (i = 0; config->hosts[i] != NULL; ++i) {
+ h2o_hostconf_t *hostconf = config->hosts[i];
+ for (j = 0; j != hostconf->paths.size; ++j) {
+ h2o_pathconf_t *pathconf = hostconf->paths.entries + j;
+ h2o_context_dispose_pathconf_context(ctx, pathconf);
+ }
+ h2o_context_dispose_pathconf_context(ctx, &hostconf->fallback_path);
+ }
+ free(ctx->_pathconfs_inited.entries);
+ free(ctx->_module_configs);
+ h2o_timeout_dispose(ctx->loop, &ctx->zero_timeout);
+ h2o_timeout_dispose(ctx->loop, &ctx->one_sec_timeout);
+ h2o_timeout_dispose(ctx->loop, &ctx->hundred_ms_timeout);
+ h2o_timeout_dispose(ctx->loop, &ctx->handshake_timeout);
+ h2o_timeout_dispose(ctx->loop, &ctx->http1.req_timeout);
+ h2o_timeout_dispose(ctx->loop, &ctx->http2.idle_timeout);
+ h2o_timeout_dispose(ctx->loop, &ctx->http2.graceful_shutdown_timeout);
+ h2o_timeout_dispose(ctx->loop, &ctx->http2.dos_delay_timeout);
+ h2o_timeout_dispose(ctx->loop, &ctx->proxy.io_timeout);
+ /* what should we do here? assert(!h2o_linklist_is_empty(&ctx->http2._conns); */
+
+ h2o_filecache_destroy(ctx->filecache);
+ ctx->filecache = NULL;
+
+ /* clear storage */
+ for (i = 0; i != ctx->storage.size; ++i) {
+ h2o_context_storage_item_t *item = ctx->storage.entries + i;
+ if (item->dispose != NULL) {
+ item->dispose(item->data);
+ }
+ }
+ free(ctx->storage.entries);
+
+ /* TODO assert that the all the getaddrinfo threads are idle */
+ h2o_multithread_unregister_receiver(ctx->queue, &ctx->receivers.hostinfo_getaddr);
+ h2o_multithread_destroy_queue(ctx->queue);
+
+ if (ctx->_timestamp_cache.value != NULL)
+ h2o_mem_release_shared(ctx->_timestamp_cache.value);
+
+#if H2O_USE_LIBUV
+ /* make sure the handles released by h2o_timeout_dispose get freed */
+ uv_run(ctx->loop, UV_RUN_NOWAIT);
+#endif
+}
+
+void h2o_context_request_shutdown(h2o_context_t *ctx)
+{
+ ctx->shutdown_requested = 1;
+ if (ctx->globalconf->http1.callbacks.request_shutdown != NULL)
+ ctx->globalconf->http1.callbacks.request_shutdown(ctx);
+ if (ctx->globalconf->http2.callbacks.request_shutdown != NULL)
+ ctx->globalconf->http2.callbacks.request_shutdown(ctx);
+}
+
+void h2o_context_update_timestamp_cache(h2o_context_t *ctx)
+{
+ time_t prev_sec = ctx->_timestamp_cache.tv_at.tv_sec;
+ ctx->_timestamp_cache.uv_now_at = h2o_now(ctx->loop);
+ gettimeofday(&ctx->_timestamp_cache.tv_at, NULL);
+ if (ctx->_timestamp_cache.tv_at.tv_sec != prev_sec) {
+ struct tm gmt;
+ /* update the string cache */
+ if (ctx->_timestamp_cache.value != NULL)
+ h2o_mem_release_shared(ctx->_timestamp_cache.value);
+ ctx->_timestamp_cache.value = h2o_mem_alloc_shared(NULL, sizeof(h2o_timestamp_string_t), NULL);
+ gmtime_r(&ctx->_timestamp_cache.tv_at.tv_sec, &gmt);
+ h2o_time2str_rfc1123(ctx->_timestamp_cache.value->rfc1123, &gmt);
+ h2o_time2str_log(ctx->_timestamp_cache.value->log, ctx->_timestamp_cache.tv_at.tv_sec);
+ }
+}
diff --git a/debian/vendor-h2o/lib/core/headers.c b/debian/vendor-h2o/lib/core/headers.c
new file mode 100644
index 0000000..d311836
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/headers.c
@@ -0,0 +1,155 @@
+/*
+ * 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 <stddef.h>
+#include <stdio.h>
+#include "h2o.h"
+
+static void add_header(h2o_mem_pool_t *pool, h2o_headers_t *headers, h2o_iovec_t *name, const char *orig_name, const char *value,
+ size_t value_len)
+{
+ h2o_header_t *slot;
+
+ h2o_vector_reserve(pool, headers, headers->size + 1);
+ slot = headers->entries + headers->size++;
+
+ slot->name = name;
+ slot->value.base = (char *)value;
+ slot->value.len = value_len;
+ slot->orig_name = orig_name ? h2o_strdup(pool, orig_name, name->len).base : NULL;
+}
+
+ssize_t h2o_find_header(const h2o_headers_t *headers, const h2o_token_t *token, ssize_t cursor)
+{
+ for (++cursor; cursor < headers->size; ++cursor) {
+ if (headers->entries[cursor].name == &token->buf) {
+ return cursor;
+ }
+ }
+ return -1;
+}
+
+ssize_t h2o_find_header_by_str(const h2o_headers_t *headers, const char *name, size_t name_len, ssize_t cursor)
+{
+ for (++cursor; cursor < headers->size; ++cursor) {
+ h2o_header_t *t = headers->entries + cursor;
+ if (h2o_memis(t->name->base, t->name->len, name, name_len)) {
+ return cursor;
+ }
+ }
+ return -1;
+}
+
+void h2o_add_header(h2o_mem_pool_t *pool, h2o_headers_t *headers, const h2o_token_t *token, const char *orig_name,
+ const char *value, size_t value_len)
+{
+ add_header(pool, headers, (h2o_iovec_t *)&token->buf, orig_name, value, value_len);
+}
+
+void h2o_add_header_by_str(h2o_mem_pool_t *pool, h2o_headers_t *headers, const char *name, size_t name_len, int maybe_token,
+ const char *orig_name, const char *value, size_t value_len)
+{
+ h2o_iovec_t *name_buf;
+
+ if (maybe_token) {
+ const h2o_token_t *token = h2o_lookup_token(name, name_len);
+ if (token != NULL) {
+ add_header(pool, headers, (h2o_iovec_t *)token, orig_name, value, value_len);
+ return;
+ }
+ }
+ name_buf = h2o_mem_alloc_pool(pool, sizeof(h2o_iovec_t));
+ name_buf->base = (char *)name;
+ name_buf->len = name_len;
+ add_header(pool, headers, name_buf, orig_name, value, value_len);
+}
+
+void h2o_set_header(h2o_mem_pool_t *pool, h2o_headers_t *headers, const h2o_token_t *token, const char *value, size_t value_len,
+ int overwrite_if_exists)
+{
+ ssize_t cursor = h2o_find_header(headers, token, -1);
+ if (cursor != -1) {
+ if (overwrite_if_exists) {
+ h2o_iovec_t *slot = &headers->entries[cursor].value;
+ slot->base = (char *)value;
+ slot->len = value_len;
+ }
+ } else {
+ h2o_add_header(pool, headers, token, NULL, value, value_len);
+ }
+}
+
+void h2o_set_header_by_str(h2o_mem_pool_t *pool, h2o_headers_t *headers, const char *name, size_t name_len, int maybe_token,
+ const char *value, size_t value_len, int overwrite_if_exists)
+{
+ ssize_t cursor;
+
+ if (maybe_token) {
+ const h2o_token_t *token = h2o_lookup_token(name, name_len);
+ if (token != NULL) {
+ h2o_set_header(pool, headers, token, value, value_len, overwrite_if_exists);
+ return;
+ }
+ }
+
+ cursor = h2o_find_header_by_str(headers, name, name_len, -1);
+ if (cursor != -1) {
+ if (overwrite_if_exists) {
+ h2o_iovec_t *slot = &headers->entries[cursor].value;
+ slot->base = (char *)value;
+ slot->len = value_len;
+ }
+ } else {
+ h2o_iovec_t *name_buf = h2o_mem_alloc_pool(pool, sizeof(h2o_iovec_t));
+ name_buf->base = (char *)name;
+ name_buf->len = name_len;
+ add_header(pool, headers, name_buf, NULL, value, value_len);
+ }
+}
+
+void h2o_set_header_token(h2o_mem_pool_t *pool, h2o_headers_t *headers, const h2o_token_t *token, const char *value,
+ size_t value_len)
+{
+ h2o_header_t *dest = NULL;
+ size_t i;
+ for (i = 0; i != headers->size; ++i) {
+ if (headers->entries[i].name == &token->buf) {
+ if (h2o_contains_token(headers->entries[i].value.base, headers->entries[i].value.len, value, value_len, ','))
+ return;
+ dest = headers->entries + i;
+ }
+ }
+ if (dest != NULL) {
+ dest->value = h2o_concat(pool, dest->value, h2o_iovec_init(H2O_STRLIT(", ")), h2o_iovec_init(value, value_len));
+ } else {
+ h2o_add_header(pool, headers, token, NULL, value, value_len);
+ }
+}
+
+ssize_t h2o_delete_header(h2o_headers_t *headers, ssize_t cursor)
+{
+ assert(cursor != -1);
+
+ --headers->size;
+ memmove(headers->entries + cursor, headers->entries + cursor + 1, sizeof(h2o_header_t) * (headers->size - cursor));
+
+ return cursor;
+}
diff --git a/debian/vendor-h2o/lib/core/logconf.c b/debian/vendor-h2o/lib/core/logconf.c
new file mode 100644
index 0000000..4d79736
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/logconf.c
@@ -0,0 +1,793 @@
+/*
+ * Copyright (c) 2014-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 <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "h2o.h"
+
+enum {
+ ELEMENT_TYPE_EMPTY, /* empty element (with suffix only) */
+ ELEMENT_TYPE_LOCAL_ADDR, /* %A */
+ ELEMENT_TYPE_BYTES_SENT, /* %b */
+ ELEMENT_TYPE_PROTOCOL, /* %H */
+ ELEMENT_TYPE_REMOTE_ADDR, /* %h */
+ ELEMENT_TYPE_LOGNAME, /* %l */
+ ELEMENT_TYPE_METHOD, /* %m */
+ ELEMENT_TYPE_LOCAL_PORT, /* %p, %{local}p */
+ ELEMENT_TYPE_REMOTE_PORT, /* %{remote}p */
+ ELEMENT_TYPE_ENV_VAR, /* %{..}e */
+ ELEMENT_TYPE_QUERY, /* %q */
+ ELEMENT_TYPE_REQUEST_LINE, /* %r */
+ ELEMENT_TYPE_STATUS, /* %s */
+ ELEMENT_TYPE_TIMESTAMP, /* %t */
+ ELEMENT_TYPE_TIMESTAMP_STRFTIME, /* %{...}t */
+ ELEMENT_TYPE_TIMESTAMP_SEC_SINCE_EPOCH, /* %{sec}t */
+ ELEMENT_TYPE_TIMESTAMP_MSEC_SINCE_EPOCH, /* %{msec}t */
+ ELEMENT_TYPE_TIMESTAMP_USEC_SINCE_EPOCH, /* %{usec}t */
+ ELEMENT_TYPE_TIMESTAMP_MSEC_FRAC, /* %{msec_frac}t */
+ ELEMENT_TYPE_TIMESTAMP_USEC_FRAC, /* %{usec_frac}t */
+ ELEMENT_TYPE_URL_PATH, /* %U */
+ ELEMENT_TYPE_REMOTE_USER, /* %u */
+ ELEMENT_TYPE_AUTHORITY, /* %V */
+ ELEMENT_TYPE_HOSTCONF, /* %v */
+ ELEMENT_TYPE_IN_HEADER_TOKEN, /* %{data.header_token}i */
+ ELEMENT_TYPE_IN_HEADER_STRING, /* %{data.name}i */
+ ELEMENT_TYPE_OUT_HEADER_TOKEN, /* %{data.header_token}o */
+ ELEMENT_TYPE_OUT_HEADER_STRING, /* %{data.name}o */
+ ELEMENT_TYPE_OUT_HEADER_TOKEN_CONCATENATED, /* %{data.header_token}o */
+ ELEMENT_TYPE_EXTENDED_VAR, /* %{data.name}x */
+ ELEMENT_TYPE_CONNECTION_ID, /* %{connection-id}x */
+ ELEMENT_TYPE_CONNECT_TIME, /* %{connect-time}x */
+ ELEMENT_TYPE_REQUEST_HEADER_TIME, /* %{request-header-time}x */
+ ELEMENT_TYPE_REQUEST_BODY_TIME, /* %{request-body-time}x */
+ ELEMENT_TYPE_REQUEST_TOTAL_TIME, /* %{request-total-time}x */
+ ELEMENT_TYPE_PROCESS_TIME, /* %{process-time}x */
+ ELEMENT_TYPE_RESPONSE_TIME, /* %{response-total-time}x */
+ ELEMENT_TYPE_DURATION, /* %{duration}x */
+ ELEMENT_TYPE_ERROR, /* %{error}x */
+ ELEMENT_TYPE_PROTOCOL_SPECIFIC, /* %{protocol-specific...}x */
+ NUM_ELEMENT_TYPES
+};
+
+struct log_element_t {
+ unsigned type;
+ h2o_iovec_t suffix;
+ union {
+ const h2o_token_t *header_token;
+ h2o_iovec_t name;
+ size_t protocol_specific_callback_index;
+ } data;
+ unsigned magically_quoted_json : 1; /* whether to omit surrounding doublequotes when the output is null */
+ unsigned original_response : 1;
+};
+
+struct st_h2o_logconf_t {
+ H2O_VECTOR(struct log_element_t) elements;
+ int escape;
+};
+
+static h2o_iovec_t strdup_lowercased(const char *s, size_t len)
+{
+ h2o_iovec_t v = h2o_strdup(NULL, s, len);
+ h2o_strtolower(v.base, v.len);
+ return v;
+}
+
+static int determine_magicquote_nodes(h2o_logconf_t *logconf, char *errbuf)
+{
+ size_t element_index;
+ int quote_char = '\0'; /* the quote char being used if the state machine is within a string literal */
+ int just_in = 0; /* if we just went into the string literal */
+
+ for (element_index = 0; element_index < logconf->elements.size; ++element_index) {
+ h2o_iovec_t suffix = logconf->elements.entries[element_index].suffix;
+ logconf->elements.entries[element_index].magically_quoted_json = just_in && suffix.len != 0 && suffix.base[0] == quote_char;
+
+ just_in = 0;
+
+ size_t i;
+ for (i = 0; i < suffix.len; ++i) {
+ just_in = 0;
+ if (quote_char != '\0') {
+ if (quote_char == suffix.base[i]) {
+ /* out of quote? */
+ size_t j, num_bs = 0;
+ for (j = i; j != 0; ++num_bs)
+ if (suffix.base[--j] != '\\')
+ break;
+ if (num_bs % 2 == 0)
+ quote_char = '\0';
+ }
+ } else {
+ if (suffix.base[i] == '"' || suffix.base[i] == '\'') {
+ quote_char = suffix.base[i];
+ just_in = 1;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+h2o_logconf_t *h2o_logconf_compile(const char *fmt, int escape, char *errbuf)
+{
+ h2o_logconf_t *logconf = h2o_mem_alloc(sizeof(*logconf));
+ const char *pt = fmt;
+ size_t fmt_len = strlen(fmt);
+
+ *logconf = (h2o_logconf_t){{NULL}, escape};
+
+#define LAST_ELEMENT() (logconf->elements.entries + logconf->elements.size - 1)
+/* suffix buffer is always guaranteed to be larger than the fmt + (sizeof('\n') - 1) (so that they would be no buffer overruns) */
+#define NEW_ELEMENT(ty) \
+ do { \
+ h2o_vector_reserve(NULL, &logconf->elements, logconf->elements.size + 1); \
+ logconf->elements.size++; \
+ *LAST_ELEMENT() = (struct log_element_t){0}; \
+ LAST_ELEMENT()->type = ty; \
+ LAST_ELEMENT()->suffix.base = h2o_mem_alloc(fmt_len + 1); \
+ } while (0)
+
+ while (*pt != '\0') {
+ if (memcmp(pt, "%%", 2) == 0) {
+ ++pt; /* emit % */
+ } else if (*pt == '%') {
+ ++pt;
+ /* handle < and > */
+ int log_original = 0;
+ for (;; ++pt) {
+ if (*pt == '<') {
+ log_original = 1;
+ } else if (*pt == '>') {
+ log_original = 0;
+ } else {
+ break;
+ }
+ }
+ /* handle {...}n */
+ if (*pt == '{') {
+ const h2o_token_t *token;
+ const char *quote_end = strchr(++pt, '}');
+ if (quote_end == NULL) {
+ sprintf(errbuf, "failed to compile log format: unterminated header name starting at: \"%16s\"", pt);
+ goto Error;
+ }
+ const char modifier = quote_end[1];
+ switch (modifier) {
+ case 'i':
+ case 'o': {
+ h2o_iovec_t name = strdup_lowercased(pt, quote_end - pt);
+ token = h2o_lookup_token(name.base, name.len);
+ if (token != NULL) {
+ free(name.base);
+ if (modifier == 'o' && token == H2O_TOKEN_SET_COOKIE) {
+ NEW_ELEMENT(ELEMENT_TYPE_OUT_HEADER_TOKEN_CONCATENATED);
+ LAST_ELEMENT()->data.header_token = token;
+ } else {
+ NEW_ELEMENT(modifier == 'i' ? ELEMENT_TYPE_IN_HEADER_TOKEN : ELEMENT_TYPE_OUT_HEADER_TOKEN);
+ LAST_ELEMENT()->data.header_token = token;
+ }
+ } else {
+ NEW_ELEMENT(modifier == 'i' ? ELEMENT_TYPE_IN_HEADER_STRING : ELEMENT_TYPE_OUT_HEADER_STRING);
+ LAST_ELEMENT()->data.name = name;
+ }
+ LAST_ELEMENT()->original_response = log_original;
+ } break;
+ case 'p':
+ if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("local"))) {
+ NEW_ELEMENT(ELEMENT_TYPE_LOCAL_PORT);
+ } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("remote"))) {
+ NEW_ELEMENT(ELEMENT_TYPE_REMOTE_PORT);
+ } else {
+ sprintf(errbuf, "failed to compile log format: unknown specifier for %%{...}p");
+ goto Error;
+ }
+ break;
+ case 'e':
+ {
+ h2o_iovec_t name = h2o_strdup(NULL, pt, quote_end - pt);
+ NEW_ELEMENT(ELEMENT_TYPE_ENV_VAR);
+ LAST_ELEMENT()->data.name = name;
+ }
+ break;
+ case 't':
+ if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("sec"))) {
+ NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_SEC_SINCE_EPOCH);
+ } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("msec"))) {
+ NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_MSEC_SINCE_EPOCH);
+ } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("usec"))) {
+ NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_USEC_SINCE_EPOCH);
+ } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("msec_frac"))) {
+ NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_MSEC_FRAC);
+ } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("usec_frac"))) {
+ NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_USEC_FRAC);
+ } else {
+ h2o_iovec_t name = h2o_strdup(NULL, pt, quote_end - pt);
+ NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_STRFTIME);
+ LAST_ELEMENT()->data.name = name;
+ }
+ break;
+ case 'x':
+#define MAP_EXT_TO_TYPE(name, id) \
+ if (h2o_lcstris(pt, quote_end - pt, H2O_STRLIT(name))) { \
+ NEW_ELEMENT(id); \
+ goto MAP_EXT_Found; \
+ }
+#define MAP_EXT_TO_PROTO(name, cb) \
+ if (h2o_lcstris(pt, quote_end - pt, H2O_STRLIT(name))) { \
+ h2o_conn_callbacks_t dummy_; \
+ NEW_ELEMENT(ELEMENT_TYPE_PROTOCOL_SPECIFIC); \
+ LAST_ELEMENT()->data.protocol_specific_callback_index = \
+ &dummy_.log_.cb - dummy_.log_.callbacks; \
+ goto MAP_EXT_Found; \
+ }
+ MAP_EXT_TO_TYPE("connection-id", ELEMENT_TYPE_CONNECTION_ID);
+ MAP_EXT_TO_TYPE("connect-time", ELEMENT_TYPE_CONNECT_TIME);
+ MAP_EXT_TO_TYPE("request-total-time", ELEMENT_TYPE_REQUEST_TOTAL_TIME);
+ MAP_EXT_TO_TYPE("request-header-time", ELEMENT_TYPE_REQUEST_HEADER_TIME);
+ MAP_EXT_TO_TYPE("request-body-time", ELEMENT_TYPE_REQUEST_BODY_TIME);
+ MAP_EXT_TO_TYPE("process-time", ELEMENT_TYPE_PROCESS_TIME);
+ MAP_EXT_TO_TYPE("response-time", ELEMENT_TYPE_RESPONSE_TIME);
+ MAP_EXT_TO_TYPE("duration", ELEMENT_TYPE_DURATION);
+ MAP_EXT_TO_TYPE("error", ELEMENT_TYPE_ERROR);
+ MAP_EXT_TO_PROTO("http1.request-index", http1.request_index);
+ MAP_EXT_TO_PROTO("http2.stream-id", http2.stream_id);
+ MAP_EXT_TO_PROTO("http2.priority.received", http2.priority_received);
+ MAP_EXT_TO_PROTO("http2.priority.received.exclusive", http2.priority_received_exclusive);
+ MAP_EXT_TO_PROTO("http2.priority.received.parent", http2.priority_received_parent);
+ MAP_EXT_TO_PROTO("http2.priority.received.weight", http2.priority_received_weight);
+ MAP_EXT_TO_PROTO("http2.priority.actual", http2.priority_actual);
+ MAP_EXT_TO_PROTO("http2.priority.actual.parent", http2.priority_actual_parent);
+ MAP_EXT_TO_PROTO("http2.priority.actual.weight", http2.priority_actual_weight);
+ MAP_EXT_TO_PROTO("ssl.protocol-version", ssl.protocol_version);
+ MAP_EXT_TO_PROTO("ssl.session-reused", ssl.session_reused);
+ MAP_EXT_TO_PROTO("ssl.cipher", ssl.cipher);
+ MAP_EXT_TO_PROTO("ssl.cipher-bits", ssl.cipher_bits);
+ MAP_EXT_TO_PROTO("ssl.session-id", ssl.session_id);
+ { /* not found */
+ h2o_iovec_t name = strdup_lowercased(pt, quote_end - pt);
+ NEW_ELEMENT(ELEMENT_TYPE_EXTENDED_VAR);
+ LAST_ELEMENT()->data.name = name;
+ }
+ MAP_EXT_Found:
+#undef MAP_EXT_TO_TYPE
+#undef MAP_EXT_TO_PROTO
+ break;
+ default:
+ sprintf(errbuf, "failed to compile log format: header name is not followed by either `i`, `o`, `x`, `e`");
+ goto Error;
+ }
+ pt = quote_end + 2;
+ continue;
+ } else {
+ unsigned type = NUM_ELEMENT_TYPES;
+ switch (*pt++) {
+#define TYPE_MAP(ch, ty) \
+ case ch: \
+ type = ty; \
+ break
+ TYPE_MAP('A', ELEMENT_TYPE_LOCAL_ADDR);
+ TYPE_MAP('b', ELEMENT_TYPE_BYTES_SENT);
+ TYPE_MAP('H', ELEMENT_TYPE_PROTOCOL);
+ TYPE_MAP('h', ELEMENT_TYPE_REMOTE_ADDR);
+ TYPE_MAP('l', ELEMENT_TYPE_LOGNAME);
+ TYPE_MAP('m', ELEMENT_TYPE_METHOD);
+ TYPE_MAP('p', ELEMENT_TYPE_LOCAL_PORT);
+ TYPE_MAP('e', ELEMENT_TYPE_ENV_VAR);
+ TYPE_MAP('q', ELEMENT_TYPE_QUERY);
+ TYPE_MAP('r', ELEMENT_TYPE_REQUEST_LINE);
+ TYPE_MAP('s', ELEMENT_TYPE_STATUS);
+ TYPE_MAP('t', ELEMENT_TYPE_TIMESTAMP);
+ TYPE_MAP('U', ELEMENT_TYPE_URL_PATH);
+ TYPE_MAP('u', ELEMENT_TYPE_REMOTE_USER);
+ TYPE_MAP('V', ELEMENT_TYPE_AUTHORITY);
+ TYPE_MAP('v', ELEMENT_TYPE_HOSTCONF);
+#undef TYPE_MAP
+ default:
+ sprintf(errbuf, "failed to compile log format: unknown escape sequence: %%%c", pt[-1]);
+ goto Error;
+ }
+ NEW_ELEMENT(type);
+ LAST_ELEMENT()->original_response = log_original;
+ continue;
+ }
+ }
+ /* emit current char */
+ if (logconf->elements.size == 0)
+ NEW_ELEMENT(ELEMENT_TYPE_EMPTY);
+ LAST_ELEMENT()->suffix.base[LAST_ELEMENT()->suffix.len++] = *pt++;
+ }
+
+ /* emit end-of-line */
+ if (logconf->elements.size == 0)
+ NEW_ELEMENT(ELEMENT_TYPE_EMPTY);
+ LAST_ELEMENT()->suffix.base[LAST_ELEMENT()->suffix.len++] = '\n';
+
+#undef NEW_ELEMENT
+#undef LAST_ELEMENT
+
+ if (escape == H2O_LOGCONF_ESCAPE_JSON) {
+ if (!determine_magicquote_nodes(logconf, errbuf))
+ goto Error;
+ }
+
+ return logconf;
+
+Error:
+ h2o_logconf_dispose(logconf);
+ return NULL;
+}
+
+void h2o_logconf_dispose(h2o_logconf_t *logconf)
+{
+ size_t i;
+
+ for (i = 0; i != logconf->elements.size; ++i) {
+ free(logconf->elements.entries[i].suffix.base);
+ switch (logconf->elements.entries[i].type) {
+ case ELEMENT_TYPE_EXTENDED_VAR:
+ case ELEMENT_TYPE_IN_HEADER_STRING:
+ case ELEMENT_TYPE_OUT_HEADER_STRING:
+ case ELEMENT_TYPE_TIMESTAMP_STRFTIME:
+ free(logconf->elements.entries[i].data.name.base);
+ break;
+ default:
+ break;
+ }
+ }
+ free(logconf->elements.entries);
+ free(logconf);
+}
+
+static inline char *append_safe_string(char *pos, const char *src, size_t len)
+{
+ memcpy(pos, src, len);
+ return pos + len;
+}
+
+static char *append_unsafe_string_apache(char *pos, const char *src, size_t len)
+{
+ const char *src_end = src + len;
+
+ for (; src != src_end; ++src) {
+ if (' ' <= *src && *src < 0x7d && *src != '"') {
+ *pos++ = *src;
+ } else {
+ *pos++ = '\\';
+ *pos++ = 'x';
+ *pos++ = ("0123456789abcdef")[(*src >> 4) & 0xf];
+ *pos++ = ("0123456789abcdef")[*src & 0xf];
+ }
+ }
+
+ return pos;
+}
+
+static char *append_unsafe_string_json(char *pos, const char *src, size_t len)
+{
+ const char *src_end = src + len;
+
+ for (; src != src_end; ++src) {
+ if (' ' <= *src && *src < 0x7e) {
+ if (*src == '"' || *src == '\\')
+ *pos++ = '\\';
+ *pos++ = *src;
+ } else {
+ *pos++ = '\\';
+ *pos++ = 'u';
+ *pos++ = '0';
+ *pos++ = '0';
+ *pos++ = ("0123456789abcdef")[(*src >> 4) & 0xf];
+ *pos++ = ("0123456789abcdef")[*src & 0xf];
+ }
+ }
+
+ return pos;
+}
+
+static char *append_addr(char *pos, socklen_t (*cb)(h2o_conn_t *conn, struct sockaddr *sa), h2o_conn_t *conn, h2o_iovec_t nullexpr)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+
+ if ((sslen = cb(conn, (void *)&ss)) == 0)
+ goto Fail;
+ size_t l = h2o_socket_getnumerichost((void *)&ss, sslen, pos);
+ if (l == SIZE_MAX)
+ goto Fail;
+ pos += l;
+ return pos;
+
+Fail:
+ memcpy(pos, nullexpr.base, nullexpr.len);
+ pos += nullexpr.len;
+ return pos;
+}
+
+static char *append_port(char *pos, socklen_t (*cb)(h2o_conn_t *conn, struct sockaddr *sa), h2o_conn_t *conn, h2o_iovec_t nullexpr)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+
+ if ((sslen = cb(conn, (void *)&ss)) == 0)
+ goto Fail;
+ int32_t port = h2o_socket_getport((void *)&ss);
+ if (port == -1)
+ goto Fail;
+ pos += sprintf(pos, "%" PRIu16, (uint16_t)port);
+ return pos;
+
+Fail:
+ memcpy(pos, nullexpr.base, nullexpr.len);
+ pos += nullexpr.len;
+ return pos;
+}
+
+#define APPEND_DURATION(pos, name) \
+ do { \
+ int64_t delta_usec; \
+ if (!h2o_time_compute_##name(req, &delta_usec)) \
+ goto EmitNull; \
+ int32_t delta_sec = (int32_t)(delta_usec / (1000 * 1000)); \
+ delta_usec -= ((int64_t)delta_sec * (1000 * 1000)); \
+ RESERVE(sizeof(H2O_INT32_LONGEST_STR ".999999") - 1); \
+ pos += sprintf(pos, "%" PRId32, delta_sec); \
+ if (delta_usec != 0) { \
+ int i; \
+ *pos++ = '.'; \
+ for (i = 5; i >= 0; --i) { \
+ pos[i] = '0' + delta_usec % 10; \
+ delta_usec /= 10; \
+ } \
+ pos += 6; \
+ } \
+ } while (0);
+
+static char *expand_line_buf(char *line, size_t *cur_size, size_t required, int should_realloc)
+{
+ size_t new_size = *cur_size;
+
+ /* determine the new size */
+ do {
+ new_size *= 2;
+ } while (new_size < required);
+
+ /* reallocate */
+ if (!should_realloc) {
+ char *newpt = h2o_mem_alloc(new_size);
+ memcpy(newpt, line, *cur_size);
+ line = newpt;
+ } else {
+ line = h2o_mem_realloc(line, new_size);
+ }
+ *cur_size = new_size;
+
+ return line;
+}
+
+char *h2o_log_request(h2o_logconf_t *logconf, h2o_req_t *req, size_t *len, char *buf)
+{
+ char *line = buf, *pos = line, *line_end = line + *len;
+ h2o_iovec_t nullexpr;
+ char *(*append_unsafe_string)(char *pos, const char *src, size_t len);
+ size_t element_index, unsafe_factor;
+ struct tm localt = {0};
+ int should_realloc_on_expand = 0;
+
+ switch (logconf->escape) {
+ case H2O_LOGCONF_ESCAPE_APACHE:
+ nullexpr = h2o_iovec_init(H2O_STRLIT("-"));
+ append_unsafe_string = append_unsafe_string_apache;
+ unsafe_factor = 4;
+ break;
+ case H2O_LOGCONF_ESCAPE_JSON:
+ nullexpr = h2o_iovec_init(H2O_STRLIT("null"));
+ append_unsafe_string = append_unsafe_string_json;
+ unsafe_factor = 6;
+ break;
+ default:
+ h2o_fatal("unexpected escape mode");
+ break;
+ }
+
+ for (element_index = 0; element_index != logconf->elements.size; ++element_index) {
+ struct log_element_t *element = logconf->elements.entries + element_index;
+
+/* reserve capacity + suffix.len */
+#define RESERVE(capacity) \
+ do { \
+ if ((capacity) + element->suffix.len > line_end - pos) { \
+ size_t off = pos - line; \
+ size_t size = line_end - line; \
+ line = expand_line_buf(line, &size, off + (capacity) + element->suffix.len, should_realloc_on_expand); \
+ pos = line + off; \
+ line_end = line + size; \
+ should_realloc_on_expand = 1; \
+ } \
+ } while (0)
+
+ switch (element->type) {
+ case ELEMENT_TYPE_EMPTY:
+ RESERVE(0);
+ break;
+ case ELEMENT_TYPE_LOCAL_ADDR: /* %A */
+ RESERVE(NI_MAXHOST);
+ pos = append_addr(pos, req->conn->callbacks->get_sockname, req->conn, nullexpr);
+ break;
+ case ELEMENT_TYPE_BYTES_SENT: /* %b */
+ RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
+ pos += sprintf(pos, "%" PRIu64, (uint64_t)req->bytes_sent);
+ break;
+ case ELEMENT_TYPE_PROTOCOL: /* %H */
+ RESERVE(sizeof("HTTP/1.1"));
+ pos += h2o_stringify_protocol_version(pos, req->version);
+ break;
+ case ELEMENT_TYPE_REMOTE_ADDR: /* %h */
+ RESERVE(NI_MAXHOST);
+ pos = append_addr(pos, req->conn->callbacks->get_peername, req->conn, nullexpr);
+ break;
+ case ELEMENT_TYPE_METHOD: /* %m */
+ RESERVE(req->input.method.len * unsafe_factor);
+ pos = append_unsafe_string(pos, req->input.method.base, req->input.method.len);
+ break;
+ case ELEMENT_TYPE_LOCAL_PORT: /* %p */
+ RESERVE(sizeof(H2O_UINT16_LONGEST_STR) - 1);
+ pos = append_port(pos, req->conn->callbacks->get_sockname, req->conn, nullexpr);
+ break;
+ case ELEMENT_TYPE_REMOTE_PORT: /* %{remote}p */
+ RESERVE(sizeof(H2O_UINT16_LONGEST_STR) - 1);
+ pos = append_port(pos, req->conn->callbacks->get_peername, req->conn, nullexpr);
+ break;
+ case ELEMENT_TYPE_ENV_VAR: /* %{..}e */ {
+ h2o_iovec_t *env_var = h2o_req_getenv(req, element->data.name.base, element->data.name.len, 0);
+ if (env_var == NULL)
+ goto EmitNull;
+ RESERVE(env_var->len * unsafe_factor);
+ pos = append_safe_string(pos, env_var->base, env_var->len);
+ } break;
+ case ELEMENT_TYPE_QUERY: /* %q */
+ if (req->input.query_at != SIZE_MAX) {
+ size_t len = req->input.path.len - req->input.query_at;
+ RESERVE(len * unsafe_factor);
+ pos = append_unsafe_string(pos, req->input.path.base + req->input.query_at, len);
+ }
+ break;
+ case ELEMENT_TYPE_REQUEST_LINE: /* %r */
+ RESERVE((req->input.method.len + req->input.path.len) * unsafe_factor + sizeof(" HTTP/1.1"));
+ pos = append_unsafe_string(pos, req->input.method.base, req->input.method.len);
+ *pos++ = ' ';
+ pos = append_unsafe_string(pos, req->input.path.base, req->input.path.len);
+ *pos++ = ' ';
+ pos += h2o_stringify_protocol_version(pos, req->version);
+ break;
+ case ELEMENT_TYPE_STATUS: /* %s */
+ RESERVE(sizeof(H2O_INT32_LONGEST_STR) - 1);
+ pos += sprintf(pos, "%" PRId32, (int32_t)(element->original_response ? req->res.original.status : req->res.status));
+ break;
+ case ELEMENT_TYPE_TIMESTAMP: /* %t */
+ if (h2o_timeval_is_null(&req->processed_at.at))
+ goto EmitNull;
+ RESERVE(H2O_TIMESTR_LOG_LEN + 2);
+ *pos++ = '[';
+ pos = append_safe_string(pos, req->processed_at.str->log, H2O_TIMESTR_LOG_LEN);
+ *pos++ = ']';
+ break;
+ case ELEMENT_TYPE_TIMESTAMP_STRFTIME: /* %{...}t */
+ if (h2o_timeval_is_null(&req->processed_at.at))
+ goto EmitNull;
+ {
+ size_t bufsz, len;
+ if (localt.tm_year == 0)
+ localtime_r(&req->processed_at.at.tv_sec, &localt);
+ for (bufsz = 128;; bufsz *= 2) {
+ RESERVE(bufsz);
+ if ((len = strftime(pos, bufsz, element->data.name.base, &localt)) != 0)
+ break;
+ }
+ pos += len;
+ }
+ break;
+ case ELEMENT_TYPE_TIMESTAMP_SEC_SINCE_EPOCH: /* %{sec}t */
+ if (h2o_timeval_is_null(&req->processed_at.at))
+ goto EmitNull;
+ RESERVE(sizeof(H2O_UINT32_LONGEST_STR) - 1);
+ pos += sprintf(pos, "%" PRIu32, (uint32_t)req->processed_at.at.tv_sec);
+ break;
+ case ELEMENT_TYPE_TIMESTAMP_MSEC_SINCE_EPOCH: /* %{msec}t */
+ if (h2o_timeval_is_null(&req->processed_at.at))
+ goto EmitNull;
+ RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
+ pos += sprintf(pos, "%" PRIu64,
+ (uint64_t)req->processed_at.at.tv_sec * 1000 + (uint64_t)req->processed_at.at.tv_usec / 1000);
+ break;
+ case ELEMENT_TYPE_TIMESTAMP_USEC_SINCE_EPOCH: /* %{usec}t */
+ if (h2o_timeval_is_null(&req->processed_at.at))
+ goto EmitNull;
+ RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
+ pos +=
+ sprintf(pos, "%" PRIu64, (uint64_t)req->processed_at.at.tv_sec * 1000000 + (uint64_t)req->processed_at.at.tv_usec);
+ break;
+ case ELEMENT_TYPE_TIMESTAMP_MSEC_FRAC: /* %{msec_frac}t */
+ if (h2o_timeval_is_null(&req->processed_at.at))
+ goto EmitNull;
+ RESERVE(3);
+ pos += sprintf(pos, "%03u", (unsigned)(req->processed_at.at.tv_usec / 1000));
+ break;
+ case ELEMENT_TYPE_TIMESTAMP_USEC_FRAC: /* %{usec_frac}t */
+ if (h2o_timeval_is_null(&req->processed_at.at))
+ goto EmitNull;
+ RESERVE(6);
+ pos += sprintf(pos, "%06u", (unsigned)req->processed_at.at.tv_usec);
+ break;
+ case ELEMENT_TYPE_URL_PATH: /* %U */ {
+ size_t path_len = req->input.query_at == SIZE_MAX ? req->input.path.len : req->input.query_at;
+ RESERVE(path_len * unsafe_factor);
+ pos = append_unsafe_string(pos, req->input.path.base, path_len);
+ } break;
+ case ELEMENT_TYPE_REMOTE_USER: /* %u */ {
+ h2o_iovec_t *remote_user = h2o_req_getenv(req, H2O_STRLIT("REMOTE_USER"), 0);
+ if (remote_user == NULL)
+ goto EmitNull;
+ RESERVE(remote_user->len * unsafe_factor);
+ pos = append_unsafe_string(pos, remote_user->base, remote_user->len);
+ } break;
+ case ELEMENT_TYPE_AUTHORITY: /* %V */
+ RESERVE(req->input.authority.len * unsafe_factor);
+ pos = append_unsafe_string(pos, req->input.authority.base, req->input.authority.len);
+ break;
+ case ELEMENT_TYPE_HOSTCONF: /* %v */
+ RESERVE(req->hostconf->authority.hostport.len * unsafe_factor);
+ pos = append_unsafe_string(pos, req->hostconf->authority.hostport.base, req->hostconf->authority.hostport.len);
+ break;
+
+#define EMIT_HEADER(__headers, concat, findfunc, ...) \
+ do { \
+ h2o_headers_t *headers = (__headers); \
+ ssize_t index = -1; \
+ int found = 0; \
+ while ((index = (findfunc)(headers, __VA_ARGS__, index)) != -1) { \
+ if (found) { \
+ RESERVE(2); \
+ *pos++ = ','; \
+ *pos++ = ' '; \
+ } else { \
+ found = 1; \
+ } \
+ const h2o_header_t *header = headers->entries + index; \
+ RESERVE(header->value.len *unsafe_factor); \
+ pos = append_unsafe_string(pos, header->value.base, header->value.len); \
+ if (!concat) \
+ break; \
+ } \
+ if (!found) \
+ goto EmitNull; \
+ } while (0)
+
+ case ELEMENT_TYPE_IN_HEADER_TOKEN:
+ EMIT_HEADER(&req->headers, 0, h2o_find_header, element->data.header_token);
+ break;
+ case ELEMENT_TYPE_IN_HEADER_STRING:
+ EMIT_HEADER(&req->headers, 0, h2o_find_header_by_str, element->data.name.base, element->data.name.len);
+ break;
+ case ELEMENT_TYPE_OUT_HEADER_TOKEN:
+ EMIT_HEADER(element->original_response ? &req->res.original.headers : &req->res.headers, 0, h2o_find_header,
+ element->data.header_token);
+ break;
+ case ELEMENT_TYPE_OUT_HEADER_STRING:
+ EMIT_HEADER(element->original_response ? &req->res.original.headers : &req->res.headers, 0, h2o_find_header_by_str,
+ element->data.name.base, element->data.name.len);
+ break;
+ case ELEMENT_TYPE_OUT_HEADER_TOKEN_CONCATENATED:
+ EMIT_HEADER(element->original_response ? &req->res.original.headers : &req->res.headers, 1, h2o_find_header,
+ element->data.header_token);
+ break;
+
+#undef EMIT_HEADER
+
+ case ELEMENT_TYPE_CONNECTION_ID:
+ RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
+ pos += sprintf(pos, "%" PRIu64, req->conn->id);
+ break;
+
+ case ELEMENT_TYPE_CONNECT_TIME:
+ APPEND_DURATION(pos, connect_time);
+ break;
+
+ case ELEMENT_TYPE_REQUEST_HEADER_TIME:
+ APPEND_DURATION(pos, header_time);
+ break;
+
+ case ELEMENT_TYPE_REQUEST_BODY_TIME:
+ APPEND_DURATION(pos, body_time);
+ break;
+
+ case ELEMENT_TYPE_REQUEST_TOTAL_TIME:
+ APPEND_DURATION(pos, request_total_time);
+ break;
+
+ case ELEMENT_TYPE_PROCESS_TIME:
+ APPEND_DURATION(pos, process_time);
+ break;
+
+ case ELEMENT_TYPE_RESPONSE_TIME:
+ APPEND_DURATION(pos, response_time);
+ break;
+
+ case ELEMENT_TYPE_DURATION:
+ APPEND_DURATION(pos, duration);
+ break;
+
+ case ELEMENT_TYPE_ERROR: {
+ size_t i;
+ for (i = 0; i != req->error_logs.size; ++i) {
+ h2o_req_error_log_t *log = req->error_logs.entries + i;
+ size_t module_len = strlen(log->module);
+ RESERVE(sizeof("[] ") - 1 + module_len + log->msg.len * unsafe_factor);
+ *pos++ = '[';
+ pos = append_safe_string(pos, log->module, module_len);
+ *pos++ = ']';
+ *pos++ = ' ';
+ pos = append_unsafe_string(pos, log->msg.base, log->msg.len);
+ }
+ } break;
+
+ case ELEMENT_TYPE_PROTOCOL_SPECIFIC: {
+ h2o_iovec_t (*cb)(h2o_req_t *) = req->conn->callbacks->log_.callbacks[element->data.protocol_specific_callback_index];
+ if (cb != NULL) {
+ h2o_iovec_t s = cb(req);
+ if (s.base == NULL)
+ goto EmitNull;
+ RESERVE(s.len);
+ pos = append_safe_string(pos, s.base, s.len);
+ } else {
+ goto EmitNull;
+ }
+ } break;
+
+ case ELEMENT_TYPE_LOGNAME: /* %l */
+ case ELEMENT_TYPE_EXTENDED_VAR: /* %{...}x */
+ EmitNull:
+ RESERVE(nullexpr.len);
+ /* special case that trims surrounding quotes */
+ if (element->magically_quoted_json) {
+ --pos;
+ pos = append_safe_string(pos, nullexpr.base, nullexpr.len);
+ pos = append_safe_string(pos, element->suffix.base + 1, element->suffix.len - 1);
+ continue;
+ }
+ pos = append_safe_string(pos, nullexpr.base, nullexpr.len);
+ break;
+
+ default:
+ assert(!"unknown type");
+ break;
+ }
+
+#undef RESERVE
+
+ pos = append_safe_string(pos, element->suffix.base, element->suffix.len);
+ }
+
+ *len = pos - line;
+ return line;
+}
diff --git a/debian/vendor-h2o/lib/core/proxy.c b/debian/vendor-h2o/lib/core/proxy.c
new file mode 100644
index 0000000..edb4baf
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/proxy.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2014,2015 DeNA Co., Ltd., Kazuho Oku, Masahiro Nagano
+ *
+ * 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 <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include "picohttpparser.h"
+#include "h2o.h"
+#include "h2o/http1.h"
+#include "h2o/http1client.h"
+#include "h2o/tunnel.h"
+
+struct rp_generator_t {
+ h2o_generator_t super;
+ h2o_req_t *src_req;
+ h2o_http1client_t *client;
+ struct {
+ h2o_iovec_t bufs[2]; /* first buf is the request line and headers, the second is the POST content */
+ int is_head;
+ } up_req;
+ h2o_buffer_t *last_content_before_send;
+ h2o_doublebuffer_t sending;
+ int is_websocket_handshake;
+ int had_body_error; /* set if an error happened while fetching the body so that we can propagate the error */
+};
+
+struct rp_ws_upgrade_info_t {
+ h2o_context_t *ctx;
+ h2o_timeout_t *timeout;
+ h2o_socket_t *upstream_sock;
+};
+
+static h2o_http1client_ctx_t *get_client_ctx(h2o_req_t *req)
+{
+ h2o_req_overrides_t *overrides = req->overrides;
+ if (overrides != NULL && overrides->client_ctx != NULL)
+ return overrides->client_ctx;
+ return &req->conn->ctx->proxy.client_ctx;
+}
+
+static h2o_iovec_t rewrite_location(h2o_mem_pool_t *pool, const char *location, size_t location_len, h2o_url_t *match,
+ const h2o_url_scheme_t *req_scheme, h2o_iovec_t req_authority, h2o_iovec_t req_basepath)
+{
+ h2o_url_t loc_parsed;
+
+ if (h2o_url_parse(location, location_len, &loc_parsed) != 0)
+ goto NoRewrite;
+ if (loc_parsed.scheme != &H2O_URL_SCHEME_HTTP)
+ goto NoRewrite;
+ if (!h2o_url_hosts_are_equal(&loc_parsed, match))
+ goto NoRewrite;
+ if (h2o_url_get_port(&loc_parsed) != h2o_url_get_port(match))
+ goto NoRewrite;
+ if (loc_parsed.path.len < match->path.len)
+ goto NoRewrite;
+ if (memcmp(loc_parsed.path.base, match->path.base, match->path.len) != 0)
+ goto NoRewrite;
+
+ return h2o_concat(pool, req_scheme->name, h2o_iovec_init(H2O_STRLIT("://")), req_authority, req_basepath,
+ h2o_iovec_init(loc_parsed.path.base + match->path.len, loc_parsed.path.len - match->path.len));
+
+NoRewrite:
+ return (h2o_iovec_t){NULL};
+}
+
+static h2o_iovec_t build_request_merge_headers(h2o_mem_pool_t *pool, h2o_iovec_t merged, h2o_iovec_t added, int seperator)
+{
+ if (added.len == 0)
+ return merged;
+ if (merged.len == 0)
+ return added;
+
+ size_t newlen = merged.len + 2 + added.len;
+ char *buf = h2o_mem_alloc_pool(pool, newlen);
+ memcpy(buf, merged.base, merged.len);
+ buf[merged.len] = seperator;
+ buf[merged.len + 1] = ' ';
+ memcpy(buf + merged.len + 2, added.base, added.len);
+ merged.base = buf;
+ merged.len = newlen;
+ return merged;
+}
+
+/*
+ * A request without neither Content-Length or Transfer-Encoding header implies a zero-length request body (see 6th rule of RFC 7230
+ * 3.3.3).
+ * OTOH, section 3.3.3 states:
+ *
+ * A user agent SHOULD send a Content-Length in a request message when
+ * no Transfer-Encoding is sent and the request method defines a meaning
+ * for an enclosed payload body. For example, a Content-Length header
+ * field is normally sent in a POST request even when the value is 0
+ * (indicating an empty payload body). A user agent SHOULD NOT send a
+ * Content-Length header field when the request message does not contain
+ * a payload body and the method semantics do not anticipate such a
+ * body.
+ *
+ * PUT and POST define a meaning for the payload body, let's emit a
+ * Content-Length header if it doesn't exist already, since the server
+ * might send a '411 Length Required' response.
+ *
+ * see also: ML thread starting at https://lists.w3.org/Archives/Public/ietf-http-wg/2016JulSep/0580.html
+ */
+static int req_requires_content_length(h2o_req_t *req)
+{
+ int is_put_or_post =
+ (req->method.len >= 1 && req->method.base[0] == 'P' && (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("POST")) ||
+ h2o_memis(req->method.base, req->method.len, H2O_STRLIT("PUT"))));
+
+ return is_put_or_post && h2o_find_header(&req->res.headers, H2O_TOKEN_TRANSFER_ENCODING, -1) == -1;
+}
+
+static h2o_iovec_t build_request(h2o_req_t *req, int keepalive, int is_websocket_handshake, int use_proxy_protocol)
+{
+ h2o_iovec_t buf;
+ size_t offset = 0, remote_addr_len = SIZE_MAX;
+ char remote_addr[NI_MAXHOST];
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ h2o_iovec_t cookie_buf = {NULL}, xff_buf = {NULL}, via_buf = {NULL};
+ int preserve_x_forwarded_proto = req->conn->ctx->globalconf->proxy.preserve_x_forwarded_proto;
+ int emit_x_forwarded_headers = req->conn->ctx->globalconf->proxy.emit_x_forwarded_headers;
+ int emit_via_header = req->conn->ctx->globalconf->proxy.emit_via_header;
+
+ /* for x-f-f */
+ if ((sslen = req->conn->callbacks->get_peername(req->conn, (void *)&ss)) != 0)
+ remote_addr_len = h2o_socket_getnumerichost((void *)&ss, sslen, remote_addr);
+
+ /* build response */
+ buf.len = req->method.len + req->path.len + req->authority.len + 512;
+ if (use_proxy_protocol)
+ buf.len += H2O_PROXY_HEADER_MAX_LENGTH;
+ buf.base = h2o_mem_alloc_pool(&req->pool, buf.len);
+
+#define RESERVE(sz) \
+ do { \
+ size_t required = offset + sz + 4 /* for "\r\n\r\n" */; \
+ if (required > buf.len) { \
+ do { \
+ buf.len *= 2; \
+ } while (required > buf.len); \
+ char *newp = h2o_mem_alloc_pool(&req->pool, buf.len); \
+ memcpy(newp, buf.base, offset); \
+ buf.base = newp; \
+ } \
+ } while (0)
+#define APPEND(s, l) \
+ do { \
+ memcpy(buf.base + offset, (s), (l)); \
+ offset += (l); \
+ } while (0)
+#define APPEND_STRLIT(lit) APPEND((lit), sizeof(lit) - 1)
+#define FLATTEN_PREFIXED_VALUE(prefix, value, add_size) \
+ do { \
+ RESERVE(sizeof(prefix) - 1 + value.len + 2 + add_size); \
+ APPEND_STRLIT(prefix); \
+ if (value.len != 0) { \
+ APPEND(value.base, value.len); \
+ if (add_size != 0) { \
+ buf.base[offset++] = ','; \
+ buf.base[offset++] = ' '; \
+ } \
+ } \
+ } while (0)
+
+ if (use_proxy_protocol)
+ offset += h2o_stringify_proxy_header(req->conn, buf.base + offset);
+
+ APPEND(req->method.base, req->method.len);
+ buf.base[offset++] = ' ';
+ APPEND(req->path.base, req->path.len);
+ APPEND_STRLIT(" HTTP/1.1\r\nconnection: ");
+ if (is_websocket_handshake) {
+ APPEND_STRLIT("upgrade\r\nupgrade: websocket\r\nhost: ");
+ } else if (keepalive) {
+ APPEND_STRLIT("keep-alive\r\nhost: ");
+ } else {
+ APPEND_STRLIT("close\r\nhost: ");
+ }
+ APPEND(req->authority.base, req->authority.len);
+ buf.base[offset++] = '\r';
+ buf.base[offset++] = '\n';
+ assert(offset <= buf.len);
+ if (req->entity.base != NULL || req_requires_content_length(req)) {
+ RESERVE(sizeof("content-length: " H2O_UINT64_LONGEST_STR) - 1);
+ offset += sprintf(buf.base + offset, "content-length: %zu\r\n", req->entity.len);
+ }
+
+ /* rewrite headers if necessary */
+ h2o_headers_t req_headers = req->headers;
+ if (req->overrides != NULL && req->overrides->headers_cmds != NULL) {
+ req_headers.entries = NULL;
+ req_headers.size = 0;
+ req_headers.capacity = 0;
+ h2o_headers_command_t *cmd;
+ h2o_vector_reserve(&req->pool, &req_headers, req->headers.capacity);
+ memcpy(req_headers.entries, req->headers.entries, sizeof(req->headers.entries[0]) * req->headers.size);
+ req_headers.size = req->headers.size;
+ for (cmd = req->overrides->headers_cmds; cmd->cmd != H2O_HEADERS_CMD_NULL; ++cmd)
+ h2o_rewrite_headers(&req->pool, &req_headers, cmd);
+ }
+
+ {
+ const h2o_header_t *h, *h_end;
+ for (h = req_headers.entries, h_end = h + req_headers.size; h != h_end; ++h) {
+ if (h2o_iovec_is_token(h->name)) {
+ const h2o_token_t *token = (void *)h->name;
+ if (token->proxy_should_drop_for_req) {
+ continue;
+ } else if (token == H2O_TOKEN_COOKIE) {
+ /* merge the cookie headers; see HTTP/2 8.1.2.5 and HTTP/1 (RFC6265 5.4) */
+ /* FIXME current algorithm is O(n^2) against the number of cookie headers */
+ cookie_buf = build_request_merge_headers(&req->pool, cookie_buf, h->value, ';');
+ continue;
+ } else if (token == H2O_TOKEN_VIA) {
+ if (!emit_via_header) {
+ goto AddHeader;
+ }
+ via_buf = build_request_merge_headers(&req->pool, via_buf, h->value, ',');
+ continue;
+ } else if (token == H2O_TOKEN_X_FORWARDED_FOR) {
+ if (!emit_x_forwarded_headers) {
+ goto AddHeader;
+ }
+ xff_buf = build_request_merge_headers(&req->pool, xff_buf, h->value, ',');
+ continue;
+ }
+ }
+ if (!preserve_x_forwarded_proto && h2o_lcstris(h->name->base, h->name->len, H2O_STRLIT("x-forwarded-proto")))
+ continue;
+ AddHeader:
+ RESERVE(h->name->len + h->value.len + 2);
+ APPEND(h->orig_name ? h->orig_name : h->name->base, h->name->len);
+ buf.base[offset++] = ':';
+ buf.base[offset++] = ' ';
+ APPEND(h->value.base, h->value.len);
+ buf.base[offset++] = '\r';
+ buf.base[offset++] = '\n';
+ }
+ }
+ if (cookie_buf.len != 0) {
+ FLATTEN_PREFIXED_VALUE("cookie: ", cookie_buf, 0);
+ buf.base[offset++] = '\r';
+ buf.base[offset++] = '\n';
+ }
+ if (emit_x_forwarded_headers) {
+ if (!preserve_x_forwarded_proto) {
+ FLATTEN_PREFIXED_VALUE("x-forwarded-proto: ", req->input.scheme->name, 0);
+ buf.base[offset++] = '\r';
+ buf.base[offset++] = '\n';
+ }
+ if (remote_addr_len != SIZE_MAX) {
+ FLATTEN_PREFIXED_VALUE("x-forwarded-for: ", xff_buf, remote_addr_len);
+ APPEND(remote_addr, remote_addr_len);
+ } else {
+ FLATTEN_PREFIXED_VALUE("x-forwarded-for: ", xff_buf, 0);
+ }
+ buf.base[offset++] = '\r';
+ buf.base[offset++] = '\n';
+ }
+ if (emit_via_header) {
+ FLATTEN_PREFIXED_VALUE("via: ", via_buf, sizeof("1.1 ") - 1 + req->input.authority.len);
+ if (req->version < 0x200) {
+ buf.base[offset++] = '1';
+ buf.base[offset++] = '.';
+ buf.base[offset++] = '0' + (0x100 <= req->version && req->version <= 0x109 ? req->version - 0x100 : 0);
+ } else {
+ buf.base[offset++] = '2';
+ }
+ buf.base[offset++] = ' ';
+ APPEND(req->input.authority.base, req->input.authority.len);
+ buf.base[offset++] = '\r';
+ buf.base[offset++] = '\n';
+ }
+ APPEND_STRLIT("\r\n");
+
+#undef RESERVE
+#undef APPEND
+#undef APPEND_STRLIT
+#undef FLATTEN_PREFIXED_VALUE
+
+ /* set the length */
+ assert(offset <= buf.len);
+ buf.len = offset;
+
+ return buf;
+}
+
+static void do_close(h2o_generator_t *generator, h2o_req_t *req)
+{
+ struct rp_generator_t *self = (void *)generator;
+
+ if (self->client != NULL) {
+ h2o_http1client_cancel(self->client);
+ self->client = NULL;
+ }
+}
+
+static void do_send(struct rp_generator_t *self)
+{
+ h2o_iovec_t vecs[1];
+ size_t veccnt;
+ h2o_send_state_t ststate;
+
+ assert(self->sending.bytes_inflight == 0);
+
+ vecs[0] = h2o_doublebuffer_prepare(&self->sending,
+ self->client != NULL ? &self->client->sock->input : &self->last_content_before_send,
+ self->src_req->preferred_chunk_size);
+
+ if (self->client == NULL && vecs[0].len == self->sending.buf->size && self->last_content_before_send->size == 0) {
+ veccnt = vecs[0].len != 0 ? 1 : 0;
+ ststate = H2O_SEND_STATE_FINAL;
+ } else {
+ if (vecs[0].len == 0)
+ return;
+ veccnt = 1;
+ ststate = H2O_SEND_STATE_IN_PROGRESS;
+ }
+
+ if (self->had_body_error)
+ ststate = H2O_SEND_STATE_ERROR;
+
+ h2o_send(self->src_req, vecs, veccnt, ststate);
+}
+
+static void do_proceed(h2o_generator_t *generator, h2o_req_t *req)
+{
+ struct rp_generator_t *self = (void *)generator;
+
+ h2o_doublebuffer_consume(&self->sending);
+ do_send(self);
+}
+
+static void on_websocket_upgrade_complete(void *_info, h2o_socket_t *sock, size_t reqsize)
+{
+ struct rp_ws_upgrade_info_t *info = _info;
+
+ if (sock != NULL) {
+ h2o_buffer_consume(&sock->input, reqsize);//It is detached from conn. Let's trash unused data.
+ h2o_tunnel_establish(info->ctx, sock, info->upstream_sock, info->timeout);
+ } else {
+ h2o_socket_close(info->upstream_sock);
+ }
+ free(info);
+}
+
+static inline void on_websocket_upgrade(struct rp_generator_t *self, h2o_timeout_t *timeout, int rlen)
+{
+ h2o_req_t *req = self->src_req;
+ h2o_socket_t *sock = h2o_http1client_steal_socket(self->client);
+ h2o_buffer_consume(&sock->input, rlen);//trash data after stealing sock.
+ struct rp_ws_upgrade_info_t *info = h2o_mem_alloc(sizeof(*info));
+ info->upstream_sock = sock;
+ info->timeout = timeout;
+ info->ctx = req->conn->ctx;
+ h2o_http1_upgrade(req, NULL, 0, on_websocket_upgrade_complete, info);
+}
+
+static int on_body(h2o_http1client_t *client, const char *errstr)
+{
+ struct rp_generator_t *self = client->data;
+
+ if (errstr != NULL) {
+ /* detach the content */
+ self->last_content_before_send = self->client->sock->input;
+ h2o_buffer_init(&self->client->sock->input, &h2o_socket_buffer_prototype);
+ self->client = NULL;
+ if (errstr != h2o_http1client_error_is_eos) {
+ h2o_req_log_error(self->src_req, "lib/core/proxy.c", "%s", errstr);
+ self->had_body_error = 1;
+ }
+ }
+ if (self->sending.bytes_inflight == 0)
+ do_send(self);
+
+ return 0;
+}
+
+static char compress_hint_to_enum(const char *val, size_t len)
+{
+ if (h2o_lcstris(val, len, H2O_STRLIT("on"))) {
+ return H2O_COMPRESS_HINT_ENABLE;
+ }
+ if (h2o_lcstris(val, len, H2O_STRLIT("off"))) {
+ return H2O_COMPRESS_HINT_DISABLE;
+ }
+ return H2O_COMPRESS_HINT_AUTO;
+}
+
+static h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *errstr, int minor_version, int status,
+ h2o_iovec_t msg, h2o_header_t *headers, size_t num_headers, int rlen)
+{
+ struct rp_generator_t *self = client->data;
+ h2o_req_t *req = self->src_req;
+ size_t i;
+
+ if (errstr != NULL && errstr != h2o_http1client_error_is_eos) {
+ self->client = NULL;
+ h2o_req_log_error(req, "lib/core/proxy.c", "%s", errstr);
+ h2o_send_error_502(req, "Gateway Error", errstr, 0);
+ return NULL;
+ }
+
+ /* copy the response (note: all the headers must be copied; http1client discards the input once we return from this callback) */
+ req->res.status = status;
+ req->res.reason = h2o_strdup(&req->pool, msg.base, msg.len).base;
+ for (i = 0; i != num_headers; ++i) {
+ if (h2o_iovec_is_token(headers[i].name)) {
+ const h2o_token_t *token = H2O_STRUCT_FROM_MEMBER(h2o_token_t, buf, headers[i].name);
+ h2o_iovec_t value;
+ if (token->proxy_should_drop_for_res) {
+ goto Skip;
+ }
+ if (token == H2O_TOKEN_CONTENT_LENGTH) {
+ if (req->res.content_length != SIZE_MAX ||
+ (req->res.content_length = h2o_strtosize(headers[i].value.base, headers[i].value.len)) == SIZE_MAX) {
+ self->client = NULL;
+ h2o_req_log_error(req, "lib/core/proxy.c", "%s", "invalid response from upstream (malformed content-length)");
+ h2o_send_error_502(req, "Gateway Error", "invalid response from upstream", 0);
+ return NULL;
+ }
+ goto Skip;
+ } else if (token == H2O_TOKEN_LOCATION) {
+ if (req->res_is_delegated && (300 <= status && status <= 399) && status != 304) {
+ self->client = NULL;
+ h2o_iovec_t method = h2o_get_redirect_method(req->method, status);
+ h2o_send_redirect_internal(req, method, headers[i].value.base, headers[i].value.len, 1);
+ return NULL;
+ }
+ if (req->overrides != NULL && req->overrides->location_rewrite.match != NULL) {
+ value = rewrite_location(&req->pool, headers[i].value.base, headers[i].value.len,
+ req->overrides->location_rewrite.match, req->input.scheme, req->input.authority,
+ req->overrides->location_rewrite.path_prefix);
+ if (value.base != NULL)
+ goto AddHeader;
+ }
+ goto AddHeaderDuped;
+ } else if (token == H2O_TOKEN_LINK) {
+ h2o_iovec_t new_value;
+ new_value = h2o_push_path_in_link_header(req, headers[i].value.base, headers[i].value.len);
+ if (!new_value.len)
+ goto Skip;
+ headers[i].value.base = new_value.base;
+ headers[i].value.len = new_value.len;
+ } else if (token == H2O_TOKEN_SERVER) {
+ if (!req->conn->ctx->globalconf->proxy.preserve_server_header)
+ goto Skip;
+ } else if (token == H2O_TOKEN_X_COMPRESS_HINT) {
+ req->compress_hint = compress_hint_to_enum(headers[i].value.base, headers[i].value.len);
+ goto Skip;
+ }
+ /* default behaviour, transfer the header downstream */
+ AddHeaderDuped:
+ value = h2o_strdup(&req->pool, headers[i].value.base, headers[i].value.len);
+ AddHeader:
+ h2o_add_header(&req->pool, &req->res.headers, token, headers[i].orig_name, value.base, value.len);
+ Skip:;
+ } else {
+ h2o_iovec_t name = h2o_strdup(&req->pool, headers[i].name->base, headers[i].name->len);
+ h2o_iovec_t value = h2o_strdup(&req->pool, headers[i].value.base, headers[i].value.len);
+ h2o_add_header_by_str(&req->pool, &req->res.headers, name.base, name.len, 0, headers[i].orig_name, value.base,
+ value.len);
+ }
+ }
+
+ if (self->is_websocket_handshake && req->res.status == 101) {
+ h2o_http1client_ctx_t *client_ctx = get_client_ctx(req);
+ assert(client_ctx->websocket_timeout != NULL);
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, NULL, H2O_STRLIT("websocket"));
+ on_websocket_upgrade(self, client_ctx->websocket_timeout, rlen);
+ self->client = NULL;
+ return NULL;
+ }
+ /* declare the start of the response */
+ h2o_start_response(req, &self->super);
+
+ if (errstr == h2o_http1client_error_is_eos) {
+ self->client = NULL;
+ h2o_send(req, NULL, 0, H2O_SEND_STATE_FINAL);
+ return NULL;
+ }
+
+ return on_body;
+}
+
+static int on_1xx(h2o_http1client_t *client, int minor_version, int status, h2o_iovec_t msg, h2o_header_t *headers,
+ size_t num_headers)
+{
+ struct rp_generator_t *self = client->data;
+ size_t i;
+
+ for (i = 0; i != num_headers; ++i) {
+ if (headers[i].name == &H2O_TOKEN_LINK->buf)
+ h2o_push_path_in_link_header(self->src_req, headers[i].value.base, headers[i].value.len);
+ }
+
+ return 0;
+}
+
+static h2o_http1client_head_cb on_connect(h2o_http1client_t *client, const char *errstr, h2o_iovec_t **reqbufs, size_t *reqbufcnt,
+ int *method_is_head)
+{
+ struct rp_generator_t *self = client->data;
+
+ if (errstr != NULL) {
+ self->client = NULL;
+ h2o_req_log_error(self->src_req, "lib/core/proxy.c", "%s", errstr);
+ h2o_send_error_502(self->src_req, "Gateway Error", errstr, 0);
+ return NULL;
+ }
+
+ *reqbufs = self->up_req.bufs;
+ *reqbufcnt = self->up_req.bufs[1].base != NULL ? 2 : 1;
+ *method_is_head = self->up_req.is_head;
+ self->client->informational_cb = on_1xx;
+ return on_head;
+}
+
+static void on_generator_dispose(void *_self)
+{
+ struct rp_generator_t *self = _self;
+
+ if (self->client != NULL) {
+ h2o_http1client_cancel(self->client);
+ self->client = NULL;
+ }
+ h2o_buffer_dispose(&self->last_content_before_send);
+ h2o_doublebuffer_dispose(&self->sending);
+}
+
+static struct rp_generator_t *proxy_send_prepare(h2o_req_t *req, int keepalive, int use_proxy_protocol)
+{
+ struct rp_generator_t *self = h2o_mem_alloc_shared(&req->pool, sizeof(*self), on_generator_dispose);
+ h2o_http1client_ctx_t *client_ctx = get_client_ctx(req);
+
+ self->super.proceed = do_proceed;
+ self->super.stop = do_close;
+ self->src_req = req;
+ if (client_ctx->websocket_timeout != NULL && h2o_lcstris(req->upgrade.base, req->upgrade.len, H2O_STRLIT("websocket"))) {
+ self->is_websocket_handshake = 1;
+ } else {
+ self->is_websocket_handshake = 0;
+ }
+ self->had_body_error = 0;
+ self->up_req.bufs[0] = build_request(req, keepalive, self->is_websocket_handshake, use_proxy_protocol);
+ self->up_req.bufs[1] = req->entity;
+ self->up_req.is_head = h2o_memis(req->method.base, req->method.len, H2O_STRLIT("HEAD"));
+ h2o_buffer_init(&self->last_content_before_send, &h2o_socket_buffer_prototype);
+ h2o_doublebuffer_init(&self->sending, &h2o_socket_buffer_prototype);
+
+ return self;
+}
+
+void h2o__proxy_process_request(h2o_req_t *req)
+{
+ h2o_req_overrides_t *overrides = req->overrides;
+ h2o_http1client_ctx_t *client_ctx = get_client_ctx(req);
+ struct rp_generator_t *self;
+
+ if (overrides != NULL) {
+ if (overrides->socketpool != NULL) {
+ if (overrides->use_proxy_protocol)
+ assert(!"proxy protocol cannot be used for a persistent upstream connection");
+ self = proxy_send_prepare(req, 1, 0);
+ h2o_http1client_connect_with_pool(&self->client, self, client_ctx, overrides->socketpool, on_connect);
+ return;
+ } else if (overrides->hostport.host.base != NULL) {
+ self = proxy_send_prepare(req, 0, overrides->use_proxy_protocol);
+ h2o_http1client_connect(&self->client, self, client_ctx, req->overrides->hostport.host, req->overrides->hostport.port,
+ 0, on_connect);
+ return;
+ }
+ }
+ { /* default logic */
+ h2o_iovec_t host;
+ uint16_t port;
+ if (h2o_url_parse_hostport(req->authority.base, req->authority.len, &host, &port) == NULL) {
+ h2o_req_log_error(req, "lib/core/proxy.c", "invalid URL supplied for internal redirection:%s://%.*s%.*s",
+ req->scheme->name.base, (int)req->authority.len, req->authority.base, (int)req->path.len,
+ req->path.base);
+ h2o_send_error_502(req, "Gateway Error", "internal error", 0);
+ return;
+ }
+ if (port == 65535)
+ port = req->scheme->default_port;
+ self = proxy_send_prepare(req, 0, overrides != NULL && overrides->use_proxy_protocol);
+ h2o_http1client_connect(&self->client, self, client_ctx, host, port, req->scheme == &H2O_URL_SCHEME_HTTPS, on_connect);
+ return;
+ }
+}
diff --git a/debian/vendor-h2o/lib/core/request.c b/debian/vendor-h2o/lib/core/request.c
new file mode 100644
index 0000000..96aabb2
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/request.c
@@ -0,0 +1,696 @@
+/*
+ * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku, Tatsuhiro Tsujikawa
+ *
+ * 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 <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+#include "h2o.h"
+
+#ifndef IOV_MAX
+#define IOV_MAX UIO_MAXIOV
+#endif
+
+#define INITIAL_INBUFSZ 8192
+
+struct st_deferred_request_action_t {
+ h2o_timeout_entry_t timeout;
+ h2o_req_t *req;
+};
+
+struct st_delegate_request_deferred_t {
+ struct st_deferred_request_action_t super;
+ h2o_handler_t *current_handler;
+};
+
+struct st_reprocess_request_deferred_t {
+ struct st_deferred_request_action_t super;
+ h2o_iovec_t method;
+ const h2o_url_scheme_t *scheme;
+ h2o_iovec_t authority;
+ h2o_iovec_t path;
+ h2o_req_overrides_t *overrides;
+ int is_delegated;
+};
+
+struct st_send_error_deferred_t {
+ h2o_req_t *req;
+ int status;
+ const char *reason;
+ const char *body;
+ int flags;
+ h2o_timeout_entry_t _timeout;
+};
+
+static void on_deferred_action_dispose(void *_action)
+{
+ struct st_deferred_request_action_t *action = _action;
+ if (h2o_timeout_is_linked(&action->timeout))
+ h2o_timeout_unlink(&action->timeout);
+}
+
+static struct st_deferred_request_action_t *create_deferred_action(h2o_req_t *req, size_t sz, h2o_timeout_cb cb)
+{
+ struct st_deferred_request_action_t *action = h2o_mem_alloc_shared(&req->pool, sz, on_deferred_action_dispose);
+ *action = (struct st_deferred_request_action_t){{0, cb}, req};
+ h2o_timeout_link(req->conn->ctx->loop, &req->conn->ctx->zero_timeout, &action->timeout);
+ return action;
+}
+
+static h2o_hostconf_t *find_hostconf(h2o_hostconf_t **hostconfs, h2o_iovec_t authority, uint16_t default_port)
+{
+ h2o_iovec_t hostname;
+ uint16_t port;
+ char *hostname_lc;
+
+ /* safe-guard for alloca */
+ if (authority.len >= 65536)
+ return NULL;
+
+ /* extract the specified hostname and port */
+ if (h2o_url_parse_hostport(authority.base, authority.len, &hostname, &port) == NULL)
+ return NULL;
+ if (port == 65535)
+ port = default_port;
+
+ /* convert supplied hostname to lower-case */
+ hostname_lc = alloca(hostname.len);
+ memcpy(hostname_lc, hostname.base, hostname.len);
+ h2o_strtolower(hostname_lc, hostname.len);
+
+ do {
+ h2o_hostconf_t *hostconf = *hostconfs;
+ if (hostconf->authority.port == port || (hostconf->authority.port == 65535 && port == default_port)) {
+ if (hostconf->authority.host.base[0] == '*') {
+ /* matching against "*.foo.bar" */
+ size_t cmplen = hostconf->authority.host.len - 1;
+ if (cmplen < hostname.len &&
+ memcmp(hostconf->authority.host.base + 1, hostname_lc + hostname.len - cmplen, cmplen) == 0)
+ return hostconf;
+ } else {
+ /* exact match */
+ if (h2o_memis(hostconf->authority.host.base, hostconf->authority.host.len, hostname_lc, hostname.len))
+ return hostconf;
+ }
+ }
+ } while (*++hostconfs != NULL);
+
+ return NULL;
+}
+
+static h2o_hostconf_t *setup_before_processing(h2o_req_t *req)
+{
+ h2o_context_t *ctx = req->conn->ctx;
+ h2o_hostconf_t *hostconf;
+
+ h2o_get_timestamp(ctx, &req->pool, &req->processed_at);
+
+ /* find the host context */
+ if (req->input.authority.base != NULL) {
+ if (req->conn->hosts[1] == NULL ||
+ (hostconf = find_hostconf(req->conn->hosts, req->input.authority, req->input.scheme->default_port)) == NULL)
+ hostconf = *req->conn->hosts;
+ } else {
+ /* set the authority name to the default one */
+ hostconf = *req->conn->hosts;
+ req->input.authority = hostconf->authority.hostport;
+ }
+
+ req->scheme = req->input.scheme;
+ req->method = req->input.method;
+ req->authority = req->input.authority;
+ req->path = req->input.path;
+ req->path_normalized =
+ h2o_url_normalize_path(&req->pool, req->input.path.base, req->input.path.len, &req->query_at, &req->norm_indexes);
+ req->input.query_at = req->query_at; /* we can do this since input.path == path */
+
+ return hostconf;
+}
+
+static void call_handlers(h2o_req_t *req, h2o_handler_t **handler)
+{
+ h2o_handler_t **end = req->pathconf->handlers.entries + req->pathconf->handlers.size;
+
+ for (; handler != end; ++handler)
+ if ((*handler)->on_req(*handler, req) == 0)
+ return;
+
+ h2o_send_error_404(req, "File Not Found", "not found", 0);
+}
+
+static void process_hosted_request(h2o_req_t *req, h2o_hostconf_t *hostconf)
+{
+ h2o_pathconf_t *selected_pathconf = &hostconf->fallback_path;
+ size_t i;
+
+ /* setup pathconf, or redirect to "path/" */
+ for (i = 0; i != hostconf->paths.size; ++i) {
+ h2o_pathconf_t *candidate = hostconf->paths.entries + i;
+ if (req->path_normalized.len >= candidate->path.len &&
+ memcmp(req->path_normalized.base, candidate->path.base, candidate->path.len) == 0 &&
+ (candidate->path.base[candidate->path.len - 1] == '/' || req->path_normalized.len == candidate->path.len ||
+ req->path_normalized.base[candidate->path.len] == '/')) {
+ selected_pathconf = candidate;
+ break;
+ }
+ }
+ h2o_req_bind_conf(req, hostconf, selected_pathconf);
+
+ call_handlers(req, req->pathconf->handlers.entries);
+}
+
+static void deferred_proceed_cb(h2o_timeout_entry_t *entry)
+{
+ h2o_req_t *req = H2O_STRUCT_FROM_MEMBER(h2o_req_t, _timeout_entry, entry);
+ h2o_proceed_response(req);
+}
+
+static void close_generator_and_filters(h2o_req_t *req)
+{
+ /* close the generator if it is still open */
+ if (req->_generator != NULL) {
+ /* close generator */
+ if (req->_generator->stop != NULL)
+ req->_generator->stop(req->_generator, req);
+ req->_generator = NULL;
+ }
+ /* close the ostreams still open */
+ while (req->_ostr_top->next != NULL) {
+ if (req->_ostr_top->stop != NULL)
+ req->_ostr_top->stop(req->_ostr_top, req);
+ req->_ostr_top = req->_ostr_top->next;
+ }
+}
+
+static void reset_response(h2o_req_t *req)
+{
+ req->res = (h2o_res_t){0, NULL, SIZE_MAX};
+ req->res.reason = "OK";
+ req->_next_filter_index = 0;
+ req->bytes_sent = 0;
+}
+
+static void retain_original_response(h2o_req_t *req)
+{
+ if (req->res.original.status != 0)
+ return;
+
+ req->res.original.status = req->res.status;
+ h2o_vector_reserve(&req->pool, &req->res.original.headers, req->res.headers.size);
+ h2o_memcpy(req->res.original.headers.entries, req->res.headers.entries,
+ sizeof(req->res.headers.entries[0]) * req->res.headers.size);
+ req->res.original.headers.size = req->res.headers.size;
+}
+
+void h2o_init_request(h2o_req_t *req, h2o_conn_t *conn, h2o_req_t *src)
+{
+ /* clear all memory (expect memory pool, since it is large) */
+ memset(req, 0, offsetof(h2o_req_t, pool));
+
+ /* init memory pool (before others, since it may be used) */
+ h2o_mem_init_pool(&req->pool);
+
+ /* init properties that should be initialized to non-zero */
+ req->conn = conn;
+ req->_timeout_entry.cb = deferred_proceed_cb;
+ req->res.reason = "OK"; /* default to "OK" regardless of the status value, it's not important after all (never sent in HTTP2) */
+ req->res.content_length = SIZE_MAX;
+ req->preferred_chunk_size = SIZE_MAX;
+
+ if (src != NULL) {
+ size_t i;
+#define COPY(buf) \
+ do { \
+ req->buf.base = h2o_mem_alloc_pool(&req->pool, src->buf.len); \
+ memcpy(req->buf.base, src->buf.base, src->buf.len); \
+ req->buf.len = src->buf.len; \
+ } while (0)
+ COPY(input.authority);
+ COPY(input.method);
+ COPY(input.path);
+ req->input.scheme = src->input.scheme;
+ req->version = src->version;
+ req->entity = src->entity;
+ req->http1_is_persistent = src->http1_is_persistent;
+ req->timestamps = src->timestamps;
+ if (src->upgrade.base != NULL) {
+ COPY(upgrade);
+ } else {
+ req->upgrade.base = NULL;
+ req->upgrade.len = 0;
+ }
+#undef COPY
+ h2o_vector_reserve(&req->pool, &req->headers, src->headers.size);
+ req->headers.size = src->headers.size;
+ for (i = 0; i != src->headers.size; ++i) {
+ h2o_header_t *dst_header = req->headers.entries + i, *src_header = src->headers.entries + i;
+ if (h2o_iovec_is_token(src_header->name)) {
+ dst_header->name = src_header->name;
+ } else {
+ dst_header->name = h2o_mem_alloc_pool(&req->pool, sizeof(*dst_header->name));
+ *dst_header->name = h2o_strdup(&req->pool, src_header->name->base, src_header->name->len);
+ }
+ dst_header->value = h2o_strdup(&req->pool, src_header->value.base, src_header->value.len);
+ if (!src_header->orig_name)
+ dst_header->orig_name = NULL;
+ else
+ dst_header->orig_name = h2o_strdup(&req->pool, src_header->orig_name, src_header->name->len).base;
+ }
+ if (src->env.size != 0) {
+ h2o_vector_reserve(&req->pool, &req->env, src->env.size);
+ req->env.size = src->env.size;
+ for (i = 0; i != req->env.size; ++i)
+ req->env.entries[i] = h2o_strdup(&req->pool, src->env.entries[i].base, src->env.entries[i].len);
+ }
+ }
+}
+
+void h2o_dispose_request(h2o_req_t *req)
+{
+ close_generator_and_filters(req);
+
+ h2o_timeout_unlink(&req->_timeout_entry);
+
+ if (req->version != 0 && req->pathconf != NULL) {
+ h2o_logger_t **logger = req->pathconf->loggers.entries, **end = logger + req->pathconf->loggers.size;
+ for (; logger != end; ++logger) {
+ (*logger)->log_access((*logger), req);
+ }
+ }
+
+ h2o_mem_clear_pool(&req->pool);
+}
+
+void h2o_process_request(h2o_req_t *req)
+{
+ h2o_hostconf_t *hostconf = setup_before_processing(req);
+ process_hosted_request(req, hostconf);
+}
+
+void h2o_delegate_request(h2o_req_t *req, h2o_handler_t *current_handler)
+{
+ h2o_handler_t **handler = req->pathconf->handlers.entries, **end = handler + req->pathconf->handlers.size;
+
+ for (; handler != end; ++handler) {
+ if (*handler == current_handler) {
+ ++handler;
+ break;
+ }
+ }
+ call_handlers(req, handler);
+}
+
+static void on_delegate_request_cb(h2o_timeout_entry_t *entry)
+{
+ struct st_delegate_request_deferred_t *args =
+ H2O_STRUCT_FROM_MEMBER(struct st_delegate_request_deferred_t, super.timeout, entry);
+ h2o_delegate_request(args->super.req, args->current_handler);
+}
+
+void h2o_delegate_request_deferred(h2o_req_t *req, h2o_handler_t *current_handler)
+{
+ struct st_delegate_request_deferred_t *args =
+ (struct st_delegate_request_deferred_t *)create_deferred_action(req, sizeof(*args), on_delegate_request_cb);
+ args->current_handler = current_handler;
+}
+
+void h2o_reprocess_request(h2o_req_t *req, h2o_iovec_t method, const h2o_url_scheme_t *scheme, h2o_iovec_t authority,
+ h2o_iovec_t path, h2o_req_overrides_t *overrides, int is_delegated)
+{
+ h2o_hostconf_t *hostconf;
+
+ retain_original_response(req);
+
+ /* close generators and filters that are already running */
+ close_generator_and_filters(req);
+
+ /* setup the request/response parameters */
+ req->method = method;
+ req->scheme = scheme;
+ req->authority = authority;
+ req->path = path;
+ req->path_normalized = h2o_url_normalize_path(&req->pool, req->path.base, req->path.len, &req->query_at, &req->norm_indexes);
+ req->overrides = overrides;
+ req->res_is_delegated |= is_delegated;
+ reset_response(req);
+
+ /* check the delegation (or reprocess) counter */
+ if (req->res_is_delegated) {
+ if (req->num_delegated == req->conn->ctx->globalconf->max_delegations) {
+ /* TODO log */
+ h2o_send_error_502(req, "Gateway Error", "too many internal delegations", 0);
+ return;
+ }
+ ++req->num_delegated;
+ } else {
+ if (req->num_reprocessed >= 5) {
+ /* TODO log */
+ h2o_send_error_502(req, "Gateway Error", "too many internal reprocesses", 0);
+ return;
+ }
+ ++req->num_reprocessed;
+ }
+
+ /* handle the response using the handlers, if hostconf exists */
+ h2o_hostconf_t **hosts = is_delegated ? req->conn->ctx->globalconf->hosts : req->conn->hosts;
+ if (req->overrides == NULL && (hostconf = find_hostconf(hosts, req->authority, req->scheme->default_port)) != NULL) {
+ req->pathconf = NULL;
+ process_hosted_request(req, hostconf);
+ return;
+ }
+
+ /* uses the current pathconf, in other words, proxy uses the previous pathconf for building filters */
+ h2o__proxy_process_request(req);
+}
+
+static void on_reprocess_request_cb(h2o_timeout_entry_t *entry)
+{
+ struct st_reprocess_request_deferred_t *args =
+ H2O_STRUCT_FROM_MEMBER(struct st_reprocess_request_deferred_t, super.timeout, entry);
+ h2o_reprocess_request(args->super.req, args->method, args->scheme, args->authority, args->path, args->overrides,
+ args->is_delegated);
+}
+
+void h2o_reprocess_request_deferred(h2o_req_t *req, h2o_iovec_t method, const h2o_url_scheme_t *scheme, h2o_iovec_t authority,
+ h2o_iovec_t path, h2o_req_overrides_t *overrides, int is_delegated)
+{
+ struct st_reprocess_request_deferred_t *args =
+ (struct st_reprocess_request_deferred_t *)create_deferred_action(req, sizeof(*args), on_reprocess_request_cb);
+ args->method = method;
+ args->scheme = scheme;
+ args->authority = authority;
+ args->path = path;
+ args->overrides = overrides;
+ args->is_delegated = is_delegated;
+}
+
+void h2o_start_response(h2o_req_t *req, h2o_generator_t *generator)
+{
+ retain_original_response(req);
+
+ /* set generator */
+ assert(req->_generator == NULL);
+ req->_generator = generator;
+
+ /* setup response filters */
+ if (req->prefilters != NULL) {
+ req->prefilters->on_setup_ostream(req->prefilters, req, &req->_ostr_top);
+ } else {
+ h2o_setup_next_ostream(req, &req->_ostr_top);
+ }
+}
+
+void h2o_send(h2o_req_t *req, h2o_iovec_t *bufs, size_t bufcnt, h2o_send_state_t state)
+{
+ assert(req->_generator != NULL);
+
+ if (!h2o_send_state_is_in_progress(state))
+ req->_generator = NULL;
+
+ req->_ostr_top->do_send(req->_ostr_top, req, bufs, bufcnt, state);
+}
+
+h2o_req_prefilter_t *h2o_add_prefilter(h2o_req_t *req, size_t sz)
+{
+ h2o_req_prefilter_t *prefilter = h2o_mem_alloc_pool(&req->pool, sz);
+ prefilter->next = req->prefilters;
+ req->prefilters = prefilter;
+ return prefilter;
+}
+
+h2o_ostream_t *h2o_add_ostream(h2o_req_t *req, size_t sz, h2o_ostream_t **slot)
+{
+ h2o_ostream_t *ostr = h2o_mem_alloc_pool(&req->pool, sz);
+ ostr->next = *slot;
+ ostr->do_send = NULL;
+ ostr->stop = NULL;
+ ostr->start_pull = NULL;
+
+ *slot = ostr;
+
+ return ostr;
+}
+
+static void apply_env(h2o_req_t *req, h2o_envconf_t *env)
+{
+ size_t i;
+
+ if (env->parent != NULL)
+ apply_env(req, env->parent);
+ for (i = 0; i != env->unsets.size; ++i)
+ h2o_req_unsetenv(req, env->unsets.entries[i].base, env->unsets.entries[i].len);
+ for (i = 0; i != env->sets.size; i += 2)
+ *h2o_req_getenv(req, env->sets.entries[i].base, env->sets.entries[i].len, 1) = env->sets.entries[i + 1];
+}
+
+void h2o_req_bind_conf(h2o_req_t *req, h2o_hostconf_t *hostconf, h2o_pathconf_t *pathconf)
+{
+ req->hostconf = hostconf;
+ req->pathconf = pathconf;
+ if (pathconf->env != NULL)
+ apply_env(req, pathconf->env);
+}
+
+void h2o_ostream_send_next(h2o_ostream_t *ostream, h2o_req_t *req, h2o_iovec_t *bufs, size_t bufcnt, h2o_send_state_t state)
+{
+ if (!h2o_send_state_is_in_progress(state)) {
+ assert(req->_ostr_top == ostream);
+ req->_ostr_top = ostream->next;
+ } else if (bufcnt == 0) {
+ h2o_timeout_link(req->conn->ctx->loop, &req->conn->ctx->zero_timeout, &req->_timeout_entry);
+ return;
+ }
+ ostream->next->do_send(ostream->next, req, bufs, bufcnt, state);
+}
+
+void h2o_req_fill_mime_attributes(h2o_req_t *req)
+{
+ ssize_t content_type_index;
+ h2o_mimemap_type_t *mime;
+
+ if (req->res.mime_attr != NULL)
+ return;
+
+ if ((content_type_index = h2o_find_header(&req->res.headers, H2O_TOKEN_CONTENT_TYPE, -1)) != -1 &&
+ (mime = h2o_mimemap_get_type_by_mimetype(req->pathconf->mimemap, req->res.headers.entries[content_type_index].value, 0)) !=
+ NULL)
+ req->res.mime_attr = &mime->data.attr;
+ else
+ req->res.mime_attr = &h2o_mime_attributes_as_is;
+}
+
+void h2o_send_inline(h2o_req_t *req, const char *body, size_t len)
+{
+ static h2o_generator_t generator = {NULL, NULL};
+
+ h2o_iovec_t buf = h2o_strdup(&req->pool, body, len);
+ /* the function intentionally does not set the content length, since it may be used for generating 304 response, etc. */
+ /* req->res.content_length = buf.len; */
+
+ h2o_start_response(req, &generator);
+
+ if (h2o_memis(req->input.method.base, req->input.method.len, H2O_STRLIT("HEAD")))
+ h2o_send(req, NULL, 0, H2O_SEND_STATE_FINAL);
+ else
+ h2o_send(req, &buf, 1, H2O_SEND_STATE_FINAL);
+}
+
+void h2o_send_error_generic(h2o_req_t *req, int status, const char *reason, const char *body, int flags)
+{
+ if (req->pathconf == NULL) {
+ h2o_hostconf_t *hostconf = setup_before_processing(req);
+ h2o_req_bind_conf(req, hostconf, &hostconf->fallback_path);
+ }
+
+ if ((flags & H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION) != 0)
+ req->http1_is_persistent = 0;
+
+ req->res.status = status;
+ req->res.reason = reason;
+ req->res.content_length = strlen(body);
+
+ if ((flags & H2O_SEND_ERROR_KEEP_HEADERS) == 0)
+ memset(&req->res.headers, 0, sizeof(req->res.headers));
+
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, H2O_STRLIT("text/plain; charset=utf-8"));
+
+ h2o_send_inline(req, body, SIZE_MAX);
+}
+
+#define DECL_SEND_ERROR_DEFERRED(status_) \
+ static void send_error_deferred_cb_##status_(h2o_timeout_entry_t *entry) \
+ { \
+ struct st_send_error_deferred_t *args = H2O_STRUCT_FROM_MEMBER(struct st_send_error_deferred_t, _timeout, entry); \
+ reset_response(args->req); \
+ args->req->conn->ctx->emitted_error_status[H2O_STATUS_ERROR_##status_]++; \
+ h2o_send_error_generic(args->req, args->status, args->reason, args->body, args->flags); \
+ } \
+ \
+ static void h2o_send_error_deferred_##status_(h2o_req_t *req, const char *reason, const char *body, int flags) \
+ { \
+ struct st_send_error_deferred_t *args = h2o_mem_alloc_pool(&req->pool, sizeof(*args)); \
+ *args = (struct st_send_error_deferred_t){req, status_, reason, body, flags}; \
+ args->_timeout.cb = send_error_deferred_cb_##status_; \
+ h2o_timeout_link(req->conn->ctx->loop, &req->conn->ctx->zero_timeout, &args->_timeout); \
+ }
+
+DECL_SEND_ERROR_DEFERRED(502)
+
+#undef DECL_SEND_ERROR_DEFERRED
+
+void h2o_req_log_error(h2o_req_t *req, const char *module, const char *fmt, ...)
+{
+#define INITIAL_BUF_SIZE 256
+
+ char *errbuf = h2o_mem_alloc_pool(&req->pool, INITIAL_BUF_SIZE);
+ int errlen;
+ va_list args;
+
+ va_start(args, fmt);
+ errlen = vsnprintf(errbuf, INITIAL_BUF_SIZE, fmt, args);
+ va_end(args);
+
+ if (errlen >= INITIAL_BUF_SIZE) {
+ errbuf = h2o_mem_alloc_pool(&req->pool, errlen + 1);
+ va_start(args, fmt);
+ errlen = vsnprintf(errbuf, errlen + 1, fmt, args);
+ va_end(args);
+ }
+
+#undef INITIAL_BUF_SIZE
+
+ /* save the log */
+ h2o_vector_reserve(&req->pool, &req->error_logs, req->error_logs.size + 1);
+ req->error_logs.entries[req->error_logs.size++] = (h2o_req_error_log_t){module, h2o_iovec_init(errbuf, errlen)};
+
+ if (req->pathconf->error_log.emit_request_errors) {
+ /* build prefix */
+ char *prefix = alloca(sizeof("[] in request::") + 32 + strlen(module)), *p = prefix;
+ p += sprintf(p, "[%s] in request:", module);
+ if (req->path.len < 32) {
+ memcpy(p, req->path.base, req->path.len);
+ p += req->path.len;
+ } else {
+ memcpy(p, req->path.base, 29);
+ p += 29;
+ memcpy(p, "...", 3);
+ p += 3;
+ }
+ *p++ = ':';
+ /* use writev(2) to emit error atomically */
+ struct iovec vecs[] = {{prefix, p - prefix}, {errbuf, errlen}, {"\n", 1}};
+ H2O_BUILD_ASSERT(sizeof(vecs) / sizeof(vecs[0]) < IOV_MAX);
+ writev(2, vecs, sizeof(vecs) / sizeof(vecs[0]));
+ }
+}
+
+void h2o_send_redirect(h2o_req_t *req, int status, const char *reason, const char *url, size_t url_len)
+{
+ if (req->res_is_delegated) {
+ h2o_iovec_t method = h2o_get_redirect_method(req->method, status);
+ h2o_send_redirect_internal(req, method, url, url_len, 0);
+ return;
+ }
+
+ static h2o_generator_t generator = {NULL, NULL};
+ static const h2o_iovec_t body_prefix = {H2O_STRLIT("<!DOCTYPE html><TITLE>Moved</TITLE><P>The document has moved <A HREF=\"")};
+ static const h2o_iovec_t body_suffix = {H2O_STRLIT("\">here</A>")};
+
+ /* build and send response */
+ h2o_iovec_t bufs[3];
+ size_t bufcnt;
+ if (h2o_memis(req->input.method.base, req->input.method.len, H2O_STRLIT("HEAD"))) {
+ req->res.content_length = SIZE_MAX;
+ bufcnt = 0;
+ } else {
+ bufs[0] = body_prefix;
+ bufs[1] = h2o_htmlescape(&req->pool, url, url_len);
+ bufs[2] = body_suffix;
+ bufcnt = 3;
+ req->res.content_length = body_prefix.len + bufs[1].len + body_suffix.len;
+ }
+ req->res.status = status;
+ req->res.reason = reason;
+ req->res.headers = (h2o_headers_t){NULL};
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LOCATION, NULL, url, url_len);
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, H2O_STRLIT("text/html; charset=utf-8"));
+ h2o_start_response(req, &generator);
+ h2o_send(req, bufs, bufcnt, H2O_SEND_STATE_FINAL);
+}
+
+void h2o_send_redirect_internal(h2o_req_t *req, h2o_iovec_t method, const char *url_str, size_t url_len, int preserve_overrides)
+{
+ h2o_url_t url;
+
+ /* parse the location URL */
+ if (h2o_url_parse_relative(url_str, url_len, &url) != 0) {
+ /* TODO log fprintf(stderr, "[proxy] cannot handle location header: %.*s\n", (int)url_len, url); */
+ h2o_send_error_deferred_502(req, "Gateway Error", "internal error", 0);
+ return;
+ }
+ /* convert the location to absolute (while creating copies of the values passed to the deferred call) */
+ if (url.scheme == NULL)
+ url.scheme = req->scheme;
+ if (url.authority.base == NULL) {
+ if (req->hostconf != NULL)
+ url.authority = req->hostconf->authority.hostport;
+ else
+ url.authority = req->authority;
+ } else {
+ if (h2o_lcstris(url.authority.base, url.authority.len, req->authority.base, req->authority.len)) {
+ url.authority = req->authority;
+ } else {
+ url.authority = h2o_strdup(&req->pool, url.authority.base, url.authority.len);
+ preserve_overrides = 0;
+ }
+ }
+ h2o_iovec_t base_path = req->path;
+ h2o_url_resolve_path(&base_path, &url.path);
+ url.path = h2o_concat(&req->pool, base_path, url.path);
+
+ h2o_reprocess_request_deferred(req, method, url.scheme, url.authority, url.path, preserve_overrides ? req->overrides : NULL, 1);
+}
+
+h2o_iovec_t h2o_get_redirect_method(h2o_iovec_t method, int status)
+{
+ if (h2o_memis(method.base, method.len, H2O_STRLIT("POST")) && !(status == 307 || status == 308))
+ method = h2o_iovec_init(H2O_STRLIT("GET"));
+ return method;
+}
+
+h2o_iovec_t h2o_push_path_in_link_header(h2o_req_t *req, const char *value, size_t value_len)
+{
+ int i;
+ h2o_iovec_t ret = h2o_iovec_init(value, value_len);
+ if (req->conn->callbacks->push_path == NULL)
+ return ret;
+
+ h2o_iovec_vector_t paths = h2o_extract_push_path_from_link_header(
+ &req->pool, value, value_len, req->path_normalized, req->input.scheme, req->input.authority,
+ req->res_is_delegated ? req->scheme : NULL, req->res_is_delegated ? &req->authority : NULL, &ret);
+ if (paths.size == 0)
+ return ret;
+
+ for (i = 0; i < paths.size; i++) {
+ req->conn->callbacks->push_path(req, paths.entries[i].base, paths.entries[i].len);
+ }
+ return ret;
+}
diff --git a/debian/vendor-h2o/lib/core/token.c b/debian/vendor-h2o/lib/core/token.c
new file mode 100644
index 0000000..e21ce23
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/token.c
@@ -0,0 +1,28 @@
+/*
+ * 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 "token_table.h"
+
+int h2o_iovec_is_token(const h2o_iovec_t *buf)
+{
+ return &h2o__tokens[0].buf <= buf && buf <= &h2o__tokens[H2O_MAX_TOKENS - 1].buf;
+}
diff --git a/debian/vendor-h2o/lib/core/token_table.h b/debian/vendor-h2o/lib/core/token_table.h
new file mode 100644
index 0000000..ae26aa6
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/token_table.h
@@ -0,0 +1,408 @@
+/*
+ * 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 &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+ */
+
+/* DO NOT EDIT! generated by tokens.pl */
+h2o_token_t h2o__tokens[] = {{{H2O_STRLIT(":authority")}, 1, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT(":method")}, 2, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT(":path")}, 4, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT(":scheme")}, 6, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT(":status")}, 8, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("accept")}, 19, 0, 0, 0, 0, 1, 0},
+ {{H2O_STRLIT("accept-charset")}, 15, 0, 0, 0, 0, 1, 0},
+ {{H2O_STRLIT("accept-encoding")}, 16, 0, 0, 0, 0, 1, 0},
+ {{H2O_STRLIT("accept-language")}, 17, 0, 0, 0, 0, 1, 0},
+ {{H2O_STRLIT("accept-ranges")}, 18, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("access-control-allow-origin")}, 20, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("age")}, 21, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("allow")}, 22, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("authorization")}, 23, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("cache-control")}, 24, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("cache-digest")}, 0, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("connection")}, 0, 1, 1, 0, 1, 0, 0},
+ {{H2O_STRLIT("content-disposition")}, 25, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("content-encoding")}, 26, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("content-language")}, 27, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("content-length")}, 28, 0, 0, 1, 0, 0, 0},
+ {{H2O_STRLIT("content-location")}, 29, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("content-range")}, 30, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("content-type")}, 31, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("cookie")}, 32, 0, 0, 0, 0, 0, 1},
+ {{H2O_STRLIT("date")}, 33, 0, 1, 0, 0, 0, 0},
+ {{H2O_STRLIT("etag")}, 34, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("expect")}, 35, 0, 0, 1, 0, 0, 0},
+ {{H2O_STRLIT("expires")}, 36, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("from")}, 37, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("host")}, 38, 0, 0, 1, 1, 0, 0},
+ {{H2O_STRLIT("http2-settings")}, 0, 1, 0, 0, 1, 0, 0},
+ {{H2O_STRLIT("if-match")}, 39, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("if-modified-since")}, 40, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("if-none-match")}, 41, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("if-range")}, 42, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("if-unmodified-since")}, 43, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("keep-alive")}, 0, 1, 1, 0, 0, 0, 0},
+ {{H2O_STRLIT("last-modified")}, 44, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("link")}, 45, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("location")}, 46, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("max-forwards")}, 47, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("proxy-authenticate")}, 48, 1, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("proxy-authorization")}, 49, 1, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("range")}, 50, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("referer")}, 51, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("refresh")}, 52, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("retry-after")}, 53, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("server")}, 54, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("set-cookie")}, 55, 0, 0, 0, 0, 0, 1},
+ {{H2O_STRLIT("strict-transport-security")}, 56, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("te")}, 0, 1, 0, 0, 1, 0, 0},
+ {{H2O_STRLIT("transfer-encoding")}, 57, 1, 1, 1, 1, 0, 0},
+ {{H2O_STRLIT("upgrade")}, 0, 1, 1, 1, 1, 0, 0},
+ {{H2O_STRLIT("user-agent")}, 58, 0, 0, 0, 0, 1, 0},
+ {{H2O_STRLIT("vary")}, 59, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("via")}, 60, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("www-authenticate")}, 61, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("x-compress-hint")}, 0, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("x-forwarded-for")}, 0, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("x-reproxy-url")}, 0, 0, 0, 0, 0, 0, 0},
+ {{H2O_STRLIT("x-traffic")}, 0, 0, 0, 0, 0, 0, 0}};
+size_t h2o__num_tokens = 62;
+
+const h2o_token_t *h2o_lookup_token(const char *name, size_t len)
+{
+ switch (len) {
+ case 2:
+ switch (name[1]) {
+ case 'e':
+ if (memcmp(name, "t", 1) == 0)
+ return H2O_TOKEN_TE;
+ break;
+ }
+ break;
+ case 3:
+ switch (name[2]) {
+ case 'a':
+ if (memcmp(name, "vi", 2) == 0)
+ return H2O_TOKEN_VIA;
+ break;
+ case 'e':
+ if (memcmp(name, "ag", 2) == 0)
+ return H2O_TOKEN_AGE;
+ break;
+ }
+ break;
+ case 4:
+ switch (name[3]) {
+ case 'e':
+ if (memcmp(name, "dat", 3) == 0)
+ return H2O_TOKEN_DATE;
+ break;
+ case 'g':
+ if (memcmp(name, "eta", 3) == 0)
+ return H2O_TOKEN_ETAG;
+ break;
+ case 'k':
+ if (memcmp(name, "lin", 3) == 0)
+ return H2O_TOKEN_LINK;
+ break;
+ case 'm':
+ if (memcmp(name, "fro", 3) == 0)
+ return H2O_TOKEN_FROM;
+ break;
+ case 't':
+ if (memcmp(name, "hos", 3) == 0)
+ return H2O_TOKEN_HOST;
+ break;
+ case 'y':
+ if (memcmp(name, "var", 3) == 0)
+ return H2O_TOKEN_VARY;
+ break;
+ }
+ break;
+ case 5:
+ switch (name[4]) {
+ case 'e':
+ if (memcmp(name, "rang", 4) == 0)
+ return H2O_TOKEN_RANGE;
+ break;
+ case 'h':
+ if (memcmp(name, ":pat", 4) == 0)
+ return H2O_TOKEN_PATH;
+ break;
+ case 'w':
+ if (memcmp(name, "allo", 4) == 0)
+ return H2O_TOKEN_ALLOW;
+ break;
+ }
+ break;
+ case 6:
+ switch (name[5]) {
+ case 'e':
+ if (memcmp(name, "cooki", 5) == 0)
+ return H2O_TOKEN_COOKIE;
+ break;
+ case 'r':
+ if (memcmp(name, "serve", 5) == 0)
+ return H2O_TOKEN_SERVER;
+ break;
+ case 't':
+ if (memcmp(name, "accep", 5) == 0)
+ return H2O_TOKEN_ACCEPT;
+ if (memcmp(name, "expec", 5) == 0)
+ return H2O_TOKEN_EXPECT;
+ break;
+ }
+ break;
+ case 7:
+ switch (name[6]) {
+ case 'd':
+ if (memcmp(name, ":metho", 6) == 0)
+ return H2O_TOKEN_METHOD;
+ break;
+ case 'e':
+ if (memcmp(name, ":schem", 6) == 0)
+ return H2O_TOKEN_SCHEME;
+ if (memcmp(name, "upgrad", 6) == 0)
+ return H2O_TOKEN_UPGRADE;
+ break;
+ case 'h':
+ if (memcmp(name, "refres", 6) == 0)
+ return H2O_TOKEN_REFRESH;
+ break;
+ case 'r':
+ if (memcmp(name, "refere", 6) == 0)
+ return H2O_TOKEN_REFERER;
+ break;
+ case 's':
+ if (memcmp(name, ":statu", 6) == 0)
+ return H2O_TOKEN_STATUS;
+ if (memcmp(name, "expire", 6) == 0)
+ return H2O_TOKEN_EXPIRES;
+ break;
+ }
+ break;
+ case 8:
+ switch (name[7]) {
+ case 'e':
+ if (memcmp(name, "if-rang", 7) == 0)
+ return H2O_TOKEN_IF_RANGE;
+ break;
+ case 'h':
+ if (memcmp(name, "if-matc", 7) == 0)
+ return H2O_TOKEN_IF_MATCH;
+ break;
+ case 'n':
+ if (memcmp(name, "locatio", 7) == 0)
+ return H2O_TOKEN_LOCATION;
+ break;
+ }
+ break;
+ case 9:
+ switch (name[8]) {
+ case 'c':
+ if (memcmp(name, "x-traffi", 8) == 0)
+ return H2O_TOKEN_X_TRAFFIC;
+ break;
+ }
+ break;
+ case 10:
+ switch (name[9]) {
+ case 'e':
+ if (memcmp(name, "keep-aliv", 9) == 0)
+ return H2O_TOKEN_KEEP_ALIVE;
+ if (memcmp(name, "set-cooki", 9) == 0)
+ return H2O_TOKEN_SET_COOKIE;
+ break;
+ case 'n':
+ if (memcmp(name, "connectio", 9) == 0)
+ return H2O_TOKEN_CONNECTION;
+ break;
+ case 't':
+ if (memcmp(name, "user-agen", 9) == 0)
+ return H2O_TOKEN_USER_AGENT;
+ break;
+ case 'y':
+ if (memcmp(name, ":authorit", 9) == 0)
+ return H2O_TOKEN_AUTHORITY;
+ break;
+ }
+ break;
+ case 11:
+ switch (name[10]) {
+ case 'r':
+ if (memcmp(name, "retry-afte", 10) == 0)
+ return H2O_TOKEN_RETRY_AFTER;
+ break;
+ }
+ break;
+ case 12:
+ switch (name[11]) {
+ case 'e':
+ if (memcmp(name, "content-typ", 11) == 0)
+ return H2O_TOKEN_CONTENT_TYPE;
+ break;
+ case 's':
+ if (memcmp(name, "max-forward", 11) == 0)
+ return H2O_TOKEN_MAX_FORWARDS;
+ break;
+ case 't':
+ if (memcmp(name, "cache-diges", 11) == 0)
+ return H2O_TOKEN_CACHE_DIGEST;
+ break;
+ }
+ break;
+ case 13:
+ switch (name[12]) {
+ case 'd':
+ if (memcmp(name, "last-modifie", 12) == 0)
+ return H2O_TOKEN_LAST_MODIFIED;
+ break;
+ case 'e':
+ if (memcmp(name, "content-rang", 12) == 0)
+ return H2O_TOKEN_CONTENT_RANGE;
+ break;
+ case 'h':
+ if (memcmp(name, "if-none-matc", 12) == 0)
+ return H2O_TOKEN_IF_NONE_MATCH;
+ break;
+ case 'l':
+ if (memcmp(name, "cache-contro", 12) == 0)
+ return H2O_TOKEN_CACHE_CONTROL;
+ if (memcmp(name, "x-reproxy-ur", 12) == 0)
+ return H2O_TOKEN_X_REPROXY_URL;
+ break;
+ case 'n':
+ if (memcmp(name, "authorizatio", 12) == 0)
+ return H2O_TOKEN_AUTHORIZATION;
+ break;
+ case 's':
+ if (memcmp(name, "accept-range", 12) == 0)
+ return H2O_TOKEN_ACCEPT_RANGES;
+ break;
+ }
+ break;
+ case 14:
+ switch (name[13]) {
+ case 'h':
+ if (memcmp(name, "content-lengt", 13) == 0)
+ return H2O_TOKEN_CONTENT_LENGTH;
+ break;
+ case 's':
+ if (memcmp(name, "http2-setting", 13) == 0)
+ return H2O_TOKEN_HTTP2_SETTINGS;
+ break;
+ case 't':
+ if (memcmp(name, "accept-charse", 13) == 0)
+ return H2O_TOKEN_ACCEPT_CHARSET;
+ break;
+ }
+ break;
+ case 15:
+ switch (name[14]) {
+ case 'e':
+ if (memcmp(name, "accept-languag", 14) == 0)
+ return H2O_TOKEN_ACCEPT_LANGUAGE;
+ break;
+ case 'g':
+ if (memcmp(name, "accept-encodin", 14) == 0)
+ return H2O_TOKEN_ACCEPT_ENCODING;
+ break;
+ case 'r':
+ if (memcmp(name, "x-forwarded-fo", 14) == 0)
+ return H2O_TOKEN_X_FORWARDED_FOR;
+ break;
+ case 't':
+ if (memcmp(name, "x-compress-hin", 14) == 0)
+ return H2O_TOKEN_X_COMPRESS_HINT;
+ break;
+ }
+ break;
+ case 16:
+ switch (name[15]) {
+ case 'e':
+ if (memcmp(name, "content-languag", 15) == 0)
+ return H2O_TOKEN_CONTENT_LANGUAGE;
+ if (memcmp(name, "www-authenticat", 15) == 0)
+ return H2O_TOKEN_WWW_AUTHENTICATE;
+ break;
+ case 'g':
+ if (memcmp(name, "content-encodin", 15) == 0)
+ return H2O_TOKEN_CONTENT_ENCODING;
+ break;
+ case 'n':
+ if (memcmp(name, "content-locatio", 15) == 0)
+ return H2O_TOKEN_CONTENT_LOCATION;
+ break;
+ }
+ break;
+ case 17:
+ switch (name[16]) {
+ case 'e':
+ if (memcmp(name, "if-modified-sinc", 16) == 0)
+ return H2O_TOKEN_IF_MODIFIED_SINCE;
+ break;
+ case 'g':
+ if (memcmp(name, "transfer-encodin", 16) == 0)
+ return H2O_TOKEN_TRANSFER_ENCODING;
+ break;
+ }
+ break;
+ case 18:
+ switch (name[17]) {
+ case 'e':
+ if (memcmp(name, "proxy-authenticat", 17) == 0)
+ return H2O_TOKEN_PROXY_AUTHENTICATE;
+ break;
+ }
+ break;
+ case 19:
+ switch (name[18]) {
+ case 'e':
+ if (memcmp(name, "if-unmodified-sinc", 18) == 0)
+ return H2O_TOKEN_IF_UNMODIFIED_SINCE;
+ break;
+ case 'n':
+ if (memcmp(name, "content-dispositio", 18) == 0)
+ return H2O_TOKEN_CONTENT_DISPOSITION;
+ if (memcmp(name, "proxy-authorizatio", 18) == 0)
+ return H2O_TOKEN_PROXY_AUTHORIZATION;
+ break;
+ }
+ break;
+ case 25:
+ switch (name[24]) {
+ case 'y':
+ if (memcmp(name, "strict-transport-securit", 24) == 0)
+ return H2O_TOKEN_STRICT_TRANSPORT_SECURITY;
+ break;
+ }
+ break;
+ case 27:
+ switch (name[26]) {
+ case 'n':
+ if (memcmp(name, "access-control-allow-origi", 26) == 0)
+ return H2O_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN;
+ break;
+ }
+ break;
+ }
+
+ return NULL;
+}
diff --git a/debian/vendor-h2o/lib/core/util.c b/debian/vendor-h2o/lib/core/util.c
new file mode 100644
index 0000000..50d2b24
--- /dev/null
+++ b/debian/vendor-h2o/lib/core/util.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku, Satoh Hiroh
+ *
+ * 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 <assert.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "h2o.h"
+#include "h2o/http1.h"
+#include "h2o/http2.h"
+
+struct st_h2o_accept_data_t {
+ h2o_accept_ctx_t *ctx;
+ h2o_socket_t *sock;
+ h2o_timeout_entry_t timeout;
+ h2o_memcached_req_t *async_resumption_get_req;
+ struct timeval connected_at;
+};
+
+static void on_accept_timeout(h2o_timeout_entry_t *entry);
+
+static struct st_h2o_accept_data_t *create_accept_data(h2o_accept_ctx_t *ctx, h2o_socket_t *sock, struct timeval connected_at)
+{
+ struct st_h2o_accept_data_t *data = h2o_mem_alloc(sizeof(*data));
+
+ data->ctx = ctx;
+ data->sock = sock;
+ data->timeout = (h2o_timeout_entry_t){0};
+ data->timeout.cb = on_accept_timeout;
+ h2o_timeout_link(ctx->ctx->loop, &ctx->ctx->handshake_timeout, &data->timeout);
+ data->async_resumption_get_req = NULL;
+ data->connected_at = connected_at;
+
+ sock->data = data;
+ return data;
+}
+
+static void free_accept_data(struct st_h2o_accept_data_t *data)
+{
+ assert(data->async_resumption_get_req == NULL);
+ h2o_timeout_unlink(&data->timeout);
+ free(data);
+}
+
+static struct {
+ h2o_memcached_context_t *memc;
+ unsigned expiration;
+} async_resumption_context;
+
+static void async_resumption_on_get(h2o_iovec_t session_data, void *_accept_data)
+{
+ struct st_h2o_accept_data_t *accept_data = _accept_data;
+ accept_data->async_resumption_get_req = NULL;
+ h2o_socket_ssl_resume_server_handshake(accept_data->sock, session_data);
+}
+
+static void async_resumption_get(h2o_socket_t *sock, h2o_iovec_t session_id)
+{
+ struct st_h2o_accept_data_t *data = sock->data;
+
+ data->async_resumption_get_req =
+ h2o_memcached_get(async_resumption_context.memc, data->ctx->libmemcached_receiver, session_id, async_resumption_on_get,
+ data, H2O_MEMCACHED_ENCODE_KEY | H2O_MEMCACHED_ENCODE_VALUE);
+}
+
+static void async_resumption_new(h2o_iovec_t session_id, h2o_iovec_t session_data)
+{
+ h2o_memcached_set(async_resumption_context.memc, session_id, session_data,
+ (uint32_t)time(NULL) + async_resumption_context.expiration,
+ H2O_MEMCACHED_ENCODE_KEY | H2O_MEMCACHED_ENCODE_VALUE);
+}
+
+void h2o_accept_setup_async_ssl_resumption(h2o_memcached_context_t *memc, unsigned expiration)
+{
+ async_resumption_context.memc = memc;
+ async_resumption_context.expiration = expiration;
+ h2o_socket_ssl_async_resumption_init(async_resumption_get, async_resumption_new);
+}
+
+void on_accept_timeout(h2o_timeout_entry_t *entry)
+{
+ /* TODO log */
+ struct st_h2o_accept_data_t *data = H2O_STRUCT_FROM_MEMBER(struct st_h2o_accept_data_t, timeout, entry);
+ if (data->async_resumption_get_req != NULL) {
+ h2o_memcached_cancel_get(async_resumption_context.memc, data->async_resumption_get_req);
+ data->async_resumption_get_req = NULL;
+ }
+ h2o_socket_t *sock = data->sock;
+ free_accept_data(data);
+ h2o_socket_close(sock);
+}
+
+static void on_ssl_handshake_complete(h2o_socket_t *sock, const char *err)
+{
+ struct st_h2o_accept_data_t *data = sock->data;
+ sock->data = NULL;
+
+ if (err != NULL) {
+ h2o_socket_close(sock);
+ goto Exit;
+ }
+
+ h2o_iovec_t proto = h2o_socket_ssl_get_selected_protocol(sock);
+ const h2o_iovec_t *ident;
+ for (ident = h2o_http2_alpn_protocols; ident->len != 0; ++ident) {
+ if (proto.len == ident->len && memcmp(proto.base, ident->base, proto.len) == 0) {
+ /* connect as http2 */
+ h2o_http2_accept(data->ctx, sock, data->connected_at);
+ goto Exit;
+ }
+ }
+ /* connect as http1 */
+ h2o_http1_accept(data->ctx, sock, data->connected_at);
+
+Exit:
+ free_accept_data(data);
+}
+
+static ssize_t parse_proxy_line(char *src, size_t len, struct sockaddr *sa, socklen_t *salen)
+{
+#define CHECK_EOF() \
+ if (p == end) \
+ return -2
+#define EXPECT_CHAR(ch) \
+ do { \
+ CHECK_EOF(); \
+ if (*p++ != ch) \
+ return -1; \
+ } while (0)
+#define SKIP_TO_WS() \
+ do { \
+ do { \
+ CHECK_EOF(); \
+ } while (*p++ != ' '); \
+ --p; \
+ } while (0)
+
+ char *p = src, *end = p + len;
+ void *addr;
+ in_port_t *port;
+
+ /* "PROXY "*/
+ EXPECT_CHAR('P');
+ EXPECT_CHAR('R');
+ EXPECT_CHAR('O');
+ EXPECT_CHAR('X');
+ EXPECT_CHAR('Y');
+ EXPECT_CHAR(' ');
+
+ /* "TCP[46] " */
+ CHECK_EOF();
+ if (*p++ != 'T') {
+ *salen = 0; /* indicate that no data has been obtained */
+ goto SkipToEOL;
+ }
+ EXPECT_CHAR('C');
+ EXPECT_CHAR('P');
+ CHECK_EOF();
+ switch (*p++) {
+ case '4':
+ *salen = sizeof(struct sockaddr_in);
+ memset(sa, 0, sizeof(struct sockaddr_in));
+ sa->sa_family = AF_INET;
+ addr = &((struct sockaddr_in *)sa)->sin_addr;
+ port = &((struct sockaddr_in *)sa)->sin_port;
+ break;
+ case '6':
+ *salen = sizeof(struct sockaddr_in6);
+ memset(sa, 0, sizeof(struct sockaddr_in6));
+ sa->sa_family = AF_INET6;
+ addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
+ port = &((struct sockaddr_in6 *)sa)->sin6_port;
+ break;
+ default:
+ return -1;
+ }
+ EXPECT_CHAR(' ');
+
+ /* parse peer address */
+ char *addr_start = p;
+ SKIP_TO_WS();
+ *p = '\0';
+ if (inet_pton(sa->sa_family, addr_start, addr) != 1)
+ return -1;
+ *p++ = ' ';
+
+ /* skip local address */
+ SKIP_TO_WS();
+ ++p;
+
+ /* parse peer port */
+ char *port_start = p;
+ SKIP_TO_WS();
+ *p = '\0';
+ unsigned short usval;
+ if (sscanf(port_start, "%hu", &usval) != 1)
+ return -1;
+ *port = htons(usval);
+ *p++ = ' ';
+
+SkipToEOL:
+ do {
+ CHECK_EOF();
+ } while (*p++ != '\r');
+ CHECK_EOF();
+ if (*p++ != '\n')
+ return -2;
+ return p - src;
+
+#undef CHECK_EOF
+#undef EXPECT_CHAR
+#undef SKIP_TO_WS
+}
+
+static void on_read_proxy_line(h2o_socket_t *sock, const char *err)
+{
+ struct st_h2o_accept_data_t *data = sock->data;
+
+ if (err != NULL) {
+ free_accept_data(data);
+ h2o_socket_close(sock);
+ return;
+ }
+
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ ssize_t r = parse_proxy_line(sock->input->bytes, sock->input->size, (void *)&addr, &addrlen);
+ switch (r) {
+ case -1: /* error, just pass the input to the next handler */
+ break;
+ case -2: /* incomplete */
+ return;
+ default:
+ h2o_buffer_consume(&sock->input, r);
+ if (addrlen != 0)
+ h2o_socket_setpeername(sock, (void *)&addr, addrlen);
+ break;
+ }
+
+ if (data->ctx->ssl_ctx != NULL) {
+ h2o_socket_ssl_handshake(sock, data->ctx->ssl_ctx, NULL, on_ssl_handshake_complete);
+ } else {
+ struct st_h2o_accept_data_t *data = sock->data;
+ sock->data = NULL;
+ h2o_http1_accept(data->ctx, sock, data->connected_at);
+ free_accept_data(data);
+ }
+}
+
+void h2o_accept(h2o_accept_ctx_t *ctx, h2o_socket_t *sock)
+{
+ struct timeval connected_at = *h2o_get_timestamp(ctx->ctx, NULL, NULL);
+
+ if (ctx->expect_proxy_line || ctx->ssl_ctx != NULL) {
+ create_accept_data(ctx, sock, connected_at);
+ if (ctx->expect_proxy_line) {
+ h2o_socket_read_start(sock, on_read_proxy_line);
+ } else {
+ h2o_socket_ssl_handshake(sock, ctx->ssl_ctx, NULL, on_ssl_handshake_complete);
+ }
+ } else {
+ h2o_http1_accept(ctx, sock, connected_at);
+ }
+}
+
+size_t h2o_stringify_protocol_version(char *dst, int version)
+{
+ char *p = dst;
+
+ if (version < 0x200) {
+ assert(version <= 0x109);
+#define PREFIX "HTTP/1."
+ memcpy(p, PREFIX, sizeof(PREFIX) - 1);
+ p += sizeof(PREFIX) - 1;
+#undef PREFIX
+ *p++ = '0' + (version & 0xff);
+ } else {
+#define PROTO "HTTP/2"
+ memcpy(p, PROTO, sizeof(PROTO) - 1);
+ p += sizeof(PROTO) - 1;
+#undef PROTO
+ }
+
+ *p = '\0';
+ return p - dst;
+}
+
+size_t h2o_stringify_proxy_header(h2o_conn_t *conn, char *buf)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ size_t strlen;
+ uint16_t peerport;
+ char *dst = buf;
+
+ if ((sslen = conn->callbacks->get_peername(conn, (void *)&ss)) == 0)
+ goto Unknown;
+ switch (ss.ss_family) {
+ case AF_INET:
+ memcpy(dst, "PROXY TCP4 ", 11);
+ dst += 11;
+ break;
+ case AF_INET6:
+ memcpy(dst, "PROXY TCP6 ", 11);
+ dst += 11;
+ break;
+ default:
+ goto Unknown;
+ }
+ if ((strlen = h2o_socket_getnumerichost((void *)&ss, sslen, dst)) == SIZE_MAX)
+ goto Unknown;
+ dst += strlen;
+ *dst++ = ' ';
+
+ peerport = h2o_socket_getport((void *)&ss);
+
+ if ((sslen = conn->callbacks->get_sockname(conn, (void *)&ss)) == 0)
+ goto Unknown;
+ if ((strlen = h2o_socket_getnumerichost((void *)&ss, sslen, dst)) == SIZE_MAX)
+ goto Unknown;
+ dst += strlen;
+ *dst++ = ' ';
+
+ dst += sprintf(dst, "%" PRIu16 " %" PRIu16 "\r\n", peerport, (uint16_t)h2o_socket_getport((void *)&ss));
+
+ return dst - buf;
+
+Unknown:
+ memcpy(buf, "PROXY UNKNOWN\r\n", 15);
+ return 15;
+}
+
+static void push_one_path(h2o_mem_pool_t *pool, h2o_iovec_vector_t *paths_to_push, h2o_iovec_t url, h2o_iovec_t base_path,
+ const h2o_url_scheme_t *input_scheme, h2o_iovec_t input_authority, const h2o_url_scheme_t *base_scheme,
+ h2o_iovec_t *base_authority)
+{
+ h2o_url_t parsed, resolved;
+
+ /* check the authority, and extract absolute path */
+ if (h2o_url_parse_relative(url.base, url.len, &parsed) != 0)
+ return;
+
+ /* fast-path for abspath form */
+ if (base_scheme == NULL && parsed.scheme == NULL && parsed.authority.base == NULL && url.len != 0 && url.base[0] == '/') {
+ h2o_vector_reserve(pool, paths_to_push, paths_to_push->size + 1);
+ paths_to_push->entries[paths_to_push->size++] = h2o_strdup(pool, url.base, url.len);
+ return;
+ }
+
+ /* check scheme and authority if given URL contains either of the two, or if base is specified */
+ h2o_url_t base = {input_scheme, input_authority, {NULL}, base_path, 65535};
+ if (base_scheme != NULL) {
+ base.scheme = base_scheme;
+ base.authority = *base_authority;
+ }
+ h2o_url_resolve(pool, &base, &parsed, &resolved);
+ if (input_scheme != resolved.scheme)
+ return;
+ if (!h2o_lcstris(input_authority.base, input_authority.len, resolved.authority.base, resolved.authority.len))
+ return;
+
+ h2o_vector_reserve(pool, paths_to_push, paths_to_push->size + 1);
+ paths_to_push->entries[paths_to_push->size++] = resolved.path;
+}
+
+h2o_iovec_vector_t h2o_extract_push_path_from_link_header(h2o_mem_pool_t *pool, const char *value, size_t value_len,
+ h2o_iovec_t base_path, const h2o_url_scheme_t *input_scheme,
+ h2o_iovec_t input_authority, const h2o_url_scheme_t *base_scheme,
+ h2o_iovec_t *base_authority, h2o_iovec_t *filtered_value)
+{
+ h2o_iovec_vector_t paths_to_push = {NULL};
+ h2o_iovec_t iter = h2o_iovec_init(value, value_len), token_value;
+ const char *token;
+ size_t token_len;
+ *filtered_value = h2o_iovec_init(NULL, 0);
+
+#define PUSH_FILTERED_VALUE(s, e) \
+ do { \
+ if (filtered_value->len != 0) { \
+ memcpy(filtered_value->base + filtered_value->len, ", ", 2); \
+ filtered_value->len += 2; \
+ } \
+ memcpy(filtered_value->base + filtered_value->len, (s), (e) - (s)); \
+ filtered_value->len += (e) - (s); \
+ } while (0)
+
+ /* extract URL values from Link: </pushed.css>; rel=preload */
+ do {
+ if ((token = h2o_next_token(&iter, ';', &token_len, NULL)) == NULL)
+ break;
+ /* first element should be <URL> */
+ if (!(token_len >= 2 && token[0] == '<' && token[token_len - 1] == '>'))
+ break;
+ h2o_iovec_t url_with_brackets = h2o_iovec_init(token, token_len);
+ /* find rel=preload */
+ int preload = 0, nopush = 0, push_only = 0;
+ while ((token = h2o_next_token(&iter, ';', &token_len, &token_value)) != NULL &&
+ !h2o_memis(token, token_len, H2O_STRLIT(","))) {
+ if (h2o_lcstris(token, token_len, H2O_STRLIT("rel")) &&
+ h2o_lcstris(token_value.base, token_value.len, H2O_STRLIT("preload"))) {
+ preload++;
+ } else if (h2o_lcstris(token, token_len, H2O_STRLIT("nopush"))) {
+ nopush++;
+ } else if (h2o_lcstris(token, token_len, H2O_STRLIT("x-http2-push-only"))) {
+ push_only++;
+ }
+ }
+ /* push the path */
+ if (!nopush && preload)
+ push_one_path(pool, &paths_to_push, h2o_iovec_init(url_with_brackets.base + 1, url_with_brackets.len - 2), base_path,
+ input_scheme, input_authority, base_scheme, base_authority);
+ /* store the elements that needs to be preserved to filtered_value */
+ if (push_only) {
+ if (filtered_value->base == NULL) {
+ /* the max. size of filtered_value would be x2 in the worst case, when "," is converted to ", " */
+ filtered_value->base = h2o_mem_alloc_pool(pool, value_len * 2);
+ const char *prev_comma = h2o_memrchr(value, ',', url_with_brackets.base - value);
+ if (prev_comma != NULL)
+ PUSH_FILTERED_VALUE(value, prev_comma);
+ }
+ } else if (filtered_value->base != NULL) {
+ PUSH_FILTERED_VALUE(url_with_brackets.base, token != NULL ? token : value + value_len);
+ }
+ } while (token != NULL);
+
+ if (filtered_value->base != NULL) {
+ if (token != NULL)
+ PUSH_FILTERED_VALUE(token, value + value_len);
+ } else {
+ *filtered_value = h2o_iovec_init(value, value_len);
+ }
+
+ return paths_to_push;
+
+#undef PUSH_FILTERED_VALUE
+}
+
+int h2o_get_compressible_types(const h2o_headers_t *headers)
+{
+ size_t header_index;
+ int compressible_types = 0;
+
+ for (header_index = 0; header_index != headers->size; ++header_index) {
+ const h2o_header_t *header = headers->entries + header_index;
+ if (H2O_UNLIKELY(header->name == &H2O_TOKEN_ACCEPT_ENCODING->buf)) {
+ h2o_iovec_t iter = h2o_iovec_init(header->value.base, header->value.len);
+ const char *token = NULL;
+ size_t token_len = 0;
+ while ((token = h2o_next_token(&iter, ',', &token_len, NULL)) != NULL) {
+ if (h2o_lcstris(token, token_len, H2O_STRLIT("gzip")))
+ compressible_types |= H2O_COMPRESSIBLE_GZIP;
+ else if (h2o_lcstris(token, token_len, H2O_STRLIT("br")))
+ compressible_types |= H2O_COMPRESSIBLE_BROTLI;
+ }
+ }
+ }
+
+ return compressible_types;
+}
+
+h2o_iovec_t h2o_build_destination(h2o_req_t *req, const char *prefix, size_t prefix_len, int use_path_normalized)
+{
+ h2o_iovec_t parts[4];
+ size_t num_parts = 0;
+ int conf_ends_with_slash = req->pathconf->path.base[req->pathconf->path.len - 1] == '/';
+ int prefix_ends_with_slash = prefix[prefix_len - 1] == '/';
+
+ /* destination starts with given prefix */
+ parts[num_parts++] = h2o_iovec_init(prefix, prefix_len);
+
+ /* make adjustments depending on the trailing slashes */
+ if (conf_ends_with_slash != prefix_ends_with_slash) {
+ if (conf_ends_with_slash) {
+ parts[num_parts++] = h2o_iovec_init(H2O_STRLIT("/"));
+ } else {
+ if (req->path_normalized.len != req->pathconf->path.len)
+ parts[num_parts - 1].len -= 1;
+ }
+ }
+
+ /* append suffix path and query */
+
+ if (use_path_normalized) {
+ parts[num_parts++] = h2o_uri_escape(&req->pool, req->path_normalized.base + req->pathconf->path.len,
+ req->path_normalized.len - req->pathconf->path.len, "/@:");
+ if (req->query_at != SIZE_MAX) {
+ parts[num_parts++] = h2o_iovec_init(req->path.base + req->query_at, req->path.len - req->query_at);
+ }
+ } else {
+ if (req->path.len > 1) {
+ /*
+ * When proxying, we want to modify the input URL as little
+ * as possible. We use norm_indexes to find the start of
+ * the path we want to forward.
+ */
+ size_t next_unnormalized;
+ if (req->norm_indexes && req->pathconf->path.len > 1) {
+ next_unnormalized = req->norm_indexes[req->pathconf->path.len - 1];
+ } else {
+ next_unnormalized = req->pathconf->path.len;
+ }
+
+ /*
+ * Special case: the input path didn't have any '/' including the first,
+ * so the first character is actually found at '0'
+ */
+ if (req->path.base[0] != '/' && next_unnormalized == 1) {
+ next_unnormalized = 0;
+ }
+ parts[num_parts++] = (h2o_iovec_t){req->path.base + next_unnormalized, req->path.len - next_unnormalized};
+ }
+ }
+
+ return h2o_concat_list(&req->pool, parts, num_parts);
+}
+
+/* h2-14 and h2-16 are kept for backwards compatibility, as they are often used */
+#define ALPN_ENTRY(s) \
+ { \
+ H2O_STRLIT(s) \
+ }
+#define ALPN_PROTOCOLS_CORE ALPN_ENTRY("h2"), ALPN_ENTRY("h2-16"), ALPN_ENTRY("h2-14")
+#define NPN_PROTOCOLS_CORE \
+ "\x02" \
+ "h2" \
+ "\x05" \
+ "h2-16" \
+ "\x05" \
+ "h2-14"
+
+static const h2o_iovec_t http2_alpn_protocols[] = {ALPN_PROTOCOLS_CORE, {NULL}};
+const h2o_iovec_t *h2o_http2_alpn_protocols = http2_alpn_protocols;
+
+static const h2o_iovec_t alpn_protocols[] = {ALPN_PROTOCOLS_CORE, {H2O_STRLIT("http/1.1")}, {NULL}};
+const h2o_iovec_t *h2o_alpn_protocols = alpn_protocols;
+
+const char *h2o_http2_npn_protocols = NPN_PROTOCOLS_CORE;
+const char *h2o_npn_protocols = NPN_PROTOCOLS_CORE "\x08"
+ "http/1.1";
+
+uint64_t h2o_connection_id = 0;