Skip to content

Commit

Permalink
Merge 1e4edfd into ee84085
Browse files Browse the repository at this point in the history
  • Loading branch information
elyscape committed Feb 27, 2018
2 parents ee84085 + 1e4edfd commit 08c726e
Show file tree
Hide file tree
Showing 16 changed files with 261 additions and 1 deletion.
11 changes: 11 additions & 0 deletions 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.
2 changes: 2 additions & 0 deletions docs/libcurl/curl_easy_setopt.3
Expand Up @@ -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
Expand Down
57 changes: 57 additions & 0 deletions 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.
1 change: 1 addition & 0 deletions docs/libcurl/opts/Makefile.inc
Expand Up @@ -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 \
Expand Down
1 change: 1 addition & 0 deletions docs/libcurl/symbols-in-versions
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions include/curl/curl.h
Expand Up @@ -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;

Expand Down
50 changes: 50 additions & 0 deletions lib/http.c
Expand Up @@ -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,
Expand Down Expand Up @@ -1357,6 +1359,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);
Expand All @@ -1382,6 +1391,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)
{
Expand Down
7 changes: 7 additions & 0 deletions lib/setopt.c
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions lib/urldata.h
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/tool_cfgable.h
Expand Up @@ -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 */
Expand Down
4 changes: 4 additions & 0 deletions src/tool_getparam.c
Expand Up @@ -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},
Expand Down Expand Up @@ -778,6 +779,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;
Expand Down
2 changes: 2 additions & 0 deletions src/tool_help.c
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions src/tool_operate.c
Expand Up @@ -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 */
Expand Down
2 changes: 1 addition & 1 deletion tests/data/Makefile.inc
Expand Up @@ -161,7 +161,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 \
Expand Down
56 changes: 56 additions & 0 deletions 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>
59 changes: 59 additions & 0 deletions 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>

0 comments on commit 08c726e

Please sign in to comment.