Hi.

Let me start a new Thread with a more clean patch for the upstream feature.

What's in the patch?

The haproxy can now use a "upstream-proxy-tunnel".

```
server https_Via_Proxy1 www.test1.com:4433 upstream-proxy-tunnel 127.0.0.1:3128 init-addr 127.0.0.1
```

It is now possible to set via "tcp-request connection upstream-proxy-header" headers for the upstream proxy

```
tcp-request connection upstream-proxy-header Host www.test1.com
tcp-request connection upstream-proxy-header Proxy-Authorization "basic base64-value"
```

What is missing for now is the possibility to use samples as shown below. At this point would be some help very appreciated.

```
tcp-request connection upstream-proxy-header Proxy-Authorization "basic %[env(MYPASS),base64]"
```

I would also like to have a name resolution for the upstream-proxy-tunnel which is one of the next task for me.

The patch looks quite huge to me ~45k, there are still some Debugging lines there and I'm pretty sure there are a lot of optimizations possible but in my test setup works the connection via a SQUID Proxy :-)

Any feedback is very welcome.

Regards
Alex
From 0b903fa0cfef0cefd0a1b819c9bd1b8e786e6aae Mon Sep 17 00:00:00 2001
From: Aleksandar Lazic <al-hapr...@none.at>
Date: Mon, 10 Jun 2024 23:58:18 +0200
Subject: [PATCH] FEATURE/MAJOR: Add upstream-proxy-tunnel feature

This commit makes it possible for HAProxy to reach
target server via a upstream http proxy.

This patch is based on the work of @brentcetinich
and refer to gh #1542
---
 doc/configuration.txt              |  26 ++++
 examples/upstream-proxy-squid.conf |  60 +++++++++
 examples/upstream-proxy.cfg        |  49 +++++++
 include/haproxy/action-t.h         |   4 +
 include/haproxy/connection-t.h     |  29 ++++-
 include/haproxy/connection.h       |   4 +
 include/haproxy/proxy-t.h          |   1 +
 include/haproxy/server-t.h         |   8 +-
 include/haproxy/tcpcheck-t.h       |   1 +
 src/backend.c                      |   5 +
 src/connection.c                   | 197 +++++++++++++++++++++++++++++
 src/proto_quic.c                   |   4 +
 src/proto_tcp.c                    |   2 +
 src/proxy.c                        |   3 +
 src/server.c                       | 140 ++++++++++++--------
 src/sock.c                         |   3 +
 src/tcp_rules.c                    |  71 ++++++++++-
 src/tcpcheck.c                     |   3 +
 src/xprt_handshake.c               |  18 +++
 19 files changed, 565 insertions(+), 63 deletions(-)
 create mode 100644 examples/upstream-proxy-squid.conf
 create mode 100644 examples/upstream-proxy.cfg

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 971c54d28..0e42cb8e3 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -14269,6 +14269,7 @@ tarpit                         -     -     -     -            X   -   -
 track-sc1                      X     X     X     -            X   X   -
 track-sc2                      X     X     X     -            X   X   -
 unset-var                      X     X     X     X            X   X   X
+upstream-proxy-header          X     -     -     -            -   -   -
 use-service                    -     -     X     -            X   -   -
 wait-for-body                  -     -     -     -            X   X   -
 wait-for-handshake             -     -     -     -            X   -   -
@@ -15708,6 +15709,22 @@ unset-var(<var-name>)
   Example:
     http-request unset-var(req.my_var)
 
+upstream-proxy-header <header> <fmt>
+  Usable in:  TCP RqCon| RqSes| RqCnt| RsCnt|    HTTP Req| Res| Aft
+                    X  |   -  |   -  |   -  |          - |  - |  -
+
+  This rule add headers to the upstream proxy connection.
+
+  Arguments :
+    <header> the Header name which should be added tot the upstream proxy
+             call.
+    <fmt>    the sample expression for the value
+
+  Example:
+    tcp-request connection upstream-proxy-header Host www.test1.com
+    tcp-request connection upstream-proxy-header Proxy-Authorization "basic %[env(MYPASS),base64]"
+
+  See also : server upstream-proxy-tunnel keyword
 
 use-service <service-name>
   Usable in:  TCP RqCon| RqSes| RqCnt| RsCnt|    HTTP Req| Res| Aft
@@ -18076,6 +18093,13 @@ tls-tickets
   It may also be used as "default-server" setting to reset any previous
   "default-server" "no-tls-tickets" setting.
 
+upstream-proxy-tunnel <addr>:<port>
+  May be used in the following contexts: tcp
+
+  This option enables upstream http proxy tunnel for outgoing connections to
+  the server. Using this option won't force the health check to go via upstream
+  http proxy by default.
+
 verify [none|required]
   May be used in the following contexts: tcp, http, log, peers, ring
 
@@ -21990,6 +22014,8 @@ fc_err_str : string
   | 41 | "SOCKS4 Proxy deny the request"                                           |
   | 42 | "SOCKS4 Proxy handshake aborted by server"                                |
   | 43 | "SSL fatal error"                                                         |
+  | 44 | "Error during reverse connect"                                            |
+  | 45 | "Upstream http proxy write error during handshake"                        |
   +----+---------------------------------------------------------------------------+
 
 fc_fackets : integer
