Skip to content

Commit

Permalink
url: Add option CURLOPT_HAPPY_EYEBALLS_TIMEOUT
Browse files Browse the repository at this point in the history
- Add new option CURLOPT_HAPPY_EYEBALLS_TIMEOUT to set libcurl's happy
  eyeball timeout value.

- Add new optval macro CURL_HET_DEFAULT to represent the default happy
  eyeballs timeout value (currently 200 ms).

- Add new tool option --happy-eyeballs-timeout-ms to expose
  CURLOPT_HAPPY_EYEBALLS_TIMEOUT. The -ms suffix is used because the
  other -timeout options in the tool expect seconds not milliseconds.

Closes curl#2260
  • Loading branch information
Andersbakken authored and jay committed Feb 20, 2018
1 parent 73050fb commit 2427d94
Show file tree
Hide file tree
Showing 18 changed files with 125 additions and 5 deletions.
4 changes: 3 additions & 1 deletion docs/cmdline-opts/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ DPAGES = abstract-unix-socket.d anyauth.d append.d basic.d cacert.d capath.d cer
form.d form-string.d ftp-account.d ftp-alternative-to-user.d \
ftp-create-dirs.d ftp-method.d ftp-pasv.d ftp-port.d ftp-pret.d \
ftp-skip-pasv-ip.d ftp-ssl-ccc.d ftp-ssl-ccc-mode.d ftp-ssl-control.d \
get.d globoff.d head.d header.d help.d hostpubmd5.d http1.0.d \
get.d globoff.d \
happy-eyeballs-timeout-ms.d \
head.d header.d help.d hostpubmd5.d http1.0.d \
http1.1.d http2.d http2-prior-knowledge.d ignore-content-length.d \
include.d insecure.d interface.d ipv4.d ipv6.d junk-session-cookies.d \
keepalive-time.d key.d key-type.d krb.d libcurl.d limit-rate.d \
Expand Down
17 changes: 17 additions & 0 deletions docs/cmdline-opts/happy-eyeballs-timeout-ms.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Long: happy-eyeballs-timeout-ms
Arg: <milliseconds>
Help: How long to wait in milliseconds for IPv6 before trying IPv4
Added: 7.59.0
---
Happy eyeballs is an algorithm that attempts to connect to both IPv4 and IPv6
addresses for dual-stack hosts, preferring IPv6 first for the number of
milliseconds. If the IPv6 address cannot be connected to within that time then
a connection attempt is made to the IPv4 address in parallel. The first
connection to be established is the one that is used.

The range of suggested useful values is limited. Happy Eyeballs RFC 6555 says
"It is RECOMMENDED that connection attempts be paced 150-250 ms apart to
balance human factors against network load." libcurl currently defaults to
200 ms. Firefox and Chrome currently default to 300 ms.

If this option is used several times, the last one will be used.
2 changes: 2 additions & 0 deletions docs/libcurl/curl_easy_setopt.3
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,8 @@ Mode for creating new remote directories. See \fICURLOPT_NEW_DIRECTORY_PERMS(3)\
.SH TELNET OPTIONS
.IP CURLOPT_TELNETOPTIONS
TELNET options. See \fICURLOPT_TELNETOPTIONS(3)\fP
.IP CURLOPT_HAPPY_EYEBALLS_TIMEOUT
Timeout for happy eyeballs. See \fICURLOPT_HAPPY_EYEBALLS_TIMEOUT(3)\fP
.SH RETURN VALUE
\fICURLE_OK\fP (zero) means that the option was set properly, non-zero means an
error occurred as \fI<curl/curl.h>\fP defines. See the \fIlibcurl-errors(3)\fP
Expand Down
59 changes: 59 additions & 0 deletions docs/libcurl/opts/CURLOPT_HAPPY_EYEBALLS_TIMEOUT.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2015, 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_HAPPY_EYEBALLS_TIMEOUT 3 "1 Feb 2018" "libcurl 7.59.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_HAPPY_EYEBALLS_TIMEOUT \- head start for ipv6 for happy eyeballs
.SH SYNOPSIS
#include <curl/curl.h>

CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HAPPY_EYEBALLS_TIMEOUT, long timeout);
.SH DESCRIPTION
Happy eyeballs is an algorithm that attempts to connect to both IPv4 and IPv6
addresses for dual-stack hosts, preferring IPv6 first for \fItimeout\fP
milliseconds. If the IPv6 address cannot be connected to within that time then
a connection attempt is made to the IPv4 address in parallel. The first
connection to be established is the one that is used.

