url: Add option CURLOPT_HAPPY_EYEBALLS_TIMEOUT

- 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 https://github.com/curl/curl/pull/2260
This commit is contained in:
Anders Bakken 2018-01-30 16:33:51 -08:00 committed by Jay Satiro
parent 73050fb6ae
commit 2427d94c6d
18 changed files with 125 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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! */
@ -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;

View File

@ -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);
}
}
@ -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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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},
@ -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 */

View File

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

View File

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