summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLawrence Matthews <lmatthew@yelp.com>2016-12-01 04:05:04 -0800
committerDaniel Stenberg <daniel@haxx.se>2018-03-17 11:50:06 +0100
commit6baeb6df35d24740c55239f24b5fc4ce86f375a5 (patch)
tree6a8ec40408e6827f40b750985517e575c89545fc
parent9572831b0470378c2f45f2891feb91dea19c16d7 (diff)
downloadcurl-6baeb6df35d24740c55239f24b5fc4ce86f375a5.tar.gz
CURLOPT_HAPROXYPROTOCOL: support the HAProxy PROXY protocol
Add --haproxy-protocol for the command line tool Closes #2162
-rw-r--r--docs/cmdline-opts/haproxy-protocol.d11
-rw-r--r--docs/libcurl/curl_easy_setopt.32
-rw-r--r--docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.357
-rw-r--r--docs/libcurl/opts/Makefile.inc1
-rw-r--r--docs/libcurl/symbols-in-versions1
-rw-r--r--include/curl/curl.h3
-rw-r--r--lib/http.c50
-rw-r--r--lib/setopt.c7
-rw-r--r--lib/urldata.h2
-rw-r--r--src/tool_cfgable.h1
-rw-r--r--src/tool_getparam.c4
-rw-r--r--src/tool_help.c2
-rw-r--r--src/tool_operate.c4
-rw-r--r--tests/data/Makefile.inc2
-rw-r--r--tests/data/test145556
-rw-r--r--tests/data/test145659
16 files changed, 261 insertions, 1 deletions
diff --git a/docs/cmdline-opts/haproxy-protocol.d b/docs/cmdline-opts/haproxy-protocol.d
new file mode 100644
index 000000000..52e156058
--- /dev/null
+++ b/docs/cmdline-opts/haproxy-protocol.d
@@ -0,0 +1,11 @@
+Long: haproxy-protocol
+Help: Send HAProxy PROXY protocol header
+Protocols: HTTP
+Added: 7.60.0
+---
+Send a HAProxy PROXY protocol header at the beginning of the connection. This
+is used by some load balancers and reverse proxies to indicate the client's
+true IP address and port.
+
+This option is primarily useful when sending test requests to a service that
+expects this header.
diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3
index 1efb467e6..b7d67f360 100644
--- a/docs/libcurl/curl_easy_setopt.3
+++ b/docs/libcurl/curl_easy_setopt.3
@@ -185,6 +185,8 @@ Socks5 GSSAPI service name. \fICURLOPT_SOCKS5_GSSAPI_SERVICE(3)\fP
Socks5 GSSAPI NEC mode. See \fICURLOPT_SOCKS5_GSSAPI_NEC(3)\fP
.IP CURLOPT_PROXY_SERVICE_NAME
Proxy authentication service name. \fICURLOPT_PROXY_SERVICE_NAME(3)\fP
+.IP CURLOPT_HAPROXYPROTOCOL
+Send an HAProxy PROXY protocol header. See \fICURLOPT_HAPROXYPROTOCOL(3)\fP
.IP CURLOPT_SERVICE_NAME
Authentication service name. \fICURLOPT_SERVICE_NAME(3)\fP
.IP CURLOPT_INTERFACE
diff --git a/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.3 b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.3
new file mode 100644
index 000000000..01e667d16
--- /dev/null
+++ b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.3
@@ -0,0 +1,57 @@
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, 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.haxx.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.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLOPT_HAPROXYPROTOCOL 3 "5 Feb 2018" "libcurl 7.60.0" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_HAPROXYPROTOCOL \- send HAProxy PROXY protocol header
+.SH SYNOPSIS
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HAPROXYPROTOCOL,
+ long haproxy_protocol);
+.SH DESCRIPTION
+A long parameter set to 1 tells the library to send an HAProxy PROXY
+protocol header at beginning of the connection. The default action is not to
+send this header.
+
+This option is primarily useful when sending test requests to a service that
+expects this header.
+
+Most applications do not need this option.
+.SH DEFAULT
+0, do not send HAProxy PROXY protocol header
+.SH PROTOCOLS
+HTTP
+.SH EXAMPLE
+.nf
+CURL *curl = curl_easy_init();
+if(curl) {
+ CURLcode ret;
+ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
+ curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L);
+ ret = curl_easy_perform(curl);
+}
+.fi
+.SH AVAILABILITY
+Along with HTTP. Added in 7.60.0.
+.SH RETURN VALUE
+Returns CURLE_OK if HTTP is enabled, and CURLE_UNKNOWN_OPTION if not.
diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc
index 2aa1acf33..b370082d6 100644
--- a/docs/libcurl/opts/Makefile.inc
+++ b/docs/libcurl/opts/Makefile.inc
@@ -137,6 +137,7 @@ man_MANS = \
CURLOPT_FTP_USE_PRET.3 \
CURLOPT_GSSAPI_DELEGATION.3 \
CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 \
+ CURLOPT_HAPROXYPROTOCOL.3 \
CURLOPT_HEADER.3 \
CURLOPT_HEADERDATA.3 \
CURLOPT_HEADERFUNCTION.3 \
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index c58086fb7..2877de7f1 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -404,6 +404,7 @@ CURLOPT_FTP_USE_EPSV 7.9.2
CURLOPT_FTP_USE_PRET 7.20.0
CURLOPT_GSSAPI_DELEGATION 7.22.0
CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS 7.59.0
+CURLOPT_HAPROXYPROTOCOL 7.60.0
CURLOPT_HEADER 7.1
CURLOPT_HEADERDATA 7.10
CURLOPT_HEADERFUNCTION 7.7.2
diff --git a/include/curl/curl.h b/include/curl/curl.h
index fa019eca9..43d5e031f 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1841,6 +1841,9 @@ typedef enum {
/* User data to pass to the resolver start callback. */
CINIT(RESOLVER_START_DATA, OBJECTPOINT, 273),
+ /* send HAProxy PROXY protocol header? */
+ CINIT(HAPROXYPROTOCOL, LONG, 274),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
diff --git a/lib/http.c b/lib/http.c
index 841f6cc0b..29dcf6562 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -92,6 +92,8 @@ static int http_getsock_do(struct connectdata *conn,
int numsocks);
static int http_should_fail(struct connectdata *conn);
+static CURLcode add_haproxy_protocol_header(struct connectdata *conn);
+
#ifdef USE_SSL
static CURLcode https_connecting(struct connectdata *conn, bool *done);
static int https_getsock(struct connectdata *conn,
@@ -1358,6 +1360,13 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
/* nothing else to do except wait right now - we're not done here. */
return CURLE_OK;
+ if(conn->data->set.haproxyprotocol) {
+ /* add HAProxy PROXY protocol header */
+ result = add_haproxy_protocol_header(conn);
+ if(result)
+ return result;
+ }
+
if(conn->given->protocol & CURLPROTO_HTTPS) {
/* perform SSL initialization */
result = https_connecting(conn, done);
@@ -1383,6 +1392,47 @@ static int http_getsock_do(struct connectdata *conn,
return GETSOCK_WRITESOCK(0);
}
+static CURLcode add_haproxy_protocol_header(struct connectdata *conn)
+{
+ char proxy_header[128];
+ Curl_send_buffer *req_buffer;
+ CURLcode result;
+ char tcp_version[5];
+
+ /* Emit the correct prefix for IPv6 */
+ if(conn->bits.ipv6) {
+ strcpy(tcp_version, "TCP6");
+ }
+ else {
+ strcpy(tcp_version, "TCP4");
+ }
+
+ snprintf(proxy_header,
+ sizeof proxy_header,
+ "PROXY %s %s %s %i %i\r\n",
+ tcp_version,
+ conn->data->info.conn_local_ip,
+ conn->data->info.conn_primary_ip,
+ conn->data->info.conn_local_port,
+ conn->data->info.conn_primary_port);
+
+ req_buffer = Curl_add_buffer_init();
+ if(!req_buffer)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = Curl_add_bufferf(req_buffer, proxy_header);
+ if(result)
+ return result;
+
+ result = Curl_add_buffer_send(req_buffer,
+ conn,
+ &conn->data->info.request_size,
+ 0,
+ FIRSTSOCKET);
+
+ return result;
+}
+
#ifdef USE_SSL
static CURLcode https_connecting(struct connectdata *conn, bool *done)
{
diff --git a/lib/setopt.c b/lib/setopt.c
index 9c96eb358..737a60f85 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -1603,6 +1603,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
+ case CURLOPT_HAPROXYPROTOCOL:
+ /*
+ * Set to send the HAProxy Proxy Protocol header
+ */
+ data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
case CURLOPT_INTERFACE:
/*
* Set what interface or address/hostname to bind the socket to when
diff --git a/lib/urldata.h b/lib/urldata.h
index 0da5fbce0..dad31cd4e 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1678,6 +1678,8 @@ struct UserDefined {
bool stream_depends_e; /* set or don't set the Exclusive bit */
int stream_weight;
+ bool haproxyprotocol; /* whether to send HAProxy PROXY protocol header */
+
struct Curl_http2_dep *stream_dependents;
bool abstract_unix_socket;
diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h
index 743ce725d..9abaa9d39 100644
--- a/src/tool_cfgable.h
+++ b/src/tool_cfgable.h
@@ -252,6 +252,7 @@ struct OperationConfig {
bool ssh_compression; /* enable/disable SSH compression */
long happy_eyeballs_timeout_ms; /* happy eyeballs timeout in milliseconds.
0 is valid. default: CURL_HET_DEFAULT. */
+ bool haproxy_protocol; /* whether to send HAProxy PROXY protocol */
struct GlobalConfig *global;
struct OperationConfig *prev;
struct OperationConfig *next; /* Always last in the struct */
diff --git a/src/tool_getparam.c b/src/tool_getparam.c
index 7ce9c28c7..19454c84a 100644
--- a/src/tool_getparam.c
+++ b/src/tool_getparam.c
@@ -112,6 +112,7 @@ static const struct LongShort aliases[]= {
{"*x", "krb", ARG_STRING},
{"*x", "krb4", ARG_STRING},
/* 'krb4' is the previous name */
+ {"*X", "haproxy-protocol", ARG_BOOL},
{"*y", "max-filesize", ARG_STRING},
{"*z", "disable-eprt", ARG_BOOL},
{"*Z", "eprt", ARG_BOOL},
@@ -779,6 +780,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
+ case 'X': /* --haproxy-protocol */
+ config->haproxy_protocol = toggle;
+ break;
case 'y': /* --max-filesize */
{
curl_off_t value;
diff --git a/src/tool_help.c b/src/tool_help.c
index 9796b7e87..4bd65269a 100644
--- a/src/tool_help.c
+++ b/src/tool_help.c
@@ -164,6 +164,8 @@ static const struct helptxt helptext[] = {
"How long to wait in milliseconds for IPv6 before trying IPv4"},
{"-I, --head",
"Show document info only"},
+ {" --haproxy-protocol",
+ "Send HAProxy PROXY protocol header"},
{"-H, --header <header/@file>",
"Pass custom header(s) to server"},
{"-h, --help",
diff --git a/src/tool_operate.c b/src/tool_operate.c
index 15cdc13da..0aad54282 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -1445,6 +1445,10 @@ static CURLcode operate_do(struct GlobalConfig *global,
my_setopt(curl, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
config->happy_eyeballs_timeout_ms);
+ /* new in 7.60.0 */
+ if(config->haproxy_protocol)
+ my_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L);
+
/* initialize retry vars for loop below */
retry_sleep_default = (config->retry_delay) ?
config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 5fcffc6eb..ca0c7edd1 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -165,7 +165,7 @@ test1424 test1425 test1426 test1427 \
test1428 test1429 test1430 test1431 test1432 test1433 test1434 test1435 \
test1436 test1437 test1438 test1439 test1440 test1441 test1442 test1443 \
test1444 test1445 test1446 test1447 test1448 test1449 test1450 test1451 \
-test1452 test1453 test1454 \
+test1452 test1453 test1454 test1455 test1456 \
test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \
test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \
test1516 test1517 \
diff --git a/tests/data/test1455 b/tests/data/test1455
new file mode 100644
index 000000000..7768a1f89
--- /dev/null
+++ b/tests/data/test1455
@@ -0,0 +1,56 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+</keywords>
+</info>
+
+#
+# Server-side
+<reply name="1455">
+<data nocheck=yes>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
+ETag: "21025-dc7-39462498"
+Accept-Ranges: bytes
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+Funny-head: barkbark
+
+-foo-
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+HTTP GET when PROXY Protocol enabled
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/1455 --haproxy-protocol --local-port 37756
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+PROXY TCP4 %CLIENTIP %HOSTIP 37756 %HTTPPORT
+GET /1455 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test1456 b/tests/data/test1456
new file mode 100644
index 000000000..07a6e7c03
--- /dev/null
+++ b/tests/data/test1456
@@ -0,0 +1,59 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+IPv6
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data nocheck=yes>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
+ETag: "21025-dc7-39462498"
+Accept-Ranges: bytes
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+Funny-head: yesyes
+
+-foo-
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<features>
+ipv6
+</features>
+<server>
+http-ipv6
+</server>
+ <name>
+HTTP-IPv6 GET with PROXY protocol
+ </name>
+ <command>
+-g "http://%HOST6IP:%HTTP6PORT/1456" --local-port 44444 --haproxy-protocol
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:
+</strip>
+<protocol>
+PROXY TCP6 ::1 ::1 44444 %HTTP6PORT
+GET /1456 HTTP/1.1
+Host: %HOST6IP:%HTTP6PORT
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>