Coverage Report

Created: 2025-10-13 07:14

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 "picotls.h"
24
#include "picotls/openssl.h"
25
#include "h2o.h"
26
#include "h2o/socketpool.h"
27
#include "h2o/balancer.h"
28
#include "h2o/http3_server.h"
29
#if H2O_USE_FUSION
30
#include "picotls/fusion.h"
31
#endif
32
33
struct rp_handler_t {
34
    h2o_handler_t super;
35
    h2o_socketpool_t *sockpool;
36
    h2o_proxy_config_vars_t config;
37
};
38
39
struct rp_handler_context_t {
40
    h2o_httpclient_connection_pool_t connpool;
41
    h2o_httpclient_ctx_t *client_ctx;
42
};
43
44
static int on_req(h2o_handler_t *_self, h2o_req_t *req)
45
3.71k
{
46
3.71k
    struct rp_handler_t *self = (void *)_self;
47
3.71k
    h2o_req_overrides_t *overrides = h2o_mem_alloc_pool(&req->pool, *overrides, 1);
48
3.71k
    struct rp_handler_context_t *handler_ctx = h2o_context_get_handler_context(req->conn->ctx, &self->super);
49
50
    /* setup overrides */
51
3.71k
    *overrides = (h2o_req_overrides_t){NULL};
52
3.71k
    overrides->connpool = &handler_ctx->connpool;
53
3.71k
    overrides->location_rewrite.path_prefix = req->pathconf->path;
54
3.71k
    overrides->use_proxy_protocol = self->config.use_proxy_protocol;
55
3.71k
    overrides->client_ctx = handler_ctx->client_ctx;
56
3.71k
    overrides->headers_cmds = self->config.headers_cmds;
57
3.71k
    overrides->proxy_preserve_host = self->config.preserve_host;
58
3.71k
    overrides->proxy_expect_mode = self->config.expect_mode;
59
3.71k
    overrides->forward_close_connection = self->config.forward_close_connection;
60
61
    /* request reprocess (note: path may become an empty string, to which one of the target URL within the socketpool will be
62
     * right-padded when lib/core/proxy connects to upstream; see #1563) */
63
3.71k
    h2o_iovec_t path = h2o_build_destination(req, NULL, 0, 0);
64
3.71k
    h2o_reprocess_request(req, req->method, req->scheme, req->authority, path, overrides, 0);
65
66
3.71k
    return 0;
67
3.71k
}
68
69
static h2o_http3client_ctx_t *create_http3_context(h2o_context_t *ctx, SSL_CTX *ssl_ctx, int use_gso)
70
0
{
71
#if H2O_USE_LIBUV
72
    h2o_fatal("no HTTP/3 support for libuv");
73
#else
74
75
0
    h2o_http3client_ctx_t *h3ctx = h2o_mem_alloc(sizeof(*h3ctx));
76
77
    /* tls (TODO inherit session cache setting of ssl_ctx) */
78
0
    h3ctx->tls = (ptls_context_t){
79
0
        .random_bytes = ptls_openssl_random_bytes,
80
0
        .get_time = &ptls_get_time,
81
0
        .key_exchanges = ptls_openssl_key_exchanges,
82
0
        .cipher_suites = ptls_openssl_cipher_suites,
83
0
    };
84
0
    h3ctx->verify_cert = (ptls_openssl_verify_certificate_t){};
85
0
    if ((SSL_CTX_get_verify_mode(ssl_ctx) & SSL_VERIFY_PEER) != 0) {
86
0
        X509_STORE *store;
87
0
        if ((store = SSL_CTX_get_cert_store(ssl_ctx)) == NULL)
88
0
            h2o_fatal("failed to obtain the store to be used for server certificate verification");
89
0
        ptls_openssl_init_verify_certificate(&h3ctx->verify_cert, store);
90
0
        h3ctx->tls.verify_certificate = &h3ctx->verify_cert.super;
91
0
    }
92
0
    quicly_amend_ptls_context(&h3ctx->tls);
93
94
    /* quic */
95
0
    h3ctx->quic = quicly_spec_context;
96
0
    h3ctx->quic.tls = &h3ctx->tls;
97
0
    h3ctx->quic.transport_params.max_streams_uni = 10;
98
0
    uint8_t cid_key[PTLS_SHA256_DIGEST_SIZE];
99
0
    ptls_openssl_random_bytes(cid_key, sizeof(cid_key));
100
0
    h3ctx->quic.cid_encryptor = quicly_new_default_cid_encryptor(
101
#if H2O_USE_FUSION
102
        ptls_fusion_is_supported_by_cpu() ? &ptls_fusion_quiclb :
103
#endif
104
0
                                          &ptls_openssl_quiclb,
105
0
        &ptls_openssl_aes128ecb, &ptls_openssl_sha256, ptls_iovec_init(cid_key, sizeof(cid_key)));
106
0
    ptls_clear_memory(cid_key, sizeof(cid_key));
107
0
    h3ctx->quic.stream_open = &h2o_httpclient_http3_on_stream_open;
108
109
    /* http3 client-specific fields */
110
0
    h3ctx->max_frame_payload_size = h2o_http3_calc_min_flow_control_size(H2O_MAX_REQLEN); /* same maximum for HEADERS frame in both
111
                                                                                           directions */
112
113
    /* h2o server http3 integration */
114
0
    int sockfd;
115
0
    if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
116
0
        char buf[256];
117
0
        h2o_fatal("failed to open UDP socket: %s", h2o_strerror_r(errno, buf, sizeof(buf)));
118
0
    }
