| 
					
				 | 
			
			
				@@ -0,0 +1,1356 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/*************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *                                  _   _ ____  _ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  Project                     ___| | | |  _ \| | 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *                             / __| | | | |_) | | 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *                            | (__| |_| |  _ <| |___ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *                             \___|\___/|_| \_\_____| 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * This software is licensed as described in the file COPYING, which 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * you should have received as part of this distribution. The terms 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * are also available at https://curl.se/docs/copyright.html. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * copies of the Software, and permit persons to whom the Software is 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * furnished to do so, under the terms of the COPYING file. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * KIND, either express or implied. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * SPDX-License-Identifier: curl 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ ***************************************************************************/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "curl_setup.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <nghttp2/nghttp2.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "urldata.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "cfilters.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "connect.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "curl_log.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "bufq.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "dynbuf.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "dynhds.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "http1.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "http_proxy.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "multiif.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "cf-h2-proxy.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* The last 3 #include files should be in this order */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "curl_printf.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "curl_memory.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "memdebug.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define H2_NW_CHUNK_SIZE  (128*1024) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define H2_NW_RECV_CHUNKS   1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define H2_NW_SEND_CHUNKS   1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define H2_TUNNEL_WINDOW_SIZE (1024 * 1024) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define H2_TUNNEL_CHUNK_SIZE   (32 * 1024) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define H2_TUNNEL_RECV_CHUNKS \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          (H2_TUNNEL_WINDOW_SIZE / H2_TUNNEL_CHUNK_SIZE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define H2_TUNNEL_SEND_CHUNKS \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          (H2_TUNNEL_WINDOW_SIZE / H2_TUNNEL_CHUNK_SIZE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+typedef enum { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TUNNEL_INIT,     /* init/default/no tunnel state */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TUNNEL_CONNECT,  /* CONNECT request is being send */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TUNNEL_RESPONSE, /* CONNECT response received completely */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TUNNEL_ESTABLISHED, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TUNNEL_FAILED 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} tunnel_state; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+struct tunnel_stream { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct http_resp *resp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct bufq recvbuf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct bufq sendbuf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char *authority; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int32_t stream_id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint32_t error; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tunnel_state state; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool has_final_response; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool closed; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool reset; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    struct tunnel_stream *ts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const char *hostname; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int port; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool ipv6_ip = cf->conn->bits.ipv6_ip; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ts->state = TUNNEL_INIT; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ts->stream_id = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_bufq_init2(&ts->recvbuf, H2_TUNNEL_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  BUFQ_OPT_SOFT_LIMIT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_bufq_init(&ts->sendbuf, H2_TUNNEL_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(cf->conn->bits.conn_to_host) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    hostname = cf->conn->conn_to_host.name; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else if(cf->sockindex == SECONDARYSOCKET) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    hostname = cf->conn->secondaryhostname; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    hostname = cf->conn->host.name; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(cf->sockindex == SECONDARYSOCKET) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    port = cf->conn->secondary_port; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else if(cf->conn->bits.conn_to_port) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    port = cf->conn->conn_to_port; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    port = cf->conn->remote_port; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(hostname != cf->conn->host.name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ipv6_ip = (strchr(hostname, ':') != NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ts->authority = /* host:port with IPv6 support */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", port); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!ts->authority) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return CURLE_OUT_OF_MEMORY; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void tunnel_stream_clear(struct tunnel_stream *ts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_http_resp_free(ts->resp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_bufq_free(&ts->recvbuf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_bufq_free(&ts->sendbuf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_safefree(ts->authority); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memset(ts, 0, sizeof(*ts)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ts->state = TUNNEL_INIT; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void tunnel_go_state(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            struct tunnel_stream *ts, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            tunnel_state new_state, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            struct Curl_easy *data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)cf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ts->state == new_state) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* leaving this one */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  switch(ts->state) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case TUNNEL_CONNECT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    data->req.ignorebody = FALSE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* entering this one */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  switch(new_state) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case TUNNEL_INIT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tunnel_stream_clear(ts); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case TUNNEL_CONNECT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ts->state = TUNNEL_CONNECT; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case TUNNEL_RESPONSE: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ts->state = TUNNEL_RESPONSE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case TUNNEL_ESTABLISHED: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    infof(data, "CONNECT phase completed"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    data->state.authproxy.done = TRUE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    data->state.authproxy.multipass = FALSE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* FALLTHROUGH */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case TUNNEL_FAILED: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(new_state == TUNNEL_FAILED) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ts->state = new_state; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* If a proxy-authorization header was used for the proxy, then we should 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       make sure that it isn't accidentally used for the document request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       after we've connected. So let's free and clear it here. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Curl_safefree(data->state.aptr.proxyuserpwd); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+struct cf_h2_proxy_ctx { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_session *h2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* The easy handle used in the current filter call, cleared at return */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_call_data call_data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct bufq inbufq;  /* network receive buffer */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct bufq outbufq; /* network send buffer */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int32_t goaway_error; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int32_t last_stream_id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  BIT(conn_closed); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  BIT(goaway); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* How to access `call_data` from a cf_h2 filter */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define CF_CTX_CALL_DATA(cf)  \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_call_data save = ctx->call_data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ctx->h2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nghttp2_session_del(ctx->h2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_bufq_free(&ctx->inbufq); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_bufq_free(&ctx->outbufq); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tunnel_stream_clear(&ctx->tunnel); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memset(ctx, 0, sizeof(*ctx)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ctx->call_data = save; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ctx) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cf_h2_proxy_ctx_clear(ctx); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    free(ctx); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ssize_t nw_in_reader(void *reader_ctx, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              unsigned char *buf, size_t buflen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              CURLcode *err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_cfilter *cf = reader_ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_easy *data = CF_DATA_CURRENT(cf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nread; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "nw_in recv(len=%zu) -> %zd, %d", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         buflen, nread, *err)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return nread; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ssize_t nw_out_writer(void *writer_ctx, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             const unsigned char *buf, size_t buflen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             CURLcode *err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_cfilter *cf = writer_ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_easy *data = CF_DATA_CURRENT(cf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nwritten; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "nw_out send(len=%zu) -> %zd", buflen, nwritten)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return nwritten; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int h2_client_new(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         nghttp2_session_callbacks *cbs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_option *o; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int rc = nghttp2_option_new(&o); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(rc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return rc; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* We handle window updates ourself to enforce buffer limits */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_option_set_no_auto_window_update(o, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#if NGHTTP2_VERSION_NUM >= 0x013200 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* with 1.50.0 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* turn off RFC 9113 leading and trailing white spaces validation against 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     HTTP field value. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#endif 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_option_del(o); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return rc; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ssize_t on_session_send(nghttp2_session *h2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              const uint8_t *buf, size_t blen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              int flags, void *userp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         void *userp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int on_stream_close(nghttp2_session *session, int32_t stream_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           uint32_t error_code, void *userp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int 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); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                int32_t stream_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                const uint8_t *mem, size_t len, void *userp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Initialize the cfilter context 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     struct Curl_easy *data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result = CURLE_OUT_OF_MEMORY; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_session_callbacks *cbs = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int rc; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(!ctx->h2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_bufq_init(&ctx->inbufq, H2_NW_CHUNK_SIZE, H2_NW_RECV_CHUNKS); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_bufq_init(&ctx->outbufq, H2_NW_CHUNK_SIZE, H2_NW_SEND_CHUNKS); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(tunnel_stream_init(cf, &ctx->tunnel)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  rc = nghttp2_session_callbacks_new(&cbs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(rc) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    failf(data, "Couldn't initialize nghttp2 callbacks"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_session_callbacks_set_on_data_chunk_recv_callback( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cbs, tunnel_recv_callback); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* The nghttp2 session is not yet setup, do it */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  rc = h2_client_new(cf, cbs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(rc) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    failf(data, "Couldn't initialize nghttp2"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nghttp2_settings_entry iv[3]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    iv[0].value = Curl_multi_max_concurrent_streams(data->multi); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    iv[1].value = H2_TUNNEL_WINDOW_SIZE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    iv[2].value = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(rc) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      failf(data, "nghttp2_submit_settings() failed: %s(%d)", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            nghttp2_strerror(rc), rc); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      result = CURLE_HTTP2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                             HTTP2_HUGE_WINDOW_SIZE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(rc) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          nghttp2_strerror(rc), rc); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = CURLE_HTTP2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* all set, traffic will be send on connect */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(cbs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nghttp2_session_callbacks_del(cbs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "init proxy ctx -> %d", result)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode nw_out_flush(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             struct Curl_easy *data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t buflen = Curl_bufq_len(&ctx->outbufq); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nwritten; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!buflen) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes", buflen)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nwritten < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if((size_t)nwritten < buflen) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return CURLE_AGAIN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Processes pending input left in network input buffer. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * This function returns 0 if it succeeds, or -1 and error code will 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * be assigned to *err. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int h2_process_pending_input(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    CURLcode *err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const unsigned char *buf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t blen; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t rv; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 "fed %zu bytes from nw to nghttp2 -> %zd", blen, rv)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(rv < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      failf(data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "process_pending_input: nghttp2_session_mem_recv() returned " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "%zd:%s", rv, nghttp2_strerror((int)rv)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      *err = CURLE_RECV_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Curl_bufq_skip(&ctx->inbufq, (size_t)rv); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(Curl_bufq_is_empty(&ctx->inbufq)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGF(LOG_CF(data, cf, "all data in connection buffer processed")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGF(LOG_CF(data, cf, "process_pending_input: %zu bytes left " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    "in connection buffer", Curl_bufq_len(&ctx->inbufq))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* No more requests are allowed in the current session, so 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       the connection may not be reused. This is set when a 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       GOAWAY frame has been received or when the limit of stream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       identifiers has been reached. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    connclose(cf->conn, "http/2: No new requests allowed"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    struct Curl_easy *data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nread; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Process network input buffer fist */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!Curl_bufq_is_empty(&ctx->inbufq)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "Process %zd bytes in connection buffer", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  Curl_bufq_len(&ctx->inbufq))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(h2_process_pending_input(cf, data, &result) < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Receive data from the "lower" filters, e.g. network until 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   * it is time to stop or we have enough data for this stream */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while(!ctx->conn_closed &&               /* not closed the connection */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        !ctx->tunnel.closed &&             /* nor the tunnel */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "read %zd bytes nw data -> %zd, %d", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  Curl_bufq_len(&ctx->inbufq), nread, result)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(nread < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if(result != CURLE_AGAIN) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        failf(data, "Failed receiving HTTP2 data"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else if(nread == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ctx->conn_closed = TRUE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(h2_process_pending_input(cf, data, &result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    connclose(cf->conn, "GOAWAY received"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Check if there's been an update in the priority / 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * dependency settings and if so it submits a PRIORITY frame with the updated 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * info. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Flush any out data pending in the network buffer. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode h2_progress_egress(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                  struct Curl_easy *data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int rv = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  rv = nghttp2_session_send(ctx->h2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nghttp2_is_fatal(rv)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  nghttp2_strerror(rv), rv)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return CURLE_SEND_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return nw_out_flush(cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ssize_t on_session_send(nghttp2_session *h2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                               const uint8_t *buf, size_t blen, int flags, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                               void *userp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_cfilter *cf = userp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_easy *data = CF_DATA_CURRENT(cf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nwritten; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)h2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)flags; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                  nw_out_writer, cf, &result); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nwritten < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result == CURLE_AGAIN) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return NGHTTP2_ERR_WOULDBLOCK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    failf(data, "Failed sending HTTP2 data"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!nwritten) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_WOULDBLOCK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return nwritten; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         void *userp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_cfilter *cf = userp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_easy *data = CF_DATA_CURRENT(cf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int32_t stream_id = frame->hd.stream_id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)session; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!stream_id) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* stream ID zero is for connection-oriented stuff */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGASSERT(data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    switch(frame->hd.type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case NGHTTP2_SETTINGS: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* we do not do anything with this for now */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case NGHTTP2_GOAWAY: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      infof(data, "recveived GOAWAY, error=%d, last_stream=%u", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  frame->goaway.error_code, frame->goaway.last_stream_id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ctx->goaway = TRUE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case NGHTTP2_WINDOW_UPDATE: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGF(LOG_CF(data, cf, "recv frame WINDOW_UPDATE")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(stream_id != ctx->tunnel.stream_id) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] rcvd FRAME not for tunnel", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  stream_id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  switch(frame->hd.type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case NGHTTP2_DATA: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* If body started on this stream, then receiving DATA is illegal. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame DATA", stream_id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case NGHTTP2_HEADERS: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame HEADERS", stream_id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* nghttp2 guarantees that :status is received, and we store it to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       stream->status_code. Fuzzing has proven this can still be reached 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       without status code having been set. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(!ctx->tunnel.resp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* Only final status code signals the end of header */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] got http status: %d", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           stream_id, ctx->tunnel.resp->status)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(!ctx->tunnel.has_final_response) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if(ctx->tunnel.resp->status / 100 != 1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ctx->tunnel.has_final_response = TRUE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case NGHTTP2_PUSH_PROMISE: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv PUSH_PROMISE", stream_id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case NGHTTP2_RST_STREAM: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv RST", stream_id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ctx->tunnel.reset = TRUE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case NGHTTP2_WINDOW_UPDATE: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv WINDOW_UPDATE", stream_id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if((data->req.keepon & KEEP_SEND_HOLD) && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       (data->req.keepon & KEEP_SEND)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      data->req.keepon &= ~KEEP_SEND_HOLD; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      Curl_expire(data, 0, EXPIRE_RUN_NOW); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGF(LOG_CF(data, cf, "[h2sid=%u] unpausing after win update", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+             stream_id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame %x", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  stream_id, frame->hd.type)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int 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) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_cfilter *cf = userp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_easy *data = CF_DATA_CURRENT(cf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int32_t stream_id = frame->hd.stream_id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)flags; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)session; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(stream_id != ctx->tunnel.stream_id) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header for non-tunnel stream: " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  "%.*s: %.*s", stream_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  (int)namelen, name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  (int)valuelen, value)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(frame->hd.type == NGHTTP2_PUSH_PROMISE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ctx->tunnel.has_final_response) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* we do not do anything with trailers for tunnel streams */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int http_status; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    struct http_resp *resp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* status: always comes first, we might get more than one response, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * link the previous ones for keepers */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = Curl_http_decode_status(&http_status, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    (const char *)value, valuelen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = Curl_http_resp_make(&resp, http_status, NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    resp->prev = ctx->tunnel.resp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ctx->tunnel.resp = resp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] status: HTTP/2 %03d", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  stream_id, ctx->tunnel.resp->status)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!ctx->tunnel.resp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = Curl_dynhds_add(&ctx->tunnel.resp->headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           (const char *)name, namelen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           (const char *)value, valuelen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header: %.*s: %.*s", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                (int)namelen, name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                (int)valuelen, value)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; /* 0 is successful */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ssize_t 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) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_cfilter *cf = userp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_easy *data = CF_DATA_CURRENT(cf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct tunnel_stream *ts; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nread; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)source; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!stream_id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_INVALID_ARGUMENT; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ts = nghttp2_session_get_stream_user_data(session, stream_id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!ts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(ts == &ctx->tunnel); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nread = Curl_bufq_read(&ts->sendbuf, buf, length, &result); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nread < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result != CURLE_AGAIN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_DEFERRED; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *data_flags = NGHTTP2_DATA_FLAG_EOF; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] tunnel_send_callback -> %zd", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ts->stream_id, nread)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return nread; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                int32_t stream_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                const uint8_t *mem, size_t len, void *userp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_cfilter *cf = userp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nwritten; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)flags; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)session; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(stream_id != ctx->tunnel.stream_id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nwritten = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &result); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nwritten < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result != CURLE_AGAIN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return NGHTTP2_ERR_CALLBACK_FAILURE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nwritten = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT((size_t)nwritten == len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int on_stream_close(nghttp2_session *session, int32_t stream_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           uint32_t error_code, void *userp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_cfilter *cf = userp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_easy *data = CF_DATA_CURRENT(cf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)session; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(stream_id != ctx->tunnel.stream_id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] on_stream_close, %s (err %d)", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_id, nghttp2_http2_strerror(error_code), error_code)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ctx->tunnel.closed = TRUE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ctx->tunnel.error = error_code; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode 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) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct dynhds h2_headers; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nghttp2_nv *nva = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  unsigned int i; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int32_t stream_id = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t nheader; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)cf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = Curl_http_req_to_h2(&h2_headers, req, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nheader = Curl_dynhds_count(&h2_headers); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nva = malloc(sizeof(nghttp2_nv) * nheader); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!nva) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = CURLE_OUT_OF_MEMORY; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for(i = 0; i < nheader; ++i) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nva[i].name = (unsigned char *)e->name; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nva[i].namelen = e->namelen; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nva[i].value = (unsigned char *)e->value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nva[i].valuelen = e->valuelen; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nva[i].flags = NGHTTP2_NV_FLAG_NONE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(read_callback) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nghttp2_data_provider data_prd; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    data_prd.read_callback = read_callback; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    data_prd.source.ptr = read_ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                       &data_prd, stream_user_data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                       NULL, stream_user_data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(stream_id < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          nghttp2_strerror(stream_id), stream_id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = CURLE_SEND_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  free(nva); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_dynhds_free(&h2_headers); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *pstream_id = stream_id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode submit_CONNECT(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                               struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                               struct tunnel_stream *ts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct httpreq *req = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  infof(data, "Establish HTTP/2 proxy tunnel to %s", ts->authority); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              NULL, 0, ts->authority, strlen(ts->authority), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              NULL, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Setup the proxy-authorization header, if any */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 req->authority, TRUE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(data->state.aptr.proxyuserpwd) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = Curl_dynhds_h1_cadd_line(&req->headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      data->state.aptr.proxyuserpwd); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     && data->set.str[STRING_USERAGENT]) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = Curl_dynhds_cadd(&req->headers, "User-Agent", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              data->set.str[STRING_USERAGENT]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = Curl_dynhds_add_custom(data, TRUE, &req->headers); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = h2_submit(&ts->stream_id, cf, data, ctx->h2, req, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     NULL, ts, tunnel_send_callback, cf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  nghttp2_strerror(ts->stream_id), ts->stream_id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(req) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Curl_http_req_free(req); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    failf(data, "Failed sending CONNECT to proxy"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode inspect_response(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 struct tunnel_stream *ts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct dynhds_entry *auth_reply = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)cf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(ts->resp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ts->resp->status/100 == 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    infof(data, "CONNECT tunnel established, response %d", ts->resp->status); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ts->resp->status == 401) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else if(ts->resp->status == 407) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(auth_reply) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  auth_reply->value)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = Curl_http_input_auth(data, ts->resp->status == 407, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                  auth_reply->value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(data->req.newurl) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* Inidicator that we should try again */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      Curl_safefree(data->req.newurl); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      tunnel_go_state(cf, ts, TUNNEL_INIT, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Seems to have failed */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return CURLE_RECV_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode CONNECT(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        struct tunnel_stream *ts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(ts); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(ts->authority); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  do { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    switch(ts->state) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case TUNNEL_INIT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* Prepare the CONNECT request and make a first attempt to send. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGF(LOG_CF(data, cf, "CONNECT start for %s", ts->authority)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      result = submit_CONNECT(cf, data, ts); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      tunnel_go_state(cf, ts, TUNNEL_CONNECT, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* FALLTHROUGH */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case TUNNEL_CONNECT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* see that the request is completely sent */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      result = h2_progress_ingress(cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if(!result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        result = h2_progress_egress(cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if(result) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tunnel_go_state(cf, ts, TUNNEL_FAILED, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if(ts->has_final_response) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        result = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* FALLTHROUGH */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case TUNNEL_RESPONSE: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGASSERT(ts->has_final_response); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      result = inspect_response(cf, data, ts); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case TUNNEL_ESTABLISHED: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case TUNNEL_FAILED: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return CURLE_RECV_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } while(ts->state == TUNNEL_INIT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result || ctx->tunnel.closed) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tunnel_go_state(cf, ts, TUNNEL_FAILED, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    bool blocking, bool *done) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_call_data save; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  timediff_t check; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct tunnel_stream *ts = &ctx->tunnel; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(cf->connected) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *done = TRUE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Connect the lower filters first */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!cf->next->connected) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = Curl_conn_cf_connect(cf->next, data, blocking, done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result || !*done) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *done = FALSE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CF_DATA_SAVE(save, cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!ctx->h2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = cf_h2_proxy_ctx_init(cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGASSERT(ts->authority); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  check = Curl_timeleft(data, NULL, TRUE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(check <= 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    failf(data, "Proxy CONNECT aborted due to timeout"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = CURLE_OPERATION_TIMEDOUT; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* for the secondary socket (FTP), use the "connect to host" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   * but ignore the "connect to port" (use the secondary port) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = CONNECT(cf, data, ts); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *done = (result == CURLE_OK) && (ts->state == TUNNEL_ESTABLISHED); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cf->connected = *done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CF_DATA_RESTORE(cf, save); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ctx) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    struct cf_call_data save; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    CF_DATA_SAVE(save, cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cf_h2_proxy_ctx_clear(ctx); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    CF_DATA_RESTORE(cf, save); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void cf_h2_proxy_destroy(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                struct Curl_easy *data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ctx) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cf_h2_proxy_ctx_free(ctx); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cf->ctx = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     const struct Curl_easy *data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if((ctx && !Curl_bufq_is_empty(&ctx->inbufq)) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     (ctx && ctx->tunnel.state == TUNNEL_ESTABLISHED && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      !Curl_bufq_is_empty(&ctx->tunnel.recvbuf))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return TRUE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int cf_h2_proxy_get_select_socks(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        curl_socket_t *sock) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int bitmap = GETSOCK_BLANK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_call_data save; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CF_DATA_SAVE(save, cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  sock[0] = Curl_conn_cf_get_socket(cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bitmap |= GETSOCK_READSOCK(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* HTTP/2 layer wants to send data) AND there's a window to send data in */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nghttp2_session_want_write(ctx->h2) && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     nghttp2_session_get_remote_window_size(ctx->h2)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bitmap |= GETSOCK_WRITESOCK(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CF_DATA_RESTORE(cf, save); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return bitmap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      CURLcode *err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t rv = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] REFUSED_STREAM, try again on a new " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  "connection", ctx->tunnel.stream_id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          ctx->tunnel.error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = CURLE_HTTP2_STREAM; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else if(ctx->tunnel.reset) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = CURLE_RECV_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *err = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  rv = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "handle_tunnel_close -> %zd, %d", rv, *err)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return rv; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           char *buf, size_t len, CURLcode *err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nread = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *err = CURLE_AGAIN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nread = Curl_bufq_read(&ctx->tunnel.recvbuf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           (unsigned char *)buf, len, err); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(nread < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGASSERT(nread > 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nread < 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(ctx->tunnel.closed) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      nread = h2_handle_tunnel_close(cf, data, err); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else if(ctx->tunnel.reset || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (ctx->goaway && ctx->last_stream_id < ctx->tunnel.stream_id)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      *err = CURLE_RECV_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      nread = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else if(nread == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = CURLE_AGAIN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nread = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "tunnel_recv(len=%zu) -> %zd, %d", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                len, nread, *err)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return nread; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                char *buf, size_t len, CURLcode *err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nread = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_call_data save; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ctx->tunnel.state != TUNNEL_ESTABLISHED) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = CURLE_RECV_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CF_DATA_SAVE(save, cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = h2_progress_ingress(cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(*err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nread = tunnel_recv(cf, data, buf, len, err); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nread > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] increase window by %zd", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  ctx->tunnel.stream_id, nread)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = h2_progress_egress(cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nread = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv(len=%zu) -> %zd %d", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ctx->tunnel.stream_id, len, nread, *err)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CF_DATA_RESTORE(cf, save); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return nread; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                struct Curl_easy *data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                const void *mem, size_t len, CURLcode *err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx = cf->ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_call_data save; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ssize_t nwritten = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const unsigned char *buf = mem; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t start_len = len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int rv; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(ctx->tunnel.state != TUNNEL_ESTABLISHED) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = CURLE_SEND_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CF_DATA_SAVE(save, cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while(len) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(nwritten <= 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if(*err && *err != CURLE_AGAIN) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        DEBUGF(LOG_CF(data, cf, "error adding data to tunnel sendbuf: %d", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               *err)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        nwritten = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* blocked */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      nwritten = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGASSERT((size_t)nwritten <= len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      buf += (size_t)nwritten; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      len -= (size_t)nwritten; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* resume the tunnel stream and let the h2 session send, which 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * triggers reading from tunnel.sendbuf */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(nghttp2_is_fatal(rv)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      *err = CURLE_SEND_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      nwritten = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = h2_progress_egress(cf, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(*err) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      nwritten = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if(!nwritten && Curl_bufq_is_full(&ctx->tunnel.sendbuf)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      size_t rwin; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* we could not add to the buffer and after session processing, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       * it is still full. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      rwin = nghttp2_session_get_stream_remote_window_size( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        ctx->h2, ctx->tunnel.stream_id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      DEBUGF(LOG_CF(data, cf, "cf_send: tunnel win %u/%zu", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+             nghttp2_session_get_remote_window_size(ctx->h2), rwin)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if(rwin == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /* We cannot upload more as the stream's remote window size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         * is 0. We need to receive WIN_UPDATEs before we can continue. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        data->req.keepon |= KEEP_SEND_HOLD; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        DEBUGF(LOG_CF(data, cf, "pausing send as remote flow " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               "window is exhausted")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  nwritten = start_len - len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(nwritten > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else if(ctx->tunnel.closed) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nwritten = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = CURLE_SEND_ERROR; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nwritten = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    *err = CURLE_AGAIN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) -> %zd, %d ", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         start_len, nwritten, *err)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CF_DATA_RESTORE(cf, save); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return nwritten; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+struct Curl_cftype Curl_cft_h2_proxy = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  "H2-PROXY", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CF_TYPE_IP_CONNECT, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURL_LOG_DEFAULT, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cf_h2_proxy_destroy, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cf_h2_proxy_connect, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cf_h2_proxy_close, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_cf_http_proxy_get_host, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cf_h2_proxy_get_select_socks, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cf_h2_proxy_data_pending, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cf_h2_proxy_send, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cf_h2_proxy_recv, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_cf_def_cntrl, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_cf_def_conn_is_alive, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_cf_def_conn_keep_alive, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_cf_def_query, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                       struct Curl_easy *data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct Curl_cfilter *cf_h2_proxy = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  struct cf_h2_proxy_ctx *ctx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURLcode result = CURLE_OUT_OF_MEMORY; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void)data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ctx = calloc(sizeof(*ctx), 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(!ctx) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    goto out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Curl_conn_cf_insert_after(cf, cf_h2_proxy); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  result = CURLE_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+out: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if(result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cf_h2_proxy_ctx_free(ctx); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */ 
			 |