1*6236dae4SAndroid Build Coastguard Worker /***************************************************************************
2*6236dae4SAndroid Build Coastguard Worker * _ _ ____ _
3*6236dae4SAndroid Build Coastguard Worker * Project ___| | | | _ \| |
4*6236dae4SAndroid Build Coastguard Worker * / __| | | | |_) | |
5*6236dae4SAndroid Build Coastguard Worker * | (__| |_| | _ <| |___
6*6236dae4SAndroid Build Coastguard Worker * \___|\___/|_| \_\_____|
7*6236dae4SAndroid Build Coastguard Worker *
8*6236dae4SAndroid Build Coastguard Worker * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
9*6236dae4SAndroid Build Coastguard Worker *
10*6236dae4SAndroid Build Coastguard Worker * This software is licensed as described in the file COPYING, which
11*6236dae4SAndroid Build Coastguard Worker * you should have received as part of this distribution. The terms
12*6236dae4SAndroid Build Coastguard Worker * are also available at https://curl.se/docs/copyright.html.
13*6236dae4SAndroid Build Coastguard Worker *
14*6236dae4SAndroid Build Coastguard Worker * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15*6236dae4SAndroid Build Coastguard Worker * copies of the Software, and permit persons to whom the Software is
16*6236dae4SAndroid Build Coastguard Worker * furnished to do so, under the terms of the COPYING file.
17*6236dae4SAndroid Build Coastguard Worker *
18*6236dae4SAndroid Build Coastguard Worker * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19*6236dae4SAndroid Build Coastguard Worker * KIND, either express or implied.
20*6236dae4SAndroid Build Coastguard Worker *
21*6236dae4SAndroid Build Coastguard Worker * SPDX-License-Identifier: curl
22*6236dae4SAndroid Build Coastguard Worker *
23*6236dae4SAndroid Build Coastguard Worker ***************************************************************************/
24*6236dae4SAndroid Build Coastguard Worker
25*6236dae4SAndroid Build Coastguard Worker #include "curl_setup.h"
26*6236dae4SAndroid Build Coastguard Worker
27*6236dae4SAndroid Build Coastguard Worker #if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY)
28*6236dae4SAndroid Build Coastguard Worker
29*6236dae4SAndroid Build Coastguard Worker #include <nghttp2/nghttp2.h>
30*6236dae4SAndroid Build Coastguard Worker #include "urldata.h"
31*6236dae4SAndroid Build Coastguard Worker #include "cfilters.h"
32*6236dae4SAndroid Build Coastguard Worker #include "connect.h"
33*6236dae4SAndroid Build Coastguard Worker #include "curl_trc.h"
34*6236dae4SAndroid Build Coastguard Worker #include "bufq.h"
35*6236dae4SAndroid Build Coastguard Worker #include "dynbuf.h"
36*6236dae4SAndroid Build Coastguard Worker #include "dynhds.h"
37*6236dae4SAndroid Build Coastguard Worker #include "http1.h"
38*6236dae4SAndroid Build Coastguard Worker #include "http2.h"
39*6236dae4SAndroid Build Coastguard Worker #include "http_proxy.h"
40*6236dae4SAndroid Build Coastguard Worker #include "multiif.h"
41*6236dae4SAndroid Build Coastguard Worker #include "sendf.h"
42*6236dae4SAndroid Build Coastguard Worker #include "cf-h2-proxy.h"
43*6236dae4SAndroid Build Coastguard Worker
44*6236dae4SAndroid Build Coastguard Worker /* The last 3 #include files should be in this order */
45*6236dae4SAndroid Build Coastguard Worker #include "curl_printf.h"
46*6236dae4SAndroid Build Coastguard Worker #include "curl_memory.h"
47*6236dae4SAndroid Build Coastguard Worker #include "memdebug.h"
48*6236dae4SAndroid Build Coastguard Worker
49*6236dae4SAndroid Build Coastguard Worker #define PROXY_H2_CHUNK_SIZE (16*1024)
50*6236dae4SAndroid Build Coastguard Worker
51*6236dae4SAndroid Build Coastguard Worker #define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024)
52*6236dae4SAndroid Build Coastguard Worker #define H2_TUNNEL_WINDOW_SIZE (10 * 1024 * 1024)
53*6236dae4SAndroid Build Coastguard Worker
54*6236dae4SAndroid Build Coastguard Worker #define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE)
55*6236dae4SAndroid Build Coastguard Worker #define PROXY_H2_NW_SEND_CHUNKS 1
56*6236dae4SAndroid Build Coastguard Worker
57*6236dae4SAndroid Build Coastguard Worker #define H2_TUNNEL_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE)
58*6236dae4SAndroid Build Coastguard Worker #define H2_TUNNEL_SEND_CHUNKS ((128 * 1024) / PROXY_H2_CHUNK_SIZE)
59*6236dae4SAndroid Build Coastguard Worker
60*6236dae4SAndroid Build Coastguard Worker
61*6236dae4SAndroid Build Coastguard Worker typedef enum {
62*6236dae4SAndroid Build Coastguard Worker H2_TUNNEL_INIT, /* init/default/no tunnel state */
63*6236dae4SAndroid Build Coastguard Worker H2_TUNNEL_CONNECT, /* CONNECT request is being send */
64*6236dae4SAndroid Build Coastguard Worker H2_TUNNEL_RESPONSE, /* CONNECT response received completely */
65*6236dae4SAndroid Build Coastguard Worker H2_TUNNEL_ESTABLISHED,
66*6236dae4SAndroid Build Coastguard Worker H2_TUNNEL_FAILED
67*6236dae4SAndroid Build Coastguard Worker } h2_tunnel_state;
68*6236dae4SAndroid Build Coastguard Worker
69*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream {
70*6236dae4SAndroid Build Coastguard Worker struct http_resp *resp;
71*6236dae4SAndroid Build Coastguard Worker struct bufq recvbuf;
72*6236dae4SAndroid Build Coastguard Worker struct bufq sendbuf;
73*6236dae4SAndroid Build Coastguard Worker char *authority;
74*6236dae4SAndroid Build Coastguard Worker int32_t stream_id;
75*6236dae4SAndroid Build Coastguard Worker uint32_t error;
76*6236dae4SAndroid Build Coastguard Worker h2_tunnel_state state;
77*6236dae4SAndroid Build Coastguard Worker BIT(has_final_response);
78*6236dae4SAndroid Build Coastguard Worker BIT(closed);
79*6236dae4SAndroid Build Coastguard Worker BIT(reset);
80*6236dae4SAndroid Build Coastguard Worker };
81*6236dae4SAndroid Build Coastguard Worker
tunnel_stream_init(struct Curl_cfilter * cf,struct tunnel_stream * ts)82*6236dae4SAndroid Build Coastguard Worker static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
83*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream *ts)
84*6236dae4SAndroid Build Coastguard Worker {
85*6236dae4SAndroid Build Coastguard Worker const char *hostname;
86*6236dae4SAndroid Build Coastguard Worker int port;
87*6236dae4SAndroid Build Coastguard Worker bool ipv6_ip;
88*6236dae4SAndroid Build Coastguard Worker CURLcode result;
89*6236dae4SAndroid Build Coastguard Worker
90*6236dae4SAndroid Build Coastguard Worker ts->state = H2_TUNNEL_INIT;
91*6236dae4SAndroid Build Coastguard Worker ts->stream_id = -1;
92*6236dae4SAndroid Build Coastguard Worker Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS,
93*6236dae4SAndroid Build Coastguard Worker BUFQ_OPT_SOFT_LIMIT);
94*6236dae4SAndroid Build Coastguard Worker Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS);
95*6236dae4SAndroid Build Coastguard Worker
96*6236dae4SAndroid Build Coastguard Worker result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
97*6236dae4SAndroid Build Coastguard Worker if(result)
98*6236dae4SAndroid Build Coastguard Worker return result;
99*6236dae4SAndroid Build Coastguard Worker
100*6236dae4SAndroid Build Coastguard Worker ts->authority = /* host:port with IPv6 support */
101*6236dae4SAndroid Build Coastguard Worker aprintf("%s%s%s:%d", ipv6_ip ? "[":"", hostname,
102*6236dae4SAndroid Build Coastguard Worker ipv6_ip ? "]" : "", port);
103*6236dae4SAndroid Build Coastguard Worker if(!ts->authority)
104*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
105*6236dae4SAndroid Build Coastguard Worker
106*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
107*6236dae4SAndroid Build Coastguard Worker }
108*6236dae4SAndroid Build Coastguard Worker
tunnel_stream_clear(struct tunnel_stream * ts)109*6236dae4SAndroid Build Coastguard Worker static void tunnel_stream_clear(struct tunnel_stream *ts)
110*6236dae4SAndroid Build Coastguard Worker {
111*6236dae4SAndroid Build Coastguard Worker Curl_http_resp_free(ts->resp);
112*6236dae4SAndroid Build Coastguard Worker Curl_bufq_free(&ts->recvbuf);
113*6236dae4SAndroid Build Coastguard Worker Curl_bufq_free(&ts->sendbuf);
114*6236dae4SAndroid Build Coastguard Worker Curl_safefree(ts->authority);
115*6236dae4SAndroid Build Coastguard Worker memset(ts, 0, sizeof(*ts));
116*6236dae4SAndroid Build Coastguard Worker ts->state = H2_TUNNEL_INIT;
117*6236dae4SAndroid Build Coastguard Worker }
118*6236dae4SAndroid Build Coastguard Worker
h2_tunnel_go_state(struct Curl_cfilter * cf,struct tunnel_stream * ts,h2_tunnel_state new_state,struct Curl_easy * data)119*6236dae4SAndroid Build Coastguard Worker static void h2_tunnel_go_state(struct Curl_cfilter *cf,
120*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream *ts,
121*6236dae4SAndroid Build Coastguard Worker h2_tunnel_state new_state,
122*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data)
123*6236dae4SAndroid Build Coastguard Worker {
124*6236dae4SAndroid Build Coastguard Worker (void)cf;
125*6236dae4SAndroid Build Coastguard Worker
126*6236dae4SAndroid Build Coastguard Worker if(ts->state == new_state)
127*6236dae4SAndroid Build Coastguard Worker return;
128*6236dae4SAndroid Build Coastguard Worker /* leaving this one */
129*6236dae4SAndroid Build Coastguard Worker switch(ts->state) {
130*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_CONNECT:
131*6236dae4SAndroid Build Coastguard Worker data->req.ignorebody = FALSE;
132*6236dae4SAndroid Build Coastguard Worker break;
133*6236dae4SAndroid Build Coastguard Worker default:
134*6236dae4SAndroid Build Coastguard Worker break;
135*6236dae4SAndroid Build Coastguard Worker }
136*6236dae4SAndroid Build Coastguard Worker /* entering this one */
137*6236dae4SAndroid Build Coastguard Worker switch(new_state) {
138*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_INIT:
139*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id);
140*6236dae4SAndroid Build Coastguard Worker tunnel_stream_clear(ts);
141*6236dae4SAndroid Build Coastguard Worker break;
142*6236dae4SAndroid Build Coastguard Worker
143*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_CONNECT:
144*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] new tunnel state 'connect'", ts->stream_id);
145*6236dae4SAndroid Build Coastguard Worker ts->state = H2_TUNNEL_CONNECT;
146*6236dae4SAndroid Build Coastguard Worker break;
147*6236dae4SAndroid Build Coastguard Worker
148*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_RESPONSE:
149*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] new tunnel state 'response'", ts->stream_id);
150*6236dae4SAndroid Build Coastguard Worker ts->state = H2_TUNNEL_RESPONSE;
151*6236dae4SAndroid Build Coastguard Worker break;
152*6236dae4SAndroid Build Coastguard Worker
153*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_ESTABLISHED:
154*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'",
155*6236dae4SAndroid Build Coastguard Worker ts->stream_id);
156*6236dae4SAndroid Build Coastguard Worker infof(data, "CONNECT phase completed");
157*6236dae4SAndroid Build Coastguard Worker data->state.authproxy.done = TRUE;
158*6236dae4SAndroid Build Coastguard Worker data->state.authproxy.multipass = FALSE;
159*6236dae4SAndroid Build Coastguard Worker FALLTHROUGH();
160*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_FAILED:
161*6236dae4SAndroid Build Coastguard Worker if(new_state == H2_TUNNEL_FAILED)
162*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] new tunnel state 'failed'", ts->stream_id);
163*6236dae4SAndroid Build Coastguard Worker ts->state = new_state;
164*6236dae4SAndroid Build Coastguard Worker /* If a proxy-authorization header was used for the proxy, then we should
165*6236dae4SAndroid Build Coastguard Worker make sure that it is not accidentally used for the document request
166*6236dae4SAndroid Build Coastguard Worker after we have connected. So let's free and clear it here. */
167*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->state.aptr.proxyuserpwd);
168*6236dae4SAndroid Build Coastguard Worker break;
169*6236dae4SAndroid Build Coastguard Worker }
170*6236dae4SAndroid Build Coastguard Worker }
171*6236dae4SAndroid Build Coastguard Worker
172*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx {
173*6236dae4SAndroid Build Coastguard Worker nghttp2_session *h2;
174*6236dae4SAndroid Build Coastguard Worker /* The easy handle used in the current filter call, cleared at return */
175*6236dae4SAndroid Build Coastguard Worker struct cf_call_data call_data;
176*6236dae4SAndroid Build Coastguard Worker
177*6236dae4SAndroid Build Coastguard Worker struct bufq inbufq; /* network receive buffer */
178*6236dae4SAndroid Build Coastguard Worker struct bufq outbufq; /* network send buffer */
179*6236dae4SAndroid Build Coastguard Worker
180*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream tunnel; /* our tunnel CONNECT stream */
181*6236dae4SAndroid Build Coastguard Worker int32_t goaway_error;
182*6236dae4SAndroid Build Coastguard Worker int32_t last_stream_id;
183*6236dae4SAndroid Build Coastguard Worker BIT(conn_closed);
184*6236dae4SAndroid Build Coastguard Worker BIT(rcvd_goaway);
185*6236dae4SAndroid Build Coastguard Worker BIT(sent_goaway);
186*6236dae4SAndroid Build Coastguard Worker BIT(nw_out_blocked);
187*6236dae4SAndroid Build Coastguard Worker };
188*6236dae4SAndroid Build Coastguard Worker
189*6236dae4SAndroid Build Coastguard Worker /* How to access `call_data` from a cf_h2 filter */
190*6236dae4SAndroid Build Coastguard Worker #undef CF_CTX_CALL_DATA
191*6236dae4SAndroid Build Coastguard Worker #define CF_CTX_CALL_DATA(cf) \
192*6236dae4SAndroid Build Coastguard Worker ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data
193*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx * ctx)194*6236dae4SAndroid Build Coastguard Worker static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx)
195*6236dae4SAndroid Build Coastguard Worker {
196*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save = ctx->call_data;
197*6236dae4SAndroid Build Coastguard Worker
198*6236dae4SAndroid Build Coastguard Worker if(ctx->h2) {
199*6236dae4SAndroid Build Coastguard Worker nghttp2_session_del(ctx->h2);
200*6236dae4SAndroid Build Coastguard Worker }
201*6236dae4SAndroid Build Coastguard Worker Curl_bufq_free(&ctx->inbufq);
202*6236dae4SAndroid Build Coastguard Worker Curl_bufq_free(&ctx->outbufq);
203*6236dae4SAndroid Build Coastguard Worker tunnel_stream_clear(&ctx->tunnel);
204*6236dae4SAndroid Build Coastguard Worker memset(ctx, 0, sizeof(*ctx));
205*6236dae4SAndroid Build Coastguard Worker ctx->call_data = save;
206*6236dae4SAndroid Build Coastguard Worker }
207*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx * ctx)208*6236dae4SAndroid Build Coastguard Worker static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx)
209*6236dae4SAndroid Build Coastguard Worker {
210*6236dae4SAndroid Build Coastguard Worker if(ctx) {
211*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_ctx_clear(ctx);
212*6236dae4SAndroid Build Coastguard Worker free(ctx);
213*6236dae4SAndroid Build Coastguard Worker }
214*6236dae4SAndroid Build Coastguard Worker }
215*6236dae4SAndroid Build Coastguard Worker
drain_tunnel(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_stream * tunnel)216*6236dae4SAndroid Build Coastguard Worker static void drain_tunnel(struct Curl_cfilter *cf,
217*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
218*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream *tunnel)
219*6236dae4SAndroid Build Coastguard Worker {
220*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
221*6236dae4SAndroid Build Coastguard Worker unsigned char bits;
222*6236dae4SAndroid Build Coastguard Worker
223*6236dae4SAndroid Build Coastguard Worker (void)cf;
224*6236dae4SAndroid Build Coastguard Worker bits = CURL_CSELECT_IN;
225*6236dae4SAndroid Build Coastguard Worker if(!tunnel->closed && !tunnel->reset &&
226*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_empty(&ctx->tunnel.sendbuf))
227*6236dae4SAndroid Build Coastguard Worker bits |= CURL_CSELECT_OUT;
228*6236dae4SAndroid Build Coastguard Worker if(data->state.select_bits != bits) {
229*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
230*6236dae4SAndroid Build Coastguard Worker tunnel->stream_id, bits);
231*6236dae4SAndroid Build Coastguard Worker data->state.select_bits = bits;
232*6236dae4SAndroid Build Coastguard Worker Curl_expire(data, 0, EXPIRE_RUN_NOW);
233*6236dae4SAndroid Build Coastguard Worker }
234*6236dae4SAndroid Build Coastguard Worker }
235*6236dae4SAndroid Build Coastguard Worker
proxy_nw_in_reader(void * reader_ctx,unsigned char * buf,size_t buflen,CURLcode * err)236*6236dae4SAndroid Build Coastguard Worker static ssize_t proxy_nw_in_reader(void *reader_ctx,
237*6236dae4SAndroid Build Coastguard Worker unsigned char *buf, size_t buflen,
238*6236dae4SAndroid Build Coastguard Worker CURLcode *err)
239*6236dae4SAndroid Build Coastguard Worker {
240*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf = reader_ctx;
241*6236dae4SAndroid Build Coastguard Worker ssize_t nread;
242*6236dae4SAndroid Build Coastguard Worker
243*6236dae4SAndroid Build Coastguard Worker if(cf) {
244*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data = CF_DATA_CURRENT(cf);
245*6236dae4SAndroid Build Coastguard Worker nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err);
246*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] nw_in_reader(len=%zu) -> %zd, %d",
247*6236dae4SAndroid Build Coastguard Worker buflen, nread, *err);
248*6236dae4SAndroid Build Coastguard Worker }
249*6236dae4SAndroid Build Coastguard Worker else {
250*6236dae4SAndroid Build Coastguard Worker nread = 0;
251*6236dae4SAndroid Build Coastguard Worker }
252*6236dae4SAndroid Build Coastguard Worker return nread;
253*6236dae4SAndroid Build Coastguard Worker }
254*6236dae4SAndroid Build Coastguard Worker
proxy_h2_nw_out_writer(void * writer_ctx,const unsigned char * buf,size_t buflen,CURLcode * err)255*6236dae4SAndroid Build Coastguard Worker static ssize_t proxy_h2_nw_out_writer(void *writer_ctx,
256*6236dae4SAndroid Build Coastguard Worker const unsigned char *buf, size_t buflen,
257*6236dae4SAndroid Build Coastguard Worker CURLcode *err)
258*6236dae4SAndroid Build Coastguard Worker {
259*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf = writer_ctx;
260*6236dae4SAndroid Build Coastguard Worker ssize_t nwritten;
261*6236dae4SAndroid Build Coastguard Worker
262*6236dae4SAndroid Build Coastguard Worker if(cf) {
263*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data = CF_DATA_CURRENT(cf);
264*6236dae4SAndroid Build Coastguard Worker nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen,
265*6236dae4SAndroid Build Coastguard Worker FALSE, err);
266*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d",
267*6236dae4SAndroid Build Coastguard Worker buflen, nwritten, *err);
268*6236dae4SAndroid Build Coastguard Worker }
269*6236dae4SAndroid Build Coastguard Worker else {
270*6236dae4SAndroid Build Coastguard Worker nwritten = 0;
271*6236dae4SAndroid Build Coastguard Worker }
272*6236dae4SAndroid Build Coastguard Worker return nwritten;
273*6236dae4SAndroid Build Coastguard Worker }
274*6236dae4SAndroid Build Coastguard Worker
proxy_h2_client_new(struct Curl_cfilter * cf,nghttp2_session_callbacks * cbs)275*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_client_new(struct Curl_cfilter *cf,
276*6236dae4SAndroid Build Coastguard Worker nghttp2_session_callbacks *cbs)
277*6236dae4SAndroid Build Coastguard Worker {
278*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
279*6236dae4SAndroid Build Coastguard Worker nghttp2_option *o;
280*6236dae4SAndroid Build Coastguard Worker
281*6236dae4SAndroid Build Coastguard Worker int rc = nghttp2_option_new(&o);
282*6236dae4SAndroid Build Coastguard Worker if(rc)
283*6236dae4SAndroid Build Coastguard Worker return rc;
284*6236dae4SAndroid Build Coastguard Worker /* We handle window updates ourself to enforce buffer limits */
285*6236dae4SAndroid Build Coastguard Worker nghttp2_option_set_no_auto_window_update(o, 1);
286*6236dae4SAndroid Build Coastguard Worker #if NGHTTP2_VERSION_NUM >= 0x013200
287*6236dae4SAndroid Build Coastguard Worker /* with 1.50.0 */
288*6236dae4SAndroid Build Coastguard Worker /* turn off RFC 9113 leading and trailing white spaces validation against
289*6236dae4SAndroid Build Coastguard Worker HTTP field value. */
290*6236dae4SAndroid Build Coastguard Worker nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
291*6236dae4SAndroid Build Coastguard Worker #endif
292*6236dae4SAndroid Build Coastguard Worker rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o);
293*6236dae4SAndroid Build Coastguard Worker nghttp2_option_del(o);
294*6236dae4SAndroid Build Coastguard Worker return rc;
295*6236dae4SAndroid Build Coastguard Worker }
296*6236dae4SAndroid Build Coastguard Worker
297*6236dae4SAndroid Build Coastguard Worker static ssize_t on_session_send(nghttp2_session *h2,
298*6236dae4SAndroid Build Coastguard Worker const uint8_t *buf, size_t blen,
299*6236dae4SAndroid Build Coastguard Worker int flags, void *userp);
300*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_on_frame_recv(nghttp2_session *session,
301*6236dae4SAndroid Build Coastguard Worker const nghttp2_frame *frame,
302*6236dae4SAndroid Build Coastguard Worker void *userp);
303*6236dae4SAndroid Build Coastguard Worker #ifndef CURL_DISABLE_VERBOSE_STRINGS
304*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_on_frame_send(nghttp2_session *session,
305*6236dae4SAndroid Build Coastguard Worker const nghttp2_frame *frame,
306*6236dae4SAndroid Build Coastguard Worker void *userp);
307*6236dae4SAndroid Build Coastguard Worker #endif
308*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_on_stream_close(nghttp2_session *session,
309*6236dae4SAndroid Build Coastguard Worker int32_t stream_id,
310*6236dae4SAndroid Build Coastguard Worker uint32_t error_code, void *userp);
311*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_on_header(nghttp2_session *session,
312*6236dae4SAndroid Build Coastguard Worker const nghttp2_frame *frame,
313*6236dae4SAndroid Build Coastguard Worker const uint8_t *name, size_t namelen,
314*6236dae4SAndroid Build Coastguard Worker const uint8_t *value, size_t valuelen,
315*6236dae4SAndroid Build Coastguard Worker uint8_t flags,
316*6236dae4SAndroid Build Coastguard Worker void *userp);
317*6236dae4SAndroid Build Coastguard Worker static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
318*6236dae4SAndroid Build Coastguard Worker int32_t stream_id,
319*6236dae4SAndroid Build Coastguard Worker const uint8_t *mem, size_t len, void *userp);
320*6236dae4SAndroid Build Coastguard Worker
321*6236dae4SAndroid Build Coastguard Worker /*
322*6236dae4SAndroid Build Coastguard Worker * Initialize the cfilter context
323*6236dae4SAndroid Build Coastguard Worker */
cf_h2_proxy_ctx_init(struct Curl_cfilter * cf,struct Curl_easy * data)324*6236dae4SAndroid Build Coastguard Worker static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
325*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data)
326*6236dae4SAndroid Build Coastguard Worker {
327*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
328*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OUT_OF_MEMORY;
329*6236dae4SAndroid Build Coastguard Worker nghttp2_session_callbacks *cbs = NULL;
330*6236dae4SAndroid Build Coastguard Worker int rc;
331*6236dae4SAndroid Build Coastguard Worker
332*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(!ctx->h2);
333*6236dae4SAndroid Build Coastguard Worker memset(&ctx->tunnel, 0, sizeof(ctx->tunnel));
334*6236dae4SAndroid Build Coastguard Worker
335*6236dae4SAndroid Build Coastguard Worker Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS);
336*6236dae4SAndroid Build Coastguard Worker Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS);
337*6236dae4SAndroid Build Coastguard Worker
338*6236dae4SAndroid Build Coastguard Worker if(tunnel_stream_init(cf, &ctx->tunnel))
339*6236dae4SAndroid Build Coastguard Worker goto out;
340*6236dae4SAndroid Build Coastguard Worker
341*6236dae4SAndroid Build Coastguard Worker rc = nghttp2_session_callbacks_new(&cbs);
342*6236dae4SAndroid Build Coastguard Worker if(rc) {
343*6236dae4SAndroid Build Coastguard Worker failf(data, "Couldn't initialize nghttp2 callbacks");
344*6236dae4SAndroid Build Coastguard Worker goto out;
345*6236dae4SAndroid Build Coastguard Worker }
346*6236dae4SAndroid Build Coastguard Worker
347*6236dae4SAndroid Build Coastguard Worker nghttp2_session_callbacks_set_send_callback(cbs, on_session_send);
348*6236dae4SAndroid Build Coastguard Worker nghttp2_session_callbacks_set_on_frame_recv_callback(
349*6236dae4SAndroid Build Coastguard Worker cbs, proxy_h2_on_frame_recv);
350*6236dae4SAndroid Build Coastguard Worker #ifndef CURL_DISABLE_VERBOSE_STRINGS
351*6236dae4SAndroid Build Coastguard Worker nghttp2_session_callbacks_set_on_frame_send_callback(cbs,
352*6236dae4SAndroid Build Coastguard Worker proxy_h2_on_frame_send);
353*6236dae4SAndroid Build Coastguard Worker #endif
354*6236dae4SAndroid Build Coastguard Worker nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
355*6236dae4SAndroid Build Coastguard Worker cbs, tunnel_recv_callback);
356*6236dae4SAndroid Build Coastguard Worker nghttp2_session_callbacks_set_on_stream_close_callback(
357*6236dae4SAndroid Build Coastguard Worker cbs, proxy_h2_on_stream_close);
358*6236dae4SAndroid Build Coastguard Worker nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header);
359*6236dae4SAndroid Build Coastguard Worker
360*6236dae4SAndroid Build Coastguard Worker /* The nghttp2 session is not yet setup, do it */
361*6236dae4SAndroid Build Coastguard Worker rc = proxy_h2_client_new(cf, cbs);
362*6236dae4SAndroid Build Coastguard Worker if(rc) {
363*6236dae4SAndroid Build Coastguard Worker failf(data, "Couldn't initialize nghttp2");
364*6236dae4SAndroid Build Coastguard Worker goto out;
365*6236dae4SAndroid Build Coastguard Worker }
366*6236dae4SAndroid Build Coastguard Worker
367*6236dae4SAndroid Build Coastguard Worker {
368*6236dae4SAndroid Build Coastguard Worker nghttp2_settings_entry iv[3];
369*6236dae4SAndroid Build Coastguard Worker
370*6236dae4SAndroid Build Coastguard Worker iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
371*6236dae4SAndroid Build Coastguard Worker iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
372*6236dae4SAndroid Build Coastguard Worker iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
373*6236dae4SAndroid Build Coastguard Worker iv[1].value = H2_TUNNEL_WINDOW_SIZE;
374*6236dae4SAndroid Build Coastguard Worker iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
375*6236dae4SAndroid Build Coastguard Worker iv[2].value = 0;
376*6236dae4SAndroid Build Coastguard Worker rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3);
377*6236dae4SAndroid Build Coastguard Worker if(rc) {
378*6236dae4SAndroid Build Coastguard Worker failf(data, "nghttp2_submit_settings() failed: %s(%d)",
379*6236dae4SAndroid Build Coastguard Worker nghttp2_strerror(rc), rc);
380*6236dae4SAndroid Build Coastguard Worker result = CURLE_HTTP2;
381*6236dae4SAndroid Build Coastguard Worker goto out;
382*6236dae4SAndroid Build Coastguard Worker }
383*6236dae4SAndroid Build Coastguard Worker }
384*6236dae4SAndroid Build Coastguard Worker
385*6236dae4SAndroid Build Coastguard Worker rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
386*6236dae4SAndroid Build Coastguard Worker PROXY_HTTP2_HUGE_WINDOW_SIZE);
387*6236dae4SAndroid Build Coastguard Worker if(rc) {
388*6236dae4SAndroid Build Coastguard Worker failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
389*6236dae4SAndroid Build Coastguard Worker nghttp2_strerror(rc), rc);
390*6236dae4SAndroid Build Coastguard Worker result = CURLE_HTTP2;
391*6236dae4SAndroid Build Coastguard Worker goto out;
392*6236dae4SAndroid Build Coastguard Worker }
393*6236dae4SAndroid Build Coastguard Worker
394*6236dae4SAndroid Build Coastguard Worker
395*6236dae4SAndroid Build Coastguard Worker /* all set, traffic will be send on connect */
396*6236dae4SAndroid Build Coastguard Worker result = CURLE_OK;
397*6236dae4SAndroid Build Coastguard Worker
398*6236dae4SAndroid Build Coastguard Worker out:
399*6236dae4SAndroid Build Coastguard Worker if(cbs)
400*6236dae4SAndroid Build Coastguard Worker nghttp2_session_callbacks_del(cbs);
401*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result);
402*6236dae4SAndroid Build Coastguard Worker return result;
403*6236dae4SAndroid Build Coastguard Worker }
404*6236dae4SAndroid Build Coastguard Worker
proxy_h2_should_close_session(struct cf_h2_proxy_ctx * ctx)405*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx)
406*6236dae4SAndroid Build Coastguard Worker {
407*6236dae4SAndroid Build Coastguard Worker return !nghttp2_session_want_read(ctx->h2) &&
408*6236dae4SAndroid Build Coastguard Worker !nghttp2_session_want_write(ctx->h2);
409*6236dae4SAndroid Build Coastguard Worker }
410*6236dae4SAndroid Build Coastguard Worker
proxy_h2_nw_out_flush(struct Curl_cfilter * cf,struct Curl_easy * data)411*6236dae4SAndroid Build Coastguard Worker static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf,
412*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data)
413*6236dae4SAndroid Build Coastguard Worker {
414*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
415*6236dae4SAndroid Build Coastguard Worker ssize_t nwritten;
416*6236dae4SAndroid Build Coastguard Worker CURLcode result;
417*6236dae4SAndroid Build Coastguard Worker
418*6236dae4SAndroid Build Coastguard Worker (void)data;
419*6236dae4SAndroid Build Coastguard Worker if(Curl_bufq_is_empty(&ctx->outbufq))
420*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
421*6236dae4SAndroid Build Coastguard Worker
422*6236dae4SAndroid Build Coastguard Worker nwritten = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf,
423*6236dae4SAndroid Build Coastguard Worker &result);
424*6236dae4SAndroid Build Coastguard Worker if(nwritten < 0) {
425*6236dae4SAndroid Build Coastguard Worker if(result == CURLE_AGAIN) {
426*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN",
427*6236dae4SAndroid Build Coastguard Worker Curl_bufq_len(&ctx->outbufq));
428*6236dae4SAndroid Build Coastguard Worker ctx->nw_out_blocked = 1;
429*6236dae4SAndroid Build Coastguard Worker }
430*6236dae4SAndroid Build Coastguard Worker return result;
431*6236dae4SAndroid Build Coastguard Worker }
432*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] nw send buffer flushed");
433*6236dae4SAndroid Build Coastguard Worker return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN;
434*6236dae4SAndroid Build Coastguard Worker }
435*6236dae4SAndroid Build Coastguard Worker
436*6236dae4SAndroid Build Coastguard Worker /*
437*6236dae4SAndroid Build Coastguard Worker * Processes pending input left in network input buffer.
438*6236dae4SAndroid Build Coastguard Worker * This function returns 0 if it succeeds, or -1 and error code will
439*6236dae4SAndroid Build Coastguard Worker * be assigned to *err.
440*6236dae4SAndroid Build Coastguard Worker */
proxy_h2_process_pending_input(struct Curl_cfilter * cf,struct Curl_easy * data,CURLcode * err)441*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_process_pending_input(struct Curl_cfilter *cf,
442*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
443*6236dae4SAndroid Build Coastguard Worker CURLcode *err)
444*6236dae4SAndroid Build Coastguard Worker {
445*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
446*6236dae4SAndroid Build Coastguard Worker const unsigned char *buf;
447*6236dae4SAndroid Build Coastguard Worker size_t blen;
448*6236dae4SAndroid Build Coastguard Worker ssize_t rv;
449*6236dae4SAndroid Build Coastguard Worker
450*6236dae4SAndroid Build Coastguard Worker while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) {
451*6236dae4SAndroid Build Coastguard Worker
452*6236dae4SAndroid Build Coastguard Worker rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen);
453*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] %zu bytes to nghttp2 -> %zd", blen, rv);
454*6236dae4SAndroid Build Coastguard Worker if(rv < 0) {
455*6236dae4SAndroid Build Coastguard Worker failf(data,
456*6236dae4SAndroid Build Coastguard Worker "process_pending_input: nghttp2_session_mem_recv() returned "
457*6236dae4SAndroid Build Coastguard Worker "%zd:%s", rv, nghttp2_strerror((int)rv));
458*6236dae4SAndroid Build Coastguard Worker *err = CURLE_RECV_ERROR;
459*6236dae4SAndroid Build Coastguard Worker return -1;
460*6236dae4SAndroid Build Coastguard Worker }
461*6236dae4SAndroid Build Coastguard Worker Curl_bufq_skip(&ctx->inbufq, (size_t)rv);
462*6236dae4SAndroid Build Coastguard Worker if(Curl_bufq_is_empty(&ctx->inbufq)) {
463*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] all data in connection buffer processed");
464*6236dae4SAndroid Build Coastguard Worker break;
465*6236dae4SAndroid Build Coastguard Worker }
466*6236dae4SAndroid Build Coastguard Worker else {
467*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] process_pending_input: %zu bytes left "
468*6236dae4SAndroid Build Coastguard Worker "in connection buffer", Curl_bufq_len(&ctx->inbufq));
469*6236dae4SAndroid Build Coastguard Worker }
470*6236dae4SAndroid Build Coastguard Worker }
471*6236dae4SAndroid Build Coastguard Worker
472*6236dae4SAndroid Build Coastguard Worker return 0;
473*6236dae4SAndroid Build Coastguard Worker }
474*6236dae4SAndroid Build Coastguard Worker
proxy_h2_progress_ingress(struct Curl_cfilter * cf,struct Curl_easy * data)475*6236dae4SAndroid Build Coastguard Worker static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
476*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data)
477*6236dae4SAndroid Build Coastguard Worker {
478*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
479*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
480*6236dae4SAndroid Build Coastguard Worker ssize_t nread;
481*6236dae4SAndroid Build Coastguard Worker
482*6236dae4SAndroid Build Coastguard Worker /* Process network input buffer fist */
483*6236dae4SAndroid Build Coastguard Worker if(!Curl_bufq_is_empty(&ctx->inbufq)) {
484*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] process %zu bytes in connection buffer",
485*6236dae4SAndroid Build Coastguard Worker Curl_bufq_len(&ctx->inbufq));
486*6236dae4SAndroid Build Coastguard Worker if(proxy_h2_process_pending_input(cf, data, &result) < 0)
487*6236dae4SAndroid Build Coastguard Worker return result;
488*6236dae4SAndroid Build Coastguard Worker }
489*6236dae4SAndroid Build Coastguard Worker
490*6236dae4SAndroid Build Coastguard Worker /* Receive data from the "lower" filters, e.g. network until
491*6236dae4SAndroid Build Coastguard Worker * it is time to stop or we have enough data for this stream */
492*6236dae4SAndroid Build Coastguard Worker while(!ctx->conn_closed && /* not closed the connection */
493*6236dae4SAndroid Build Coastguard Worker !ctx->tunnel.closed && /* nor the tunnel */
494*6236dae4SAndroid Build Coastguard Worker Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */
495*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) {
496*6236dae4SAndroid Build Coastguard Worker
497*6236dae4SAndroid Build Coastguard Worker nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result);
498*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %zd, %d",
499*6236dae4SAndroid Build Coastguard Worker Curl_bufq_len(&ctx->inbufq), nread, result);
500*6236dae4SAndroid Build Coastguard Worker if(nread < 0) {
501*6236dae4SAndroid Build Coastguard Worker if(result != CURLE_AGAIN) {
502*6236dae4SAndroid Build Coastguard Worker failf(data, "Failed receiving HTTP2 data");
503*6236dae4SAndroid Build Coastguard Worker return result;
504*6236dae4SAndroid Build Coastguard Worker }
505*6236dae4SAndroid Build Coastguard Worker break;
506*6236dae4SAndroid Build Coastguard Worker }
507*6236dae4SAndroid Build Coastguard Worker else if(nread == 0) {
508*6236dae4SAndroid Build Coastguard Worker ctx->conn_closed = TRUE;
509*6236dae4SAndroid Build Coastguard Worker break;
510*6236dae4SAndroid Build Coastguard Worker }
511*6236dae4SAndroid Build Coastguard Worker
512*6236dae4SAndroid Build Coastguard Worker if(proxy_h2_process_pending_input(cf, data, &result))
513*6236dae4SAndroid Build Coastguard Worker return result;
514*6236dae4SAndroid Build Coastguard Worker }
515*6236dae4SAndroid Build Coastguard Worker
516*6236dae4SAndroid Build Coastguard Worker if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
517*6236dae4SAndroid Build Coastguard Worker connclose(cf->conn, "GOAWAY received");
518*6236dae4SAndroid Build Coastguard Worker }
519*6236dae4SAndroid Build Coastguard Worker
520*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
521*6236dae4SAndroid Build Coastguard Worker }
522*6236dae4SAndroid Build Coastguard Worker
proxy_h2_progress_egress(struct Curl_cfilter * cf,struct Curl_easy * data)523*6236dae4SAndroid Build Coastguard Worker static CURLcode proxy_h2_progress_egress(struct Curl_cfilter *cf,
524*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data)
525*6236dae4SAndroid Build Coastguard Worker {
526*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
527*6236dae4SAndroid Build Coastguard Worker int rv = 0;
528*6236dae4SAndroid Build Coastguard Worker
529*6236dae4SAndroid Build Coastguard Worker ctx->nw_out_blocked = 0;
530*6236dae4SAndroid Build Coastguard Worker while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2))
531*6236dae4SAndroid Build Coastguard Worker rv = nghttp2_session_send(ctx->h2);
532*6236dae4SAndroid Build Coastguard Worker
533*6236dae4SAndroid Build Coastguard Worker if(nghttp2_is_fatal(rv)) {
534*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] nghttp2_session_send error (%s)%d",
535*6236dae4SAndroid Build Coastguard Worker nghttp2_strerror(rv), rv);
536*6236dae4SAndroid Build Coastguard Worker return CURLE_SEND_ERROR;
537*6236dae4SAndroid Build Coastguard Worker }
538*6236dae4SAndroid Build Coastguard Worker return proxy_h2_nw_out_flush(cf, data);
539*6236dae4SAndroid Build Coastguard Worker }
540*6236dae4SAndroid Build Coastguard Worker
on_session_send(nghttp2_session * h2,const uint8_t * buf,size_t blen,int flags,void * userp)541*6236dae4SAndroid Build Coastguard Worker static ssize_t on_session_send(nghttp2_session *h2,
542*6236dae4SAndroid Build Coastguard Worker const uint8_t *buf, size_t blen, int flags,
543*6236dae4SAndroid Build Coastguard Worker void *userp)
544*6236dae4SAndroid Build Coastguard Worker {
545*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf = userp;
546*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
547*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data = CF_DATA_CURRENT(cf);
548*6236dae4SAndroid Build Coastguard Worker ssize_t nwritten;
549*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
550*6236dae4SAndroid Build Coastguard Worker
551*6236dae4SAndroid Build Coastguard Worker (void)h2;
552*6236dae4SAndroid Build Coastguard Worker (void)flags;
553*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(data);
554*6236dae4SAndroid Build Coastguard Worker
555*6236dae4SAndroid Build Coastguard Worker nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
556*6236dae4SAndroid Build Coastguard Worker proxy_h2_nw_out_writer, cf, &result);
557*6236dae4SAndroid Build Coastguard Worker if(nwritten < 0) {
558*6236dae4SAndroid Build Coastguard Worker if(result == CURLE_AGAIN) {
559*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_WOULDBLOCK;
560*6236dae4SAndroid Build Coastguard Worker }
561*6236dae4SAndroid Build Coastguard Worker failf(data, "Failed sending HTTP2 data");
562*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
563*6236dae4SAndroid Build Coastguard Worker }
564*6236dae4SAndroid Build Coastguard Worker
565*6236dae4SAndroid Build Coastguard Worker if(!nwritten)
566*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_WOULDBLOCK;
567*6236dae4SAndroid Build Coastguard Worker
568*6236dae4SAndroid Build Coastguard Worker return nwritten;
569*6236dae4SAndroid Build Coastguard Worker }
570*6236dae4SAndroid Build Coastguard Worker
571*6236dae4SAndroid Build Coastguard Worker #ifndef CURL_DISABLE_VERBOSE_STRINGS
proxy_h2_fr_print(const nghttp2_frame * frame,char * buffer,size_t blen)572*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_fr_print(const nghttp2_frame *frame,
573*6236dae4SAndroid Build Coastguard Worker char *buffer, size_t blen)
574*6236dae4SAndroid Build Coastguard Worker {
575*6236dae4SAndroid Build Coastguard Worker switch(frame->hd.type) {
576*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_DATA: {
577*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen,
578*6236dae4SAndroid Build Coastguard Worker "FRAME[DATA, len=%d, eos=%d, padlen=%d]",
579*6236dae4SAndroid Build Coastguard Worker (int)frame->hd.length,
580*6236dae4SAndroid Build Coastguard Worker !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM),
581*6236dae4SAndroid Build Coastguard Worker (int)frame->data.padlen);
582*6236dae4SAndroid Build Coastguard Worker }
583*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_HEADERS: {
584*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen,
585*6236dae4SAndroid Build Coastguard Worker "FRAME[HEADERS, len=%d, hend=%d, eos=%d]",
586*6236dae4SAndroid Build Coastguard Worker (int)frame->hd.length,
587*6236dae4SAndroid Build Coastguard Worker !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
588*6236dae4SAndroid Build Coastguard Worker !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
589*6236dae4SAndroid Build Coastguard Worker }
590*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_PRIORITY: {
591*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen,
592*6236dae4SAndroid Build Coastguard Worker "FRAME[PRIORITY, len=%d, flags=%d]",
593*6236dae4SAndroid Build Coastguard Worker (int)frame->hd.length, frame->hd.flags);
594*6236dae4SAndroid Build Coastguard Worker }
595*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_RST_STREAM: {
596*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen,
597*6236dae4SAndroid Build Coastguard Worker "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]",
598*6236dae4SAndroid Build Coastguard Worker (int)frame->hd.length, frame->hd.flags,
599*6236dae4SAndroid Build Coastguard Worker frame->rst_stream.error_code);
600*6236dae4SAndroid Build Coastguard Worker }
601*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_SETTINGS: {
602*6236dae4SAndroid Build Coastguard Worker if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
603*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]");
604*6236dae4SAndroid Build Coastguard Worker }
605*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen,
606*6236dae4SAndroid Build Coastguard Worker "FRAME[SETTINGS, len=%d]", (int)frame->hd.length);
607*6236dae4SAndroid Build Coastguard Worker }
608*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_PUSH_PROMISE:
609*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen,
610*6236dae4SAndroid Build Coastguard Worker "FRAME[PUSH_PROMISE, len=%d, hend=%d]",
611*6236dae4SAndroid Build Coastguard Worker (int)frame->hd.length,
612*6236dae4SAndroid Build Coastguard Worker !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS));
613*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_PING:
614*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen,
615*6236dae4SAndroid Build Coastguard Worker "FRAME[PING, len=%d, ack=%d]",
616*6236dae4SAndroid Build Coastguard Worker (int)frame->hd.length,
617*6236dae4SAndroid Build Coastguard Worker frame->hd.flags & NGHTTP2_FLAG_ACK);
618*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_GOAWAY: {
619*6236dae4SAndroid Build Coastguard Worker char scratch[128];
620*6236dae4SAndroid Build Coastguard Worker size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
621*6236dae4SAndroid Build Coastguard Worker size_t len = (frame->goaway.opaque_data_len < s_len) ?
622*6236dae4SAndroid Build Coastguard Worker frame->goaway.opaque_data_len : s_len-1;
623*6236dae4SAndroid Build Coastguard Worker if(len)
624*6236dae4SAndroid Build Coastguard Worker memcpy(scratch, frame->goaway.opaque_data, len);
625*6236dae4SAndroid Build Coastguard Worker scratch[len] = '\0';
626*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', "
627*6236dae4SAndroid Build Coastguard Worker "last_stream=%d]", frame->goaway.error_code,
628*6236dae4SAndroid Build Coastguard Worker scratch, frame->goaway.last_stream_id);
629*6236dae4SAndroid Build Coastguard Worker }
630*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_WINDOW_UPDATE: {
631*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen,
632*6236dae4SAndroid Build Coastguard Worker "FRAME[WINDOW_UPDATE, incr=%d]",
633*6236dae4SAndroid Build Coastguard Worker frame->window_update.window_size_increment);
634*6236dae4SAndroid Build Coastguard Worker }
635*6236dae4SAndroid Build Coastguard Worker default:
636*6236dae4SAndroid Build Coastguard Worker return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]",
637*6236dae4SAndroid Build Coastguard Worker frame->hd.type, (int)frame->hd.length,
638*6236dae4SAndroid Build Coastguard Worker frame->hd.flags);
639*6236dae4SAndroid Build Coastguard Worker }
640*6236dae4SAndroid Build Coastguard Worker }
641*6236dae4SAndroid Build Coastguard Worker
proxy_h2_on_frame_send(nghttp2_session * session,const nghttp2_frame * frame,void * userp)642*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_on_frame_send(nghttp2_session *session,
643*6236dae4SAndroid Build Coastguard Worker const nghttp2_frame *frame,
644*6236dae4SAndroid Build Coastguard Worker void *userp)
645*6236dae4SAndroid Build Coastguard Worker {
646*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf = userp;
647*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data = CF_DATA_CURRENT(cf);
648*6236dae4SAndroid Build Coastguard Worker
649*6236dae4SAndroid Build Coastguard Worker (void)session;
650*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(data);
651*6236dae4SAndroid Build Coastguard Worker if(data && Curl_trc_cf_is_verbose(cf, data)) {
652*6236dae4SAndroid Build Coastguard Worker char buffer[256];
653*6236dae4SAndroid Build Coastguard Worker int len;
654*6236dae4SAndroid Build Coastguard Worker len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1);
655*6236dae4SAndroid Build Coastguard Worker buffer[len] = 0;
656*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer);
657*6236dae4SAndroid Build Coastguard Worker }
658*6236dae4SAndroid Build Coastguard Worker return 0;
659*6236dae4SAndroid Build Coastguard Worker }
660*6236dae4SAndroid Build Coastguard Worker #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
661*6236dae4SAndroid Build Coastguard Worker
proxy_h2_on_frame_recv(nghttp2_session * session,const nghttp2_frame * frame,void * userp)662*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_on_frame_recv(nghttp2_session *session,
663*6236dae4SAndroid Build Coastguard Worker const nghttp2_frame *frame,
664*6236dae4SAndroid Build Coastguard Worker void *userp)
665*6236dae4SAndroid Build Coastguard Worker {
666*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf = userp;
667*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
668*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data = CF_DATA_CURRENT(cf);
669*6236dae4SAndroid Build Coastguard Worker int32_t stream_id = frame->hd.stream_id;
670*6236dae4SAndroid Build Coastguard Worker
671*6236dae4SAndroid Build Coastguard Worker (void)session;
672*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(data);
673*6236dae4SAndroid Build Coastguard Worker #ifndef CURL_DISABLE_VERBOSE_STRINGS
674*6236dae4SAndroid Build Coastguard Worker if(Curl_trc_cf_is_verbose(cf, data)) {
675*6236dae4SAndroid Build Coastguard Worker char buffer[256];
676*6236dae4SAndroid Build Coastguard Worker int len;
677*6236dae4SAndroid Build Coastguard Worker len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1);
678*6236dae4SAndroid Build Coastguard Worker buffer[len] = 0;
679*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer);
680*6236dae4SAndroid Build Coastguard Worker }
681*6236dae4SAndroid Build Coastguard Worker #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
682*6236dae4SAndroid Build Coastguard Worker
683*6236dae4SAndroid Build Coastguard Worker if(!stream_id) {
684*6236dae4SAndroid Build Coastguard Worker /* stream ID zero is for connection-oriented stuff */
685*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(data);
686*6236dae4SAndroid Build Coastguard Worker switch(frame->hd.type) {
687*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_SETTINGS:
688*6236dae4SAndroid Build Coastguard Worker /* Since the initial stream window is 64K, a request might be on HOLD,
689*6236dae4SAndroid Build Coastguard Worker * due to exhaustion. The (initial) SETTINGS may announce a much larger
690*6236dae4SAndroid Build Coastguard Worker * window and *assume* that we treat this like a WINDOW_UPDATE. Some
691*6236dae4SAndroid Build Coastguard Worker * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
692*6236dae4SAndroid Build Coastguard Worker * To be safe, we UNHOLD a stream in order not to stall. */
693*6236dae4SAndroid Build Coastguard Worker if(CURL_WANT_SEND(data)) {
694*6236dae4SAndroid Build Coastguard Worker drain_tunnel(cf, data, &ctx->tunnel);
695*6236dae4SAndroid Build Coastguard Worker }
696*6236dae4SAndroid Build Coastguard Worker break;
697*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_GOAWAY:
698*6236dae4SAndroid Build Coastguard Worker ctx->rcvd_goaway = TRUE;
699*6236dae4SAndroid Build Coastguard Worker break;
700*6236dae4SAndroid Build Coastguard Worker default:
701*6236dae4SAndroid Build Coastguard Worker break;
702*6236dae4SAndroid Build Coastguard Worker }
703*6236dae4SAndroid Build Coastguard Worker return 0;
704*6236dae4SAndroid Build Coastguard Worker }
705*6236dae4SAndroid Build Coastguard Worker
706*6236dae4SAndroid Build Coastguard Worker if(stream_id != ctx->tunnel.stream_id) {
707*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] rcvd FRAME not for tunnel", stream_id);
708*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
709*6236dae4SAndroid Build Coastguard Worker }
710*6236dae4SAndroid Build Coastguard Worker
711*6236dae4SAndroid Build Coastguard Worker switch(frame->hd.type) {
712*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_HEADERS:
713*6236dae4SAndroid Build Coastguard Worker /* nghttp2 guarantees that :status is received, and we store it to
714*6236dae4SAndroid Build Coastguard Worker stream->status_code. Fuzzing has proven this can still be reached
715*6236dae4SAndroid Build Coastguard Worker without status code having been set. */
716*6236dae4SAndroid Build Coastguard Worker if(!ctx->tunnel.resp)
717*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
718*6236dae4SAndroid Build Coastguard Worker /* Only final status code signals the end of header */
719*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] got http status: %d",
720*6236dae4SAndroid Build Coastguard Worker stream_id, ctx->tunnel.resp->status);
721*6236dae4SAndroid Build Coastguard Worker if(!ctx->tunnel.has_final_response) {
722*6236dae4SAndroid Build Coastguard Worker if(ctx->tunnel.resp->status / 100 != 1) {
723*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.has_final_response = TRUE;
724*6236dae4SAndroid Build Coastguard Worker }
725*6236dae4SAndroid Build Coastguard Worker }
726*6236dae4SAndroid Build Coastguard Worker break;
727*6236dae4SAndroid Build Coastguard Worker case NGHTTP2_WINDOW_UPDATE:
728*6236dae4SAndroid Build Coastguard Worker if(CURL_WANT_SEND(data)) {
729*6236dae4SAndroid Build Coastguard Worker drain_tunnel(cf, data, &ctx->tunnel);
730*6236dae4SAndroid Build Coastguard Worker }
731*6236dae4SAndroid Build Coastguard Worker break;
732*6236dae4SAndroid Build Coastguard Worker default:
733*6236dae4SAndroid Build Coastguard Worker break;
734*6236dae4SAndroid Build Coastguard Worker }
735*6236dae4SAndroid Build Coastguard Worker return 0;
736*6236dae4SAndroid Build Coastguard Worker }
737*6236dae4SAndroid Build Coastguard Worker
proxy_h2_on_header(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * userp)738*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_on_header(nghttp2_session *session,
739*6236dae4SAndroid Build Coastguard Worker const nghttp2_frame *frame,
740*6236dae4SAndroid Build Coastguard Worker const uint8_t *name, size_t namelen,
741*6236dae4SAndroid Build Coastguard Worker const uint8_t *value, size_t valuelen,
742*6236dae4SAndroid Build Coastguard Worker uint8_t flags,
743*6236dae4SAndroid Build Coastguard Worker void *userp)
744*6236dae4SAndroid Build Coastguard Worker {
745*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf = userp;
746*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
747*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data = CF_DATA_CURRENT(cf);
748*6236dae4SAndroid Build Coastguard Worker int32_t stream_id = frame->hd.stream_id;
749*6236dae4SAndroid Build Coastguard Worker CURLcode result;
750*6236dae4SAndroid Build Coastguard Worker
751*6236dae4SAndroid Build Coastguard Worker (void)flags;
752*6236dae4SAndroid Build Coastguard Worker (void)data;
753*6236dae4SAndroid Build Coastguard Worker (void)session;
754*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
755*6236dae4SAndroid Build Coastguard Worker if(stream_id != ctx->tunnel.stream_id) {
756*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] header for non-tunnel stream: "
757*6236dae4SAndroid Build Coastguard Worker "%.*s: %.*s", stream_id,
758*6236dae4SAndroid Build Coastguard Worker (int)namelen, name, (int)valuelen, value);
759*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
760*6236dae4SAndroid Build Coastguard Worker }
761*6236dae4SAndroid Build Coastguard Worker
762*6236dae4SAndroid Build Coastguard Worker if(frame->hd.type == NGHTTP2_PUSH_PROMISE)
763*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
764*6236dae4SAndroid Build Coastguard Worker
765*6236dae4SAndroid Build Coastguard Worker if(ctx->tunnel.has_final_response) {
766*6236dae4SAndroid Build Coastguard Worker /* we do not do anything with trailers for tunnel streams */
767*6236dae4SAndroid Build Coastguard Worker return 0;
768*6236dae4SAndroid Build Coastguard Worker }
769*6236dae4SAndroid Build Coastguard Worker
770*6236dae4SAndroid Build Coastguard Worker if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 &&
771*6236dae4SAndroid Build Coastguard Worker memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) {
772*6236dae4SAndroid Build Coastguard Worker int http_status;
773*6236dae4SAndroid Build Coastguard Worker struct http_resp *resp;
774*6236dae4SAndroid Build Coastguard Worker
775*6236dae4SAndroid Build Coastguard Worker /* status: always comes first, we might get more than one response,
776*6236dae4SAndroid Build Coastguard Worker * link the previous ones for keepers */
777*6236dae4SAndroid Build Coastguard Worker result = Curl_http_decode_status(&http_status,
778*6236dae4SAndroid Build Coastguard Worker (const char *)value, valuelen);
779*6236dae4SAndroid Build Coastguard Worker if(result)
780*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
781*6236dae4SAndroid Build Coastguard Worker result = Curl_http_resp_make(&resp, http_status, NULL);
782*6236dae4SAndroid Build Coastguard Worker if(result)
783*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
784*6236dae4SAndroid Build Coastguard Worker resp->prev = ctx->tunnel.resp;
785*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.resp = resp;
786*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d",
787*6236dae4SAndroid Build Coastguard Worker stream_id, ctx->tunnel.resp->status);
788*6236dae4SAndroid Build Coastguard Worker return 0;
789*6236dae4SAndroid Build Coastguard Worker }
790*6236dae4SAndroid Build Coastguard Worker
791*6236dae4SAndroid Build Coastguard Worker if(!ctx->tunnel.resp)
792*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
793*6236dae4SAndroid Build Coastguard Worker
794*6236dae4SAndroid Build Coastguard Worker result = Curl_dynhds_add(&ctx->tunnel.resp->headers,
795*6236dae4SAndroid Build Coastguard Worker (const char *)name, namelen,
796*6236dae4SAndroid Build Coastguard Worker (const char *)value, valuelen);
797*6236dae4SAndroid Build Coastguard Worker if(result)
798*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
799*6236dae4SAndroid Build Coastguard Worker
800*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s",
801*6236dae4SAndroid Build Coastguard Worker stream_id, (int)namelen, name, (int)valuelen, value);
802*6236dae4SAndroid Build Coastguard Worker
803*6236dae4SAndroid Build Coastguard Worker return 0; /* 0 is successful */
804*6236dae4SAndroid Build Coastguard Worker }
805*6236dae4SAndroid Build Coastguard Worker
tunnel_send_callback(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * userp)806*6236dae4SAndroid Build Coastguard Worker static ssize_t tunnel_send_callback(nghttp2_session *session,
807*6236dae4SAndroid Build Coastguard Worker int32_t stream_id,
808*6236dae4SAndroid Build Coastguard Worker uint8_t *buf, size_t length,
809*6236dae4SAndroid Build Coastguard Worker uint32_t *data_flags,
810*6236dae4SAndroid Build Coastguard Worker nghttp2_data_source *source,
811*6236dae4SAndroid Build Coastguard Worker void *userp)
812*6236dae4SAndroid Build Coastguard Worker {
813*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf = userp;
814*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
815*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data = CF_DATA_CURRENT(cf);
816*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream *ts;
817*6236dae4SAndroid Build Coastguard Worker CURLcode result;
818*6236dae4SAndroid Build Coastguard Worker ssize_t nread;
819*6236dae4SAndroid Build Coastguard Worker
820*6236dae4SAndroid Build Coastguard Worker (void)source;
821*6236dae4SAndroid Build Coastguard Worker (void)data;
822*6236dae4SAndroid Build Coastguard Worker (void)ctx;
823*6236dae4SAndroid Build Coastguard Worker
824*6236dae4SAndroid Build Coastguard Worker if(!stream_id)
825*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_INVALID_ARGUMENT;
826*6236dae4SAndroid Build Coastguard Worker
827*6236dae4SAndroid Build Coastguard Worker ts = nghttp2_session_get_stream_user_data(session, stream_id);
828*6236dae4SAndroid Build Coastguard Worker if(!ts)
829*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
830*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(ts == &ctx->tunnel);
831*6236dae4SAndroid Build Coastguard Worker
832*6236dae4SAndroid Build Coastguard Worker nread = Curl_bufq_read(&ts->sendbuf, buf, length, &result);
833*6236dae4SAndroid Build Coastguard Worker if(nread < 0) {
834*6236dae4SAndroid Build Coastguard Worker if(result != CURLE_AGAIN)
835*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
836*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_DEFERRED;
837*6236dae4SAndroid Build Coastguard Worker }
838*6236dae4SAndroid Build Coastguard Worker if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf))
839*6236dae4SAndroid Build Coastguard Worker *data_flags = NGHTTP2_DATA_FLAG_EOF;
840*6236dae4SAndroid Build Coastguard Worker
841*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd",
842*6236dae4SAndroid Build Coastguard Worker ts->stream_id, nread);
843*6236dae4SAndroid Build Coastguard Worker return nread;
844*6236dae4SAndroid Build Coastguard Worker }
845*6236dae4SAndroid Build Coastguard Worker
tunnel_recv_callback(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * mem,size_t len,void * userp)846*6236dae4SAndroid Build Coastguard Worker static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
847*6236dae4SAndroid Build Coastguard Worker int32_t stream_id,
848*6236dae4SAndroid Build Coastguard Worker const uint8_t *mem, size_t len, void *userp)
849*6236dae4SAndroid Build Coastguard Worker {
850*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf = userp;
851*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
852*6236dae4SAndroid Build Coastguard Worker ssize_t nwritten;
853*6236dae4SAndroid Build Coastguard Worker CURLcode result;
854*6236dae4SAndroid Build Coastguard Worker
855*6236dae4SAndroid Build Coastguard Worker (void)flags;
856*6236dae4SAndroid Build Coastguard Worker (void)session;
857*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
858*6236dae4SAndroid Build Coastguard Worker
859*6236dae4SAndroid Build Coastguard Worker if(stream_id != ctx->tunnel.stream_id)
860*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
861*6236dae4SAndroid Build Coastguard Worker
862*6236dae4SAndroid Build Coastguard Worker nwritten = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &result);
863*6236dae4SAndroid Build Coastguard Worker if(nwritten < 0) {
864*6236dae4SAndroid Build Coastguard Worker if(result != CURLE_AGAIN)
865*6236dae4SAndroid Build Coastguard Worker return NGHTTP2_ERR_CALLBACK_FAILURE;
866*6236dae4SAndroid Build Coastguard Worker nwritten = 0;
867*6236dae4SAndroid Build Coastguard Worker }
868*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT((size_t)nwritten == len);
869*6236dae4SAndroid Build Coastguard Worker return 0;
870*6236dae4SAndroid Build Coastguard Worker }
871*6236dae4SAndroid Build Coastguard Worker
proxy_h2_on_stream_close(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * userp)872*6236dae4SAndroid Build Coastguard Worker static int proxy_h2_on_stream_close(nghttp2_session *session,
873*6236dae4SAndroid Build Coastguard Worker int32_t stream_id,
874*6236dae4SAndroid Build Coastguard Worker uint32_t error_code, void *userp)
875*6236dae4SAndroid Build Coastguard Worker {
876*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf = userp;
877*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
878*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data = CF_DATA_CURRENT(cf);
879*6236dae4SAndroid Build Coastguard Worker
880*6236dae4SAndroid Build Coastguard Worker (void)session;
881*6236dae4SAndroid Build Coastguard Worker (void)data;
882*6236dae4SAndroid Build Coastguard Worker
883*6236dae4SAndroid Build Coastguard Worker if(stream_id != ctx->tunnel.stream_id)
884*6236dae4SAndroid Build Coastguard Worker return 0;
885*6236dae4SAndroid Build Coastguard Worker
886*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)",
887*6236dae4SAndroid Build Coastguard Worker stream_id, nghttp2_http2_strerror(error_code), error_code);
888*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.closed = TRUE;
889*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.error = error_code;
890*6236dae4SAndroid Build Coastguard Worker
891*6236dae4SAndroid Build Coastguard Worker return 0;
892*6236dae4SAndroid Build Coastguard Worker }
893*6236dae4SAndroid Build Coastguard Worker
proxy_h2_submit(int32_t * pstream_id,struct Curl_cfilter * cf,struct Curl_easy * data,nghttp2_session * h2,struct httpreq * req,const nghttp2_priority_spec * pri_spec,void * stream_user_data,nghttp2_data_source_read_callback read_callback,void * read_ctx)894*6236dae4SAndroid Build Coastguard Worker static CURLcode proxy_h2_submit(int32_t *pstream_id,
895*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf,
896*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
897*6236dae4SAndroid Build Coastguard Worker nghttp2_session *h2,
898*6236dae4SAndroid Build Coastguard Worker struct httpreq *req,
899*6236dae4SAndroid Build Coastguard Worker const nghttp2_priority_spec *pri_spec,
900*6236dae4SAndroid Build Coastguard Worker void *stream_user_data,
901*6236dae4SAndroid Build Coastguard Worker nghttp2_data_source_read_callback read_callback,
902*6236dae4SAndroid Build Coastguard Worker void *read_ctx)
903*6236dae4SAndroid Build Coastguard Worker {
904*6236dae4SAndroid Build Coastguard Worker struct dynhds h2_headers;
905*6236dae4SAndroid Build Coastguard Worker nghttp2_nv *nva = NULL;
906*6236dae4SAndroid Build Coastguard Worker int32_t stream_id = -1;
907*6236dae4SAndroid Build Coastguard Worker size_t nheader;
908*6236dae4SAndroid Build Coastguard Worker CURLcode result;
909*6236dae4SAndroid Build Coastguard Worker
910*6236dae4SAndroid Build Coastguard Worker (void)cf;
911*6236dae4SAndroid Build Coastguard Worker Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
912*6236dae4SAndroid Build Coastguard Worker result = Curl_http_req_to_h2(&h2_headers, req, data);
913*6236dae4SAndroid Build Coastguard Worker if(result)
914*6236dae4SAndroid Build Coastguard Worker goto out;
915*6236dae4SAndroid Build Coastguard Worker
916*6236dae4SAndroid Build Coastguard Worker nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
917*6236dae4SAndroid Build Coastguard Worker if(!nva) {
918*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
919*6236dae4SAndroid Build Coastguard Worker goto out;
920*6236dae4SAndroid Build Coastguard Worker }
921*6236dae4SAndroid Build Coastguard Worker
922*6236dae4SAndroid Build Coastguard Worker if(read_callback) {
923*6236dae4SAndroid Build Coastguard Worker nghttp2_data_provider data_prd;
924*6236dae4SAndroid Build Coastguard Worker
925*6236dae4SAndroid Build Coastguard Worker data_prd.read_callback = read_callback;
926*6236dae4SAndroid Build Coastguard Worker data_prd.source.ptr = read_ctx;
927*6236dae4SAndroid Build Coastguard Worker stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader,
928*6236dae4SAndroid Build Coastguard Worker &data_prd, stream_user_data);
929*6236dae4SAndroid Build Coastguard Worker }
930*6236dae4SAndroid Build Coastguard Worker else {
931*6236dae4SAndroid Build Coastguard Worker stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader,
932*6236dae4SAndroid Build Coastguard Worker NULL, stream_user_data);
933*6236dae4SAndroid Build Coastguard Worker }
934*6236dae4SAndroid Build Coastguard Worker
935*6236dae4SAndroid Build Coastguard Worker if(stream_id < 0) {
936*6236dae4SAndroid Build Coastguard Worker failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
937*6236dae4SAndroid Build Coastguard Worker nghttp2_strerror(stream_id), stream_id);
938*6236dae4SAndroid Build Coastguard Worker result = CURLE_SEND_ERROR;
939*6236dae4SAndroid Build Coastguard Worker goto out;
940*6236dae4SAndroid Build Coastguard Worker }
941*6236dae4SAndroid Build Coastguard Worker result = CURLE_OK;
942*6236dae4SAndroid Build Coastguard Worker
943*6236dae4SAndroid Build Coastguard Worker out:
944*6236dae4SAndroid Build Coastguard Worker free(nva);
945*6236dae4SAndroid Build Coastguard Worker Curl_dynhds_free(&h2_headers);
946*6236dae4SAndroid Build Coastguard Worker *pstream_id = stream_id;
947*6236dae4SAndroid Build Coastguard Worker return result;
948*6236dae4SAndroid Build Coastguard Worker }
949*6236dae4SAndroid Build Coastguard Worker
submit_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_stream * ts)950*6236dae4SAndroid Build Coastguard Worker static CURLcode submit_CONNECT(struct Curl_cfilter *cf,
951*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
952*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream *ts)
953*6236dae4SAndroid Build Coastguard Worker {
954*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
955*6236dae4SAndroid Build Coastguard Worker CURLcode result;
956*6236dae4SAndroid Build Coastguard Worker struct httpreq *req = NULL;
957*6236dae4SAndroid Build Coastguard Worker
958*6236dae4SAndroid Build Coastguard Worker result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2);
959*6236dae4SAndroid Build Coastguard Worker if(result)
960*6236dae4SAndroid Build Coastguard Worker goto out;
961*6236dae4SAndroid Build Coastguard Worker result = Curl_creader_set_null(data);
962*6236dae4SAndroid Build Coastguard Worker if(result)
963*6236dae4SAndroid Build Coastguard Worker goto out;
964*6236dae4SAndroid Build Coastguard Worker
965*6236dae4SAndroid Build Coastguard Worker infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority);
966*6236dae4SAndroid Build Coastguard Worker
967*6236dae4SAndroid Build Coastguard Worker result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req,
968*6236dae4SAndroid Build Coastguard Worker NULL, ts, tunnel_send_callback, cf);
969*6236dae4SAndroid Build Coastguard Worker if(result) {
970*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] send, nghttp2_submit_request error: %s",
971*6236dae4SAndroid Build Coastguard Worker ts->stream_id, nghttp2_strerror(ts->stream_id));
972*6236dae4SAndroid Build Coastguard Worker }
973*6236dae4SAndroid Build Coastguard Worker
974*6236dae4SAndroid Build Coastguard Worker out:
975*6236dae4SAndroid Build Coastguard Worker if(req)
976*6236dae4SAndroid Build Coastguard Worker Curl_http_req_free(req);
977*6236dae4SAndroid Build Coastguard Worker if(result)
978*6236dae4SAndroid Build Coastguard Worker failf(data, "Failed sending CONNECT to proxy");
979*6236dae4SAndroid Build Coastguard Worker return result;
980*6236dae4SAndroid Build Coastguard Worker }
981*6236dae4SAndroid Build Coastguard Worker
inspect_response(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_stream * ts)982*6236dae4SAndroid Build Coastguard Worker static CURLcode inspect_response(struct Curl_cfilter *cf,
983*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
984*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream *ts)
985*6236dae4SAndroid Build Coastguard Worker {
986*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
987*6236dae4SAndroid Build Coastguard Worker struct dynhds_entry *auth_reply = NULL;
988*6236dae4SAndroid Build Coastguard Worker (void)cf;
989*6236dae4SAndroid Build Coastguard Worker
990*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(ts->resp);
991*6236dae4SAndroid Build Coastguard Worker if(ts->resp->status/100 == 2) {
992*6236dae4SAndroid Build Coastguard Worker infof(data, "CONNECT tunnel established, response %d", ts->resp->status);
993*6236dae4SAndroid Build Coastguard Worker h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data);
994*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
995*6236dae4SAndroid Build Coastguard Worker }
996*6236dae4SAndroid Build Coastguard Worker
997*6236dae4SAndroid Build Coastguard Worker if(ts->resp->status == 401) {
998*6236dae4SAndroid Build Coastguard Worker auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate");
999*6236dae4SAndroid Build Coastguard Worker }
1000*6236dae4SAndroid Build Coastguard Worker else if(ts->resp->status == 407) {
1001*6236dae4SAndroid Build Coastguard Worker auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate");
1002*6236dae4SAndroid Build Coastguard Worker }
1003*6236dae4SAndroid Build Coastguard Worker
1004*6236dae4SAndroid Build Coastguard Worker if(auth_reply) {
1005*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'",
1006*6236dae4SAndroid Build Coastguard Worker auth_reply->value);
1007*6236dae4SAndroid Build Coastguard Worker result = Curl_http_input_auth(data, ts->resp->status == 407,
1008*6236dae4SAndroid Build Coastguard Worker auth_reply->value);
1009*6236dae4SAndroid Build Coastguard Worker if(result)
1010*6236dae4SAndroid Build Coastguard Worker return result;
1011*6236dae4SAndroid Build Coastguard Worker if(data->req.newurl) {
1012*6236dae4SAndroid Build Coastguard Worker /* Indicator that we should try again */
1013*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->req.newurl);
1014*6236dae4SAndroid Build Coastguard Worker h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data);
1015*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
1016*6236dae4SAndroid Build Coastguard Worker }
1017*6236dae4SAndroid Build Coastguard Worker }
1018*6236dae4SAndroid Build Coastguard Worker
1019*6236dae4SAndroid Build Coastguard Worker /* Seems to have failed */
1020*6236dae4SAndroid Build Coastguard Worker return CURLE_RECV_ERROR;
1021*6236dae4SAndroid Build Coastguard Worker }
1022*6236dae4SAndroid Build Coastguard Worker
H2_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_stream * ts)1023*6236dae4SAndroid Build Coastguard Worker static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
1024*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1025*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream *ts)
1026*6236dae4SAndroid Build Coastguard Worker {
1027*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1028*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
1029*6236dae4SAndroid Build Coastguard Worker
1030*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(ts);
1031*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(ts->authority);
1032*6236dae4SAndroid Build Coastguard Worker do {
1033*6236dae4SAndroid Build Coastguard Worker switch(ts->state) {
1034*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_INIT:
1035*6236dae4SAndroid Build Coastguard Worker /* Prepare the CONNECT request and make a first attempt to send. */
1036*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority);
1037*6236dae4SAndroid Build Coastguard Worker result = submit_CONNECT(cf, data, ts);
1038*6236dae4SAndroid Build Coastguard Worker if(result)
1039*6236dae4SAndroid Build Coastguard Worker goto out;
1040*6236dae4SAndroid Build Coastguard Worker h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data);
1041*6236dae4SAndroid Build Coastguard Worker FALLTHROUGH();
1042*6236dae4SAndroid Build Coastguard Worker
1043*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_CONNECT:
1044*6236dae4SAndroid Build Coastguard Worker /* see that the request is completely sent */
1045*6236dae4SAndroid Build Coastguard Worker result = proxy_h2_progress_ingress(cf, data);
1046*6236dae4SAndroid Build Coastguard Worker if(!result)
1047*6236dae4SAndroid Build Coastguard Worker result = proxy_h2_progress_egress(cf, data);
1048*6236dae4SAndroid Build Coastguard Worker if(result && result != CURLE_AGAIN) {
1049*6236dae4SAndroid Build Coastguard Worker h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data);
1050*6236dae4SAndroid Build Coastguard Worker break;
1051*6236dae4SAndroid Build Coastguard Worker }
1052*6236dae4SAndroid Build Coastguard Worker
1053*6236dae4SAndroid Build Coastguard Worker if(ts->has_final_response) {
1054*6236dae4SAndroid Build Coastguard Worker h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data);
1055*6236dae4SAndroid Build Coastguard Worker }
1056*6236dae4SAndroid Build Coastguard Worker else {
1057*6236dae4SAndroid Build Coastguard Worker result = CURLE_OK;
1058*6236dae4SAndroid Build Coastguard Worker goto out;
1059*6236dae4SAndroid Build Coastguard Worker }
1060*6236dae4SAndroid Build Coastguard Worker FALLTHROUGH();
1061*6236dae4SAndroid Build Coastguard Worker
1062*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_RESPONSE:
1063*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(ts->has_final_response);
1064*6236dae4SAndroid Build Coastguard Worker result = inspect_response(cf, data, ts);
1065*6236dae4SAndroid Build Coastguard Worker if(result)
1066*6236dae4SAndroid Build Coastguard Worker goto out;
1067*6236dae4SAndroid Build Coastguard Worker break;
1068*6236dae4SAndroid Build Coastguard Worker
1069*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_ESTABLISHED:
1070*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
1071*6236dae4SAndroid Build Coastguard Worker
1072*6236dae4SAndroid Build Coastguard Worker case H2_TUNNEL_FAILED:
1073*6236dae4SAndroid Build Coastguard Worker return CURLE_RECV_ERROR;
1074*6236dae4SAndroid Build Coastguard Worker
1075*6236dae4SAndroid Build Coastguard Worker default:
1076*6236dae4SAndroid Build Coastguard Worker break;
1077*6236dae4SAndroid Build Coastguard Worker }
1078*6236dae4SAndroid Build Coastguard Worker
1079*6236dae4SAndroid Build Coastguard Worker } while(ts->state == H2_TUNNEL_INIT);
1080*6236dae4SAndroid Build Coastguard Worker
1081*6236dae4SAndroid Build Coastguard Worker out:
1082*6236dae4SAndroid Build Coastguard Worker if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed)
1083*6236dae4SAndroid Build Coastguard Worker h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data);
1084*6236dae4SAndroid Build Coastguard Worker return result;
1085*6236dae4SAndroid Build Coastguard Worker }
1086*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1087*6236dae4SAndroid Build Coastguard Worker static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
1088*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1089*6236dae4SAndroid Build Coastguard Worker bool blocking, bool *done)
1090*6236dae4SAndroid Build Coastguard Worker {
1091*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1092*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
1093*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save;
1094*6236dae4SAndroid Build Coastguard Worker timediff_t check;
1095*6236dae4SAndroid Build Coastguard Worker struct tunnel_stream *ts = &ctx->tunnel;
1096*6236dae4SAndroid Build Coastguard Worker
1097*6236dae4SAndroid Build Coastguard Worker if(cf->connected) {
1098*6236dae4SAndroid Build Coastguard Worker *done = TRUE;
1099*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
1100*6236dae4SAndroid Build Coastguard Worker }
1101*6236dae4SAndroid Build Coastguard Worker
1102*6236dae4SAndroid Build Coastguard Worker /* Connect the lower filters first */
1103*6236dae4SAndroid Build Coastguard Worker if(!cf->next->connected) {
1104*6236dae4SAndroid Build Coastguard Worker result = Curl_conn_cf_connect(cf->next, data, blocking, done);
1105*6236dae4SAndroid Build Coastguard Worker if(result || !*done)
1106*6236dae4SAndroid Build Coastguard Worker return result;
1107*6236dae4SAndroid Build Coastguard Worker }
1108*6236dae4SAndroid Build Coastguard Worker
1109*6236dae4SAndroid Build Coastguard Worker *done = FALSE;
1110*6236dae4SAndroid Build Coastguard Worker
1111*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1112*6236dae4SAndroid Build Coastguard Worker if(!ctx->h2) {
1113*6236dae4SAndroid Build Coastguard Worker result = cf_h2_proxy_ctx_init(cf, data);
1114*6236dae4SAndroid Build Coastguard Worker if(result)
1115*6236dae4SAndroid Build Coastguard Worker goto out;
1116*6236dae4SAndroid Build Coastguard Worker }
1117*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(ts->authority);
1118*6236dae4SAndroid Build Coastguard Worker
1119*6236dae4SAndroid Build Coastguard Worker check = Curl_timeleft(data, NULL, TRUE);
1120*6236dae4SAndroid Build Coastguard Worker if(check <= 0) {
1121*6236dae4SAndroid Build Coastguard Worker failf(data, "Proxy CONNECT aborted due to timeout");
1122*6236dae4SAndroid Build Coastguard Worker result = CURLE_OPERATION_TIMEDOUT;
1123*6236dae4SAndroid Build Coastguard Worker goto out;
1124*6236dae4SAndroid Build Coastguard Worker }
1125*6236dae4SAndroid Build Coastguard Worker
1126*6236dae4SAndroid Build Coastguard Worker /* for the secondary socket (FTP), use the "connect to host"
1127*6236dae4SAndroid Build Coastguard Worker * but ignore the "connect to port" (use the secondary port)
1128*6236dae4SAndroid Build Coastguard Worker */
1129*6236dae4SAndroid Build Coastguard Worker result = H2_CONNECT(cf, data, ts);
1130*6236dae4SAndroid Build Coastguard Worker
1131*6236dae4SAndroid Build Coastguard Worker out:
1132*6236dae4SAndroid Build Coastguard Worker *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED);
1133*6236dae4SAndroid Build Coastguard Worker if(*done) {
1134*6236dae4SAndroid Build Coastguard Worker cf->connected = TRUE;
1135*6236dae4SAndroid Build Coastguard Worker /* The real request will follow the CONNECT, reset request partially */
1136*6236dae4SAndroid Build Coastguard Worker Curl_req_soft_reset(&data->req, data);
1137*6236dae4SAndroid Build Coastguard Worker Curl_client_reset(data);
1138*6236dae4SAndroid Build Coastguard Worker }
1139*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1140*6236dae4SAndroid Build Coastguard Worker return result;
1141*6236dae4SAndroid Build Coastguard Worker }
1142*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_close(struct Curl_cfilter * cf,struct Curl_easy * data)1143*6236dae4SAndroid Build Coastguard Worker static void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data)
1144*6236dae4SAndroid Build Coastguard Worker {
1145*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1146*6236dae4SAndroid Build Coastguard Worker
1147*6236dae4SAndroid Build Coastguard Worker if(ctx) {
1148*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save;
1149*6236dae4SAndroid Build Coastguard Worker
1150*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1151*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_ctx_clear(ctx);
1152*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1153*6236dae4SAndroid Build Coastguard Worker }
1154*6236dae4SAndroid Build Coastguard Worker if(cf->next)
1155*6236dae4SAndroid Build Coastguard Worker cf->next->cft->do_close(cf->next, data);
1156*6236dae4SAndroid Build Coastguard Worker }
1157*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1158*6236dae4SAndroid Build Coastguard Worker static void cf_h2_proxy_destroy(struct Curl_cfilter *cf,
1159*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data)
1160*6236dae4SAndroid Build Coastguard Worker {
1161*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1162*6236dae4SAndroid Build Coastguard Worker
1163*6236dae4SAndroid Build Coastguard Worker (void)data;
1164*6236dae4SAndroid Build Coastguard Worker if(ctx) {
1165*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_ctx_free(ctx);
1166*6236dae4SAndroid Build Coastguard Worker cf->ctx = NULL;
1167*6236dae4SAndroid Build Coastguard Worker }
1168*6236dae4SAndroid Build Coastguard Worker }
1169*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_shutdown(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)1170*6236dae4SAndroid Build Coastguard Worker static CURLcode cf_h2_proxy_shutdown(struct Curl_cfilter *cf,
1171*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data, bool *done)
1172*6236dae4SAndroid Build Coastguard Worker {
1173*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1174*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save;
1175*6236dae4SAndroid Build Coastguard Worker CURLcode result;
1176*6236dae4SAndroid Build Coastguard Worker int rv;
1177*6236dae4SAndroid Build Coastguard Worker
1178*6236dae4SAndroid Build Coastguard Worker if(!cf->connected || !ctx->h2 || cf->shutdown || ctx->conn_closed) {
1179*6236dae4SAndroid Build Coastguard Worker *done = TRUE;
1180*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
1181*6236dae4SAndroid Build Coastguard Worker }
1182*6236dae4SAndroid Build Coastguard Worker
1183*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1184*6236dae4SAndroid Build Coastguard Worker
1185*6236dae4SAndroid Build Coastguard Worker if(!ctx->sent_goaway) {
1186*6236dae4SAndroid Build Coastguard Worker rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE,
1187*6236dae4SAndroid Build Coastguard Worker 0, 0,
1188*6236dae4SAndroid Build Coastguard Worker (const uint8_t *)"shutdown",
1189*6236dae4SAndroid Build Coastguard Worker sizeof("shutdown"));
1190*6236dae4SAndroid Build Coastguard Worker if(rv) {
1191*6236dae4SAndroid Build Coastguard Worker failf(data, "nghttp2_submit_goaway() failed: %s(%d)",
1192*6236dae4SAndroid Build Coastguard Worker nghttp2_strerror(rv), rv);
1193*6236dae4SAndroid Build Coastguard Worker result = CURLE_SEND_ERROR;
1194*6236dae4SAndroid Build Coastguard Worker goto out;
1195*6236dae4SAndroid Build Coastguard Worker }
1196*6236dae4SAndroid Build Coastguard Worker ctx->sent_goaway = TRUE;
1197*6236dae4SAndroid Build Coastguard Worker }
1198*6236dae4SAndroid Build Coastguard Worker /* GOAWAY submitted, process egress and ingress until nghttp2 is done. */
1199*6236dae4SAndroid Build Coastguard Worker result = CURLE_OK;
1200*6236dae4SAndroid Build Coastguard Worker if(nghttp2_session_want_write(ctx->h2))
1201*6236dae4SAndroid Build Coastguard Worker result = proxy_h2_progress_egress(cf, data);
1202*6236dae4SAndroid Build Coastguard Worker if(!result && nghttp2_session_want_read(ctx->h2))
1203*6236dae4SAndroid Build Coastguard Worker result = proxy_h2_progress_ingress(cf, data);
1204*6236dae4SAndroid Build Coastguard Worker
1205*6236dae4SAndroid Build Coastguard Worker *done = (ctx->conn_closed ||
1206*6236dae4SAndroid Build Coastguard Worker (!result && !nghttp2_session_want_write(ctx->h2) &&
1207*6236dae4SAndroid Build Coastguard Worker !nghttp2_session_want_read(ctx->h2)));
1208*6236dae4SAndroid Build Coastguard Worker out:
1209*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1210*6236dae4SAndroid Build Coastguard Worker cf->shutdown = (result || *done);
1211*6236dae4SAndroid Build Coastguard Worker return result;
1212*6236dae4SAndroid Build Coastguard Worker }
1213*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)1214*6236dae4SAndroid Build Coastguard Worker static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf,
1215*6236dae4SAndroid Build Coastguard Worker const struct Curl_easy *data)
1216*6236dae4SAndroid Build Coastguard Worker {
1217*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1218*6236dae4SAndroid Build Coastguard Worker if((ctx && !Curl_bufq_is_empty(&ctx->inbufq)) ||
1219*6236dae4SAndroid Build Coastguard Worker (ctx && ctx->tunnel.state == H2_TUNNEL_ESTABLISHED &&
1220*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_empty(&ctx->tunnel.recvbuf)))
1221*6236dae4SAndroid Build Coastguard Worker return TRUE;
1222*6236dae4SAndroid Build Coastguard Worker return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
1223*6236dae4SAndroid Build Coastguard Worker }
1224*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)1225*6236dae4SAndroid Build Coastguard Worker static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
1226*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1227*6236dae4SAndroid Build Coastguard Worker struct easy_pollset *ps)
1228*6236dae4SAndroid Build Coastguard Worker {
1229*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1230*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save;
1231*6236dae4SAndroid Build Coastguard Worker curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
1232*6236dae4SAndroid Build Coastguard Worker bool want_recv, want_send;
1233*6236dae4SAndroid Build Coastguard Worker
1234*6236dae4SAndroid Build Coastguard Worker if(!cf->connected && ctx->h2) {
1235*6236dae4SAndroid Build Coastguard Worker want_send = nghttp2_session_want_write(ctx->h2) ||
1236*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_empty(&ctx->outbufq) ||
1237*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_empty(&ctx->tunnel.sendbuf);
1238*6236dae4SAndroid Build Coastguard Worker want_recv = nghttp2_session_want_read(ctx->h2);
1239*6236dae4SAndroid Build Coastguard Worker }
1240*6236dae4SAndroid Build Coastguard Worker else
1241*6236dae4SAndroid Build Coastguard Worker Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
1242*6236dae4SAndroid Build Coastguard Worker
1243*6236dae4SAndroid Build Coastguard Worker if(ctx->h2 && (want_recv || want_send)) {
1244*6236dae4SAndroid Build Coastguard Worker bool c_exhaust, s_exhaust;
1245*6236dae4SAndroid Build Coastguard Worker
1246*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1247*6236dae4SAndroid Build Coastguard Worker c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2);
1248*6236dae4SAndroid Build Coastguard Worker s_exhaust = ctx->tunnel.stream_id >= 0 &&
1249*6236dae4SAndroid Build Coastguard Worker !nghttp2_session_get_stream_remote_window_size(
1250*6236dae4SAndroid Build Coastguard Worker ctx->h2, ctx->tunnel.stream_id);
1251*6236dae4SAndroid Build Coastguard Worker want_recv = (want_recv || c_exhaust || s_exhaust);
1252*6236dae4SAndroid Build Coastguard Worker want_send = (!s_exhaust && want_send) ||
1253*6236dae4SAndroid Build Coastguard Worker (!c_exhaust && nghttp2_session_want_write(ctx->h2)) ||
1254*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_empty(&ctx->outbufq) ||
1255*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_empty(&ctx->tunnel.sendbuf);
1256*6236dae4SAndroid Build Coastguard Worker
1257*6236dae4SAndroid Build Coastguard Worker Curl_pollset_set(data, ps, sock, want_recv, want_send);
1258*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d",
1259*6236dae4SAndroid Build Coastguard Worker want_recv, want_send);
1260*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1261*6236dae4SAndroid Build Coastguard Worker }
1262*6236dae4SAndroid Build Coastguard Worker else if(ctx->sent_goaway && !cf->shutdown) {
1263*6236dae4SAndroid Build Coastguard Worker /* shutdown in progress */
1264*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1265*6236dae4SAndroid Build Coastguard Worker want_send = nghttp2_session_want_write(ctx->h2) ||
1266*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_empty(&ctx->outbufq) ||
1267*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_empty(&ctx->tunnel.sendbuf);
1268*6236dae4SAndroid Build Coastguard Worker want_recv = nghttp2_session_want_read(ctx->h2);
1269*6236dae4SAndroid Build Coastguard Worker Curl_pollset_set(data, ps, sock, want_recv, want_send);
1270*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d",
1271*6236dae4SAndroid Build Coastguard Worker want_recv, want_send);
1272*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1273*6236dae4SAndroid Build Coastguard Worker }
1274*6236dae4SAndroid Build Coastguard Worker }
1275*6236dae4SAndroid Build Coastguard Worker
h2_handle_tunnel_close(struct Curl_cfilter * cf,struct Curl_easy * data,CURLcode * err)1276*6236dae4SAndroid Build Coastguard Worker static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf,
1277*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1278*6236dae4SAndroid Build Coastguard Worker CURLcode *err)
1279*6236dae4SAndroid Build Coastguard Worker {
1280*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1281*6236dae4SAndroid Build Coastguard Worker ssize_t rv = 0;
1282*6236dae4SAndroid Build Coastguard Worker
1283*6236dae4SAndroid Build Coastguard Worker if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) {
1284*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
1285*6236dae4SAndroid Build Coastguard Worker "connection", ctx->tunnel.stream_id);
1286*6236dae4SAndroid Build Coastguard Worker connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */
1287*6236dae4SAndroid Build Coastguard Worker *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
1288*6236dae4SAndroid Build Coastguard Worker return -1;
1289*6236dae4SAndroid Build Coastguard Worker }
1290*6236dae4SAndroid Build Coastguard Worker else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) {
1291*6236dae4SAndroid Build Coastguard Worker failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
1292*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error),
1293*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.error);
1294*6236dae4SAndroid Build Coastguard Worker *err = CURLE_HTTP2_STREAM;
1295*6236dae4SAndroid Build Coastguard Worker return -1;
1296*6236dae4SAndroid Build Coastguard Worker }
1297*6236dae4SAndroid Build Coastguard Worker else if(ctx->tunnel.reset) {
1298*6236dae4SAndroid Build Coastguard Worker failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id);
1299*6236dae4SAndroid Build Coastguard Worker *err = CURLE_RECV_ERROR;
1300*6236dae4SAndroid Build Coastguard Worker return -1;
1301*6236dae4SAndroid Build Coastguard Worker }
1302*6236dae4SAndroid Build Coastguard Worker
1303*6236dae4SAndroid Build Coastguard Worker *err = CURLE_OK;
1304*6236dae4SAndroid Build Coastguard Worker rv = 0;
1305*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> %zd, %d",
1306*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.stream_id, rv, *err);
1307*6236dae4SAndroid Build Coastguard Worker return rv;
1308*6236dae4SAndroid Build Coastguard Worker }
1309*6236dae4SAndroid Build Coastguard Worker
tunnel_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)1310*6236dae4SAndroid Build Coastguard Worker static ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1311*6236dae4SAndroid Build Coastguard Worker char *buf, size_t len, CURLcode *err)
1312*6236dae4SAndroid Build Coastguard Worker {
1313*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1314*6236dae4SAndroid Build Coastguard Worker ssize_t nread = -1;
1315*6236dae4SAndroid Build Coastguard Worker
1316*6236dae4SAndroid Build Coastguard Worker *err = CURLE_AGAIN;
1317*6236dae4SAndroid Build Coastguard Worker if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) {
1318*6236dae4SAndroid Build Coastguard Worker nread = Curl_bufq_read(&ctx->tunnel.recvbuf,
1319*6236dae4SAndroid Build Coastguard Worker (unsigned char *)buf, len, err);
1320*6236dae4SAndroid Build Coastguard Worker if(nread < 0)
1321*6236dae4SAndroid Build Coastguard Worker goto out;
1322*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(nread > 0);
1323*6236dae4SAndroid Build Coastguard Worker }
1324*6236dae4SAndroid Build Coastguard Worker
1325*6236dae4SAndroid Build Coastguard Worker if(nread < 0) {
1326*6236dae4SAndroid Build Coastguard Worker if(ctx->tunnel.closed) {
1327*6236dae4SAndroid Build Coastguard Worker nread = h2_handle_tunnel_close(cf, data, err);
1328*6236dae4SAndroid Build Coastguard Worker }
1329*6236dae4SAndroid Build Coastguard Worker else if(ctx->tunnel.reset ||
1330*6236dae4SAndroid Build Coastguard Worker (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
1331*6236dae4SAndroid Build Coastguard Worker (ctx->rcvd_goaway &&
1332*6236dae4SAndroid Build Coastguard Worker ctx->last_stream_id < ctx->tunnel.stream_id)) {
1333*6236dae4SAndroid Build Coastguard Worker *err = CURLE_RECV_ERROR;
1334*6236dae4SAndroid Build Coastguard Worker nread = -1;
1335*6236dae4SAndroid Build Coastguard Worker }
1336*6236dae4SAndroid Build Coastguard Worker }
1337*6236dae4SAndroid Build Coastguard Worker else if(nread == 0) {
1338*6236dae4SAndroid Build Coastguard Worker *err = CURLE_AGAIN;
1339*6236dae4SAndroid Build Coastguard Worker nread = -1;
1340*6236dae4SAndroid Build Coastguard Worker }
1341*6236dae4SAndroid Build Coastguard Worker
1342*6236dae4SAndroid Build Coastguard Worker out:
1343*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %zd, %d",
1344*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.stream_id, len, nread, *err);
1345*6236dae4SAndroid Build Coastguard Worker return nread;
1346*6236dae4SAndroid Build Coastguard Worker }
1347*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)1348*6236dae4SAndroid Build Coastguard Worker static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf,
1349*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1350*6236dae4SAndroid Build Coastguard Worker char *buf, size_t len, CURLcode *err)
1351*6236dae4SAndroid Build Coastguard Worker {
1352*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1353*6236dae4SAndroid Build Coastguard Worker ssize_t nread = -1;
1354*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save;
1355*6236dae4SAndroid Build Coastguard Worker CURLcode result;
1356*6236dae4SAndroid Build Coastguard Worker
1357*6236dae4SAndroid Build Coastguard Worker if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
1358*6236dae4SAndroid Build Coastguard Worker *err = CURLE_RECV_ERROR;
1359*6236dae4SAndroid Build Coastguard Worker return -1;
1360*6236dae4SAndroid Build Coastguard Worker }
1361*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1362*6236dae4SAndroid Build Coastguard Worker
1363*6236dae4SAndroid Build Coastguard Worker if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) {
1364*6236dae4SAndroid Build Coastguard Worker *err = proxy_h2_progress_ingress(cf, data);
1365*6236dae4SAndroid Build Coastguard Worker if(*err)
1366*6236dae4SAndroid Build Coastguard Worker goto out;
1367*6236dae4SAndroid Build Coastguard Worker }
1368*6236dae4SAndroid Build Coastguard Worker
1369*6236dae4SAndroid Build Coastguard Worker nread = tunnel_recv(cf, data, buf, len, err);
1370*6236dae4SAndroid Build Coastguard Worker
1371*6236dae4SAndroid Build Coastguard Worker if(nread > 0) {
1372*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] increase window by %zd",
1373*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.stream_id, nread);
1374*6236dae4SAndroid Build Coastguard Worker nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread);
1375*6236dae4SAndroid Build Coastguard Worker }
1376*6236dae4SAndroid Build Coastguard Worker
1377*6236dae4SAndroid Build Coastguard Worker result = proxy_h2_progress_egress(cf, data);
1378*6236dae4SAndroid Build Coastguard Worker if(result && (result != CURLE_AGAIN)) {
1379*6236dae4SAndroid Build Coastguard Worker *err = result;
1380*6236dae4SAndroid Build Coastguard Worker nread = -1;
1381*6236dae4SAndroid Build Coastguard Worker }
1382*6236dae4SAndroid Build Coastguard Worker
1383*6236dae4SAndroid Build Coastguard Worker out:
1384*6236dae4SAndroid Build Coastguard Worker if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
1385*6236dae4SAndroid Build Coastguard Worker (nread >= 0 || *err == CURLE_AGAIN)) {
1386*6236dae4SAndroid Build Coastguard Worker /* data pending and no fatal error to report. Need to trigger
1387*6236dae4SAndroid Build Coastguard Worker * draining to avoid stalling when no socket events happen. */
1388*6236dae4SAndroid Build Coastguard Worker drain_tunnel(cf, data, &ctx->tunnel);
1389*6236dae4SAndroid Build Coastguard Worker }
1390*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d",
1391*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.stream_id, len, nread, *err);
1392*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1393*6236dae4SAndroid Build Coastguard Worker return nread;
1394*6236dae4SAndroid Build Coastguard Worker }
1395*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,bool eos,CURLcode * err)1396*6236dae4SAndroid Build Coastguard Worker static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf,
1397*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1398*6236dae4SAndroid Build Coastguard Worker const void *buf, size_t len, bool eos,
1399*6236dae4SAndroid Build Coastguard Worker CURLcode *err)
1400*6236dae4SAndroid Build Coastguard Worker {
1401*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1402*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save;
1403*6236dae4SAndroid Build Coastguard Worker int rv;
1404*6236dae4SAndroid Build Coastguard Worker ssize_t nwritten;
1405*6236dae4SAndroid Build Coastguard Worker CURLcode result;
1406*6236dae4SAndroid Build Coastguard Worker
1407*6236dae4SAndroid Build Coastguard Worker (void)eos; /* TODO, maybe useful for blocks? */
1408*6236dae4SAndroid Build Coastguard Worker if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
1409*6236dae4SAndroid Build Coastguard Worker *err = CURLE_SEND_ERROR;
1410*6236dae4SAndroid Build Coastguard Worker return -1;
1411*6236dae4SAndroid Build Coastguard Worker }
1412*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1413*6236dae4SAndroid Build Coastguard Worker
1414*6236dae4SAndroid Build Coastguard Worker if(ctx->tunnel.closed) {
1415*6236dae4SAndroid Build Coastguard Worker nwritten = -1;
1416*6236dae4SAndroid Build Coastguard Worker *err = CURLE_SEND_ERROR;
1417*6236dae4SAndroid Build Coastguard Worker goto out;
1418*6236dae4SAndroid Build Coastguard Worker }
1419*6236dae4SAndroid Build Coastguard Worker else {
1420*6236dae4SAndroid Build Coastguard Worker nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err);
1421*6236dae4SAndroid Build Coastguard Worker if(nwritten < 0 && (*err != CURLE_AGAIN))
1422*6236dae4SAndroid Build Coastguard Worker goto out;
1423*6236dae4SAndroid Build Coastguard Worker }
1424*6236dae4SAndroid Build Coastguard Worker
1425*6236dae4SAndroid Build Coastguard Worker if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
1426*6236dae4SAndroid Build Coastguard Worker /* req body data is buffered, resume the potentially suspended stream */
1427*6236dae4SAndroid Build Coastguard Worker rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id);
1428*6236dae4SAndroid Build Coastguard Worker if(nghttp2_is_fatal(rv)) {
1429*6236dae4SAndroid Build Coastguard Worker *err = CURLE_SEND_ERROR;
1430*6236dae4SAndroid Build Coastguard Worker nwritten = -1;
1431*6236dae4SAndroid Build Coastguard Worker goto out;
1432*6236dae4SAndroid Build Coastguard Worker }
1433*6236dae4SAndroid Build Coastguard Worker }
1434*6236dae4SAndroid Build Coastguard Worker
1435*6236dae4SAndroid Build Coastguard Worker result = proxy_h2_progress_ingress(cf, data);
1436*6236dae4SAndroid Build Coastguard Worker if(result) {
1437*6236dae4SAndroid Build Coastguard Worker *err = result;
1438*6236dae4SAndroid Build Coastguard Worker nwritten = -1;
1439*6236dae4SAndroid Build Coastguard Worker goto out;
1440*6236dae4SAndroid Build Coastguard Worker }
1441*6236dae4SAndroid Build Coastguard Worker
1442*6236dae4SAndroid Build Coastguard Worker /* Call the nghttp2 send loop and flush to write ALL buffered data,
1443*6236dae4SAndroid Build Coastguard Worker * headers and/or request body completely out to the network */
1444*6236dae4SAndroid Build Coastguard Worker result = proxy_h2_progress_egress(cf, data);
1445*6236dae4SAndroid Build Coastguard Worker if(result && (result != CURLE_AGAIN)) {
1446*6236dae4SAndroid Build Coastguard Worker *err = result;
1447*6236dae4SAndroid Build Coastguard Worker nwritten = -1;
1448*6236dae4SAndroid Build Coastguard Worker goto out;
1449*6236dae4SAndroid Build Coastguard Worker }
1450*6236dae4SAndroid Build Coastguard Worker
1451*6236dae4SAndroid Build Coastguard Worker if(proxy_h2_should_close_session(ctx)) {
1452*6236dae4SAndroid Build Coastguard Worker /* nghttp2 thinks this session is done. If the stream has not been
1453*6236dae4SAndroid Build Coastguard Worker * closed, this is an error state for out transfer */
1454*6236dae4SAndroid Build Coastguard Worker if(ctx->tunnel.closed) {
1455*6236dae4SAndroid Build Coastguard Worker *err = CURLE_SEND_ERROR;
1456*6236dae4SAndroid Build Coastguard Worker nwritten = -1;
1457*6236dae4SAndroid Build Coastguard Worker }
1458*6236dae4SAndroid Build Coastguard Worker else {
1459*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session");
1460*6236dae4SAndroid Build Coastguard Worker *err = CURLE_HTTP2;
1461*6236dae4SAndroid Build Coastguard Worker nwritten = -1;
1462*6236dae4SAndroid Build Coastguard Worker }
1463*6236dae4SAndroid Build Coastguard Worker }
1464*6236dae4SAndroid Build Coastguard Worker
1465*6236dae4SAndroid Build Coastguard Worker out:
1466*6236dae4SAndroid Build Coastguard Worker if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
1467*6236dae4SAndroid Build Coastguard Worker (nwritten >= 0 || *err == CURLE_AGAIN)) {
1468*6236dae4SAndroid Build Coastguard Worker /* data pending and no fatal error to report. Need to trigger
1469*6236dae4SAndroid Build Coastguard Worker * draining to avoid stalling when no socket events happen. */
1470*6236dae4SAndroid Build Coastguard Worker drain_tunnel(cf, data, &ctx->tunnel);
1471*6236dae4SAndroid Build Coastguard Worker }
1472*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
1473*6236dae4SAndroid Build Coastguard Worker "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)",
1474*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.stream_id, len, nwritten, *err,
1475*6236dae4SAndroid Build Coastguard Worker nghttp2_session_get_stream_remote_window_size(
1476*6236dae4SAndroid Build Coastguard Worker ctx->h2, ctx->tunnel.stream_id),
1477*6236dae4SAndroid Build Coastguard Worker nghttp2_session_get_remote_window_size(ctx->h2),
1478*6236dae4SAndroid Build Coastguard Worker Curl_bufq_len(&ctx->tunnel.sendbuf),
1479*6236dae4SAndroid Build Coastguard Worker Curl_bufq_len(&ctx->outbufq));
1480*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1481*6236dae4SAndroid Build Coastguard Worker return nwritten;
1482*6236dae4SAndroid Build Coastguard Worker }
1483*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_flush(struct Curl_cfilter * cf,struct Curl_easy * data)1484*6236dae4SAndroid Build Coastguard Worker static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf,
1485*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data)
1486*6236dae4SAndroid Build Coastguard Worker {
1487*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1488*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save;
1489*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
1490*6236dae4SAndroid Build Coastguard Worker
1491*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1492*6236dae4SAndroid Build Coastguard Worker if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
1493*6236dae4SAndroid Build Coastguard Worker /* resume the potentially suspended tunnel */
1494*6236dae4SAndroid Build Coastguard Worker int rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id);
1495*6236dae4SAndroid Build Coastguard Worker if(nghttp2_is_fatal(rv)) {
1496*6236dae4SAndroid Build Coastguard Worker result = CURLE_SEND_ERROR;
1497*6236dae4SAndroid Build Coastguard Worker goto out;
1498*6236dae4SAndroid Build Coastguard Worker }
1499*6236dae4SAndroid Build Coastguard Worker }
1500*6236dae4SAndroid Build Coastguard Worker
1501*6236dae4SAndroid Build Coastguard Worker result = proxy_h2_progress_egress(cf, data);
1502*6236dae4SAndroid Build Coastguard Worker
1503*6236dae4SAndroid Build Coastguard Worker out:
1504*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[%d] flush -> %d, "
1505*6236dae4SAndroid Build Coastguard Worker "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)",
1506*6236dae4SAndroid Build Coastguard Worker ctx->tunnel.stream_id, result,
1507*6236dae4SAndroid Build Coastguard Worker nghttp2_session_get_stream_remote_window_size(
1508*6236dae4SAndroid Build Coastguard Worker ctx->h2, ctx->tunnel.stream_id),
1509*6236dae4SAndroid Build Coastguard Worker nghttp2_session_get_remote_window_size(ctx->h2),
1510*6236dae4SAndroid Build Coastguard Worker Curl_bufq_len(&ctx->tunnel.sendbuf),
1511*6236dae4SAndroid Build Coastguard Worker Curl_bufq_len(&ctx->outbufq));
1512*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1513*6236dae4SAndroid Build Coastguard Worker return result;
1514*6236dae4SAndroid Build Coastguard Worker }
1515*6236dae4SAndroid Build Coastguard Worker
proxy_h2_connisalive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)1516*6236dae4SAndroid Build Coastguard Worker static bool proxy_h2_connisalive(struct Curl_cfilter *cf,
1517*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1518*6236dae4SAndroid Build Coastguard Worker bool *input_pending)
1519*6236dae4SAndroid Build Coastguard Worker {
1520*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1521*6236dae4SAndroid Build Coastguard Worker bool alive = TRUE;
1522*6236dae4SAndroid Build Coastguard Worker
1523*6236dae4SAndroid Build Coastguard Worker *input_pending = FALSE;
1524*6236dae4SAndroid Build Coastguard Worker if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
1525*6236dae4SAndroid Build Coastguard Worker return FALSE;
1526*6236dae4SAndroid Build Coastguard Worker
1527*6236dae4SAndroid Build Coastguard Worker if(*input_pending) {
1528*6236dae4SAndroid Build Coastguard Worker /* This happens before we have sent off a request and the connection is
1529*6236dae4SAndroid Build Coastguard Worker not in use by any other transfer, there should not be any data here,
1530*6236dae4SAndroid Build Coastguard Worker only "protocol frames" */
1531*6236dae4SAndroid Build Coastguard Worker CURLcode result;
1532*6236dae4SAndroid Build Coastguard Worker ssize_t nread = -1;
1533*6236dae4SAndroid Build Coastguard Worker
1534*6236dae4SAndroid Build Coastguard Worker *input_pending = FALSE;
1535*6236dae4SAndroid Build Coastguard Worker nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result);
1536*6236dae4SAndroid Build Coastguard Worker if(nread != -1) {
1537*6236dae4SAndroid Build Coastguard Worker if(proxy_h2_process_pending_input(cf, data, &result) < 0)
1538*6236dae4SAndroid Build Coastguard Worker /* immediate error, considered dead */
1539*6236dae4SAndroid Build Coastguard Worker alive = FALSE;
1540*6236dae4SAndroid Build Coastguard Worker else {
1541*6236dae4SAndroid Build Coastguard Worker alive = !proxy_h2_should_close_session(ctx);
1542*6236dae4SAndroid Build Coastguard Worker }
1543*6236dae4SAndroid Build Coastguard Worker }
1544*6236dae4SAndroid Build Coastguard Worker else if(result != CURLE_AGAIN) {
1545*6236dae4SAndroid Build Coastguard Worker /* the read failed so let's say this is dead anyway */
1546*6236dae4SAndroid Build Coastguard Worker alive = FALSE;
1547*6236dae4SAndroid Build Coastguard Worker }
1548*6236dae4SAndroid Build Coastguard Worker }
1549*6236dae4SAndroid Build Coastguard Worker
1550*6236dae4SAndroid Build Coastguard Worker return alive;
1551*6236dae4SAndroid Build Coastguard Worker }
1552*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)1553*6236dae4SAndroid Build Coastguard Worker static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf,
1554*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1555*6236dae4SAndroid Build Coastguard Worker bool *input_pending)
1556*6236dae4SAndroid Build Coastguard Worker {
1557*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1558*6236dae4SAndroid Build Coastguard Worker CURLcode result;
1559*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save;
1560*6236dae4SAndroid Build Coastguard Worker
1561*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1562*6236dae4SAndroid Build Coastguard Worker result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending));
1563*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d",
1564*6236dae4SAndroid Build Coastguard Worker result, *input_pending);
1565*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1566*6236dae4SAndroid Build Coastguard Worker return result;
1567*6236dae4SAndroid Build Coastguard Worker }
1568*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)1569*6236dae4SAndroid Build Coastguard Worker static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf,
1570*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1571*6236dae4SAndroid Build Coastguard Worker int query, int *pres1, void *pres2)
1572*6236dae4SAndroid Build Coastguard Worker {
1573*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx = cf->ctx;
1574*6236dae4SAndroid Build Coastguard Worker
1575*6236dae4SAndroid Build Coastguard Worker switch(query) {
1576*6236dae4SAndroid Build Coastguard Worker case CF_QUERY_NEED_FLUSH: {
1577*6236dae4SAndroid Build Coastguard Worker if(!Curl_bufq_is_empty(&ctx->outbufq) ||
1578*6236dae4SAndroid Build Coastguard Worker !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
1579*6236dae4SAndroid Build Coastguard Worker CURL_TRC_CF(data, cf, "needs flush");
1580*6236dae4SAndroid Build Coastguard Worker *pres1 = TRUE;
1581*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
1582*6236dae4SAndroid Build Coastguard Worker }
1583*6236dae4SAndroid Build Coastguard Worker break;
1584*6236dae4SAndroid Build Coastguard Worker }
1585*6236dae4SAndroid Build Coastguard Worker default:
1586*6236dae4SAndroid Build Coastguard Worker break;
1587*6236dae4SAndroid Build Coastguard Worker }
1588*6236dae4SAndroid Build Coastguard Worker return cf->next ?
1589*6236dae4SAndroid Build Coastguard Worker cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1590*6236dae4SAndroid Build Coastguard Worker CURLE_UNKNOWN_OPTION;
1591*6236dae4SAndroid Build Coastguard Worker }
1592*6236dae4SAndroid Build Coastguard Worker
cf_h2_proxy_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)1593*6236dae4SAndroid Build Coastguard Worker static CURLcode cf_h2_proxy_cntrl(struct Curl_cfilter *cf,
1594*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data,
1595*6236dae4SAndroid Build Coastguard Worker int event, int arg1, void *arg2)
1596*6236dae4SAndroid Build Coastguard Worker {
1597*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
1598*6236dae4SAndroid Build Coastguard Worker struct cf_call_data save;
1599*6236dae4SAndroid Build Coastguard Worker
1600*6236dae4SAndroid Build Coastguard Worker (void)arg1;
1601*6236dae4SAndroid Build Coastguard Worker (void)arg2;
1602*6236dae4SAndroid Build Coastguard Worker
1603*6236dae4SAndroid Build Coastguard Worker switch(event) {
1604*6236dae4SAndroid Build Coastguard Worker case CF_CTRL_FLUSH:
1605*6236dae4SAndroid Build Coastguard Worker CF_DATA_SAVE(save, cf, data);
1606*6236dae4SAndroid Build Coastguard Worker result = cf_h2_proxy_flush(cf, data);
1607*6236dae4SAndroid Build Coastguard Worker CF_DATA_RESTORE(cf, save);
1608*6236dae4SAndroid Build Coastguard Worker break;
1609*6236dae4SAndroid Build Coastguard Worker default:
1610*6236dae4SAndroid Build Coastguard Worker break;
1611*6236dae4SAndroid Build Coastguard Worker }
1612*6236dae4SAndroid Build Coastguard Worker return result;
1613*6236dae4SAndroid Build Coastguard Worker }
1614*6236dae4SAndroid Build Coastguard Worker
1615*6236dae4SAndroid Build Coastguard Worker struct Curl_cftype Curl_cft_h2_proxy = {
1616*6236dae4SAndroid Build Coastguard Worker "H2-PROXY",
1617*6236dae4SAndroid Build Coastguard Worker CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
1618*6236dae4SAndroid Build Coastguard Worker CURL_LOG_LVL_NONE,
1619*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_destroy,
1620*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_connect,
1621*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_close,
1622*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_shutdown,
1623*6236dae4SAndroid Build Coastguard Worker Curl_cf_http_proxy_get_host,
1624*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_adjust_pollset,
1625*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_data_pending,
1626*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_send,
1627*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_recv,
1628*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_cntrl,
1629*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_is_alive,
1630*6236dae4SAndroid Build Coastguard Worker Curl_cf_def_conn_keep_alive,
1631*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_query,
1632*6236dae4SAndroid Build Coastguard Worker };
1633*6236dae4SAndroid Build Coastguard Worker
Curl_cf_h2_proxy_insert_after(struct Curl_cfilter * cf,struct Curl_easy * data)1634*6236dae4SAndroid Build Coastguard Worker CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf,
1635*6236dae4SAndroid Build Coastguard Worker struct Curl_easy *data)
1636*6236dae4SAndroid Build Coastguard Worker {
1637*6236dae4SAndroid Build Coastguard Worker struct Curl_cfilter *cf_h2_proxy = NULL;
1638*6236dae4SAndroid Build Coastguard Worker struct cf_h2_proxy_ctx *ctx;
1639*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OUT_OF_MEMORY;
1640*6236dae4SAndroid Build Coastguard Worker
1641*6236dae4SAndroid Build Coastguard Worker (void)data;
1642*6236dae4SAndroid Build Coastguard Worker ctx = calloc(1, sizeof(*ctx));
1643*6236dae4SAndroid Build Coastguard Worker if(!ctx)
1644*6236dae4SAndroid Build Coastguard Worker goto out;
1645*6236dae4SAndroid Build Coastguard Worker
1646*6236dae4SAndroid Build Coastguard Worker result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx);
1647*6236dae4SAndroid Build Coastguard Worker if(result)
1648*6236dae4SAndroid Build Coastguard Worker goto out;
1649*6236dae4SAndroid Build Coastguard Worker
1650*6236dae4SAndroid Build Coastguard Worker Curl_conn_cf_insert_after(cf, cf_h2_proxy);
1651*6236dae4SAndroid Build Coastguard Worker result = CURLE_OK;
1652*6236dae4SAndroid Build Coastguard Worker
1653*6236dae4SAndroid Build Coastguard Worker out:
1654*6236dae4SAndroid Build Coastguard Worker if(result)
1655*6236dae4SAndroid Build Coastguard Worker cf_h2_proxy_ctx_free(ctx);
1656*6236dae4SAndroid Build Coastguard Worker return result;
1657*6236dae4SAndroid Build Coastguard Worker }
1658*6236dae4SAndroid Build Coastguard Worker
1659*6236dae4SAndroid Build Coastguard Worker #endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */
1660