The range of suggested useful values for \fItimeout\fP is limited. Happy
Eyeballs RFC 6555 says "It is RECOMMENDED that connection attempts be paced
150-250 ms apart to balance human factors against network load." libcurl
currently defaults to 200 ms. Firefox and Chrome currently default to 300 ms.
.SH DEFAULT
CURL_HET_DEFAULT (currently defined as 200L)
.SH EXAMPLE
.nf
CURL *curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
curl_easy_setopt(curl, CURLOPT_HAPPY_EYEBALLS_TIMEOUT, 300L);

curl_easy_perform(curl);

/* always cleanup */
curl_easy_cleanup(curl);
}
.fi
.SH AVAILABILITY
Added in 7.59.0
.SH RETURN VALUE
Returns CURLE_OK
1 change: 1 addition & 0 deletions docs/libcurl/opts/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ man_MANS = \
CURLOPT_FTP_USE_EPSV.3 \
CURLOPT_FTP_USE_PRET.3 \
CURLOPT_GSSAPI_DELEGATION.3 \
CURLOPT_HAPPY_EYEBALLS_TIMEOUT.3 \
CURLOPT_HEADER.3 \
CURLOPT_HEADERDATA.3 \
CURLOPT_HEADERFUNCTION.3 \
Expand Down
2 changes: 2 additions & 0 deletions docs/libcurl/symbols-in-versions
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ CURLOPT_FTP_USE_EPRT 7.10.5
CURLOPT_FTP_USE_EPSV 7.9.2
CURLOPT_FTP_USE_PRET 7.20.0
CURLOPT_GSSAPI_DELEGATION 7.22.0
CURLOPT_HAPPY_EYEBALLS_TIMEOUT 7.59.0
CURLOPT_HEADER 7.1
CURLOPT_HEADERDATA 7.10
CURLOPT_HEADERFUNCTION 7.7.2
Expand Down Expand Up @@ -741,6 +742,7 @@ CURL_GLOBAL_DEFAULT 7.8
CURL_GLOBAL_NOTHING 7.8
CURL_GLOBAL_SSL 7.8
CURL_GLOBAL_WIN32 7.8.1
CURL_HET_DEFAULT 7.59.0
CURL_HTTPPOST_BUFFER 7.46.0
CURL_HTTPPOST_CALLBACK 7.46.0
CURL_HTTPPOST_FILENAME 7.46.0
Expand Down
8 changes: 8 additions & 0 deletions include/curl/curl.h
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,11 @@ typedef enum {
SSL backends where such behavior is present. */
#define CURLSSLOPT_NO_REVOKE (1<<1)

/* The default connection attempt delay in milliseconds for happy eyeballs.
CURLOPT_HAPPY_EYEBALLS_TIMEOUT.3 and happy-eyeballs-timeout-ms.d document
this value, keep them in sync. */
#define CURL_HET_DEFAULT 200L

#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
the obsolete stuff removed! */

Expand Down Expand Up @@ -1825,6 +1830,9 @@ typedef enum {
seconds since 1 Jan 1970. */
CINIT(TIMEVALUE_LARGE, OFF_T, 270),

/* Head start in milliseconds to give happy eyeballs. */
CINIT(HAPPY_EYEBALLS_TIMEOUT, LONG, 271),

CURLOPT_LASTENTRY /* the last unused */
} CURLoption;

Expand Down
6 changes: 4 additions & 2 deletions lib/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,8 @@ CURLcode Curl_is_connected(struct connectdata *conn,

/* should we try another protocol family? */
if(i == 0 && conn->tempaddr[1] == NULL &&
Curl_timediff(now, conn->connecttime) >= HAPPY_EYEBALLS_TIMEOUT) {
(Curl_timediff(now, conn->connecttime) >=
data->set.happy_eyeballs_timeout)) {
trynextip(conn, sockindex, 1);
}
}
Expand Down Expand Up @@ -1206,7 +1207,8 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
}

data->info.numconnects++; /* to track the number of connections made */
Curl_expire(conn->data, HAPPY_EYEBALLS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS);
Curl_expire(conn->data, data->set.happy_eyeballs_timeout,
EXPIRE_HAPPY_EYEBALLS);

return CURLE_OK;
}
Expand Down
2 changes: 0 additions & 2 deletions lib/connect.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
bool duringconnect);

#define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
#define HAPPY_EYEBALLS_TIMEOUT 200 /* milliseconds to wait between
IPv4/IPv6 connection attempts */

