113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#include <nghttp2/nghttp2.h> 3013498266Sopenharmony_ci#include "urldata.h" 3113498266Sopenharmony_ci#include "cfilters.h" 3213498266Sopenharmony_ci#include "connect.h" 3313498266Sopenharmony_ci#include "curl_trc.h" 3413498266Sopenharmony_ci#include "bufq.h" 3513498266Sopenharmony_ci#include "dynbuf.h" 3613498266Sopenharmony_ci#include "dynhds.h" 3713498266Sopenharmony_ci#include "http1.h" 3813498266Sopenharmony_ci#include "http2.h" 3913498266Sopenharmony_ci#include "http_proxy.h" 4013498266Sopenharmony_ci#include "multiif.h" 4113498266Sopenharmony_ci#include "cf-h2-proxy.h" 4213498266Sopenharmony_ci 4313498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 4413498266Sopenharmony_ci#include "curl_printf.h" 4513498266Sopenharmony_ci#include "curl_memory.h" 4613498266Sopenharmony_ci#include "memdebug.h" 4713498266Sopenharmony_ci 4813498266Sopenharmony_ci#define PROXY_H2_CHUNK_SIZE (16*1024) 4913498266Sopenharmony_ci 5013498266Sopenharmony_ci#define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024) 5113498266Sopenharmony_ci#define H2_TUNNEL_WINDOW_SIZE (10 * 1024 * 1024) 5213498266Sopenharmony_ci 5313498266Sopenharmony_ci#define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) 5413498266Sopenharmony_ci#define PROXY_H2_NW_SEND_CHUNKS 1 5513498266Sopenharmony_ci 5613498266Sopenharmony_ci#define H2_TUNNEL_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) 5713498266Sopenharmony_ci#define H2_TUNNEL_SEND_CHUNKS ((128 * 1024) / PROXY_H2_CHUNK_SIZE) 5813498266Sopenharmony_ci 5913498266Sopenharmony_ci 6013498266Sopenharmony_citypedef enum { 6113498266Sopenharmony_ci H2_TUNNEL_INIT, /* init/default/no tunnel state */ 6213498266Sopenharmony_ci H2_TUNNEL_CONNECT, /* CONNECT request is being send */ 6313498266Sopenharmony_ci H2_TUNNEL_RESPONSE, /* CONNECT response received completely */ 6413498266Sopenharmony_ci H2_TUNNEL_ESTABLISHED, 6513498266Sopenharmony_ci H2_TUNNEL_FAILED 6613498266Sopenharmony_ci} h2_tunnel_state; 6713498266Sopenharmony_ci 6813498266Sopenharmony_cistruct tunnel_stream { 6913498266Sopenharmony_ci struct http_resp *resp; 7013498266Sopenharmony_ci struct bufq recvbuf; 7113498266Sopenharmony_ci struct bufq sendbuf; 7213498266Sopenharmony_ci char *authority; 7313498266Sopenharmony_ci int32_t stream_id; 7413498266Sopenharmony_ci uint32_t error; 7513498266Sopenharmony_ci size_t upload_blocked_len; 7613498266Sopenharmony_ci h2_tunnel_state state; 7713498266Sopenharmony_ci BIT(has_final_response); 7813498266Sopenharmony_ci BIT(closed); 7913498266Sopenharmony_ci BIT(reset); 8013498266Sopenharmony_ci}; 8113498266Sopenharmony_ci 8213498266Sopenharmony_cistatic CURLcode tunnel_stream_init(struct Curl_cfilter *cf, 8313498266Sopenharmony_ci struct tunnel_stream *ts) 8413498266Sopenharmony_ci{ 8513498266Sopenharmony_ci const char *hostname; 8613498266Sopenharmony_ci int port; 8713498266Sopenharmony_ci bool ipv6_ip; 8813498266Sopenharmony_ci CURLcode result; 8913498266Sopenharmony_ci 9013498266Sopenharmony_ci ts->state = H2_TUNNEL_INIT; 9113498266Sopenharmony_ci ts->stream_id = -1; 9213498266Sopenharmony_ci Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, 9313498266Sopenharmony_ci BUFQ_OPT_SOFT_LIMIT); 9413498266Sopenharmony_ci Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); 9513498266Sopenharmony_ci 9613498266Sopenharmony_ci result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); 9713498266Sopenharmony_ci if(result) 9813498266Sopenharmony_ci return result; 9913498266Sopenharmony_ci 10013498266Sopenharmony_ci ts->authority = /* host:port with IPv6 support */ 10113498266Sopenharmony_ci aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", port); 10213498266Sopenharmony_ci if(!ts->authority) 10313498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 10413498266Sopenharmony_ci 10513498266Sopenharmony_ci return CURLE_OK; 10613498266Sopenharmony_ci} 10713498266Sopenharmony_ci 10813498266Sopenharmony_cistatic void tunnel_stream_clear(struct tunnel_stream *ts) 10913498266Sopenharmony_ci{ 11013498266Sopenharmony_ci Curl_http_resp_free(ts->resp); 11113498266Sopenharmony_ci Curl_bufq_free(&ts->recvbuf); 11213498266Sopenharmony_ci Curl_bufq_free(&ts->sendbuf); 11313498266Sopenharmony_ci Curl_safefree(ts->authority); 11413498266Sopenharmony_ci memset(ts, 0, sizeof(*ts)); 11513498266Sopenharmony_ci ts->state = H2_TUNNEL_INIT; 11613498266Sopenharmony_ci} 11713498266Sopenharmony_ci 11813498266Sopenharmony_cistatic void h2_tunnel_go_state(struct Curl_cfilter *cf, 11913498266Sopenharmony_ci struct tunnel_stream *ts, 12013498266Sopenharmony_ci h2_tunnel_state new_state, 12113498266Sopenharmony_ci struct Curl_easy *data) 12213498266Sopenharmony_ci{ 12313498266Sopenharmony_ci (void)cf; 12413498266Sopenharmony_ci 12513498266Sopenharmony_ci if(ts->state == new_state) 12613498266Sopenharmony_ci return; 12713498266Sopenharmony_ci /* leaving this one */ 12813498266Sopenharmony_ci switch(ts->state) { 12913498266Sopenharmony_ci case H2_TUNNEL_CONNECT: 13013498266Sopenharmony_ci data->req.ignorebody = FALSE; 13113498266Sopenharmony_ci break; 13213498266Sopenharmony_ci default: 13313498266Sopenharmony_ci break; 13413498266Sopenharmony_ci } 13513498266Sopenharmony_ci /* entering this one */ 13613498266Sopenharmony_ci switch(new_state) { 13713498266Sopenharmony_ci case H2_TUNNEL_INIT: 13813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id); 13913498266Sopenharmony_ci tunnel_stream_clear(ts); 14013498266Sopenharmony_ci break; 14113498266Sopenharmony_ci 14213498266Sopenharmony_ci case H2_TUNNEL_CONNECT: 14313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] new tunnel state 'connect'", ts->stream_id); 14413498266Sopenharmony_ci ts->state = H2_TUNNEL_CONNECT; 14513498266Sopenharmony_ci break; 14613498266Sopenharmony_ci 14713498266Sopenharmony_ci case H2_TUNNEL_RESPONSE: 14813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] new tunnel state 'response'", ts->stream_id); 14913498266Sopenharmony_ci ts->state = H2_TUNNEL_RESPONSE; 15013498266Sopenharmony_ci break; 15113498266Sopenharmony_ci 15213498266Sopenharmony_ci case H2_TUNNEL_ESTABLISHED: 15313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'", 15413498266Sopenharmony_ci ts->stream_id); 15513498266Sopenharmony_ci infof(data, "CONNECT phase completed"); 15613498266Sopenharmony_ci data->state.authproxy.done = TRUE; 15713498266Sopenharmony_ci data->state.authproxy.multipass = FALSE; 15813498266Sopenharmony_ci FALLTHROUGH(); 15913498266Sopenharmony_ci case H2_TUNNEL_FAILED: 16013498266Sopenharmony_ci if(new_state == H2_TUNNEL_FAILED) 16113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] new tunnel state 'failed'", ts->stream_id); 16213498266Sopenharmony_ci ts->state = new_state; 16313498266Sopenharmony_ci /* If a proxy-authorization header was used for the proxy, then we should 16413498266Sopenharmony_ci make sure that it isn't accidentally used for the document request 16513498266Sopenharmony_ci after we've connected. So let's free and clear it here. */ 16613498266Sopenharmony_ci Curl_safefree(data->state.aptr.proxyuserpwd); 16713498266Sopenharmony_ci break; 16813498266Sopenharmony_ci } 16913498266Sopenharmony_ci} 17013498266Sopenharmony_ci 17113498266Sopenharmony_cistruct cf_h2_proxy_ctx { 17213498266Sopenharmony_ci nghttp2_session *h2; 17313498266Sopenharmony_ci /* The easy handle used in the current filter call, cleared at return */ 17413498266Sopenharmony_ci struct cf_call_data call_data; 17513498266Sopenharmony_ci 17613498266Sopenharmony_ci struct bufq inbufq; /* network receive buffer */ 17713498266Sopenharmony_ci struct bufq outbufq; /* network send buffer */ 17813498266Sopenharmony_ci 17913498266Sopenharmony_ci struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ 18013498266Sopenharmony_ci int32_t goaway_error; 18113498266Sopenharmony_ci int32_t last_stream_id; 18213498266Sopenharmony_ci BIT(conn_closed); 18313498266Sopenharmony_ci BIT(goaway); 18413498266Sopenharmony_ci BIT(nw_out_blocked); 18513498266Sopenharmony_ci}; 18613498266Sopenharmony_ci 18713498266Sopenharmony_ci/* How to access `call_data` from a cf_h2 filter */ 18813498266Sopenharmony_ci#undef CF_CTX_CALL_DATA 18913498266Sopenharmony_ci#define CF_CTX_CALL_DATA(cf) \ 19013498266Sopenharmony_ci ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data 19113498266Sopenharmony_ci 19213498266Sopenharmony_cistatic void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx) 19313498266Sopenharmony_ci{ 19413498266Sopenharmony_ci struct cf_call_data save = ctx->call_data; 19513498266Sopenharmony_ci 19613498266Sopenharmony_ci if(ctx->h2) { 19713498266Sopenharmony_ci nghttp2_session_del(ctx->h2); 19813498266Sopenharmony_ci } 19913498266Sopenharmony_ci Curl_bufq_free(&ctx->inbufq); 20013498266Sopenharmony_ci Curl_bufq_free(&ctx->outbufq); 20113498266Sopenharmony_ci tunnel_stream_clear(&ctx->tunnel); 20213498266Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 20313498266Sopenharmony_ci ctx->call_data = save; 20413498266Sopenharmony_ci} 20513498266Sopenharmony_ci 20613498266Sopenharmony_cistatic void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) 20713498266Sopenharmony_ci{ 20813498266Sopenharmony_ci if(ctx) { 20913498266Sopenharmony_ci cf_h2_proxy_ctx_clear(ctx); 21013498266Sopenharmony_ci free(ctx); 21113498266Sopenharmony_ci } 21213498266Sopenharmony_ci} 21313498266Sopenharmony_ci 21413498266Sopenharmony_cistatic void drain_tunnel(struct Curl_cfilter *cf, 21513498266Sopenharmony_ci struct Curl_easy *data, 21613498266Sopenharmony_ci struct tunnel_stream *tunnel) 21713498266Sopenharmony_ci{ 21813498266Sopenharmony_ci unsigned char bits; 21913498266Sopenharmony_ci 22013498266Sopenharmony_ci (void)cf; 22113498266Sopenharmony_ci bits = CURL_CSELECT_IN; 22213498266Sopenharmony_ci if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len) 22313498266Sopenharmony_ci bits |= CURL_CSELECT_OUT; 22413498266Sopenharmony_ci if(data->state.select_bits != bits) { 22513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x", 22613498266Sopenharmony_ci tunnel->stream_id, bits); 22713498266Sopenharmony_ci data->state.select_bits = bits; 22813498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 22913498266Sopenharmony_ci } 23013498266Sopenharmony_ci} 23113498266Sopenharmony_ci 23213498266Sopenharmony_cistatic ssize_t proxy_nw_in_reader(void *reader_ctx, 23313498266Sopenharmony_ci unsigned char *buf, size_t buflen, 23413498266Sopenharmony_ci CURLcode *err) 23513498266Sopenharmony_ci{ 23613498266Sopenharmony_ci struct Curl_cfilter *cf = reader_ctx; 23713498266Sopenharmony_ci ssize_t nread; 23813498266Sopenharmony_ci 23913498266Sopenharmony_ci if(cf) { 24013498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 24113498266Sopenharmony_ci nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); 24213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] nw_in_reader(len=%zu) -> %zd, %d", 24313498266Sopenharmony_ci buflen, nread, *err); 24413498266Sopenharmony_ci } 24513498266Sopenharmony_ci else { 24613498266Sopenharmony_ci nread = 0; 24713498266Sopenharmony_ci } 24813498266Sopenharmony_ci return nread; 24913498266Sopenharmony_ci} 25013498266Sopenharmony_ci 25113498266Sopenharmony_cistatic ssize_t proxy_h2_nw_out_writer(void *writer_ctx, 25213498266Sopenharmony_ci const unsigned char *buf, size_t buflen, 25313498266Sopenharmony_ci CURLcode *err) 25413498266Sopenharmony_ci{ 25513498266Sopenharmony_ci struct Curl_cfilter *cf = writer_ctx; 25613498266Sopenharmony_ci ssize_t nwritten; 25713498266Sopenharmony_ci 25813498266Sopenharmony_ci if(cf) { 25913498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 26013498266Sopenharmony_ci nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, 26113498266Sopenharmony_ci err); 26213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d", 26313498266Sopenharmony_ci buflen, nwritten, *err); 26413498266Sopenharmony_ci } 26513498266Sopenharmony_ci else { 26613498266Sopenharmony_ci nwritten = 0; 26713498266Sopenharmony_ci } 26813498266Sopenharmony_ci return nwritten; 26913498266Sopenharmony_ci} 27013498266Sopenharmony_ci 27113498266Sopenharmony_cistatic int proxy_h2_client_new(struct Curl_cfilter *cf, 27213498266Sopenharmony_ci nghttp2_session_callbacks *cbs) 27313498266Sopenharmony_ci{ 27413498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 27513498266Sopenharmony_ci nghttp2_option *o; 27613498266Sopenharmony_ci 27713498266Sopenharmony_ci int rc = nghttp2_option_new(&o); 27813498266Sopenharmony_ci if(rc) 27913498266Sopenharmony_ci return rc; 28013498266Sopenharmony_ci /* We handle window updates ourself to enforce buffer limits */ 28113498266Sopenharmony_ci nghttp2_option_set_no_auto_window_update(o, 1); 28213498266Sopenharmony_ci#if NGHTTP2_VERSION_NUM >= 0x013200 28313498266Sopenharmony_ci /* with 1.50.0 */ 28413498266Sopenharmony_ci /* turn off RFC 9113 leading and trailing white spaces validation against 28513498266Sopenharmony_ci HTTP field value. */ 28613498266Sopenharmony_ci nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); 28713498266Sopenharmony_ci#endif 28813498266Sopenharmony_ci rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); 28913498266Sopenharmony_ci nghttp2_option_del(o); 29013498266Sopenharmony_ci return rc; 29113498266Sopenharmony_ci} 29213498266Sopenharmony_ci 29313498266Sopenharmony_cistatic ssize_t on_session_send(nghttp2_session *h2, 29413498266Sopenharmony_ci const uint8_t *buf, size_t blen, 29513498266Sopenharmony_ci int flags, void *userp); 29613498266Sopenharmony_cistatic int proxy_h2_on_frame_recv(nghttp2_session *session, 29713498266Sopenharmony_ci const nghttp2_frame *frame, 29813498266Sopenharmony_ci void *userp); 29913498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 30013498266Sopenharmony_cistatic int proxy_h2_on_frame_send(nghttp2_session *session, 30113498266Sopenharmony_ci const nghttp2_frame *frame, 30213498266Sopenharmony_ci void *userp); 30313498266Sopenharmony_ci#endif 30413498266Sopenharmony_cistatic int proxy_h2_on_stream_close(nghttp2_session *session, 30513498266Sopenharmony_ci int32_t stream_id, 30613498266Sopenharmony_ci uint32_t error_code, void *userp); 30713498266Sopenharmony_cistatic int proxy_h2_on_header(nghttp2_session *session, 30813498266Sopenharmony_ci const nghttp2_frame *frame, 30913498266Sopenharmony_ci const uint8_t *name, size_t namelen, 31013498266Sopenharmony_ci const uint8_t *value, size_t valuelen, 31113498266Sopenharmony_ci uint8_t flags, 31213498266Sopenharmony_ci void *userp); 31313498266Sopenharmony_cistatic int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, 31413498266Sopenharmony_ci int32_t stream_id, 31513498266Sopenharmony_ci const uint8_t *mem, size_t len, void *userp); 31613498266Sopenharmony_ci 31713498266Sopenharmony_ci/* 31813498266Sopenharmony_ci * Initialize the cfilter context 31913498266Sopenharmony_ci */ 32013498266Sopenharmony_cistatic CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, 32113498266Sopenharmony_ci struct Curl_easy *data) 32213498266Sopenharmony_ci{ 32313498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 32413498266Sopenharmony_ci CURLcode result = CURLE_OUT_OF_MEMORY; 32513498266Sopenharmony_ci nghttp2_session_callbacks *cbs = NULL; 32613498266Sopenharmony_ci int rc; 32713498266Sopenharmony_ci 32813498266Sopenharmony_ci DEBUGASSERT(!ctx->h2); 32913498266Sopenharmony_ci memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); 33013498266Sopenharmony_ci 33113498266Sopenharmony_ci Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); 33213498266Sopenharmony_ci Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); 33313498266Sopenharmony_ci 33413498266Sopenharmony_ci if(tunnel_stream_init(cf, &ctx->tunnel)) 33513498266Sopenharmony_ci goto out; 33613498266Sopenharmony_ci 33713498266Sopenharmony_ci rc = nghttp2_session_callbacks_new(&cbs); 33813498266Sopenharmony_ci if(rc) { 33913498266Sopenharmony_ci failf(data, "Couldn't initialize nghttp2 callbacks"); 34013498266Sopenharmony_ci goto out; 34113498266Sopenharmony_ci } 34213498266Sopenharmony_ci 34313498266Sopenharmony_ci nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); 34413498266Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_recv_callback( 34513498266Sopenharmony_ci cbs, proxy_h2_on_frame_recv); 34613498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 34713498266Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_send_callback(cbs, 34813498266Sopenharmony_ci proxy_h2_on_frame_send); 34913498266Sopenharmony_ci#endif 35013498266Sopenharmony_ci nghttp2_session_callbacks_set_on_data_chunk_recv_callback( 35113498266Sopenharmony_ci cbs, tunnel_recv_callback); 35213498266Sopenharmony_ci nghttp2_session_callbacks_set_on_stream_close_callback( 35313498266Sopenharmony_ci cbs, proxy_h2_on_stream_close); 35413498266Sopenharmony_ci nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header); 35513498266Sopenharmony_ci 35613498266Sopenharmony_ci /* The nghttp2 session is not yet setup, do it */ 35713498266Sopenharmony_ci rc = proxy_h2_client_new(cf, cbs); 35813498266Sopenharmony_ci if(rc) { 35913498266Sopenharmony_ci failf(data, "Couldn't initialize nghttp2"); 36013498266Sopenharmony_ci goto out; 36113498266Sopenharmony_ci } 36213498266Sopenharmony_ci 36313498266Sopenharmony_ci { 36413498266Sopenharmony_ci nghttp2_settings_entry iv[3]; 36513498266Sopenharmony_ci 36613498266Sopenharmony_ci iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; 36713498266Sopenharmony_ci iv[0].value = Curl_multi_max_concurrent_streams(data->multi); 36813498266Sopenharmony_ci iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; 36913498266Sopenharmony_ci iv[1].value = H2_TUNNEL_WINDOW_SIZE; 37013498266Sopenharmony_ci iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; 37113498266Sopenharmony_ci iv[2].value = 0; 37213498266Sopenharmony_ci rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3); 37313498266Sopenharmony_ci if(rc) { 37413498266Sopenharmony_ci failf(data, "nghttp2_submit_settings() failed: %s(%d)", 37513498266Sopenharmony_ci nghttp2_strerror(rc), rc); 37613498266Sopenharmony_ci result = CURLE_HTTP2; 37713498266Sopenharmony_ci goto out; 37813498266Sopenharmony_ci } 37913498266Sopenharmony_ci } 38013498266Sopenharmony_ci 38113498266Sopenharmony_ci rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, 38213498266Sopenharmony_ci PROXY_HTTP2_HUGE_WINDOW_SIZE); 38313498266Sopenharmony_ci if(rc) { 38413498266Sopenharmony_ci failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", 38513498266Sopenharmony_ci nghttp2_strerror(rc), rc); 38613498266Sopenharmony_ci result = CURLE_HTTP2; 38713498266Sopenharmony_ci goto out; 38813498266Sopenharmony_ci } 38913498266Sopenharmony_ci 39013498266Sopenharmony_ci 39113498266Sopenharmony_ci /* all set, traffic will be send on connect */ 39213498266Sopenharmony_ci result = CURLE_OK; 39313498266Sopenharmony_ci 39413498266Sopenharmony_ciout: 39513498266Sopenharmony_ci if(cbs) 39613498266Sopenharmony_ci nghttp2_session_callbacks_del(cbs); 39713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); 39813498266Sopenharmony_ci return result; 39913498266Sopenharmony_ci} 40013498266Sopenharmony_ci 40113498266Sopenharmony_cistatic int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx) 40213498266Sopenharmony_ci{ 40313498266Sopenharmony_ci return !nghttp2_session_want_read(ctx->h2) && 40413498266Sopenharmony_ci !nghttp2_session_want_write(ctx->h2); 40513498266Sopenharmony_ci} 40613498266Sopenharmony_ci 40713498266Sopenharmony_cistatic CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf, 40813498266Sopenharmony_ci struct Curl_easy *data) 40913498266Sopenharmony_ci{ 41013498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 41113498266Sopenharmony_ci ssize_t nwritten; 41213498266Sopenharmony_ci CURLcode result; 41313498266Sopenharmony_ci 41413498266Sopenharmony_ci (void)data; 41513498266Sopenharmony_ci if(Curl_bufq_is_empty(&ctx->outbufq)) 41613498266Sopenharmony_ci return CURLE_OK; 41713498266Sopenharmony_ci 41813498266Sopenharmony_ci nwritten = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf, 41913498266Sopenharmony_ci &result); 42013498266Sopenharmony_ci if(nwritten < 0) { 42113498266Sopenharmony_ci if(result == CURLE_AGAIN) { 42213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN", 42313498266Sopenharmony_ci Curl_bufq_len(&ctx->outbufq)); 42413498266Sopenharmony_ci ctx->nw_out_blocked = 1; 42513498266Sopenharmony_ci } 42613498266Sopenharmony_ci return result; 42713498266Sopenharmony_ci } 42813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] nw send buffer flushed"); 42913498266Sopenharmony_ci return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; 43013498266Sopenharmony_ci} 43113498266Sopenharmony_ci 43213498266Sopenharmony_ci/* 43313498266Sopenharmony_ci * Processes pending input left in network input buffer. 43413498266Sopenharmony_ci * This function returns 0 if it succeeds, or -1 and error code will 43513498266Sopenharmony_ci * be assigned to *err. 43613498266Sopenharmony_ci */ 43713498266Sopenharmony_cistatic int proxy_h2_process_pending_input(struct Curl_cfilter *cf, 43813498266Sopenharmony_ci struct Curl_easy *data, 43913498266Sopenharmony_ci CURLcode *err) 44013498266Sopenharmony_ci{ 44113498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 44213498266Sopenharmony_ci const unsigned char *buf; 44313498266Sopenharmony_ci size_t blen; 44413498266Sopenharmony_ci ssize_t rv; 44513498266Sopenharmony_ci 44613498266Sopenharmony_ci while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { 44713498266Sopenharmony_ci 44813498266Sopenharmony_ci rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); 44913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] %zu bytes to nghttp2 -> %zd", blen, rv); 45013498266Sopenharmony_ci if(rv < 0) { 45113498266Sopenharmony_ci failf(data, 45213498266Sopenharmony_ci "process_pending_input: nghttp2_session_mem_recv() returned " 45313498266Sopenharmony_ci "%zd:%s", rv, nghttp2_strerror((int)rv)); 45413498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 45513498266Sopenharmony_ci return -1; 45613498266Sopenharmony_ci } 45713498266Sopenharmony_ci Curl_bufq_skip(&ctx->inbufq, (size_t)rv); 45813498266Sopenharmony_ci if(Curl_bufq_is_empty(&ctx->inbufq)) { 45913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] all data in connection buffer processed"); 46013498266Sopenharmony_ci break; 46113498266Sopenharmony_ci } 46213498266Sopenharmony_ci else { 46313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] process_pending_input: %zu bytes left " 46413498266Sopenharmony_ci "in connection buffer", Curl_bufq_len(&ctx->inbufq)); 46513498266Sopenharmony_ci } 46613498266Sopenharmony_ci } 46713498266Sopenharmony_ci 46813498266Sopenharmony_ci return 0; 46913498266Sopenharmony_ci} 47013498266Sopenharmony_ci 47113498266Sopenharmony_cistatic CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, 47213498266Sopenharmony_ci struct Curl_easy *data) 47313498266Sopenharmony_ci{ 47413498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 47513498266Sopenharmony_ci CURLcode result = CURLE_OK; 47613498266Sopenharmony_ci ssize_t nread; 47713498266Sopenharmony_ci 47813498266Sopenharmony_ci /* Process network input buffer fist */ 47913498266Sopenharmony_ci if(!Curl_bufq_is_empty(&ctx->inbufq)) { 48013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] process %zu bytes in connection buffer", 48113498266Sopenharmony_ci Curl_bufq_len(&ctx->inbufq)); 48213498266Sopenharmony_ci if(proxy_h2_process_pending_input(cf, data, &result) < 0) 48313498266Sopenharmony_ci return result; 48413498266Sopenharmony_ci } 48513498266Sopenharmony_ci 48613498266Sopenharmony_ci /* Receive data from the "lower" filters, e.g. network until 48713498266Sopenharmony_ci * it is time to stop or we have enough data for this stream */ 48813498266Sopenharmony_ci while(!ctx->conn_closed && /* not closed the connection */ 48913498266Sopenharmony_ci !ctx->tunnel.closed && /* nor the tunnel */ 49013498266Sopenharmony_ci Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */ 49113498266Sopenharmony_ci !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) { 49213498266Sopenharmony_ci 49313498266Sopenharmony_ci nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); 49413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %zd, %d", 49513498266Sopenharmony_ci Curl_bufq_len(&ctx->inbufq), nread, result); 49613498266Sopenharmony_ci if(nread < 0) { 49713498266Sopenharmony_ci if(result != CURLE_AGAIN) { 49813498266Sopenharmony_ci failf(data, "Failed receiving HTTP2 data"); 49913498266Sopenharmony_ci return result; 50013498266Sopenharmony_ci } 50113498266Sopenharmony_ci break; 50213498266Sopenharmony_ci } 50313498266Sopenharmony_ci else if(nread == 0) { 50413498266Sopenharmony_ci ctx->conn_closed = TRUE; 50513498266Sopenharmony_ci break; 50613498266Sopenharmony_ci } 50713498266Sopenharmony_ci 50813498266Sopenharmony_ci if(proxy_h2_process_pending_input(cf, data, &result)) 50913498266Sopenharmony_ci return result; 51013498266Sopenharmony_ci } 51113498266Sopenharmony_ci 51213498266Sopenharmony_ci if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { 51313498266Sopenharmony_ci connclose(cf->conn, "GOAWAY received"); 51413498266Sopenharmony_ci } 51513498266Sopenharmony_ci 51613498266Sopenharmony_ci return CURLE_OK; 51713498266Sopenharmony_ci} 51813498266Sopenharmony_ci 51913498266Sopenharmony_cistatic CURLcode proxy_h2_progress_egress(struct Curl_cfilter *cf, 52013498266Sopenharmony_ci struct Curl_easy *data) 52113498266Sopenharmony_ci{ 52213498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 52313498266Sopenharmony_ci int rv = 0; 52413498266Sopenharmony_ci 52513498266Sopenharmony_ci ctx->nw_out_blocked = 0; 52613498266Sopenharmony_ci while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2)) 52713498266Sopenharmony_ci rv = nghttp2_session_send(ctx->h2); 52813498266Sopenharmony_ci 52913498266Sopenharmony_ci if(nghttp2_is_fatal(rv)) { 53013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] nghttp2_session_send error (%s)%d", 53113498266Sopenharmony_ci nghttp2_strerror(rv), rv); 53213498266Sopenharmony_ci return CURLE_SEND_ERROR; 53313498266Sopenharmony_ci } 53413498266Sopenharmony_ci return proxy_h2_nw_out_flush(cf, data); 53513498266Sopenharmony_ci} 53613498266Sopenharmony_ci 53713498266Sopenharmony_cistatic ssize_t on_session_send(nghttp2_session *h2, 53813498266Sopenharmony_ci const uint8_t *buf, size_t blen, int flags, 53913498266Sopenharmony_ci void *userp) 54013498266Sopenharmony_ci{ 54113498266Sopenharmony_ci struct Curl_cfilter *cf = userp; 54213498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 54313498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 54413498266Sopenharmony_ci ssize_t nwritten; 54513498266Sopenharmony_ci CURLcode result = CURLE_OK; 54613498266Sopenharmony_ci 54713498266Sopenharmony_ci (void)h2; 54813498266Sopenharmony_ci (void)flags; 54913498266Sopenharmony_ci DEBUGASSERT(data); 55013498266Sopenharmony_ci 55113498266Sopenharmony_ci nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, 55213498266Sopenharmony_ci proxy_h2_nw_out_writer, cf, &result); 55313498266Sopenharmony_ci if(nwritten < 0) { 55413498266Sopenharmony_ci if(result == CURLE_AGAIN) { 55513498266Sopenharmony_ci return NGHTTP2_ERR_WOULDBLOCK; 55613498266Sopenharmony_ci } 55713498266Sopenharmony_ci failf(data, "Failed sending HTTP2 data"); 55813498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 55913498266Sopenharmony_ci } 56013498266Sopenharmony_ci 56113498266Sopenharmony_ci if(!nwritten) 56213498266Sopenharmony_ci return NGHTTP2_ERR_WOULDBLOCK; 56313498266Sopenharmony_ci 56413498266Sopenharmony_ci return nwritten; 56513498266Sopenharmony_ci} 56613498266Sopenharmony_ci 56713498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 56813498266Sopenharmony_cistatic int proxy_h2_fr_print(const nghttp2_frame *frame, 56913498266Sopenharmony_ci char *buffer, size_t blen) 57013498266Sopenharmony_ci{ 57113498266Sopenharmony_ci switch(frame->hd.type) { 57213498266Sopenharmony_ci case NGHTTP2_DATA: { 57313498266Sopenharmony_ci return msnprintf(buffer, blen, 57413498266Sopenharmony_ci "FRAME[DATA, len=%d, eos=%d, padlen=%d]", 57513498266Sopenharmony_ci (int)frame->hd.length, 57613498266Sopenharmony_ci !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), 57713498266Sopenharmony_ci (int)frame->data.padlen); 57813498266Sopenharmony_ci } 57913498266Sopenharmony_ci case NGHTTP2_HEADERS: { 58013498266Sopenharmony_ci return msnprintf(buffer, blen, 58113498266Sopenharmony_ci "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", 58213498266Sopenharmony_ci (int)frame->hd.length, 58313498266Sopenharmony_ci !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), 58413498266Sopenharmony_ci !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); 58513498266Sopenharmony_ci } 58613498266Sopenharmony_ci case NGHTTP2_PRIORITY: { 58713498266Sopenharmony_ci return msnprintf(buffer, blen, 58813498266Sopenharmony_ci "FRAME[PRIORITY, len=%d, flags=%d]", 58913498266Sopenharmony_ci (int)frame->hd.length, frame->hd.flags); 59013498266Sopenharmony_ci } 59113498266Sopenharmony_ci case NGHTTP2_RST_STREAM: { 59213498266Sopenharmony_ci return msnprintf(buffer, blen, 59313498266Sopenharmony_ci "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", 59413498266Sopenharmony_ci (int)frame->hd.length, frame->hd.flags, 59513498266Sopenharmony_ci frame->rst_stream.error_code); 59613498266Sopenharmony_ci } 59713498266Sopenharmony_ci case NGHTTP2_SETTINGS: { 59813498266Sopenharmony_ci if(frame->hd.flags & NGHTTP2_FLAG_ACK) { 59913498266Sopenharmony_ci return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); 60013498266Sopenharmony_ci } 60113498266Sopenharmony_ci return msnprintf(buffer, blen, 60213498266Sopenharmony_ci "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); 60313498266Sopenharmony_ci } 60413498266Sopenharmony_ci case NGHTTP2_PUSH_PROMISE: { 60513498266Sopenharmony_ci return msnprintf(buffer, blen, 60613498266Sopenharmony_ci "FRAME[PUSH_PROMISE, len=%d, hend=%d]", 60713498266Sopenharmony_ci (int)frame->hd.length, 60813498266Sopenharmony_ci !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); 60913498266Sopenharmony_ci } 61013498266Sopenharmony_ci case NGHTTP2_PING: { 61113498266Sopenharmony_ci return msnprintf(buffer, blen, 61213498266Sopenharmony_ci "FRAME[PING, len=%d, ack=%d]", 61313498266Sopenharmony_ci (int)frame->hd.length, 61413498266Sopenharmony_ci frame->hd.flags&NGHTTP2_FLAG_ACK); 61513498266Sopenharmony_ci } 61613498266Sopenharmony_ci case NGHTTP2_GOAWAY: { 61713498266Sopenharmony_ci char scratch[128]; 61813498266Sopenharmony_ci size_t s_len = sizeof(scratch)/sizeof(scratch[0]); 61913498266Sopenharmony_ci size_t len = (frame->goaway.opaque_data_len < s_len)? 62013498266Sopenharmony_ci frame->goaway.opaque_data_len : s_len-1; 62113498266Sopenharmony_ci if(len) 62213498266Sopenharmony_ci memcpy(scratch, frame->goaway.opaque_data, len); 62313498266Sopenharmony_ci scratch[len] = '\0'; 62413498266Sopenharmony_ci return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', " 62513498266Sopenharmony_ci "last_stream=%d]", frame->goaway.error_code, 62613498266Sopenharmony_ci scratch, frame->goaway.last_stream_id); 62713498266Sopenharmony_ci } 62813498266Sopenharmony_ci case NGHTTP2_WINDOW_UPDATE: { 62913498266Sopenharmony_ci return msnprintf(buffer, blen, 63013498266Sopenharmony_ci "FRAME[WINDOW_UPDATE, incr=%d]", 63113498266Sopenharmony_ci frame->window_update.window_size_increment); 63213498266Sopenharmony_ci } 63313498266Sopenharmony_ci default: 63413498266Sopenharmony_ci return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", 63513498266Sopenharmony_ci frame->hd.type, (int)frame->hd.length, 63613498266Sopenharmony_ci frame->hd.flags); 63713498266Sopenharmony_ci } 63813498266Sopenharmony_ci} 63913498266Sopenharmony_ci 64013498266Sopenharmony_cistatic int proxy_h2_on_frame_send(nghttp2_session *session, 64113498266Sopenharmony_ci const nghttp2_frame *frame, 64213498266Sopenharmony_ci void *userp) 64313498266Sopenharmony_ci{ 64413498266Sopenharmony_ci struct Curl_cfilter *cf = userp; 64513498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 64613498266Sopenharmony_ci 64713498266Sopenharmony_ci (void)session; 64813498266Sopenharmony_ci DEBUGASSERT(data); 64913498266Sopenharmony_ci if(data && Curl_trc_cf_is_verbose(cf, data)) { 65013498266Sopenharmony_ci char buffer[256]; 65113498266Sopenharmony_ci int len; 65213498266Sopenharmony_ci len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1); 65313498266Sopenharmony_ci buffer[len] = 0; 65413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); 65513498266Sopenharmony_ci } 65613498266Sopenharmony_ci return 0; 65713498266Sopenharmony_ci} 65813498266Sopenharmony_ci#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ 65913498266Sopenharmony_ci 66013498266Sopenharmony_cistatic int proxy_h2_on_frame_recv(nghttp2_session *session, 66113498266Sopenharmony_ci const nghttp2_frame *frame, 66213498266Sopenharmony_ci void *userp) 66313498266Sopenharmony_ci{ 66413498266Sopenharmony_ci struct Curl_cfilter *cf = userp; 66513498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 66613498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 66713498266Sopenharmony_ci int32_t stream_id = frame->hd.stream_id; 66813498266Sopenharmony_ci 66913498266Sopenharmony_ci (void)session; 67013498266Sopenharmony_ci DEBUGASSERT(data); 67113498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 67213498266Sopenharmony_ci if(Curl_trc_cf_is_verbose(cf, data)) { 67313498266Sopenharmony_ci char buffer[256]; 67413498266Sopenharmony_ci int len; 67513498266Sopenharmony_ci len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1); 67613498266Sopenharmony_ci buffer[len] = 0; 67713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer); 67813498266Sopenharmony_ci } 67913498266Sopenharmony_ci#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ 68013498266Sopenharmony_ci 68113498266Sopenharmony_ci if(!stream_id) { 68213498266Sopenharmony_ci /* stream ID zero is for connection-oriented stuff */ 68313498266Sopenharmony_ci DEBUGASSERT(data); 68413498266Sopenharmony_ci switch(frame->hd.type) { 68513498266Sopenharmony_ci case NGHTTP2_SETTINGS: 68613498266Sopenharmony_ci /* Since the initial stream window is 64K, a request might be on HOLD, 68713498266Sopenharmony_ci * due to exhaustion. The (initial) SETTINGS may announce a much larger 68813498266Sopenharmony_ci * window and *assume* that we treat this like a WINDOW_UPDATE. Some 68913498266Sopenharmony_ci * servers send an explicit WINDOW_UPDATE, but not all seem to do that. 69013498266Sopenharmony_ci * To be safe, we UNHOLD a stream in order not to stall. */ 69113498266Sopenharmony_ci if(CURL_WANT_SEND(data)) { 69213498266Sopenharmony_ci drain_tunnel(cf, data, &ctx->tunnel); 69313498266Sopenharmony_ci } 69413498266Sopenharmony_ci break; 69513498266Sopenharmony_ci case NGHTTP2_GOAWAY: 69613498266Sopenharmony_ci ctx->goaway = TRUE; 69713498266Sopenharmony_ci break; 69813498266Sopenharmony_ci default: 69913498266Sopenharmony_ci break; 70013498266Sopenharmony_ci } 70113498266Sopenharmony_ci return 0; 70213498266Sopenharmony_ci } 70313498266Sopenharmony_ci 70413498266Sopenharmony_ci if(stream_id != ctx->tunnel.stream_id) { 70513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] rcvd FRAME not for tunnel", stream_id); 70613498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 70713498266Sopenharmony_ci } 70813498266Sopenharmony_ci 70913498266Sopenharmony_ci switch(frame->hd.type) { 71013498266Sopenharmony_ci case NGHTTP2_HEADERS: 71113498266Sopenharmony_ci /* nghttp2 guarantees that :status is received, and we store it to 71213498266Sopenharmony_ci stream->status_code. Fuzzing has proven this can still be reached 71313498266Sopenharmony_ci without status code having been set. */ 71413498266Sopenharmony_ci if(!ctx->tunnel.resp) 71513498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 71613498266Sopenharmony_ci /* Only final status code signals the end of header */ 71713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] got http status: %d", 71813498266Sopenharmony_ci stream_id, ctx->tunnel.resp->status); 71913498266Sopenharmony_ci if(!ctx->tunnel.has_final_response) { 72013498266Sopenharmony_ci if(ctx->tunnel.resp->status / 100 != 1) { 72113498266Sopenharmony_ci ctx->tunnel.has_final_response = TRUE; 72213498266Sopenharmony_ci } 72313498266Sopenharmony_ci } 72413498266Sopenharmony_ci break; 72513498266Sopenharmony_ci case NGHTTP2_WINDOW_UPDATE: 72613498266Sopenharmony_ci if(CURL_WANT_SEND(data)) { 72713498266Sopenharmony_ci drain_tunnel(cf, data, &ctx->tunnel); 72813498266Sopenharmony_ci } 72913498266Sopenharmony_ci break; 73013498266Sopenharmony_ci default: 73113498266Sopenharmony_ci break; 73213498266Sopenharmony_ci } 73313498266Sopenharmony_ci return 0; 73413498266Sopenharmony_ci} 73513498266Sopenharmony_ci 73613498266Sopenharmony_cistatic int proxy_h2_on_header(nghttp2_session *session, 73713498266Sopenharmony_ci const nghttp2_frame *frame, 73813498266Sopenharmony_ci const uint8_t *name, size_t namelen, 73913498266Sopenharmony_ci const uint8_t *value, size_t valuelen, 74013498266Sopenharmony_ci uint8_t flags, 74113498266Sopenharmony_ci void *userp) 74213498266Sopenharmony_ci{ 74313498266Sopenharmony_ci struct Curl_cfilter *cf = userp; 74413498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 74513498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 74613498266Sopenharmony_ci int32_t stream_id = frame->hd.stream_id; 74713498266Sopenharmony_ci CURLcode result; 74813498266Sopenharmony_ci 74913498266Sopenharmony_ci (void)flags; 75013498266Sopenharmony_ci (void)data; 75113498266Sopenharmony_ci (void)session; 75213498266Sopenharmony_ci DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 75313498266Sopenharmony_ci if(stream_id != ctx->tunnel.stream_id) { 75413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] header for non-tunnel stream: " 75513498266Sopenharmony_ci "%.*s: %.*s", stream_id, 75613498266Sopenharmony_ci (int)namelen, name, (int)valuelen, value); 75713498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 75813498266Sopenharmony_ci } 75913498266Sopenharmony_ci 76013498266Sopenharmony_ci if(frame->hd.type == NGHTTP2_PUSH_PROMISE) 76113498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 76213498266Sopenharmony_ci 76313498266Sopenharmony_ci if(ctx->tunnel.has_final_response) { 76413498266Sopenharmony_ci /* we do not do anything with trailers for tunnel streams */ 76513498266Sopenharmony_ci return 0; 76613498266Sopenharmony_ci } 76713498266Sopenharmony_ci 76813498266Sopenharmony_ci if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && 76913498266Sopenharmony_ci memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { 77013498266Sopenharmony_ci int http_status; 77113498266Sopenharmony_ci struct http_resp *resp; 77213498266Sopenharmony_ci 77313498266Sopenharmony_ci /* status: always comes first, we might get more than one response, 77413498266Sopenharmony_ci * link the previous ones for keepers */ 77513498266Sopenharmony_ci result = Curl_http_decode_status(&http_status, 77613498266Sopenharmony_ci (const char *)value, valuelen); 77713498266Sopenharmony_ci if(result) 77813498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 77913498266Sopenharmony_ci result = Curl_http_resp_make(&resp, http_status, NULL); 78013498266Sopenharmony_ci if(result) 78113498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 78213498266Sopenharmony_ci resp->prev = ctx->tunnel.resp; 78313498266Sopenharmony_ci ctx->tunnel.resp = resp; 78413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", 78513498266Sopenharmony_ci stream_id, ctx->tunnel.resp->status); 78613498266Sopenharmony_ci return 0; 78713498266Sopenharmony_ci } 78813498266Sopenharmony_ci 78913498266Sopenharmony_ci if(!ctx->tunnel.resp) 79013498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 79113498266Sopenharmony_ci 79213498266Sopenharmony_ci result = Curl_dynhds_add(&ctx->tunnel.resp->headers, 79313498266Sopenharmony_ci (const char *)name, namelen, 79413498266Sopenharmony_ci (const char *)value, valuelen); 79513498266Sopenharmony_ci if(result) 79613498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 79713498266Sopenharmony_ci 79813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s", 79913498266Sopenharmony_ci stream_id, (int)namelen, name, (int)valuelen, value); 80013498266Sopenharmony_ci 80113498266Sopenharmony_ci return 0; /* 0 is successful */ 80213498266Sopenharmony_ci} 80313498266Sopenharmony_ci 80413498266Sopenharmony_cistatic ssize_t tunnel_send_callback(nghttp2_session *session, 80513498266Sopenharmony_ci int32_t stream_id, 80613498266Sopenharmony_ci uint8_t *buf, size_t length, 80713498266Sopenharmony_ci uint32_t *data_flags, 80813498266Sopenharmony_ci nghttp2_data_source *source, 80913498266Sopenharmony_ci void *userp) 81013498266Sopenharmony_ci{ 81113498266Sopenharmony_ci struct Curl_cfilter *cf = userp; 81213498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 81313498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 81413498266Sopenharmony_ci struct tunnel_stream *ts; 81513498266Sopenharmony_ci CURLcode result; 81613498266Sopenharmony_ci ssize_t nread; 81713498266Sopenharmony_ci 81813498266Sopenharmony_ci (void)source; 81913498266Sopenharmony_ci (void)data; 82013498266Sopenharmony_ci (void)ctx; 82113498266Sopenharmony_ci 82213498266Sopenharmony_ci if(!stream_id) 82313498266Sopenharmony_ci return NGHTTP2_ERR_INVALID_ARGUMENT; 82413498266Sopenharmony_ci 82513498266Sopenharmony_ci ts = nghttp2_session_get_stream_user_data(session, stream_id); 82613498266Sopenharmony_ci if(!ts) 82713498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 82813498266Sopenharmony_ci DEBUGASSERT(ts == &ctx->tunnel); 82913498266Sopenharmony_ci 83013498266Sopenharmony_ci nread = Curl_bufq_read(&ts->sendbuf, buf, length, &result); 83113498266Sopenharmony_ci if(nread < 0) { 83213498266Sopenharmony_ci if(result != CURLE_AGAIN) 83313498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 83413498266Sopenharmony_ci return NGHTTP2_ERR_DEFERRED; 83513498266Sopenharmony_ci } 83613498266Sopenharmony_ci if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf)) 83713498266Sopenharmony_ci *data_flags = NGHTTP2_DATA_FLAG_EOF; 83813498266Sopenharmony_ci 83913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd", 84013498266Sopenharmony_ci ts->stream_id, nread); 84113498266Sopenharmony_ci return nread; 84213498266Sopenharmony_ci} 84313498266Sopenharmony_ci 84413498266Sopenharmony_cistatic int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, 84513498266Sopenharmony_ci int32_t stream_id, 84613498266Sopenharmony_ci const uint8_t *mem, size_t len, void *userp) 84713498266Sopenharmony_ci{ 84813498266Sopenharmony_ci struct Curl_cfilter *cf = userp; 84913498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 85013498266Sopenharmony_ci ssize_t nwritten; 85113498266Sopenharmony_ci CURLcode result; 85213498266Sopenharmony_ci 85313498266Sopenharmony_ci (void)flags; 85413498266Sopenharmony_ci (void)session; 85513498266Sopenharmony_ci DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 85613498266Sopenharmony_ci 85713498266Sopenharmony_ci if(stream_id != ctx->tunnel.stream_id) 85813498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 85913498266Sopenharmony_ci 86013498266Sopenharmony_ci nwritten = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &result); 86113498266Sopenharmony_ci if(nwritten < 0) { 86213498266Sopenharmony_ci if(result != CURLE_AGAIN) 86313498266Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 86413498266Sopenharmony_ci nwritten = 0; 86513498266Sopenharmony_ci } 86613498266Sopenharmony_ci DEBUGASSERT((size_t)nwritten == len); 86713498266Sopenharmony_ci return 0; 86813498266Sopenharmony_ci} 86913498266Sopenharmony_ci 87013498266Sopenharmony_cistatic int proxy_h2_on_stream_close(nghttp2_session *session, 87113498266Sopenharmony_ci int32_t stream_id, 87213498266Sopenharmony_ci uint32_t error_code, void *userp) 87313498266Sopenharmony_ci{ 87413498266Sopenharmony_ci struct Curl_cfilter *cf = userp; 87513498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 87613498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 87713498266Sopenharmony_ci 87813498266Sopenharmony_ci (void)session; 87913498266Sopenharmony_ci (void)data; 88013498266Sopenharmony_ci 88113498266Sopenharmony_ci if(stream_id != ctx->tunnel.stream_id) 88213498266Sopenharmony_ci return 0; 88313498266Sopenharmony_ci 88413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)", 88513498266Sopenharmony_ci stream_id, nghttp2_http2_strerror(error_code), error_code); 88613498266Sopenharmony_ci ctx->tunnel.closed = TRUE; 88713498266Sopenharmony_ci ctx->tunnel.error = error_code; 88813498266Sopenharmony_ci 88913498266Sopenharmony_ci return 0; 89013498266Sopenharmony_ci} 89113498266Sopenharmony_ci 89213498266Sopenharmony_cistatic CURLcode proxy_h2_submit(int32_t *pstream_id, 89313498266Sopenharmony_ci struct Curl_cfilter *cf, 89413498266Sopenharmony_ci struct Curl_easy *data, 89513498266Sopenharmony_ci nghttp2_session *h2, 89613498266Sopenharmony_ci struct httpreq *req, 89713498266Sopenharmony_ci const nghttp2_priority_spec *pri_spec, 89813498266Sopenharmony_ci void *stream_user_data, 89913498266Sopenharmony_ci nghttp2_data_source_read_callback read_callback, 90013498266Sopenharmony_ci void *read_ctx) 90113498266Sopenharmony_ci{ 90213498266Sopenharmony_ci struct dynhds h2_headers; 90313498266Sopenharmony_ci nghttp2_nv *nva = NULL; 90413498266Sopenharmony_ci int32_t stream_id = -1; 90513498266Sopenharmony_ci size_t nheader; 90613498266Sopenharmony_ci CURLcode result; 90713498266Sopenharmony_ci 90813498266Sopenharmony_ci (void)cf; 90913498266Sopenharmony_ci Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); 91013498266Sopenharmony_ci result = Curl_http_req_to_h2(&h2_headers, req, data); 91113498266Sopenharmony_ci if(result) 91213498266Sopenharmony_ci goto out; 91313498266Sopenharmony_ci 91413498266Sopenharmony_ci nva = Curl_dynhds_to_nva(&h2_headers, &nheader); 91513498266Sopenharmony_ci if(!nva) { 91613498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 91713498266Sopenharmony_ci goto out; 91813498266Sopenharmony_ci } 91913498266Sopenharmony_ci 92013498266Sopenharmony_ci if(read_callback) { 92113498266Sopenharmony_ci nghttp2_data_provider data_prd; 92213498266Sopenharmony_ci 92313498266Sopenharmony_ci data_prd.read_callback = read_callback; 92413498266Sopenharmony_ci data_prd.source.ptr = read_ctx; 92513498266Sopenharmony_ci stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, 92613498266Sopenharmony_ci &data_prd, stream_user_data); 92713498266Sopenharmony_ci } 92813498266Sopenharmony_ci else { 92913498266Sopenharmony_ci stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, 93013498266Sopenharmony_ci NULL, stream_user_data); 93113498266Sopenharmony_ci } 93213498266Sopenharmony_ci 93313498266Sopenharmony_ci if(stream_id < 0) { 93413498266Sopenharmony_ci failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", 93513498266Sopenharmony_ci nghttp2_strerror(stream_id), stream_id); 93613498266Sopenharmony_ci result = CURLE_SEND_ERROR; 93713498266Sopenharmony_ci goto out; 93813498266Sopenharmony_ci } 93913498266Sopenharmony_ci result = CURLE_OK; 94013498266Sopenharmony_ci 94113498266Sopenharmony_ciout: 94213498266Sopenharmony_ci free(nva); 94313498266Sopenharmony_ci Curl_dynhds_free(&h2_headers); 94413498266Sopenharmony_ci *pstream_id = stream_id; 94513498266Sopenharmony_ci return result; 94613498266Sopenharmony_ci} 94713498266Sopenharmony_ci 94813498266Sopenharmony_cistatic CURLcode submit_CONNECT(struct Curl_cfilter *cf, 94913498266Sopenharmony_ci struct Curl_easy *data, 95013498266Sopenharmony_ci struct tunnel_stream *ts) 95113498266Sopenharmony_ci{ 95213498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 95313498266Sopenharmony_ci CURLcode result; 95413498266Sopenharmony_ci struct httpreq *req = NULL; 95513498266Sopenharmony_ci 95613498266Sopenharmony_ci result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2); 95713498266Sopenharmony_ci if(result) 95813498266Sopenharmony_ci goto out; 95913498266Sopenharmony_ci 96013498266Sopenharmony_ci infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority); 96113498266Sopenharmony_ci 96213498266Sopenharmony_ci result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, 96313498266Sopenharmony_ci NULL, ts, tunnel_send_callback, cf); 96413498266Sopenharmony_ci if(result) { 96513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] send, nghttp2_submit_request error: %s", 96613498266Sopenharmony_ci ts->stream_id, nghttp2_strerror(ts->stream_id)); 96713498266Sopenharmony_ci } 96813498266Sopenharmony_ci 96913498266Sopenharmony_ciout: 97013498266Sopenharmony_ci if(req) 97113498266Sopenharmony_ci Curl_http_req_free(req); 97213498266Sopenharmony_ci if(result) 97313498266Sopenharmony_ci failf(data, "Failed sending CONNECT to proxy"); 97413498266Sopenharmony_ci return result; 97513498266Sopenharmony_ci} 97613498266Sopenharmony_ci 97713498266Sopenharmony_cistatic CURLcode inspect_response(struct Curl_cfilter *cf, 97813498266Sopenharmony_ci struct Curl_easy *data, 97913498266Sopenharmony_ci struct tunnel_stream *ts) 98013498266Sopenharmony_ci{ 98113498266Sopenharmony_ci CURLcode result = CURLE_OK; 98213498266Sopenharmony_ci struct dynhds_entry *auth_reply = NULL; 98313498266Sopenharmony_ci (void)cf; 98413498266Sopenharmony_ci 98513498266Sopenharmony_ci DEBUGASSERT(ts->resp); 98613498266Sopenharmony_ci if(ts->resp->status/100 == 2) { 98713498266Sopenharmony_ci infof(data, "CONNECT tunnel established, response %d", ts->resp->status); 98813498266Sopenharmony_ci h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data); 98913498266Sopenharmony_ci return CURLE_OK; 99013498266Sopenharmony_ci } 99113498266Sopenharmony_ci 99213498266Sopenharmony_ci if(ts->resp->status == 401) { 99313498266Sopenharmony_ci auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate"); 99413498266Sopenharmony_ci } 99513498266Sopenharmony_ci else if(ts->resp->status == 407) { 99613498266Sopenharmony_ci auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate"); 99713498266Sopenharmony_ci } 99813498266Sopenharmony_ci 99913498266Sopenharmony_ci if(auth_reply) { 100013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'", 100113498266Sopenharmony_ci auth_reply->value); 100213498266Sopenharmony_ci result = Curl_http_input_auth(data, ts->resp->status == 407, 100313498266Sopenharmony_ci auth_reply->value); 100413498266Sopenharmony_ci if(result) 100513498266Sopenharmony_ci return result; 100613498266Sopenharmony_ci if(data->req.newurl) { 100713498266Sopenharmony_ci /* Indicator that we should try again */ 100813498266Sopenharmony_ci Curl_safefree(data->req.newurl); 100913498266Sopenharmony_ci h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); 101013498266Sopenharmony_ci return CURLE_OK; 101113498266Sopenharmony_ci } 101213498266Sopenharmony_ci } 101313498266Sopenharmony_ci 101413498266Sopenharmony_ci /* Seems to have failed */ 101513498266Sopenharmony_ci return CURLE_RECV_ERROR; 101613498266Sopenharmony_ci} 101713498266Sopenharmony_ci 101813498266Sopenharmony_cistatic CURLcode H2_CONNECT(struct Curl_cfilter *cf, 101913498266Sopenharmony_ci struct Curl_easy *data, 102013498266Sopenharmony_ci struct tunnel_stream *ts) 102113498266Sopenharmony_ci{ 102213498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 102313498266Sopenharmony_ci CURLcode result = CURLE_OK; 102413498266Sopenharmony_ci 102513498266Sopenharmony_ci DEBUGASSERT(ts); 102613498266Sopenharmony_ci DEBUGASSERT(ts->authority); 102713498266Sopenharmony_ci do { 102813498266Sopenharmony_ci switch(ts->state) { 102913498266Sopenharmony_ci case H2_TUNNEL_INIT: 103013498266Sopenharmony_ci /* Prepare the CONNECT request and make a first attempt to send. */ 103113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority); 103213498266Sopenharmony_ci result = submit_CONNECT(cf, data, ts); 103313498266Sopenharmony_ci if(result) 103413498266Sopenharmony_ci goto out; 103513498266Sopenharmony_ci h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data); 103613498266Sopenharmony_ci FALLTHROUGH(); 103713498266Sopenharmony_ci 103813498266Sopenharmony_ci case H2_TUNNEL_CONNECT: 103913498266Sopenharmony_ci /* see that the request is completely sent */ 104013498266Sopenharmony_ci result = proxy_h2_progress_ingress(cf, data); 104113498266Sopenharmony_ci if(!result) 104213498266Sopenharmony_ci result = proxy_h2_progress_egress(cf, data); 104313498266Sopenharmony_ci if(result && result != CURLE_AGAIN) { 104413498266Sopenharmony_ci h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); 104513498266Sopenharmony_ci break; 104613498266Sopenharmony_ci } 104713498266Sopenharmony_ci 104813498266Sopenharmony_ci if(ts->has_final_response) { 104913498266Sopenharmony_ci h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data); 105013498266Sopenharmony_ci } 105113498266Sopenharmony_ci else { 105213498266Sopenharmony_ci result = CURLE_OK; 105313498266Sopenharmony_ci goto out; 105413498266Sopenharmony_ci } 105513498266Sopenharmony_ci FALLTHROUGH(); 105613498266Sopenharmony_ci 105713498266Sopenharmony_ci case H2_TUNNEL_RESPONSE: 105813498266Sopenharmony_ci DEBUGASSERT(ts->has_final_response); 105913498266Sopenharmony_ci result = inspect_response(cf, data, ts); 106013498266Sopenharmony_ci if(result) 106113498266Sopenharmony_ci goto out; 106213498266Sopenharmony_ci break; 106313498266Sopenharmony_ci 106413498266Sopenharmony_ci case H2_TUNNEL_ESTABLISHED: 106513498266Sopenharmony_ci return CURLE_OK; 106613498266Sopenharmony_ci 106713498266Sopenharmony_ci case H2_TUNNEL_FAILED: 106813498266Sopenharmony_ci return CURLE_RECV_ERROR; 106913498266Sopenharmony_ci 107013498266Sopenharmony_ci default: 107113498266Sopenharmony_ci break; 107213498266Sopenharmony_ci } 107313498266Sopenharmony_ci 107413498266Sopenharmony_ci } while(ts->state == H2_TUNNEL_INIT); 107513498266Sopenharmony_ci 107613498266Sopenharmony_ciout: 107713498266Sopenharmony_ci if(result || ctx->tunnel.closed) 107813498266Sopenharmony_ci h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); 107913498266Sopenharmony_ci return result; 108013498266Sopenharmony_ci} 108113498266Sopenharmony_ci 108213498266Sopenharmony_cistatic CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, 108313498266Sopenharmony_ci struct Curl_easy *data, 108413498266Sopenharmony_ci bool blocking, bool *done) 108513498266Sopenharmony_ci{ 108613498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 108713498266Sopenharmony_ci CURLcode result = CURLE_OK; 108813498266Sopenharmony_ci struct cf_call_data save; 108913498266Sopenharmony_ci timediff_t check; 109013498266Sopenharmony_ci struct tunnel_stream *ts = &ctx->tunnel; 109113498266Sopenharmony_ci 109213498266Sopenharmony_ci if(cf->connected) { 109313498266Sopenharmony_ci *done = TRUE; 109413498266Sopenharmony_ci return CURLE_OK; 109513498266Sopenharmony_ci } 109613498266Sopenharmony_ci 109713498266Sopenharmony_ci /* Connect the lower filters first */ 109813498266Sopenharmony_ci if(!cf->next->connected) { 109913498266Sopenharmony_ci result = Curl_conn_cf_connect(cf->next, data, blocking, done); 110013498266Sopenharmony_ci if(result || !*done) 110113498266Sopenharmony_ci return result; 110213498266Sopenharmony_ci } 110313498266Sopenharmony_ci 110413498266Sopenharmony_ci *done = FALSE; 110513498266Sopenharmony_ci 110613498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 110713498266Sopenharmony_ci if(!ctx->h2) { 110813498266Sopenharmony_ci result = cf_h2_proxy_ctx_init(cf, data); 110913498266Sopenharmony_ci if(result) 111013498266Sopenharmony_ci goto out; 111113498266Sopenharmony_ci } 111213498266Sopenharmony_ci DEBUGASSERT(ts->authority); 111313498266Sopenharmony_ci 111413498266Sopenharmony_ci check = Curl_timeleft(data, NULL, TRUE); 111513498266Sopenharmony_ci if(check <= 0) { 111613498266Sopenharmony_ci failf(data, "Proxy CONNECT aborted due to timeout"); 111713498266Sopenharmony_ci result = CURLE_OPERATION_TIMEDOUT; 111813498266Sopenharmony_ci goto out; 111913498266Sopenharmony_ci } 112013498266Sopenharmony_ci 112113498266Sopenharmony_ci /* for the secondary socket (FTP), use the "connect to host" 112213498266Sopenharmony_ci * but ignore the "connect to port" (use the secondary port) 112313498266Sopenharmony_ci */ 112413498266Sopenharmony_ci result = H2_CONNECT(cf, data, ts); 112513498266Sopenharmony_ci 112613498266Sopenharmony_ciout: 112713498266Sopenharmony_ci *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED); 112813498266Sopenharmony_ci cf->connected = *done; 112913498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 113013498266Sopenharmony_ci return result; 113113498266Sopenharmony_ci} 113213498266Sopenharmony_ci 113313498266Sopenharmony_cistatic void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) 113413498266Sopenharmony_ci{ 113513498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 113613498266Sopenharmony_ci 113713498266Sopenharmony_ci if(ctx) { 113813498266Sopenharmony_ci struct cf_call_data save; 113913498266Sopenharmony_ci 114013498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 114113498266Sopenharmony_ci cf_h2_proxy_ctx_clear(ctx); 114213498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 114313498266Sopenharmony_ci } 114413498266Sopenharmony_ci if(cf->next) 114513498266Sopenharmony_ci cf->next->cft->do_close(cf->next, data); 114613498266Sopenharmony_ci} 114713498266Sopenharmony_ci 114813498266Sopenharmony_cistatic void cf_h2_proxy_destroy(struct Curl_cfilter *cf, 114913498266Sopenharmony_ci struct Curl_easy *data) 115013498266Sopenharmony_ci{ 115113498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 115213498266Sopenharmony_ci 115313498266Sopenharmony_ci (void)data; 115413498266Sopenharmony_ci if(ctx) { 115513498266Sopenharmony_ci cf_h2_proxy_ctx_free(ctx); 115613498266Sopenharmony_ci cf->ctx = NULL; 115713498266Sopenharmony_ci } 115813498266Sopenharmony_ci} 115913498266Sopenharmony_ci 116013498266Sopenharmony_cistatic bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, 116113498266Sopenharmony_ci const struct Curl_easy *data) 116213498266Sopenharmony_ci{ 116313498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 116413498266Sopenharmony_ci if((ctx && !Curl_bufq_is_empty(&ctx->inbufq)) || 116513498266Sopenharmony_ci (ctx && ctx->tunnel.state == H2_TUNNEL_ESTABLISHED && 116613498266Sopenharmony_ci !Curl_bufq_is_empty(&ctx->tunnel.recvbuf))) 116713498266Sopenharmony_ci return TRUE; 116813498266Sopenharmony_ci return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; 116913498266Sopenharmony_ci} 117013498266Sopenharmony_ci 117113498266Sopenharmony_cistatic void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, 117213498266Sopenharmony_ci struct Curl_easy *data, 117313498266Sopenharmony_ci struct easy_pollset *ps) 117413498266Sopenharmony_ci{ 117513498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 117613498266Sopenharmony_ci curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); 117713498266Sopenharmony_ci bool want_recv, want_send; 117813498266Sopenharmony_ci 117913498266Sopenharmony_ci Curl_pollset_check(data, ps, sock, &want_recv, &want_send); 118013498266Sopenharmony_ci if(ctx->h2 && (want_recv || want_send)) { 118113498266Sopenharmony_ci struct cf_call_data save; 118213498266Sopenharmony_ci bool c_exhaust, s_exhaust; 118313498266Sopenharmony_ci 118413498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 118513498266Sopenharmony_ci c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2); 118613498266Sopenharmony_ci s_exhaust = ctx->tunnel.stream_id >= 0 && 118713498266Sopenharmony_ci !nghttp2_session_get_stream_remote_window_size( 118813498266Sopenharmony_ci ctx->h2, ctx->tunnel.stream_id); 118913498266Sopenharmony_ci want_recv = (want_recv || c_exhaust || s_exhaust); 119013498266Sopenharmony_ci want_send = (!s_exhaust && want_send) || 119113498266Sopenharmony_ci (!c_exhaust && nghttp2_session_want_write(ctx->h2)); 119213498266Sopenharmony_ci 119313498266Sopenharmony_ci Curl_pollset_set(data, ps, sock, want_recv, want_send); 119413498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 119513498266Sopenharmony_ci } 119613498266Sopenharmony_ci} 119713498266Sopenharmony_ci 119813498266Sopenharmony_cistatic ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, 119913498266Sopenharmony_ci struct Curl_easy *data, 120013498266Sopenharmony_ci CURLcode *err) 120113498266Sopenharmony_ci{ 120213498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 120313498266Sopenharmony_ci ssize_t rv = 0; 120413498266Sopenharmony_ci 120513498266Sopenharmony_ci if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { 120613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " 120713498266Sopenharmony_ci "connection", ctx->tunnel.stream_id); 120813498266Sopenharmony_ci connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ 120913498266Sopenharmony_ci *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ 121013498266Sopenharmony_ci return -1; 121113498266Sopenharmony_ci } 121213498266Sopenharmony_ci else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) { 121313498266Sopenharmony_ci failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", 121413498266Sopenharmony_ci ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error), 121513498266Sopenharmony_ci ctx->tunnel.error); 121613498266Sopenharmony_ci *err = CURLE_HTTP2_STREAM; 121713498266Sopenharmony_ci return -1; 121813498266Sopenharmony_ci } 121913498266Sopenharmony_ci else if(ctx->tunnel.reset) { 122013498266Sopenharmony_ci failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id); 122113498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 122213498266Sopenharmony_ci return -1; 122313498266Sopenharmony_ci } 122413498266Sopenharmony_ci 122513498266Sopenharmony_ci *err = CURLE_OK; 122613498266Sopenharmony_ci rv = 0; 122713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> %zd, %d", 122813498266Sopenharmony_ci ctx->tunnel.stream_id, rv, *err); 122913498266Sopenharmony_ci return rv; 123013498266Sopenharmony_ci} 123113498266Sopenharmony_ci 123213498266Sopenharmony_cistatic ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 123313498266Sopenharmony_ci char *buf, size_t len, CURLcode *err) 123413498266Sopenharmony_ci{ 123513498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 123613498266Sopenharmony_ci ssize_t nread = -1; 123713498266Sopenharmony_ci 123813498266Sopenharmony_ci *err = CURLE_AGAIN; 123913498266Sopenharmony_ci if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { 124013498266Sopenharmony_ci nread = Curl_bufq_read(&ctx->tunnel.recvbuf, 124113498266Sopenharmony_ci (unsigned char *)buf, len, err); 124213498266Sopenharmony_ci if(nread < 0) 124313498266Sopenharmony_ci goto out; 124413498266Sopenharmony_ci DEBUGASSERT(nread > 0); 124513498266Sopenharmony_ci } 124613498266Sopenharmony_ci 124713498266Sopenharmony_ci if(nread < 0) { 124813498266Sopenharmony_ci if(ctx->tunnel.closed) { 124913498266Sopenharmony_ci nread = h2_handle_tunnel_close(cf, data, err); 125013498266Sopenharmony_ci } 125113498266Sopenharmony_ci else if(ctx->tunnel.reset || 125213498266Sopenharmony_ci (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || 125313498266Sopenharmony_ci (ctx->goaway && ctx->last_stream_id < ctx->tunnel.stream_id)) { 125413498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 125513498266Sopenharmony_ci nread = -1; 125613498266Sopenharmony_ci } 125713498266Sopenharmony_ci } 125813498266Sopenharmony_ci else if(nread == 0) { 125913498266Sopenharmony_ci *err = CURLE_AGAIN; 126013498266Sopenharmony_ci nread = -1; 126113498266Sopenharmony_ci } 126213498266Sopenharmony_ci 126313498266Sopenharmony_ciout: 126413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %zd, %d", 126513498266Sopenharmony_ci ctx->tunnel.stream_id, len, nread, *err); 126613498266Sopenharmony_ci return nread; 126713498266Sopenharmony_ci} 126813498266Sopenharmony_ci 126913498266Sopenharmony_cistatic ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf, 127013498266Sopenharmony_ci struct Curl_easy *data, 127113498266Sopenharmony_ci char *buf, size_t len, CURLcode *err) 127213498266Sopenharmony_ci{ 127313498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 127413498266Sopenharmony_ci ssize_t nread = -1; 127513498266Sopenharmony_ci struct cf_call_data save; 127613498266Sopenharmony_ci CURLcode result; 127713498266Sopenharmony_ci 127813498266Sopenharmony_ci if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { 127913498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 128013498266Sopenharmony_ci return -1; 128113498266Sopenharmony_ci } 128213498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 128313498266Sopenharmony_ci 128413498266Sopenharmony_ci if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { 128513498266Sopenharmony_ci *err = proxy_h2_progress_ingress(cf, data); 128613498266Sopenharmony_ci if(*err) 128713498266Sopenharmony_ci goto out; 128813498266Sopenharmony_ci } 128913498266Sopenharmony_ci 129013498266Sopenharmony_ci nread = tunnel_recv(cf, data, buf, len, err); 129113498266Sopenharmony_ci 129213498266Sopenharmony_ci if(nread > 0) { 129313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] increase window by %zd", 129413498266Sopenharmony_ci ctx->tunnel.stream_id, nread); 129513498266Sopenharmony_ci nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread); 129613498266Sopenharmony_ci } 129713498266Sopenharmony_ci 129813498266Sopenharmony_ci result = proxy_h2_progress_egress(cf, data); 129913498266Sopenharmony_ci if(result == CURLE_AGAIN) { 130013498266Sopenharmony_ci /* pending data to send, need to be called again. Ideally, we'd 130113498266Sopenharmony_ci * monitor the socket for POLLOUT, but we might not be in SENDING 130213498266Sopenharmony_ci * transfer state any longer and are unable to make this happen. 130313498266Sopenharmony_ci */ 130413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] egress blocked, DRAIN", 130513498266Sopenharmony_ci ctx->tunnel.stream_id); 130613498266Sopenharmony_ci drain_tunnel(cf, data, &ctx->tunnel); 130713498266Sopenharmony_ci } 130813498266Sopenharmony_ci else if(result) { 130913498266Sopenharmony_ci *err = result; 131013498266Sopenharmony_ci nread = -1; 131113498266Sopenharmony_ci } 131213498266Sopenharmony_ci 131313498266Sopenharmony_ciout: 131413498266Sopenharmony_ci if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && 131513498266Sopenharmony_ci (nread >= 0 || *err == CURLE_AGAIN)) { 131613498266Sopenharmony_ci /* data pending and no fatal error to report. Need to trigger 131713498266Sopenharmony_ci * draining to avoid stalling when no socket events happen. */ 131813498266Sopenharmony_ci drain_tunnel(cf, data, &ctx->tunnel); 131913498266Sopenharmony_ci } 132013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d", 132113498266Sopenharmony_ci ctx->tunnel.stream_id, len, nread, *err); 132213498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 132313498266Sopenharmony_ci return nread; 132413498266Sopenharmony_ci} 132513498266Sopenharmony_ci 132613498266Sopenharmony_cistatic ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, 132713498266Sopenharmony_ci struct Curl_easy *data, 132813498266Sopenharmony_ci const void *buf, size_t len, CURLcode *err) 132913498266Sopenharmony_ci{ 133013498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 133113498266Sopenharmony_ci struct cf_call_data save; 133213498266Sopenharmony_ci int rv; 133313498266Sopenharmony_ci ssize_t nwritten; 133413498266Sopenharmony_ci CURLcode result; 133513498266Sopenharmony_ci int blocked = 0; 133613498266Sopenharmony_ci 133713498266Sopenharmony_ci if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { 133813498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 133913498266Sopenharmony_ci return -1; 134013498266Sopenharmony_ci } 134113498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 134213498266Sopenharmony_ci 134313498266Sopenharmony_ci if(ctx->tunnel.closed) { 134413498266Sopenharmony_ci nwritten = -1; 134513498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 134613498266Sopenharmony_ci goto out; 134713498266Sopenharmony_ci } 134813498266Sopenharmony_ci else if(ctx->tunnel.upload_blocked_len) { 134913498266Sopenharmony_ci /* the data in `buf` has already been submitted or added to the 135013498266Sopenharmony_ci * buffers, but have been EAGAINed on the last invocation. */ 135113498266Sopenharmony_ci DEBUGASSERT(len >= ctx->tunnel.upload_blocked_len); 135213498266Sopenharmony_ci if(len < ctx->tunnel.upload_blocked_len) { 135313498266Sopenharmony_ci /* Did we get called again with a smaller `len`? This should not 135413498266Sopenharmony_ci * happen. We are not prepared to handle that. */ 135513498266Sopenharmony_ci failf(data, "HTTP/2 proxy, send again with decreased length"); 135613498266Sopenharmony_ci *err = CURLE_HTTP2; 135713498266Sopenharmony_ci nwritten = -1; 135813498266Sopenharmony_ci goto out; 135913498266Sopenharmony_ci } 136013498266Sopenharmony_ci nwritten = (ssize_t)ctx->tunnel.upload_blocked_len; 136113498266Sopenharmony_ci ctx->tunnel.upload_blocked_len = 0; 136213498266Sopenharmony_ci *err = CURLE_OK; 136313498266Sopenharmony_ci } 136413498266Sopenharmony_ci else { 136513498266Sopenharmony_ci nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err); 136613498266Sopenharmony_ci if(nwritten < 0) { 136713498266Sopenharmony_ci if(*err != CURLE_AGAIN) 136813498266Sopenharmony_ci goto out; 136913498266Sopenharmony_ci nwritten = 0; 137013498266Sopenharmony_ci } 137113498266Sopenharmony_ci } 137213498266Sopenharmony_ci 137313498266Sopenharmony_ci if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { 137413498266Sopenharmony_ci /* req body data is buffered, resume the potentially suspended stream */ 137513498266Sopenharmony_ci rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id); 137613498266Sopenharmony_ci if(nghttp2_is_fatal(rv)) { 137713498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 137813498266Sopenharmony_ci nwritten = -1; 137913498266Sopenharmony_ci goto out; 138013498266Sopenharmony_ci } 138113498266Sopenharmony_ci } 138213498266Sopenharmony_ci 138313498266Sopenharmony_ci result = proxy_h2_progress_ingress(cf, data); 138413498266Sopenharmony_ci if(result) { 138513498266Sopenharmony_ci *err = result; 138613498266Sopenharmony_ci nwritten = -1; 138713498266Sopenharmony_ci goto out; 138813498266Sopenharmony_ci } 138913498266Sopenharmony_ci 139013498266Sopenharmony_ci /* Call the nghttp2 send loop and flush to write ALL buffered data, 139113498266Sopenharmony_ci * headers and/or request body completely out to the network */ 139213498266Sopenharmony_ci result = proxy_h2_progress_egress(cf, data); 139313498266Sopenharmony_ci if(result == CURLE_AGAIN) { 139413498266Sopenharmony_ci blocked = 1; 139513498266Sopenharmony_ci } 139613498266Sopenharmony_ci else if(result) { 139713498266Sopenharmony_ci *err = result; 139813498266Sopenharmony_ci nwritten = -1; 139913498266Sopenharmony_ci goto out; 140013498266Sopenharmony_ci } 140113498266Sopenharmony_ci else if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { 140213498266Sopenharmony_ci /* although we wrote everything that nghttp2 wants to send now, 140313498266Sopenharmony_ci * there is data left in our stream send buffer unwritten. This may 140413498266Sopenharmony_ci * be due to the stream's HTTP/2 flow window being exhausted. */ 140513498266Sopenharmony_ci blocked = 1; 140613498266Sopenharmony_ci } 140713498266Sopenharmony_ci 140813498266Sopenharmony_ci if(blocked) { 140913498266Sopenharmony_ci /* Unable to send all data, due to connection blocked or H2 window 141013498266Sopenharmony_ci * exhaustion. Data is left in our stream buffer, or nghttp2's internal 141113498266Sopenharmony_ci * frame buffer or our network out buffer. */ 141213498266Sopenharmony_ci size_t rwin = nghttp2_session_get_stream_remote_window_size( 141313498266Sopenharmony_ci ctx->h2, ctx->tunnel.stream_id); 141413498266Sopenharmony_ci if(rwin == 0) { 141513498266Sopenharmony_ci /* H2 flow window exhaustion. 141613498266Sopenharmony_ci * FIXME: there is no way to HOLD all transfers that use this 141713498266Sopenharmony_ci * proxy connection AND to UNHOLD all of them again when the 141813498266Sopenharmony_ci * window increases. 141913498266Sopenharmony_ci * We *could* iterate over all data on this conn maybe? */ 142013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] remote flow " 142113498266Sopenharmony_ci "window is exhausted", ctx->tunnel.stream_id); 142213498266Sopenharmony_ci } 142313498266Sopenharmony_ci 142413498266Sopenharmony_ci /* Whatever the cause, we need to return CURL_EAGAIN for this call. 142513498266Sopenharmony_ci * We have unwritten state that needs us being invoked again and EAGAIN 142613498266Sopenharmony_ci * is the only way to ensure that. */ 142713498266Sopenharmony_ci ctx->tunnel.upload_blocked_len = nwritten; 142813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " 142913498266Sopenharmony_ci "blocked_len=%zu", 143013498266Sopenharmony_ci ctx->tunnel.stream_id, len, 143113498266Sopenharmony_ci nghttp2_session_get_remote_window_size(ctx->h2), rwin, 143213498266Sopenharmony_ci nwritten); 143313498266Sopenharmony_ci drain_tunnel(cf, data, &ctx->tunnel); 143413498266Sopenharmony_ci *err = CURLE_AGAIN; 143513498266Sopenharmony_ci nwritten = -1; 143613498266Sopenharmony_ci goto out; 143713498266Sopenharmony_ci } 143813498266Sopenharmony_ci else if(proxy_h2_should_close_session(ctx)) { 143913498266Sopenharmony_ci /* nghttp2 thinks this session is done. If the stream has not been 144013498266Sopenharmony_ci * closed, this is an error state for out transfer */ 144113498266Sopenharmony_ci if(ctx->tunnel.closed) { 144213498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 144313498266Sopenharmony_ci nwritten = -1; 144413498266Sopenharmony_ci } 144513498266Sopenharmony_ci else { 144613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session"); 144713498266Sopenharmony_ci *err = CURLE_HTTP2; 144813498266Sopenharmony_ci nwritten = -1; 144913498266Sopenharmony_ci } 145013498266Sopenharmony_ci } 145113498266Sopenharmony_ci 145213498266Sopenharmony_ciout: 145313498266Sopenharmony_ci if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && 145413498266Sopenharmony_ci (nwritten >= 0 || *err == CURLE_AGAIN)) { 145513498266Sopenharmony_ci /* data pending and no fatal error to report. Need to trigger 145613498266Sopenharmony_ci * draining to avoid stalling when no socket events happen. */ 145713498266Sopenharmony_ci drain_tunnel(cf, data, &ctx->tunnel); 145813498266Sopenharmony_ci } 145913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, " 146013498266Sopenharmony_ci "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", 146113498266Sopenharmony_ci ctx->tunnel.stream_id, len, nwritten, *err, 146213498266Sopenharmony_ci nghttp2_session_get_stream_remote_window_size( 146313498266Sopenharmony_ci ctx->h2, ctx->tunnel.stream_id), 146413498266Sopenharmony_ci nghttp2_session_get_remote_window_size(ctx->h2), 146513498266Sopenharmony_ci Curl_bufq_len(&ctx->tunnel.sendbuf), 146613498266Sopenharmony_ci Curl_bufq_len(&ctx->outbufq)); 146713498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 146813498266Sopenharmony_ci return nwritten; 146913498266Sopenharmony_ci} 147013498266Sopenharmony_ci 147113498266Sopenharmony_cistatic bool proxy_h2_connisalive(struct Curl_cfilter *cf, 147213498266Sopenharmony_ci struct Curl_easy *data, 147313498266Sopenharmony_ci bool *input_pending) 147413498266Sopenharmony_ci{ 147513498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 147613498266Sopenharmony_ci bool alive = TRUE; 147713498266Sopenharmony_ci 147813498266Sopenharmony_ci *input_pending = FALSE; 147913498266Sopenharmony_ci if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) 148013498266Sopenharmony_ci return FALSE; 148113498266Sopenharmony_ci 148213498266Sopenharmony_ci if(*input_pending) { 148313498266Sopenharmony_ci /* This happens before we've sent off a request and the connection is 148413498266Sopenharmony_ci not in use by any other transfer, there shouldn't be any data here, 148513498266Sopenharmony_ci only "protocol frames" */ 148613498266Sopenharmony_ci CURLcode result; 148713498266Sopenharmony_ci ssize_t nread = -1; 148813498266Sopenharmony_ci 148913498266Sopenharmony_ci *input_pending = FALSE; 149013498266Sopenharmony_ci nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); 149113498266Sopenharmony_ci if(nread != -1) { 149213498266Sopenharmony_ci if(proxy_h2_process_pending_input(cf, data, &result) < 0) 149313498266Sopenharmony_ci /* immediate error, considered dead */ 149413498266Sopenharmony_ci alive = FALSE; 149513498266Sopenharmony_ci else { 149613498266Sopenharmony_ci alive = !proxy_h2_should_close_session(ctx); 149713498266Sopenharmony_ci } 149813498266Sopenharmony_ci } 149913498266Sopenharmony_ci else if(result != CURLE_AGAIN) { 150013498266Sopenharmony_ci /* the read failed so let's say this is dead anyway */ 150113498266Sopenharmony_ci alive = FALSE; 150213498266Sopenharmony_ci } 150313498266Sopenharmony_ci } 150413498266Sopenharmony_ci 150513498266Sopenharmony_ci return alive; 150613498266Sopenharmony_ci} 150713498266Sopenharmony_ci 150813498266Sopenharmony_cistatic bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, 150913498266Sopenharmony_ci struct Curl_easy *data, 151013498266Sopenharmony_ci bool *input_pending) 151113498266Sopenharmony_ci{ 151213498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx = cf->ctx; 151313498266Sopenharmony_ci CURLcode result; 151413498266Sopenharmony_ci struct cf_call_data save; 151513498266Sopenharmony_ci 151613498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 151713498266Sopenharmony_ci result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending)); 151813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d", 151913498266Sopenharmony_ci result, *input_pending); 152013498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 152113498266Sopenharmony_ci return result; 152213498266Sopenharmony_ci} 152313498266Sopenharmony_ci 152413498266Sopenharmony_cistruct Curl_cftype Curl_cft_h2_proxy = { 152513498266Sopenharmony_ci "H2-PROXY", 152613498266Sopenharmony_ci CF_TYPE_IP_CONNECT, 152713498266Sopenharmony_ci CURL_LOG_LVL_NONE, 152813498266Sopenharmony_ci cf_h2_proxy_destroy, 152913498266Sopenharmony_ci cf_h2_proxy_connect, 153013498266Sopenharmony_ci cf_h2_proxy_close, 153113498266Sopenharmony_ci Curl_cf_http_proxy_get_host, 153213498266Sopenharmony_ci cf_h2_proxy_adjust_pollset, 153313498266Sopenharmony_ci cf_h2_proxy_data_pending, 153413498266Sopenharmony_ci cf_h2_proxy_send, 153513498266Sopenharmony_ci cf_h2_proxy_recv, 153613498266Sopenharmony_ci Curl_cf_def_cntrl, 153713498266Sopenharmony_ci cf_h2_proxy_is_alive, 153813498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 153913498266Sopenharmony_ci Curl_cf_def_query, 154013498266Sopenharmony_ci}; 154113498266Sopenharmony_ci 154213498266Sopenharmony_ciCURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, 154313498266Sopenharmony_ci struct Curl_easy *data) 154413498266Sopenharmony_ci{ 154513498266Sopenharmony_ci struct Curl_cfilter *cf_h2_proxy = NULL; 154613498266Sopenharmony_ci struct cf_h2_proxy_ctx *ctx; 154713498266Sopenharmony_ci CURLcode result = CURLE_OUT_OF_MEMORY; 154813498266Sopenharmony_ci 154913498266Sopenharmony_ci (void)data; 155013498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 155113498266Sopenharmony_ci if(!ctx) 155213498266Sopenharmony_ci goto out; 155313498266Sopenharmony_ci 155413498266Sopenharmony_ci result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); 155513498266Sopenharmony_ci if(result) 155613498266Sopenharmony_ci goto out; 155713498266Sopenharmony_ci 155813498266Sopenharmony_ci Curl_conn_cf_insert_after(cf, cf_h2_proxy); 155913498266Sopenharmony_ci result = CURLE_OK; 156013498266Sopenharmony_ci 156113498266Sopenharmony_ciout: 156213498266Sopenharmony_ci if(result) 156313498266Sopenharmony_ci cf_h2_proxy_ctx_free(ctx); 156413498266Sopenharmony_ci return result; 156513498266Sopenharmony_ci} 156613498266Sopenharmony_ci 156713498266Sopenharmony_ci#endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */ 1568