Coverage Report

Created: 2025-08-26 06:35

/src/h2o/lib/handler/headers.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2015 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
24
struct st_headers_filter_t {
25
    h2o_filter_t super;
26
    h2o_headers_command_t *cmds;
27
};
28
29
struct st_headers_early_hints_handler_t {
30
    h2o_handler_t super;
31
    h2o_headers_command_t *cmds;
32
};
33
34
struct st_headers_early_hints_sender_t {
35
    h2o_req_t *req;
36
    h2o_headers_command_t *cmds;
37
    h2o_timer_t deferred_timeout_entry;
38
};
39
40
static void on_setup_ostream(h2o_filter_t *_self, h2o_req_t *req, h2o_ostream_t **slot)
41
0
{
42
0
    struct st_headers_filter_t *self = (void *)_self;
43
0
    h2o_headers_command_t *cmd;
44
45
0
    for (cmd = self->cmds; cmd->cmd != H2O_HEADERS_CMD_NULL; ++cmd) {
46
0
        if (cmd->when != H2O_HEADERS_CMD_WHEN_EARLY)
47
0
            h2o_rewrite_headers(&req->pool, &req->res.headers, cmd);
48
0
    }
49
50
0
    h2o_setup_next_ostream(req, slot);
51
0
}
52
53
static void on_informational(h2o_filter_t *_self, h2o_req_t *req)
54
0
{
55
0
    struct st_headers_filter_t *self = (void *)_self;
56
0
    h2o_headers_command_t *cmd;
57
58
0
    if (req->res.status != 103)
59
0
        return;
60
61
0
    for (cmd = self->cmds; cmd->cmd != H2O_HEADERS_CMD_NULL; ++cmd) {
62
0
        if (cmd->when != H2O_HEADERS_CMD_WHEN_FINAL)
63
0
            h2o_rewrite_headers(&req->pool, &req->res.headers, cmd);
64
0
    }
65
0
}
66
67
static void on_sender_deferred_timeout(h2o_timer_t *entry)
68
0
{
69
0
    struct st_headers_early_hints_sender_t *sender =
70
0
        H2O_STRUCT_FROM_MEMBER(struct st_headers_early_hints_sender_t, deferred_timeout_entry, entry);
71
72
0
    if (sender->req->res.status != 0)
73
0
        return;
74
75
0
    sender->req->res.status = 103;
76
77
    /* expect on_informational will be called and applies headers commands */
78
0
    h2o_send_informational(sender->req);
79
0
}
80
81
static void on_sender_dispose(void *_sender)
82
0
{
83
0
    struct st_headers_early_hints_sender_t *sender = (struct st_headers_early_hints_sender_t *)_sender;
84
0
    if (h2o_timer_is_linked(&sender->deferred_timeout_entry))
85
0
        h2o_timer_unlink(&sender->deferred_timeout_entry);
86
0
}
87
88
static int on_req(h2o_handler_t *_handler, h2o_req_t *req)
89
0
{
90
0
    struct st_headers_early_hints_handler_t *handler = (void *)_handler;
91
92
0
    struct st_headers_early_hints_sender_t *sender = h2o_mem_alloc_shared(&req->pool, sizeof(*sender), on_sender_dispose);
93
0
    sender->req = req;
94
0
    sender->cmds = handler->cmds;
95
0
    h2o_timer_init(&sender->deferred_timeout_entry, on_sender_deferred_timeout);
96
0
    h2o_timer_link(req->conn->ctx->loop, 0, &sender->deferred_timeout_entry);
97
98
0
    return -1;
99
0
}
100
101
static int requires_early_hints_handler(struct st_headers_filter_t *self)
102
0
{
103
0
    h2o_headers_command_t *cmd;
104
0
    for (cmd = self->cmds; cmd->cmd != H2O_HEADERS_CMD_NULL; ++cmd) {
105
0
        if (cmd->cmd != H2O_HEADERS_CMD_UNSET && cmd->when != H2O_HEADERS_CMD_WHEN_FINAL)
106
0
            return 1;
107
0
    }
108
0
    return 0;
109
0
}
110
111
void h2o_headers_register(h2o_pathconf_t *pathconf, h2o_headers_command_t *cmds)
112
0
{
113
0
    struct st_headers_filter_t *self = (void *)h2o_create_filter(pathconf, sizeof(*self));
114
115
0
    self->super.on_setup_ostream = on_setup_ostream;
116
0
    self->super.on_informational = on_informational;
117
0
    self->cmds = cmds;
118
119
0
    if (requires_early_hints_handler(self)) {
120
0
        struct st_headers_early_hints_handler_t *handler = (void *)h2o_create_handler(pathconf, sizeof(*handler));
121
0
        handler->cmds = cmds;
122
0
        handler->super.on_req = on_req;
123
124
        /* move this handler to first */
125
0
        memmove(pathconf->handlers.entries + 1, pathconf->handlers.entries,
126
0
                sizeof(h2o_handler_t *) * (pathconf->handlers.size - 1));
127
0
        pathconf->handlers.entries[0] = &handler->super;
128
0
    }
129
0
}
130
131
int h2o_headers_is_prohibited_name(const h2o_token_t *token)
132
0
{
133
    /* prohibit connection-specific headers */
134
0
    if (token == H2O_TOKEN_CONNECTION || token == H2O_TOKEN_CONTENT_LENGTH || token == H2O_TOKEN_TRANSFER_ENCODING)
135
0
        return 1;
136
    /* prohibit headers added at protocol layer */
137
0
    if (token == H2O_TOKEN_DATE || token == H2O_TOKEN_SERVER)
138
0
        return 1;
139
    /* all others are permitted */
140
0
    return 0;
141
0
}