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