119
0
    struct sockaddr_in sin = {};
120
0
    if (bind(sockfd, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
121
0
        char buf[256];
122
0
        h2o_fatal("failed to bind default address to UDP socket: %s", h2o_strerror_r(errno, buf, sizeof(buf)));
123
0
    }
124
0
    h2o_socket_t *sock = h2o_evloop_socket_create(ctx->loop, sockfd, H2O_SOCKET_FLAG_DONT_READ);
125
0
    h2o_http3_server_init_context(ctx, &h3ctx->h3, ctx->loop, sock, &h3ctx->quic, &ctx->http3.next_cid, NULL,
126
0
                                  h2o_httpclient_http3_notify_connection_update, use_gso);
127
128
0
    h3ctx->load_session = NULL; /* TODO reuse session? */
129
130
0
    return h3ctx;
131
0
#endif
132
0
}
133
134
static void destroy_http3_context(h2o_http3client_ctx_t *h3ctx)
135
0
{
136
0
    h2o_quic_dispose_context(&h3ctx->h3);
137
0
    quicly_free_default_cid_encryptor(h3ctx->quic.cid_encryptor);
138
0
    if (h3ctx->verify_cert.super.cb != NULL)
139
0
        ptls_openssl_dispose_verify_certificate(&h3ctx->verify_cert);
140
0
    free(h3ctx);
141
0
}
142
143
static void on_context_init(h2o_handler_t *_self, h2o_context_t *ctx)
144
3
{
145
3
    struct rp_handler_t *self = (void *)_self;
146
147
    /* use the loop of first context for handling socketpool timeouts */
148
3
    h2o_socketpool_register_loop(self->sockpool, ctx->loop);
149
150
3
    struct rp_handler_context_t *handler_ctx = h2o_mem_alloc(sizeof(*handler_ctx));
151
3
    memset(handler_ctx, 0, sizeof(*handler_ctx));
152
3
    h2o_httpclient_connection_pool_init(&handler_ctx->connpool, self->sockpool);
153
3
    h2o_context_set_handler_context(ctx, &self->super, handler_ctx);
154
155
    /* setup a specific client context only if we need to */
156
3
    if (ctx->globalconf->proxy.io_timeout == self->config.io_timeout &&
157
0
        ctx->globalconf->proxy.connect_timeout == self->config.connect_timeout &&
158
0
        ctx->globalconf->proxy.first_byte_timeout == self->config.first_byte_timeout &&
159
0
        ctx->globalconf->proxy.keepalive_timeout == self->config.keepalive_timeout &&
160
0
        ctx->globalconf->proxy.max_buffer_size == self->config.max_buffer_size &&
161
0
        ctx->globalconf->proxy.protocol_ratio.http2 == self->config.protocol_ratio.http2 &&
162
0
        ctx->globalconf->proxy.protocol_ratio.http3 == self->config.protocol_ratio.http3 && !self->config.tunnel_enabled)
163
0
        return;
164
165
3
    h2o_httpclient_ctx_t *client_ctx = h2o_mem_alloc(sizeof(*ctx));
166
3
    *client_ctx = (h2o_httpclient_ctx_t){
167
3
        .loop = ctx->loop,
168
3
        .getaddr_receiver = &ctx->receivers.hostinfo_getaddr,
169
3
        .io_timeout = self->config.io_timeout,
170
3
        .connect_timeout = self->config.connect_timeout,
171
3
        .first_byte_timeout = self->config.first_byte_timeout,
172
3
        .keepalive_timeout = self->config.keepalive_timeout,
173
3
        .max_buffer_size = self->config.max_buffer_size,
174
3
        .tunnel_enabled = self->config.tunnel_enabled,
175
3
        .protocol_selector = {.ratio = self->config.protocol_ratio},
176
3
        .force_cleartext_http2 = self->config.http2.force_cleartext,
177
3
        .http2 =
178
3
            {
179
3
                .latency_optimization = ctx->globalconf->http2.latency_optimization, /* TODO provide config knob, or disable? */
180
3
                .max_concurrent_streams = self->config.http2.max_concurrent_streams,
181
3
            },
182
3
        .http3 = self->config.protocol_ratio.http3 != 0
183
3
                     ? create_http3_context(ctx, self->sockpool->_ssl_ctx, ctx->globalconf->http3.use_gso)
184
3
                     : NULL,
185
3
    };
186
187
3
    handler_ctx->client_ctx = client_ctx;
188
3
}
189
190
static void on_context_dispose(h2o_handler_t *_self, h2o_context_t *ctx)
191
0
{
192
0
    struct rp_handler_t *self = (void *)_self;
193
0
    struct rp_handler_context_t *handler_ctx = h2o_context_get_handler_context(ctx, &self->super);
194
195
0
    if (handler_ctx->client_ctx != NULL) {
196
0
        if (handler_ctx->client_ctx->http3 != NULL)
197
0
            destroy_http3_context(handler_ctx->client_ctx->http3);
198
0
        free(handler_ctx->client_ctx);
199
0
    }
200
201
0
    h2o_socketpool_unregister_loop(self->sockpool, ctx->loop);
202
0
}
203
204
static void on_handler_dispose(h2o_handler_t *_self)
205
0
{
206
0
    struct rp_handler_t *self = (void *)_self;
207
208
0
    h2o_socketpool_dispose(self->sockpool);
209
0
    free(self->sockpool);
210
0
}
211
212
void h2o_proxy_register_reverse_proxy(h2o_pathconf_t *pathconf, h2o_proxy_config_vars_t *config, h2o_socketpool_t *sockpool)
213
3
{
214
3
    assert(config->max_buffer_size != 0);
215
216
3
    struct rp_handler_t *self = (void *)h2o_create_handler(pathconf, sizeof(*self));
217
218
3
    self->super.on_context_init = on_context_init;
219
3
    self->super.on_context_dispose = on_context_dispose;
220
3
    self->super.dispose = on_handler_dispose;
221
3
    self->super.on_req = on_req;
222
3
    self->super.supports_request_streaming = 1;
223
3
    self->super.handles_expect = config->expect_mode == H2O_PROXY_EXPECT_FORWARD;
224
3
    self->config = *config;
225
3
    self->sockpool = sockpool;
226
3
}