/*
* Used to extract socket and connectdata struct for the most recent
Expand Down
6 changes: 6 additions & 0 deletions lib/setopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -2533,6 +2533,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
case CURLOPT_SSH_COMPRESSION:
data->set.ssh_compression = (0 != va_arg(param, long))?TRUE:FALSE;
break;
case CURLOPT_HAPPY_EYEBALLS_TIMEOUT:
arg = va_arg(param, long);
if(arg < 0)
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.happy_eyeballs_timeout = arg;
break;
default:
/* unknown tag and its companion, just ignore: */
result = CURLE_UNKNOWN_OPTION;
Expand Down
1 change: 1 addition & 0 deletions lib/url.c
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->expect_100_timeout = 1000L; /* Wait for a second by default. */
set->sep_headers = TRUE; /* separated header lists by default */
set->buffer_size = READBUFFER_SIZE;
set->happy_eyeballs_timeout = CURL_HET_DEFAULT;

Curl_http2_init_userset(set);
return result;
Expand Down
1 change: 1 addition & 0 deletions lib/urldata.h
Original file line number Diff line number Diff line change
Expand Up @@ -1520,6 +1520,7 @@ struct UserDefined {
long timeout; /* in milliseconds, 0 means no timeout */
long connecttimeout; /* in milliseconds, 0 means no timeout */
long accepttimeout; /* in milliseconds, 0 means no timeout */
long happy_eyeballs_timeout; /* in milliseconds, 0 is a valid value */
long server_response_timeout; /* in milliseconds, 0 means no timeout */
long tftp_blksize; /* in bytes, 0 means use default */
bool tftp_no_options; /* do not send TFTP options requests */
Expand Down
4 changes: 4 additions & 0 deletions packages/OS400/curl.inc.in
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,10 @@
d c 00268
d CURLOPT_MIMEPOST...
d c 10269
d CURLOPT_TIMEVALUE_LARGE...
d c 30270
d CURLOPT_HAPPY_EYEBALLS_TIMEOUT...
d c 00271
*
/if not defined(CURL_NO_OLDIES)
d CURLOPT_FILE c 10001
Expand Down
1 change: 1 addition & 0 deletions src/tool_cfgable.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ void config_init(struct OperationConfig* config)
config->proto_redir_present = FALSE;
config->proto_default = NULL;
config->tcp_nodelay = TRUE; /* enabled by default */
config->happy_eyeballs_timeout_ms = CURL_HET_DEFAULT;
}

static void free_config_fields(struct OperationConfig *config)
Expand Down
2 changes: 2 additions & 0 deletions src/tool_cfgable.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ struct OperationConfig {
curl_error synthetic_error; /* if non-zero, it overrides any libcurl
error */
bool ssh_compression; /* enable/disable SSH compression */
long happy_eyeballs_timeout_ms; /* happy eyeballs timeout in milliseconds.
0 is valid. default: CURL_HET_DEFAULT. */
struct GlobalConfig *global;
struct OperationConfig *prev;
struct OperationConfig *next; /* Always last in the struct */
Expand Down
7 changes: 7 additions & 0 deletions src/tool_getparam.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ static const struct LongShort aliases[]= {
{"$X", "tls-max", ARG_STRING},
{"$Y", "suppress-connect-headers", ARG_BOOL},
{"$Z", "compressed-ssh", ARG_BOOL},
{"$~", "happy-eyeballs-timeout-ms", ARG_STRING},
{"0", "http1.0", ARG_NONE},
{"01", "http1.1", ARG_NONE},
{"02", "http2", ARG_NONE},
Expand Down Expand Up @@ -1111,6 +1112,12 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
case 'Z': /* --compressed-ssh */
config->ssh_compression = toggle;
break;
case '~': /* --happy-eyeballs-timeout-ms */
err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg);
if(err)
return err;
/* 0 is a valid value for this timeout */
break;
}
break;
case '#': /* --progress-bar */
Expand Down
2 changes: 2 additions & 0 deletions src/tool_help.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ static const struct helptxt helptext[] = {
"Put the post data in the URL and use GET"},
{"-g, --globoff",
"Disable URL sequences and ranges using {} and []"},
{" --happy-eyeballs-timeout-ms",
"How long to wait in milliseconds for IPv6 before trying IPv4"},
{"-I, --head",
"Show document info only"},
{"-H, --header <header/@file>",
Expand Down
5 changes: 5 additions & 0 deletions src/tool_operate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1440,6 +1440,11 @@ static CURLcode operate_do(struct GlobalConfig *global,
if(config->tftp_no_options)
my_setopt(curl, CURLOPT_TFTP_NO_OPTIONS, 1L);

/* new in 7.59.0 */
if(config->happy_eyeballs_timeout_ms != CURL_HET_DEFAULT)
my_setopt(curl, CURLOPT_HAPPY_EYEBALLS_TIMEOUT,
config->happy_eyeballs_timeout_ms);

/* initialize retry vars for loop below */
retry_sleep_default = (config->retry_delay) ?
config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */
Expand Down

0 comments on commit 2427d94

Please sign in to comment.