1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-23 16:48:49 -05:00

CURLOPT_HAPROXYPROTOCOL: support the HAProxy PROXY protocol

Add --haproxy-protocol for the command line tool

Closes #2162
This commit is contained in:
Lawrence Matthews 2016-12-01 04:05:04 -08:00 committed by Daniel Stenberg
parent 9572831b04
commit 6baeb6df35
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
16 changed files with 261 additions and 1 deletions

View File

@ -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.

View File

@ -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 Socks5 GSSAPI NEC mode. See \fICURLOPT_SOCKS5_GSSAPI_NEC(3)\fP
.IP CURLOPT_PROXY_SERVICE_NAME .IP CURLOPT_PROXY_SERVICE_NAME
Proxy authentication service name. \fICURLOPT_PROXY_SERVICE_NAME(3)\fP 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 .IP CURLOPT_SERVICE_NAME
Authentication service name. \fICURLOPT_SERVICE_NAME(3)\fP Authentication service name. \fICURLOPT_SERVICE_NAME(3)\fP
.IP CURLOPT_INTERFACE .IP CURLOPT_INTERFACE

View File

@ -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.

View File

@ -137,6 +137,7 @@ man_MANS = \
CURLOPT_FTP_USE_PRET.3 \ CURLOPT_FTP_USE_PRET.3 \
CURLOPT_GSSAPI_DELEGATION.3 \ CURLOPT_GSSAPI_DELEGATION.3 \
CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 \ CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 \
CURLOPT_HAPROXYPROTOCOL.3 \
CURLOPT_HEADER.3 \ CURLOPT_HEADER.3 \
CURLOPT_HEADERDATA.3 \ CURLOPT_HEADERDATA.3 \
CURLOPT_HEADERFUNCTION.3 \ CURLOPT_HEADERFUNCTION.3 \

View File

@ -404,6 +404,7 @@ CURLOPT_FTP_USE_EPSV 7.9.2
CURLOPT_FTP_USE_PRET 7.20.0 CURLOPT_FTP_USE_PRET 7.20.0
CURLOPT_GSSAPI_DELEGATION 7.22.0 CURLOPT_GSSAPI_DELEGATION 7.22.0
CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS 7.59.0 CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS 7.59.0
CURLOPT_HAPROXYPROTOCOL 7.60.0
CURLOPT_HEADER 7.1 CURLOPT_HEADER 7.1
CURLOPT_HEADERDATA 7.10 CURLOPT_HEADERDATA 7.10
CURLOPT_HEADERFUNCTION 7.7.2 CURLOPT_HEADERFUNCTION 7.7.2

View File

@ -1841,6 +1841,9 @@ typedef enum {
/* User data to pass to the resolver start callback. */ /* User data to pass to the resolver start callback. */
CINIT(RESOLVER_START_DATA, OBJECTPOINT, 273), CINIT(RESOLVER_START_DATA, OBJECTPOINT, 273),
/* send HAProxy PROXY protocol header? */
CINIT(HAPROXYPROTOCOL, LONG, 274),
CURLOPT_LASTENTRY /* the last unused */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } CURLoption;

View File

