Coverage Report

Created: 2025-07-18 06:42

/src/h2o/lib/handler/configurator/errordoc.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2015-2016 DeNA Co., Ltd., Kazuho Oku
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#include "h2o.h"
23
#include "h2o/configurator.h"
24
25
struct errordoc_configurator_t {
26
    h2o_configurator_t super;
27
    h2o_mem_pool_t pool;
28
    H2O_VECTOR(h2o_errordoc_t) * vars, _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1];
29
};
30
31
static int register_errordoc(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *hash)
32
0
{
33
0
    struct errordoc_configurator_t *self = (void *)cmd->configurator;
34
0
    yoml_t **url_node, **status_nodes;
35
0
    size_t i, j, num_status;
36
37
    /* extract the nodes to handle */
38
0
    if (h2o_configurator_parse_mapping(cmd, hash, "url:s,status:*", NULL, &url_node, &status_nodes) != 0)
39
0
        return -1;
40
0
    switch ((*status_nodes)->type) {
41
0
    case YOML_TYPE_SCALAR:
42
0
        num_status = 1;
43
0
        break;
44
0
    case YOML_TYPE_SEQUENCE:
45
0
        if ((*status_nodes)->data.sequence.size == 0) {
46
0
            h2o_configurator_errprintf(cmd, *status_nodes, "status cannot be an empty sequence");
47
0
            return -1;
48
0
        }
49
0
        num_status = (*status_nodes)->data.sequence.size;
50
0
        status_nodes = (*status_nodes)->data.sequence.elements;
51
0
        break;
52
0
    default:
53
0
        h2o_configurator_errprintf(cmd, *status_nodes, "status must be a 3-digit scalar or a sequence of 3-digit scalars");
54
0
        return -1;
55
0
    }
56
57
    /* convert list of status_nodes (in string) to list of 3-digit codes */
58
0
    int *status_codes = alloca(sizeof(*status_codes) * num_status);
59
0
    for (i = 0; i != num_status; ++i) {
60
0
        if (h2o_configurator_scanf(cmd, status_nodes[i], "%d", &status_codes[i]) != 0)
61
0
            return -1;
62
0
        if (!(400 <= status_codes[i] && status_codes[i] <= 599)) {
63
0
            h2o_configurator_errprintf(cmd, status_nodes[i], "status must be within range of 400 to 599");
64
0
            return -1;
65
0
        }
66
        /* check the scanned status hasn't already appeared */
67
0
        for (j = 0; j != i; ++j) {
68
0
            if (status_codes[j] == status_codes[i]) {
69
0
                h2o_configurator_errprintf(cmd, status_nodes[i], "status %d appears multiple times", status_codes[i]);
70
0
                return -1;
71
0
            }
72
0
        }
73
0
    }
74
75
0
    h2o_iovec_t url = h2o_strdup(&self->pool, (*url_node)->data.scalar, SIZE_MAX);
76
0
    for (i = 0; i != num_status; ++i) {
77
        /* register */
78
0
        h2o_vector_reserve(&self->pool, self->vars, self->vars->size + 1);
79
0
        h2o_errordoc_t *errordoc = self->vars->entries + self->vars->size++;
80
0
        errordoc->status = status_codes[i];
81
0
        errordoc->url = url;
82
0
    }
83
84
0
    return 0;
85
0
}
86
87
static int on_config_errordoc(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
88
0
{
89
0
    switch (node->type) {
90
0
    case YOML_TYPE_SEQUENCE: {
91
0
        size_t i;
92
0
        for (i = 0; i != node->data.sequence.size; ++i) {
93
0
            yoml_t *e = node->data.sequence.elements[i];
94
0
            if (e->type != YOML_TYPE_MAPPING) {
95
0
                h2o_configurator_errprintf(cmd, e, "element must be a mapping");
96
0
                return -1;
97
0
            }
98
0
            if (register_errordoc(cmd, ctx, e) != 0)
99
0
                return -1;
100
0
        }
101
0
        return 0;
102
0
    }
103
0
    case YOML_TYPE_MAPPING:
104
0
        return register_errordoc(cmd, ctx, node);
105
0
    default:
106
0
        break;
107
0
    }
108
109
0
    h2o_configurator_errprintf(cmd, node, "argument must be either of: sequence, mapping");
110
0
    return -1;
111
0
}
112
113
static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node)
114
0
{
115
0
    struct errordoc_configurator_t *self = (void *)_self;
116
117
0
    if (self->vars == self->_vars_stack) {
118
        /* entering global level */
119
0
        h2o_mem_init_pool(&self->pool);
120
0
    }
121
122
    /* copy vars */
123
0
    memset(&self->vars[1], 0, sizeof(self->vars[1]));
124
0
    h2o_vector_reserve(&self->pool, &self->vars[1], self->vars[0].size);
125
0
    h2o_memcpy(self->vars[1].entries, self->vars[0].entries, sizeof(self->vars[0].entries[0]) * self->vars[0].size);
126
0
    self->vars[1].size = self->vars[0].size;
127
128
0
    ++self->vars;
129
0
    return 0;
130
0
}
131
132
static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node)
133
0
{
134
0
    struct errordoc_configurator_t *self = (void *)_self;
135
136
0
    if (ctx->pathconf != NULL && !h2o_configurator_at_extension_level(ctx) && self->vars->size != 0)
137
0
        h2o_errordoc_register(ctx->pathconf, self->vars->entries, self->vars->size);
138
139
0
    --self->vars;
140
0
    if (self->vars == self->_vars_stack) {
141
        /* exitting global level */
142
0
        h2o_mem_clear_pool(&self->pool);
143
0
    }
144
145
0
    return 0;
146
0
}
147
148
void h2o_errordoc_register_configurator(h2o_globalconf_t *conf)
149
0
{
150
0
    struct errordoc_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c));
151
152
    /* set default vars */
153
0
    c->vars = c->_vars_stack;
154
155
    /* setup handlers */
156
0
    c->super.enter = on_config_enter;
157
0
    c->super.exit = on_config_exit;
158
159
    /* reproxy: ON | OFF */
160
0
    h2o_configurator_define_command(&c->super, "error-doc",
161
0
                                    H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_HOST | H2O_CONFIGURATOR_FLAG_PATH,
162
0
                                    on_config_errordoc);
163
0
}