/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 | } |