@ -92,6 +92,8 @@ static int http_getsock_do(struct connectdata *conn,
int numsocks); int numsocks);
static int http_should_fail(struct connectdata *conn); static int http_should_fail(struct connectdata *conn);
static CURLcode add_haproxy_protocol_header(struct connectdata *conn);
#ifdef USE_SSL #ifdef USE_SSL
static CURLcode https_connecting(struct connectdata *conn, bool *done); static CURLcode https_connecting(struct connectdata *conn, bool *done);
static int https_getsock(struct connectdata *conn, 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. */ /* nothing else to do except wait right now - we're not done here. */
return CURLE_OK; 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) { if(conn->given->protocol & CURLPROTO_HTTPS) {
/* perform SSL initialization */ /* perform SSL initialization */
result = https_connecting(conn, done); result = https_connecting(conn, done);
@ -1383,6 +1392,47 @@ static int http_getsock_do(struct connectdata *conn,
return GETSOCK_WRITESOCK(0); 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 #ifdef USE_SSL
static CURLcode https_connecting(struct connectdata *conn, bool *done) static CURLcode https_connecting(struct connectdata *conn, bool *done)
{ {

View File

@ -1603,6 +1603,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE; data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE;
break; 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: case CURLOPT_INTERFACE:
/* /*
* Set what interface or address/hostname to bind the socket to when * Set what interface or address/hostname to bind the socket to when

View File

@ -1678,6 +1678,8 @@ struct UserDefined {
bool stream_depends_e; /* set or don't set the Exclusive bit */ bool stream_depends_e; /* set or don't set the Exclusive bit */
int stream_weight; int stream_weight;
bool haproxyprotocol; /* whether to send HAProxy PROXY protocol header */
struct Curl_http2_dep *stream_dependents; struct Curl_http2_dep *stream_dependents;
bool abstract_unix_socket; bool abstract_unix_socket;

View File

@ -252,6 +252,7 @@ struct OperationConfig {
bool ssh_compression; /* enable/disable SSH compression */ bool ssh_compression; /* enable/disable SSH compression */
long happy_eyeballs_timeout_ms; /* happy eyeballs timeout in milliseconds. long happy_eyeballs_timeout_ms; /* happy eyeballs timeout in milliseconds.
0 is valid. default: CURL_HET_DEFAULT. */ 0 is valid. default: CURL_HET_DEFAULT. */
bool haproxy_protocol; /* whether to send HAProxy PROXY protocol */
struct GlobalConfig *global; struct GlobalConfig *global;
struct OperationConfig *prev; struct OperationConfig *prev;
struct OperationConfig *next; /* Always last in the struct */ struct OperationConfig *next; /* Always last in the struct */

View File

@ -112,6 +112,7 @@ static const struct LongShort aliases[]= {
{"*x", "krb", ARG_STRING}, {"*x", "krb", ARG_STRING},
{"*x", "krb4", ARG_STRING}, {"*x", "krb4", ARG_STRING},
/* 'krb4' is the previous name */ /* 'krb4' is the previous name */
{"*X", "haproxy-protocol", ARG_BOOL},
{"*y", "max-filesize", ARG_STRING}, {"*y", "max-filesize", ARG_STRING},
{"*z", "disable-eprt", ARG_BOOL}, {"*z", "disable-eprt", ARG_BOOL},
{"*Z", "eprt", ARG_BOOL}, {"*Z", "eprt", ARG_BOOL},
@ -779,6 +780,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
else else
return PARAM_LIBCURL_DOESNT_SUPPORT; return PARAM_LIBCURL_DOESNT_SUPPORT;
break; break;
case 'X': /* --haproxy-protocol */
config->haproxy_protocol = toggle;
break;
case 'y': /* --max-filesize */ case 'y': /* --max-filesize */
{ {
curl_off_t value; curl_off_t value;

View File

@ -164,6 +164,8 @@ static const struct helptxt helptext[] = {
"How long to wait in milliseconds for IPv6 before trying IPv4"}, "How long to wait in milliseconds for IPv6 before trying IPv4"},
{"-I, --head", {"-I, --head",
"Show document info only"}, "Show document info only"},
{" --haproxy-protocol",
"Send HAProxy PROXY protocol header"},
{"-H, --header <header/@file>", {"-H, --header <header/@file>",
"Pass custom header(s) to server"}, "Pass custom header(s) to server"},
{"-h, --help", {"-h, --help",

View File

@ -1445,6 +1445,10 @@ static CURLcode operate_do(struct GlobalConfig *global,
my_setopt(curl, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, my_setopt(curl, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
config->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 */ /* initialize retry vars for loop below */
retry_sleep_default = (config->retry_delay) ? retry_sleep_default = (config->retry_delay) ?
config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */ config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */

View File

@ -165,7 +165,7 @@ test1424 test1425 test1426 test1427 \
test1428 test1429 test1430 test1431 test1432 test1433 test1434 test1435 \ test1428 test1429 test1430 test1431 test1432 test1433 test1434 test1435 \
test1436 test1437 test1438 test1439 test1440 test1441 test1442 test1443 \ test1436 test1437 test1438 test1439 test1440 test1441 test1442 test1443 \
test1444 test1445 test1446 test1447 test1448 test1449 test1450 test1451 \ test1444 test1445 test1446 test1447 test1448 test1449 test1450 test1451 \
test1452 test1453 test1454 \ test1452 test1453 test1454 test1455 test1456 \
test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \ test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \
test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \ test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \
test1516 test1517 \ test1516 test1517 \

56
tests/data/test1455 Normal file
View File

@ -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>

59
tests/data/test1456 Normal file
View File

@ -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>