xref: /aosp_15_r20/external/curl/lib/cf-h2-proxy.c (revision 6236dae45794135f37c4eb022389c904c8b0090d)
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