Coverage Report

Created: 2026-09-01 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h2o/lib/handler/proxy.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2014,2015 DeNA Co., Ltd., Masahiro Nagano
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 <sys/un.h>
23
#include "h2o.h"
24
#include "h2o/socketpool.h"
25
#include "h2o/balancer.h"
26
27
struct rp_handler_t {
28
    h2o_handler_t super;
29
    h2o_socketpool_t *sockpool;
30
    h2o_proxy_config_vars_t config;
31
};
32
33
struct rp_handler_context_t {
34
    h2o_httpclient_connection_pool_t connpool;
35
    h2o_httpclient_ctx_t *client_ctx;
36
};
37
38
static int on_req(h2o_handler_t *_self, h2o_req_t *req)
39
3.91k
{
40
3.91k
    struct rp_handler_t *self = (void *)_self;
41
3.91k
    h2o_req_overrides_t *overrides = h2o_mem_alloc_pool(&req->pool, *overrides, 1);
42
3.91k
    struct rp_handler_context_t *handler_ctx = h2o_context_get_handler_context(req->conn->ctx, &self->super);
43
44
    /* setup overrides */
45
3.91k
    *overrides = (h2o_req_overrides_t){NULL};
46
3.91k
    overrides->connpool = &handler_ctx->connpool;
47
3.91k
    overrides->location_rewrite.path_prefix = req->pathconf->path;
48
3.91k
    overrides->use_proxy_protocol = self->config.use_proxy_protocol;
49
3.91k
    overrides->client_ctx = handler_ctx->client_ctx;
50
3.91k
    overrides->headers_cmds = self->config.headers_cmds;
51
3.91k
    overrides->proxy_preserve_host = self->config.preserve_host;
52
3.91k
    overrides->proxy_expect_mode = self->config.expect_mode;
53
3.91k
    overrides->forward_close_connection = self->config.forward_close_connection;
54
55
    /* request reprocess (note: path may become an empty string, to which one of the target URL within the socketpool will be
56
     * right-padded when lib/core/proxy connects to upstream; see #1563) */
57
3.91k
    h2o_iovec_t path = h2o_build_destination(req, NULL, 0, 0);
58
3.91k
    h2o_reprocess_request(req, req->method, req->scheme, req->authority, path, overrides, 0);
59
60
3.91k
    return 0;
61
3.91k
}
62
63
static void on_context_init(h2o_handler_t *_self, h2o_context_t *ctx)
64
3
{
65
3
    struct rp_handler_t *self = (void *)_self;
66
67
    /* use the loop of first context for handling socketpool timeouts */
68
3
    h2o_socketpool_register_loop(self->sockpool, ctx->loop);
69
70
3
    struct rp_handler_context_t *handler_ctx = h2o_mem_alloc(sizeof(*handler_ctx));
71
3
    memset(handler_ctx, 0, sizeof(*handler_ctx));
72
3
    h2o_httpclient_connection_pool_init(&handler_ctx->connpool, self->sockpool);
73
3
    h2o_context_set_handler_context(ctx, &self->super, handler_ctx);
74
75
    /* setup a specific client context only if we need to */
76
3
    if (ctx->globalconf->proxy.io_timeout == self->config.io_timeout &&
77
0
        ctx->globalconf->proxy.connect_timeout == self->config.connect_timeout &&
78
0
        ctx->globalconf->proxy.first_byte_timeout == self->config.first_byte_timeout &&
79
0
        ctx->globalconf->proxy.keepalive_timeout == self->config.keepalive_timeout &&
80
0
        ctx->globalconf->proxy.max_buffer_size == self->config.max_buffer_size &&
81
0
        ctx->globalconf->proxy.protocol_ratio.http2 == self->config.protocol_ratio.http2 &&
82
0
        ctx->globalconf->proxy.protocol_ratio.http3 == self->config.protocol_ratio.http3 &&
83
0
        ctx->globalconf->proxy.http3.ecn == self->config.http3.ecn && !self->config.tunnel_enabled)
84
0
        return;
85
86
3
    h2o_httpclient_ctx_t *client_ctx = h2o_mem_alloc(sizeof(*ctx));
87
3
    *client_ctx = (h2o_httpclient_ctx_t){
88
3
        .loop = ctx->loop,
89
3
        .getaddr_receiver = &ctx->receivers.hostinfo_getaddr,
90
3
        .io_timeout = self->config.io_timeout,
91
3
        .connect_timeout = self->config.connect_timeout,
92
3
        .first_byte_timeout = self->config.first_byte_timeout,
93
3
        .keepalive_timeout = self->config.keepalive_timeout,
94
3
        .max_buffer_size = self->config.max_buffer_size,
95
3
        .tunnel_enabled = self->config.tunnel_enabled,
96
3
        .protocol_selector = {.ratio = self->config.protocol_ratio},
97
3
        .force_cleartext_http2 = self->config.http2.force_cleartext,
98
3
        .http2 =
99
3
            {
100
3
                .latency_optimization = ctx->globalconf->http2.latency_optimization, /* TODO provide config knob, or disable? */
101
3
                .max_concurrent_streams = self->config.http2.max_concurrent_streams,
102
3
            },
103
3
        .http3 = self->config.protocol_ratio.http3 != 0
104
3
                     ? h2o_create_proxy_http3_context(ctx, self->sockpool->_ssl_ctx, self->config.http3.ecn,
105
0
                                                      ctx->globalconf->http3.use_gso)
106
3
                     : NULL,
107
3
    };
108
109
3
    handler_ctx->client_ctx = client_ctx;
110
3
}
111
112
static void on_context_dispose(h2o_handler_t *_self, h2o_context_t *ctx)
113
0
{
114
0
    struct rp_handler_t *self = (void *)_self;
115
0
    struct rp_handler_context_t *handler_ctx = h2o_context_get_handler_context(ctx, &self->super);
116
117
0
    if (handler_ctx->client_ctx != NULL) {
118
0
        if (handler_ctx->client_ctx->http3 != NULL)
119
0
            h2o_destroy_proxy_http3_context(handler_ctx->client_ctx->http3);
120
0
        free(handler_ctx->client_ctx);
121
0
    }
122
123
0
    h2o_socketpool_unregister_loop(self->sockpool, ctx->loop);
124
0
}
125
126
static void on_handler_dispose(h2o_handler_t *_self)
127
0
{
128
0
    struct rp_handler_t *self = (void *)_self;
129
130
0
    h2o_socketpool_dispose(self->sockpool);
131
0
    free(self->sockpool);
132
0
}
133
134
void h2o_proxy_register_reverse_proxy(h2o_pathconf_t *pathconf, h2o_proxy_config_vars_t *config, h2o_socketpool_t *sockpool)
135
3
{
136
3
    assert(config->max_buffer_size != 0);
137
138
3
    struct rp_handler_t *self = (void *)h2o_create_handler(pathconf, sizeof(*self));
139
140
3
    self->super.on_context_init = on_context_init;
141
3
    self->super.on_context_dispose = on_context_dispose;
142
3
    self->super.dispose = on_handler_dispose;
143
3
    self->super.on_req = on_req;
144
3
    self->super.supports_request_streaming = 1;
145
3
    self->super.handles_expect = config->expect_mode == H2O_PROXY_EXPECT_FORWARD;
146
3
    self->config = *config;
147
3
    self->sockpool = sockpool;
148
3
}