diff --git a/examples/upstream-proxy-squid.conf b/examples/upstream-proxy-squid.conf
new file mode 100644
index 000000000..7410f0969
--- /dev/null
+++ b/examples/upstream-proxy-squid.conf
@@ -0,0 +1,60 @@
+#
+# ATTETNTION: This squid config is only for test purpose!
+# ATTETNTION: http_access allow all
+# ATTETNTION: http_access allow CONNECT all
+#
+
+#debug_options all,9
+
+acl localnet src 0.0.0.1-0.255.255.255	# RFC 1122 "this" network (LAN)
+acl localnet src 10.0.0.0/8		# RFC 1918 local private network (LAN)
+acl localnet src 100.64.0.0/10		# RFC 6598 shared address space (CGN)
+acl localnet src 169.254.0.0/16 	# RFC 3927 link-local (directly plugged) machines
+acl localnet src 172.16.0.0/12		# RFC 1918 local private network (LAN)
+acl localnet src 192.168.0.0/16		# RFC 1918 local private network (LAN)
+acl localnet src fc00::/7       	# RFC 4193 local private network range
+acl localnet src fe80::/10      	# RFC 4291 link-local (directly plugged) machines
+
+acl SSL_ports port 443
+acl Safe_ports port 80		# http
+acl Safe_ports port 21		# ftp
+acl Safe_ports port 443		# https
+acl Safe_ports port 70		# gopher
+acl Safe_ports port 210		# wais
+acl Safe_ports port 1025-65535	# unregistered ports
+acl Safe_ports port 280		# http-mgmt
+acl Safe_ports port 488		# gss-http
+acl Safe_ports port 591		# filemaker
+acl Safe_ports port 777		# multiling http
+
+http_access allow all
+
+http_access allow CONNECT all
+
+# http_access allow localhost manager
+# http_access deny manager
+
+# http_access allow localhost
+
+
+# http_access deny to_localhost
+
+# http_access deny to_linklocal
+
+include /etc/squid/conf.d/*.conf
+
+
+#http_access deny all
+
+http_port 3128
+
+coredump_dir /var/spool/squid
+
+refresh_pattern ^ftp:		1440	20%	10080
+refresh_pattern -i (/cgi-bin/|\?) 0	0%	0
+refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
+refresh_pattern \/Release(|\.gpg)$ 0 0% 0 refresh-ims
+refresh_pattern \/InRelease$ 0 0% 0 refresh-ims
+refresh_pattern \/(Translation-.*)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
+refresh_pattern .		0	20%	4320
+
diff --git a/examples/upstream-proxy.cfg b/examples/upstream-proxy.cfg
new file mode 100644
index 000000000..a79dde602
--- /dev/null
+++ b/examples/upstream-proxy.cfg
@@ -0,0 +1,49 @@
+# Test setup.
+# shell1: curl -vk --connect-to www.test1.com:4433:127.0.0.1:8080 -H "Host: www.test1.com" https://www.test1.com:4433
+# shell2: ./haproxy -d -f examples/upstream-proxy.cfg
+# shell3: sudo podman run --rm -it --name squid -e TZ=UTC -p 3128:3128 --network host -v /datadisk/git-repos/haproxy/examples/upstream-proxy-squid.conf:/etc/squid/squid.conf ubuntu/squid > squid_debug_all_allow_all.log 2>&1
+# shell4: openssl s_server -trace -www -bugs -debug -cert reg-tests/ssl/common.pem
+
+global
+	log stdout format raw daemon debug
+	stats timeout 30s
+
+	# Should be set outsite of haproxy config
+	setenv PASS MY-PASS
+
+    #tune.pt.zero-copy-forwarding off
+
+	# turn on stats unix socket
+  	stats socket /datadisk/git-repos/haproxy/stats mode 660 level admin
+
+	#expose-experimental-directives
+	#trace h1 sink stdout
+	#trace h1 level developer
+	#trace h1 verbosity complete
+	#trace h1 start now
+
+	#trace stream sink stdout
+	#trace stream level developer
+	#trace stream verbosity complete
+	#trace stream start now
+
+	#trace pt sink stdout
+	#trace pt level developer
+	#trace pt verbosity complete
+	#trace pt start now
+
+listen proxy-forward-8080
+	bind 0.0.0.0:8080
+	mode tcp
+    log global
+	option tcplog
+	maxconn 2000
+	timeout connect  5s
+	timeout client  50s
+	timeout server  50s
+
+	tcp-request connection upstream-proxy-header Host www.test1.com
+	tcp-request connection upstream-proxy-header Proxy-Authorization "basic %[env(MYPASS),base64]"
+
+	server https_Via_Proxy1 www.test1.com:4433 upstream-proxy-tunnel 127.0.0.1:3128 init-addr 127.0.0.1
+
diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h
index eee16a312..b9e3943e7 100644
--- a/include/haproxy/action-t.h
+++ b/include/haproxy/action-t.h
@@ -93,6 +93,10 @@ enum act_name {
 	/* tcp actions */
 	ACT_TCP_EXPECT_PX,
 	ACT_TCP_EXPECT_CIP,
+
+	/* Add Upstream header */
+	ACT_TCP_UPSTREAM_HEADER,
+
 	ACT_TCP_CLOSE, /* close at the sender's */
 };
 
diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h
index 83969da06..beea75dce 100644
--- a/include/haproxy/connection-t.h
+++ b/include/haproxy/connection-t.h
@@ -86,7 +86,9 @@ enum {
 
 	CO_FL_OPT_TOS       = 0x00000020,  /* connection has a special sockopt tos */
 
-	/* unused : 0x00000040, 0x00000080 */
+	/*  Upstream http proxy */
+	CO_FL_UPSTREAM_PROXY_TUNNEL_SEND = 0x00000040,  /* handshaking with upstream http proxy, going to send the handshake */
+	CO_FL_UPSTREAM_PROXY_TUNNEL_RECV = 0x00000080,  /* handshaking with upstream http proxy, going to check if handshake succeed */
 
 	/* These flags indicate whether the Control and Transport layers are initialized */
 	CO_FL_CTRL_READY    = 0x00000100, /* FD was registered, fd_delete() needed */
@@ -133,7 +135,7 @@ enum {
 	CO_FL_ACCEPT_CIP    = 0x04000000,  /* receive a valid NetScaler Client IP header */
 
 	/* below we have all handshake flags grouped into one */
-	CO_FL_HANDSHAKE     = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
+	CO_FL_HANDSHAKE     = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV | CO_FL_UPSTREAM_PROXY_TUNNEL_SEND | CO_FL_UPSTREAM_PROXY_TUNNEL_RECV,
 	CO_FL_WAIT_XPRT     = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN,
 
 	CO_FL_SSL_WAIT_HS   = 0x08000000,  /* wait for an SSL handshake to complete */
@@ -155,6 +157,10 @@ enum {
 
 	/* below we have all SOCKS handshake flags grouped into one */
 	CO_FL_SOCKS4        = CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
+
+	/* below we have all upstream http proxy tunnel handshake flags grouped into one */
+	CO_FL_UPSTREAM_PROXY_TUNNEL        = CO_FL_UPSTREAM_PROXY_TUNNEL_SEND | CO_FL_UPSTREAM_PROXY_TUNNEL_RECV,
+
 };
 
 /* This function is used to report flags in debugging tools. Please reflect
@@ -174,8 +180,8 @@ static forceinline char *conn_show_flags(char *buf, size_t len, const char *deli
 	_(CO_FL_SOCK_WR_SH, _(CO_FL_ERROR, _(CO_FL_FDLESS, _(CO_FL_WAIT_L4_CONN,
 	_(CO_FL_WAIT_L6_CONN, _(CO_FL_SEND_PROXY, _(CO_FL_ACCEPT_PROXY, _(CO_FL_ACCEPT_CIP,
 	_(CO_FL_SSL_WAIT_HS, _(CO_FL_PRIVATE, _(CO_FL_RCVD_PROXY, _(CO_FL_SESS_IDLE,
-	_(CO_FL_XPRT_TRACKED
-	))))))))))))))))))))))))))));
+	_(CO_FL_XPRT_TRACKED, _(CO_FL_UPSTREAM_PROXY_TUNNEL_SEND ,_(CO_FL_UPSTREAM_PROXY_TUNNEL_RECV
+	))))))))))))))))))))))))))))));
 	/* epilogue */
 	_(~0U);
 	return buf;
@@ -241,6 +247,9 @@ enum {
 	CO_ERR_SSL_FATAL,        /* SSL fatal error during a SSL_read or SSL_write */
 
 	CO_ER_REVERSE,           /* Error during reverse connect */
+
+	CO_ER_PROXY_CONNECT_SEND, /* Upstream http proxy write error during handshake */
+	CO_ER_PROXY_CONNECT_RECV, /* Upstream http proxy read error during handshake */
 };
 
 /* error return codes for accept_conn() */
@@ -356,6 +365,16 @@ struct socks4_request {
 	char user_id[8];	/* the user ID string, variable length, terminated with a null (0x00); Using "HAProxy\0" */
 };
 
+/*
+ * Upstream proxy header list
+ */
+
+struct uph_list {
+	struct list list;
+	struct buffer name;
+	struct buffer value;
+};
+
 /* A connection handle is how we differentiate two connections on the lower
  * layers. It usually is a file descriptor but can be a connection id. The
  * CO_FL_FDLESS flag indicates which one is relevant.
@@ -526,7 +545,7 @@ struct connection {
 	/* first cache line */
 	enum obj_type obj_type;       /* differentiates connection from applet context */
 	unsigned char err_code;       /* CO_ER_* */
-	signed short send_proxy_ofs;  /* <0 = offset to (re)send from the end, >0 = send all (reused for SOCKS4) */
+	signed short send_proxy_ofs;  /* <0 = offset to (re)send from the end, >0 = send all (reused for SOCKS4 and upstream http proxy) */
 	unsigned int flags;           /* CO_FL_* */
 	const struct protocol *ctrl;  /* operations at the socket layer */
 	const struct xprt_ops *xprt;  /* operations at the transport layer */
diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h
index aa61cc71a..03e6cea0d 100644
--- a/include/haproxy/connection.h
+++ b/include/haproxy/connection.h
@@ -74,6 +74,10 @@ int conn_ctrl_drain(struct connection *conn);
 int conn_send_socks4_proxy_request(struct connection *conn);
 int conn_recv_socks4_proxy_response(struct connection *conn);
 
+/* upstream http proxy handshake */
+int conn_send_upstream_proxy_tunnel_request(struct connection *conn, int flag);
+int conn_recv_upstream_proxy_tunnel_response(struct connection *conn, int flag);
+
 /* If we delayed the mux creation because we were waiting for the handshake, do it now */
 int conn_create_mux(struct connection *conn);
 int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake);
diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h
index f6ed211c1..b15a81e7f 100644
--- a/include/haproxy/proxy-t.h
+++ b/include/haproxy/proxy-t.h
@@ -301,6 +301,7 @@ struct proxy {
 		struct list inspect_rules;      /* inspection rules */
 		struct list l4_rules;           /* layer4 rules */
 		struct list l5_rules;           /* layer5 rules */
+		struct list uph_rules;			/* upstream rules */
 	} tcp_req;
 	struct {                                /* TCP request processing */
 		unsigned int inspect_delay;     /* inspection delay */
diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h
index af58a5609..75f03e023 100644
--- a/include/haproxy/server-t.h
+++ b/include/haproxy/server-t.h
@@ -154,6 +154,7 @@ enum srv_initaddr {
 #define SRV_F_NON_PURGEABLE 0x2000       /* this server cannot be removed at runtime */
 #define SRV_F_DEFSRV_USE_SSL 0x4000      /* default-server uses SSL */
 #define SRV_F_DELETED 0x8000             /* srv is deleted but not yet purged */
+#define SRV_F_UPSTREAM_PROXY_TUNNEL 0x10000  /* this server uses a upstream proxy tunnel with CONNECT method */
 
 /* configured server options for send-proxy (server->pp_opts) */
 #define SRV_PP_V1               0x0001   /* proxy protocol version 1 */
@@ -469,9 +470,10 @@ struct server {
 	struct guid_node guid;			/* GUID global tree node */
 
 	/* warning, these structs are huge, keep them at the bottom */
-	struct conn_src conn_src;               /* connection source settings */
-	struct sockaddr_storage addr;           /* the address to connect to, doesn't include the port */
-	struct sockaddr_storage socks4_addr;	/* the address of the SOCKS4 Proxy, including the port */
+	struct conn_src conn_src;                           /* connection source settings */
+	struct sockaddr_storage addr;                       /* the address to connect to, doesn't include the port */
+	struct sockaddr_storage socks4_addr;                /* the address of the SOCKS4 Proxy, including the port */
+	struct sockaddr_storage upstream_proxy_tunnel_addr; /* the address of the upstream PROXY for proxy tunnel, including the port */
 
 	EXTRA_COUNTERS(extra_counters);
 };
diff --git a/include/haproxy/tcpcheck-t.h b/include/haproxy/tcpcheck-t.h
index 22310eee0..ee861aaea 100644
--- a/include/haproxy/tcpcheck-t.h
+++ b/include/haproxy/tcpcheck-t.h
@@ -36,6 +36,7 @@
 #define TCPCHK_OPT_IMPLICIT        0x0010  /* Implicit connect */
 #define TCPCHK_OPT_SOCKS4          0x0020  /* check the connection via socks4 proxy */
 #define TCPCHK_OPT_HAS_DATA        0x0040  /* data should be sent after connection */
+/* TODO: add upstream HTTP PROXY Check */
 
 enum tcpcheck_send_type {
 	TCPCHK_SEND_UNDEF = 0,  /* Send is not parsed. */
diff --git a/src/backend.c b/src/backend.c
index d74ae4082..d0dc6057a 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1716,6 +1716,11 @@ int connect_server(struct stream *s)
 			srv_conn->flags |= CO_FL_SOCKS4;
 		}
 
+		if (srv && (srv->flags & SRV_F_UPSTREAM_PROXY_TUNNEL)) {
+			srv_conn->send_proxy_ofs = 1; /* TODO: maybe dont reuse this? */
+			srv_conn->flags |= CO_FL_UPSTREAM_PROXY_TUNNEL;
+		}
+
 #if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
 		/* if websocket stream, try to update connection ALPN. */
 		if (unlikely(s->flags & SF_WEBSOCKET) &&
diff --git a/src/connection.c b/src/connection.c
index 3fedad9a5..59cb4b0fc 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -758,6 +758,8 @@ const char *conn_err_code_str(struct connection *c)
 	case CO_ERR_SSL_FATAL:     return "SSL fatal error";
 
 	case CO_ER_REVERSE:        return "Reverse connect failure";
+
+	case CO_ER_PROXY_CONNECT_SEND: return "Upstream http proxy write error during handshake";
 	}
 	return NULL;
 }
@@ -1783,6 +1785,201 @@ int conn_recv_socks4_proxy_response(struct connection *conn)
 	return 0;
 }
 
+/* TODO: use *TRACE* features from HAProxy instead of DPRINTF */
+int conn_send_upstream_proxy_tunnel_request(struct connection *conn, int flag) {
+
+	int connect_length;
+	struct server *srv = NULL;
+	struct proxy  *prx = NULL;
+	struct uph_list *my_headers;
+
+	DPRINTF(stderr, "HTTP TUNNEL SEND start\n");
+
+	if (!conn_ctrl_ready(conn))
+		goto out_error;
+
+	if (!conn_get_dst(conn))
+		goto out_error;
+
+	/* srv must be set */
+	srv = objt_server(conn->target);
+	BUG_ON(!srv);
+
+	prx = srv->proxy;
+
+	if (!prx){
+		DPRINTF(stderr," NO PROXYS\n");	
+	}
+
+	DPRINTF(stderr,"proxy->id :%s:\n", prx->id);
+	DPRINTF(stderr,"hostname: %s\n", srv->hostname);
+
+	chunk_reset(&trash);
+	connect_length = chunk_appendf(&trash,"CONNECT %s:%u HTTP/1.1\r\n",
+							srv->hostname,
+							ntohs(get_net_port(conn->dst)));
+
+
+	DPRINTF(stderr,"trash->data :%d:\n", (unsigned int)b_data(&trash));
+
+	list_for_each_entry(my_headers, &prx->tcp_req.uph_rules, list) {
+		DPRINTF(stderr,"list each name  :%s:\n",ist0(ist2(b_orig(&my_headers->name), b_data(&my_headers->name))));
+		DPRINTF(stderr,"list each value :%s:\n",ist0(ist2(b_orig(&my_headers->value), b_data(&my_headers->value))));
+		chunk_memcat(&trash,b_orig(&my_headers->name), b_data(&my_headers->name));
+		chunk_memcat(&trash,": ",2);
+		chunk_memcat(&trash,b_orig(&my_headers->value), b_data(&my_headers->value));
+		chunk_memcat(&trash, "\r\n", 2);
+	}
+
+	connect_length += chunk_memcat(&trash, "\r\n", 2);
+
+	DPRINTF(stderr,"connect_length :%d:\n", connect_length);
+	DPRINTF(stderr,"trash->data :%d:\n", (unsigned int)b_data(&trash));
+	//DPRINTF(stderr,"trash->orig :%s:\n",b_orig(&trash));
+	DPRINTF(stderr,"send_proxy_ofs: %d\n", conn->send_proxy_ofs);
+
+	if (conn->send_proxy_ofs > 0) {
+		/*
+		 * This is the first call to send the request
+		 */
+		conn->send_proxy_ofs = -(int) b_data(&trash);
+	}
+
+	if (conn->send_proxy_ofs < 0) {
+		int ret = 0;
+
+		/* we are sending the htt-connect req_line here. If the data layer
+		 * has a pending write, we'll also set MSG_MORE.
+		 */
+		/* ret = conn_ctrl_send(
+				conn,
+				((char *) (proxy_connect)),
+				-conn->send_proxy_ofs,
+				(conn->subs && conn->subs->events & SUB_RETRY_SEND) ? CO_SFL_MSG_MORE : 0);
+		 */
+
+		ret = conn_ctrl_send(
+				conn,
+				b_orig(&trash),
+				-conn->send_proxy_ofs,
+				(conn->subs && conn->subs->events & SUB_RETRY_SEND) ? CO_SFL_MSG_MORE : 0);
+		
+		DPRINTF(stderr, "HTTP TUNNEL send_proxy_ofs FD[%04X]: Before send remain is [%d], sent [%d]\n",
+				conn->handle.fd, conn->send_proxy_ofs, ret);
+
+		if (ret < 0) {
+			DPRINTF(stderr, "HTTP TUNNEL FD[%04X]: Before send remain is [%d], sent [%d]\n",
+				conn->handle.fd, conn->send_proxy_ofs, ret);
+
+			goto out_error;
+		}
+
+		conn->send_proxy_ofs += ret; /* becomes zero once complete */
+		if (conn->send_proxy_ofs != 0) {
+			goto out_wait;
+		}
+	}
+
+	/* OK we've the whole request sent */
+	//conn->flags &= ~CO_FL_UPSTREAM_PROXY_TUNNEL_SEND;
+	conn->flags &= ~flag;
+
+
+	/* The connection is ready now, simply return and let the connection
+	 * handler notify upper layers if needed.
+	 */
+	conn->flags &= ~CO_FL_WAIT_L4_CONN;
+
+	DPRINTF(stderr,"HTTP TUNNEL SEND end: okay\n");
+	DPRINTF(stderr,"send_proxy_ofs: %d\n", conn->send_proxy_ofs);
+
+	return 1;
+
+ out_error:
+	/* Write error on the file descriptor */
+	conn->flags |= CO_FL_ERROR;
+	if (conn->err_code == CO_ER_NONE) {
+		conn->err_code = CO_ER_PROXY_CONNECT_SEND;
+	}
+	DPRINTF(stderr, "HTTP TUNNEL SEND end: out_error\n");
+	return 0;
+
+ out_wait:
+	DPRINTF(stderr, "HTTP TUNNEL SEND end: out_wait\n");
+	return 0;
+}
+
+
+int conn_recv_upstream_proxy_tunnel_response(struct connection *conn, int flag) {
+
+	struct ist upstream_proxy_response = IST_NULL;
+	struct ist upstream_proxy_successful = ist("HTTP/1.1 200 Connection established");
+	int ret;
+
+	//DPRINTF(stderr, "HTTP TUNNEL RECV start\n");
+
+	if (!conn_ctrl_ready(conn))
+		goto fail;
+
+	BUG_ON(conn->flags & CO_FL_FDLESS);
+
+	if (!fd_recv_ready(conn->handle.fd)){
+		DPRINTF(stderr, "HTTP TUNNEL RECV fd_recv_ready\n");
+		goto not_ready;
+	}
+
+	chunk_reset(&trash);
+
+	while (1) {
+		ret = recv(conn->handle.fd, b_orig(&trash), b_size(&trash), MSG_DONTWAIT);
+
+		if (ret < 0) {
+			if (errno == EAGAIN)
+				goto not_ready;
+		}
+
+		b_set_data(&trash, ret);
+		upstream_proxy_response = ist2(b_orig(&trash), b_data(&trash));
+
+		/* get the first line from proxy response*/
+    	upstream_proxy_response = iststop(upstream_proxy_response,'\r');
+		DPRINTF(stderr,"upstream_proxy_response len :%ld:\n",istlen(upstream_proxy_response));
+		DPRINTF(stderr,"upstream_proxy_response :%s:\n",ist0(upstream_proxy_response));
+		DPRINTF(stderr,"Test isteq :%d:\n",isteq(upstream_proxy_response,upstream_proxy_successful));
+
+		/* check for HTTP/1.1 200 Connection established */
+		if(!isteq(upstream_proxy_response,upstream_proxy_successful)){
+			DPRINTF(stderr,"HTTP TUNNEL no 200 ret :%d: errno :%d: strerror :%s:\n", ret, errno ,strerror(errno));
+			goto recv_abort;
+		}
+		break;
+	}
+
+	DPRINTF(stderr, "HTTP TUNNEL RECV end\n");
+	conn->flags &= ~flag;
+	conn->flags &= ~CO_FL_WAIT_L4_CONN;
+
+	return 1;
+
+ not_ready:
+	//DPRINTF(stderr, "HTTP TUNNEL RECV not_ready out\n");
+	conn->flags |= CO_FL_WAIT_L4_CONN;
+	return 0;
+
+ recv_abort:
+	DPRINTF(stderr, "HTTP TUNNEL RECV recv_abort out\n");
+	if (conn->err_code == CO_ER_NONE) {
+		conn->err_code = CO_ER_PROXY_CONNECT_RECV;
+	}
+	conn->flags |= (CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH);
+	goto fail;
+
+ fail:
+ 	DPRINTF(stderr, "HTTP TUNNEL RECV fail out\n");
+	conn->flags |= CO_FL_ERROR;
+	return 0;
+}
+
 /* registers proto mux list <list>. Modifies the list element! */
 void register_mux_proto(struct mux_proto_list *list)
 {
diff --git a/src/proto_quic.c b/src/proto_quic.c
index 93a24af4b..cbf8c2c31 100644
--- a/src/proto_quic.c
+++ b/src/proto_quic.c
@@ -415,6 +415,10 @@ int quic_connect_server(struct connection *conn, int flags)
                 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
 
 	addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
+
+	/* TODO: Can an upstream proxy be used for quic? */
+	addr = (conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL) ? &srv->upstream_proxy_tunnel_addr : conn->dst;
+
 	if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
 		if (errno == EINPROGRESS || errno == EALREADY) {
 			/* common case, let's wait for connect status */
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 63be77508..10bdb9b0d 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -453,6 +453,8 @@ int tcp_connect_server(struct connection *conn, int flags)
                 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
 
 	addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
+	addr = (conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL) ? &srv->upstream_proxy_tunnel_addr : conn->dst;
+
 	if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
 		if (errno == EINPROGRESS || errno == EALREADY) {
 			/* common case, let's wait for connect status */
diff --git a/src/proxy.c b/src/proxy.c
index f1d9d7ab0..1a8cd7903 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -274,6 +274,7 @@ void free_proxy(struct proxy *p)
 	free_act_rules(&p->tcp_rep.inspect_rules);
 	free_act_rules(&p->tcp_req.l4_rules);
 	free_act_rules(&p->tcp_req.l5_rules);
+	free_act_rules(&p->tcp_req.uph_rules);
 	free_act_rules(&p->http_req_rules);
 	free_act_rules(&p->http_res_rules);
 	free_act_rules(&p->http_after_res_rules);
@@ -1355,6 +1356,7 @@ void init_new_proxy(struct proxy *p)
 	LIST_INIT(&p->tcp_rep.inspect_rules);
 	LIST_INIT(&p->tcp_req.l4_rules);
 	LIST_INIT(&p->tcp_req.l5_rules);
+	LIST_INIT(&p->tcp_req.uph_rules);
 	MT_LIST_INIT(&p->listener_queue);
 	LIST_INIT(&p->loggers);
 	LIST_INIT(&p->conf.bind);
@@ -1461,6 +1463,7 @@ void proxy_free_defaults(struct proxy *defproxy)
 	free_act_rules(&defproxy->tcp_rep.inspect_rules);
 	free_act_rules(&defproxy->tcp_req.l4_rules);
 	free_act_rules(&defproxy->tcp_req.l5_rules);
+	free_act_rules(&defproxy->tcp_req.uph_rules);
 	free_act_rules(&defproxy->http_req_rules);
 	free_act_rules(&defproxy->http_res_rules);
 	free_act_rules(&defproxy->http_after_res_rules);
diff --git a/src/server.c b/src/server.c
index caf2f40bb..d3128dcb2 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1935,6 +1935,38 @@ static int srv_parse_socks4(char **args, int *cur_arg,
 	return ERR_ALERT | ERR_FATAL;
 }
 
+/* Parse the "upstream-proxy-tunnel" server keyword */
+static int srv_parse_upstream_proxy_tunnel(char **args, int *cur_arg,
+								  struct proxy *curproxy, struct server *newsrv, char **err) {
+	char *errmsg;
+	int port_low, port_high;
+	struct sockaddr_storage *sk;
+
+	errmsg = NULL;
+
+	if (!*args[*cur_arg + 1]) {
+		memprintf(err, "'%s' expects <addr>:<port> as argument.\n", args[*cur_arg]);
+		goto err;
+	}
+
+	/* 'sk' is statically allocated (no need to be freed). */
+	sk = str2sa_range(args[*cur_arg + 1], NULL, &port_low, &port_high, NULL, NULL, NULL,
+	                  &errmsg, NULL, NULL,
+	                  PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_CONNECT);
+	if (!sk) {
+		memprintf(err, "'%s %s' : %s\n", args[*cur_arg], args[*cur_arg + 1], errmsg);
+		goto err;
+	}
+
+	newsrv->flags |= SRV_F_UPSTREAM_PROXY_TUNNEL;
+	newsrv->upstream_proxy_tunnel_addr = *sk;
+
+	return 0;
+
+ err:
+	free(errmsg);
+	return ERR_ALERT | ERR_FATAL;
+}
 
 /* parse the "tfo" server keyword */
 static int srv_parse_tfo(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
@@ -2283,55 +2315,56 @@ void srv_compute_all_admin_states(struct proxy *px)
  * Note: -1 as ->skip value means that the number of arguments are variable.
  */
 static struct srv_kw_list srv_kws = { "ALL", { }, {
-	{ "backup",               srv_parse_backup,               0,  1,  1 }, /* Flag as backup server */
-	{ "cookie",               srv_parse_cookie,               1,  1,  1 }, /* Assign a cookie to the server */
-	{ "disabled",             srv_parse_disabled,             0,  1,  1 }, /* Start the server in 'disabled' state */
-	{ "enabled",              srv_parse_enabled,              0,  1,  0 }, /* Start the server in 'enabled' state */
-	{ "error-limit",          srv_parse_error_limit,          1,  1,  1 }, /* Configure the consecutive count of check failures to consider a server on error */
-	{ "guid",                 srv_parse_guid,                 1,  0,  1 }, /* Set global unique ID of the server */
-	{ "ws",                   srv_parse_ws,                   1,  1,  1 }, /* websocket protocol */
-	{ "hash-key",             srv_parse_hash_key,             1,  1,  1 }, /* Configure how chash keys are computed */
-	{ "id",                   srv_parse_id,                   1,  0,  1 }, /* set id# of server */
-	{ "init-addr",            srv_parse_init_addr,            1,  1,  0 }, /* */
-	{ "log-bufsize",          srv_parse_log_bufsize,          1,  1,  0 }, /* Set the ring bufsize for log server (only for log backends) */
-	{ "log-proto",            srv_parse_log_proto,            1,  1,  0 }, /* Set the protocol for event messages, only relevant in a log or ring section */
-	{ "maxconn",              srv_parse_maxconn,              1,  1,  1 }, /* Set the max number of concurrent connection */
-	{ "maxqueue",             srv_parse_maxqueue,             1,  1,  1 }, /* Set the max number of connection to put in queue */
-	{ "max-reuse",            srv_parse_max_reuse,            1,  1,  0 }, /* Set the max number of requests on a connection, -1 means unlimited */
-	{ "minconn",              srv_parse_minconn,              1,  1,  1 }, /* Enable a dynamic maxconn limit */
-	{ "namespace",            srv_parse_namespace,            1,  1,  0 }, /* Namespace the server socket belongs to (if supported) */
-	{ "no-backup",            srv_parse_no_backup,            0,  1,  1 }, /* Flag as non-backup server */
-	{ "no-send-proxy",        srv_parse_no_send_proxy,        0,  1,  1 }, /* Disable use of PROXY V1 protocol */
-	{ "no-send-proxy-v2",     srv_parse_no_send_proxy_v2,     0,  1,  1 }, /* Disable use of PROXY V2 protocol */
-	{ "no-tfo",               srv_parse_no_tfo,               0,  1,  1 }, /* Disable use of TCP Fast Open */
-	{ "non-stick",            srv_parse_non_stick,            0,  1,  0 }, /* Disable stick-table persistence */
-	{ "observe",              srv_parse_observe,              1,  1,  1 }, /* Enables health adjusting based on observing communication with the server */
-	{ "on-error",             srv_parse_on_error,             1,  1,  1 }, /* Configure the action on check failure */
-	{ "on-marked-down",       srv_parse_on_marked_down,       1,  1,  1 }, /* Configure the action when a server is marked down */
-	{ "on-marked-up",         srv_parse_on_marked_up,         1,  1,  1 }, /* Configure the action when a server is marked up */
-	{ "pool-conn-name",       srv_parse_pool_conn_name,       1,  1,  1 }, /* Define expression to identify connections in idle pool */
-	{ "pool-low-conn",        srv_parse_pool_low_conn,        1,  1,  1 }, /* Set the min number of orphan idle connecbefore being allowed to pick from other threads */
-	{ "pool-max-conn",        srv_parse_pool_max_conn,        1,  1,  1 }, /* Set the max number of orphan idle connections, -1 means unlimited */
-	{ "pool-purge-delay",     srv_parse_pool_purge_delay,     1,  1,  1 }, /* Set the time before we destroy orphan idle connections, defaults to 1s */
-	{ "proto",                srv_parse_proto,                1,  1,  1 }, /* Set the proto to use for all outgoing connections */
-	{ "proxy-v2-options",     srv_parse_proxy_v2_options,     1,  1,  1 }, /* options for send-proxy-v2 */
-	{ "redir",                srv_parse_redir,                1,  1,  0 }, /* Enable redirection mode */
-	{ "resolve-net",          srv_parse_resolve_net,          1,  1,  0 }, /* Set the preferred network range for name resolution */
-	{ "resolve-opts",         srv_parse_resolve_opts,         1,  1,  0 }, /* Set options for name resolution */
-	{ "resolve-prefer",       srv_parse_resolve_prefer,       1,  1,  0 }, /* Set the preferred family for name resolution */
-	{ "resolvers",            srv_parse_resolvers,            1,  1,  0 }, /* Configure the resolver to use for name resolution */
-	{ "send-proxy",           srv_parse_send_proxy,           0,  1,  1 }, /* Enforce use of PROXY V1 protocol */
-	{ "send-proxy-v2",        srv_parse_send_proxy_v2,        0,  1,  1 }, /* Enforce use of PROXY V2 protocol */
-	{ "set-proxy-v2-tlv-fmt", srv_parse_set_proxy_v2_tlv_fmt, 0,  1,  1 }, /* Set TLV of PROXY V2 protocol */
-	{ "shard",                srv_parse_shard,                1,  1,  1 }, /* Server shard (only in peers protocol context) */
-	{ "slowstart",            srv_parse_slowstart,            1,  1,  1 }, /* Set the warm-up timer for a previously failed server */
-	{ "source",               srv_parse_source,              -1,  1,  1 }, /* Set the source address to be used to connect to the server */
-	{ "stick",                srv_parse_stick,                0,  1,  0 }, /* Enable stick-table persistence */
-	{ "tfo",                  srv_parse_tfo,                  0,  1,  1 }, /* enable TCP Fast Open of server */
-	{ "track",                srv_parse_track,                1,  1,  1 }, /* Set the current state of the server, tracking another one */
-	{ "socks4",               srv_parse_socks4,               1,  1,  0 }, /* Set the socks4 proxy of the server*/
-	{ "usesrc",               srv_parse_usesrc,               0,  1,  1 }, /* safe-guard against usesrc without preceding <source> keyword */
-	{ "weight",               srv_parse_weight,               1,  1,  1 }, /* Set the load-balancing weight */
+	{ "backup",               srv_parse_backup,                 0,  1,  1 }, /* Flag as backup server */
+	{ "cookie",               srv_parse_cookie,                 1,  1,  1 }, /* Assign a cookie to the server */
+	{ "disabled",             srv_parse_disabled,               0,  1,  1 }, /* Start the server in 'disabled' state */
+	{ "enabled",              srv_parse_enabled,                0,  1,  0 }, /* Start the server in 'enabled' state */
+	{ "error-limit",          srv_parse_error_limit,            1,  1,  1 }, /* Configure the consecutive count of check failures to consider a server on error */
+	{ "guid",                 srv_parse_guid,                   1,  0,  1 }, /* Set global unique ID of the server */
+	{ "ws",                   srv_parse_ws,                     1,  1,  1 }, /* websocket protocol */
+	{ "hash-key",             srv_parse_hash_key,               1,  1,  1 }, /* Configure how chash keys are computed */
+	{ "id",                   srv_parse_id,                     1,  0,  1 }, /* set id# of server */
+	{ "init-addr",            srv_parse_init_addr,              1,  1,  0 }, /* */
+	{ "log-bufsize",          srv_parse_log_bufsize,            1,  1,  0 }, /* Set the ring bufsize for log server (only for log backends) */
+	{ "log-proto",            srv_parse_log_proto,              1,  1,  0 }, /* Set the protocol for event messages, only relevant in a log or ring section */
+	{ "maxconn",              srv_parse_maxconn,                1,  1,  1 }, /* Set the max number of concurrent connection */
+	{ "maxqueue",             srv_parse_maxqueue,               1,  1,  1 }, /* Set the max number of connection to put in queue */
+	{ "max-reuse",            srv_parse_max_reuse,              1,  1,  0 }, /* Set the max number of requests on a connection, -1 means unlimited */
+	{ "minconn",              srv_parse_minconn,                1,  1,  1 }, /* Enable a dynamic maxconn limit */
+	{ "namespace",            srv_parse_namespace,              1,  1,  0 }, /* Namespace the server socket belongs to (if supported) */
+	{ "no-backup",            srv_parse_no_backup,              0,  1,  1 }, /* Flag as non-backup server */
+	{ "no-send-proxy",        srv_parse_no_send_proxy,          0,  1,  1 }, /* Disable use of PROXY V1 protocol */
+	{ "no-send-proxy-v2",     srv_parse_no_send_proxy_v2,       0,  1,  1 }, /* Disable use of PROXY V2 protocol */
+	{ "no-tfo",               srv_parse_no_tfo,                 0,  1,  1 }, /* Disable use of TCP Fast Open */
+	{ "non-stick",            srv_parse_non_stick,              0,  1,  0 }, /* Disable stick-table persistence */
+	{ "observe",              srv_parse_observe,                1,  1,  1 }, /* Enables health adjusting based on observing communication with the server */
+	{ "on-error",             srv_parse_on_error,               1,  1,  1 }, /* Configure the action on check failure */
+	{ "on-marked-down",       srv_parse_on_marked_down,         1,  1,  1 }, /* Configure the action when a server is marked down */
+	{ "on-marked-up",         srv_parse_on_marked_up,           1,  1,  1 }, /* Configure the action when a server is marked up */
+	{ "pool-conn-name",       srv_parse_pool_conn_name,         1,  1,  1 }, /* Define expression to identify connections in idle pool */
+	{ "pool-low-conn",        srv_parse_pool_low_conn,          1,  1,  1 }, /* Set the min number of orphan idle connecbefore being allowed to pick from other threads */
+	{ "pool-max-conn",        srv_parse_pool_max_conn,          1,  1,  1 }, /* Set the max number of orphan idle connections, -1 means unlimited */
+	{ "pool-purge-delay",     srv_parse_pool_purge_delay,       1,  1,  1 }, /* Set the time before we destroy orphan idle connections, defaults to 1s */
+	{ "proto",                srv_parse_proto,                  1,  1,  1 }, /* Set the proto to use for all outgoing connections */
+	{ "proxy-v2-options",     srv_parse_proxy_v2_options,       1,  1,  1 }, /* options for send-proxy-v2 */
+	{ "redir",                srv_parse_redir,                  1,  1,  0 }, /* Enable redirection mode */
+	{ "resolve-net",          srv_parse_resolve_net,            1,  1,  0 }, /* Set the preferred network range for name resolution */
+	{ "resolve-opts",         srv_parse_resolve_opts,           1,  1,  0 }, /* Set options for name resolution */
+	{ "resolve-prefer",       srv_parse_resolve_prefer,         1,  1,  0 }, /* Set the preferred family for name resolution */
+	{ "resolvers",            srv_parse_resolvers,              1,  1,  0 }, /* Configure the resolver to use for name resolution */
+	{ "send-proxy",           srv_parse_send_proxy,             0,  1,  1 }, /* Enforce use of PROXY V1 protocol */
+	{ "send-proxy-v2",        srv_parse_send_proxy_v2,          0,  1,  1 }, /* Enforce use of PROXY V2 protocol */
+	{ "set-proxy-v2-tlv-fmt", srv_parse_set_proxy_v2_tlv_fmt,   0,  1,  1 }, /* Set TLV of PROXY V2 protocol */
+	{ "shard",                srv_parse_shard,                  1,  1,  1 }, /* Server shard (only in peers protocol context) */
+	{ "slowstart",            srv_parse_slowstart,              1,  1,  1 }, /* Set the warm-up timer for a previously failed server */
+	{ "source",               srv_parse_source,              -  1,  1,  1 }, /* Set the source address to be used to connect to the server */
+	{ "stick",                srv_parse_stick,                  0,  1,  0 }, /* Enable stick-table persistence */
+	{ "tfo",                  srv_parse_tfo,                    0,  1,  1 }, /* enable TCP Fast Open of server */
+	{ "track",                srv_parse_track,                  1,  1,  1 }, /* Set the current state of the server, tracking another one */
+	{ "upstream-proxy-tunnel", srv_parse_upstream_proxy_tunnel, 1,  1,  0 }, /* Set the upstream proxy tunnel backend of the server*/
+	{ "socks4",               srv_parse_socks4,                 1,  1,  0 }, /* Set the socks4 proxy of the server*/
+	{ "usesrc",               srv_parse_usesrc,                 0,  1,  1 }, /* safe-guard against usesrc without preceding <source> keyword */
+	{ "weight",               srv_parse_weight,                 1,  1,  1 }, /* Set the load-balancing weight */
 	{ NULL, NULL, 0 },
 }};
 
@@ -2850,10 +2883,11 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
 	if (srv_tmpl)
 		srv->srvrq = src->srvrq;
 
-	srv->netns                    = src->netns;
-	srv->check.via_socks4         = src->check.via_socks4;
-	srv->socks4_addr              = src->socks4_addr;
-	srv->log_bufsize              = src->log_bufsize;
+	srv->netns                      = src->netns;
+	srv->check.via_socks4           = src->check.via_socks4;
+	srv->socks4_addr                = src->socks4_addr;
+	srv->log_bufsize                = src->log_bufsize;
+	srv->upstream_proxy_tunnel_addr = src->upstream_proxy_tunnel_addr;
 
 	LIST_INIT(&srv->pp_tlvs);
 
diff --git a/src/sock.c b/src/sock.c
index df82c6ea7..5c6f4e005 100644
--- a/src/sock.c
+++ b/src/sock.c
@@ -897,6 +897,9 @@ int sock_conn_check(struct connection *conn)
 	if ((conn->flags & CO_FL_SOCKS4) && obj_type(conn->target) == OBJ_TYPE_SERVER)
 		addr = &objt_server(conn->target)->socks4_addr;
 
+	if ((conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL) && obj_type(conn->target) == OBJ_TYPE_SERVER)
+		addr = &objt_server(conn->target)->upstream_proxy_tunnel_addr;
+
 	if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
 		if (errno == EALREADY || errno == EINPROGRESS)
 			goto wait;
diff --git a/src/tcp_rules.c b/src/tcp_rules.c
index 9ce6c9037..19b0f3df6 100644
--- a/src/tcp_rules.c
+++ b/src/tcp_rules.c
@@ -1018,6 +1018,73 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
 		arg += 2;
 		rule->action = ACT_TCP_EXPECT_CIP;
 	}
+	else if (strcmp(args[arg], "upstream-proxy-header") == 0) {
+		struct sample_expr *expr;
+		struct uph_list *my_headers;
+		int ret;
+		int kw = arg;
+
+		/*
+		 * args[0] = tcp-request
+		 * args[1] = connection
+		 */
+
+		/* skip over keyword "upstream-proxy-header" */
+		arg++;
+
+		my_headers = calloc(1,sizeof(*my_headers));
+
+		if (!my_headers)
+		{
+			DPRINTF(stderr,"Can't calloc my_headers\n");
+		}
+		
+		LIST_INIT(&my_headers->list);
+
+		chunk_reset(&trash);
+
+		ret = chunk_printf(&trash, "%s",args[arg]);
+		chunk_dup(&my_headers->name,&trash);
+
+		DPRINTF(stderr,"b_size :%ld:\n",b_size(&my_headers->name));
+		DPRINTF(stderr,"b_data :%ld:\n",b_data(&my_headers->name));
+		DPRINTF(stderr,"Header name :%s:\n",args[arg]);
+
+		arg++;
+
+		chunk_reset(&trash);
+
+		ret = chunk_printf(&trash, "%s",args[arg]);
+		chunk_dup(&my_headers->value,&trash);
+
+		DPRINTF(stderr,"Header value :%s:\n",args[arg]);
+		ret = chunk_printf(&my_headers->value, "%s",args[arg]);
+		arg++;
+
+		LIST_APPEND(&curpx->tcp_req.uph_rules, &my_headers->list);
+
+		DPRINTF(stderr,"name  :%s:\n",ist0(ist2(b_orig(&my_headers->name), b_data(&my_headers->name))));
+		DPRINTF(stderr,"value :%s:\n",ist0(ist2(b_orig(&my_headers->value), b_data(&my_headers->value))));
+
+/* 
+		curpx->conf.args.ctx = ARGC_TCO;
+		expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args, NULL);
+		if (!expr) {
+			memprintf(err,
+			          "'%s %s %s' : %s",
+			          args[0], args[1], args[kw], *err);
+			return -1;
+		}
+ */
+		/*
+		 * args[arg]   => Header name
+		 * args[arg+1] => Header value
+		 */
+		//DPRINTF(stderr,"'%s %s' is in '%s %s' rules in %s '%s'\n",
+		//		  args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
+		//arg += 2;
+		rule->action = ACT_TCP_UPSTREAM_HEADER;
+	}
 	else {
 		struct action_kw *kw;
 		if (where & SMP_VAL_FE_CON_ACC) {
@@ -1038,7 +1105,7 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
 			if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
 				return -1;
 		} else {
-			const char *extra[] = { "accept", "reject", "capture", "track-sc", "expect-proxy", "expect-netscaler-cip", NULL };
+			const char *extra[] = { "accept", "reject", "capture", "track-sc", "expect-proxy", "expect-netscaler-cip","upstream-proxy-header", NULL };
 			const char *best = NULL;
 
 
@@ -1056,7 +1123,7 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
 			}
 
 			memprintf(err,
-			          "'%s %s' expects 'accept', 'reject', 'capture', 'expect-proxy', 'expect-netscaler-cip', 'track-sc0' ... 'track-sc%d', %s "
+			          "'%s %s' expects 'accept', 'reject', 'capture', 'expect-proxy', 'expect-netscaler-cip', 'upstream-proxy-header', 'track-sc0' ... 'track-sc%d', %s "
 			          "in %s '%s' (got '%s').%s%s%s\n",
 			          args[0], args[1], global.tune.nb_stk_ctr-1,
 			          trash.area, proxy_type_str(curpx),
diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index e820fdc2e..b516b2ac0 100644
--- a/src/tcpcheck.c
+++ b/src/tcpcheck.c
@@ -1164,6 +1164,9 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
 		conn->flags |= CO_FL_SOCKS4;
 		TRACE_DEVEL("configure SOCKS4 proxy", CHK_EV_TCPCHK_CONN);
 	}
+
+	/* TODO: Add check via upstream http proxy */
+
 	else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
 		conn->send_proxy_ofs = 1;
 		conn->flags |= CO_FL_SOCKS4;
diff --git a/src/xprt_handshake.c b/src/xprt_handshake.c
index 33f775087..bd6c6cf51 100644
--- a/src/xprt_handshake.c
+++ b/src/xprt_handshake.c
@@ -56,6 +56,24 @@ struct task *xprt_handshake_io_cb(struct task *t, void *bctx, unsigned int state
 			goto out;
 		}
 
+	/* TODO: check flags to send optional headers like keep alive host etc */
+	if (conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL_SEND) {
+		if (!conn_send_upstream_proxy_tunnel_request(conn,CO_FL_UPSTREAM_PROXY_TUNNEL_SEND)) {
+			ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_SEND,
+								 &ctx->wait_event);
+			goto out;
+		}
+	}
+
+	/* TODO: check flags to send optional headers like keep alive host etc */
+	if (conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL_RECV) {
+		if (!conn_recv_upstream_proxy_tunnel_response(conn, CO_FL_UPSTREAM_PROXY_TUNNEL_RECV)) {
+			ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
+								 &ctx->wait_event);
+			goto out;
+		}
+	}
+
 	if (conn->flags & CO_FL_ACCEPT_CIP)
 		if (!conn_recv_netscaler_cip(conn, CO_FL_ACCEPT_CIP)) {
 			ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
-- 
2.34.1

Reply via email to