data->change.referer_alloc = FALSE;
}
data->change.referer = NULL;
+ if(data->change.url_alloc) {
+ Curl_safefree(data->change.url);
+ data->change.url_alloc = FALSE;
+ }
+ data->change.url = NULL;
}
static CURLcode setstropt(char **charp, char *s)
set->convtonetwork = ZERO_NULL;
set->convfromutf8 = ZERO_NULL;
- set->infilesize = -1; /* we don't know any size */
+ set->filesize = -1; /* we don't know the size */
set->postfieldsize = -1; /* unknown size */
set->maxredirs = -1; /* allow any amount by default */
set->ssl_enable_npn = TRUE;
set->ssl_enable_alpn = TRUE;
+
+ set->expect_100_timeout = 1000L; /* Wait for a second by default. */
return res;
}
data->set.headers = va_arg(param, struct curl_slist *);
break;
+ case CURLOPT_PROXYHEADER:
+ /*
+ * Set a list with proxy headers to use (or replace internals with)
+ *
+ * Since CURLOPT_HTTPHEADER was the only way to set HTTP headers for a
+ * long time we remain doing it this way until CURLOPT_PROXYHEADER is
+ * used. As soon as this option has been used, if set to anything but
+ * NULL, custom headers for proxies are only picked from this list.
+ *
+ * Set this option to NULL to restore the previous behavior.
+ */
+ data->set.proxyheaders = va_arg(param, struct curl_slist *);
+ break;
+
+ case CURLOPT_HEADEROPT:
+ /*
+ * Set header option.
+ */
+ arg = va_arg(param, long);
+ data->set.sep_headers = (arg & CURLHEADER_SEPARATE)? TRUE: FALSE;
+ break;
+
case CURLOPT_HTTP200ALIASES:
/*
* Set a list of aliases for HTTP 200 in response header
if(argptr == NULL)
break;
- Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-
if(Curl_raw_equal(argptr, "ALL")) {
/* clear all cookies */
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_clearall(data->cookies);
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
else if(Curl_raw_equal(argptr, "SESS")) {
/* clear session cookies */
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_clearsess(data->cookies);
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
else if(Curl_raw_equal(argptr, "FLUSH")) {
- /* flush cookies to file */
+ /* flush cookies to file, takes care of the locking */
Curl_flush_cookies(data, 0);
}
else {
result = CURLE_OUT_OF_MEMORY;
}
else {
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
if(checkprefix("Set-Cookie:", argptr))
/* HTTP Header format line */
/* Netscape format line */
Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL);
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
free(argptr);
}
}
- Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
break;
#endif /* CURL_DISABLE_COOKIES */
}
break;
+ case CURLOPT_EXPECT_100_TIMEOUT_MS:
+ /*
+ * Time to wait for a response to a HTTP request containing an
+ * Expect: 100-continue header before sending the data anyway.
+ */
+ data->set.expect_100_timeout = va_arg(param, long);
+ break;
+
#endif /* CURL_DISABLE_HTTP */
case CURLOPT_CUSTOMREQUEST:
break;
#endif
- case CURLOPT_WRITEHEADER:
+ case CURLOPT_HEADERDATA:
/*
* Custom pointer to pass the header write callback function
*/
*/
data->set.errorbuffer = va_arg(param, char *);
break;
- case CURLOPT_FILE:
+ case CURLOPT_WRITEDATA:
/*
* FILE pointer to write to. Or possibly
* used as argument to the write callback.
* If known, this should inform curl about the file size of the
* to-be-uploaded file.
*/
- data->set.infilesize = va_arg(param, long);
+ data->set.filesize = va_arg(param, long);
break;
case CURLOPT_INFILESIZE_LARGE:
/*
* If known, this should inform curl about the file size of the
* to-be-uploaded file.
*/
- data->set.infilesize = va_arg(param, curl_off_t);
+ data->set.filesize = va_arg(param, curl_off_t);
break;
case CURLOPT_LOW_SPEED_LIMIT:
/*
static bool IsPipeliningPossible(const struct SessionHandle *handle,
const struct connectdata *conn)
{
- if((conn->handler->protocol & CURLPROTO_HTTP) &&
+ if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
Curl_multi_pipeline_enabled(handle->multi) &&
(handle->set.httpreq == HTTPREQ_GET ||
handle->set.httpreq == HTTPREQ_HEAD) &&
struct connectdata *check;
struct connectdata *chosen = 0;
bool canPipeline = IsPipeliningPossible(data, needle);
- bool wantNTLM = (data->state.authhost.want & CURLAUTH_NTLM) ||
- (data->state.authhost.want & CURLAUTH_NTLM_WB) ? TRUE : FALSE;
+ bool wantNTLMhttp = ((data->state.authhost.want & CURLAUTH_NTLM) ||
+ (data->state.authhost.want & CURLAUTH_NTLM_WB)) &&
+ (needle->handler->protocol & PROTO_FAMILY_HTTP) ? TRUE : FALSE;
struct connectbundle *bundle;
*force_reuse = FALSE;
continue;
}
- if((needle->handler->protocol & CURLPROTO_FTP) ||
- ((needle->handler->protocol & CURLPROTO_HTTP) && wantNTLM)) {
- /* This is FTP or HTTP+NTLM, verify that we're using the same name
- and password as well */
- if(!strequal(needle->user, check->user) ||
- !strequal(needle->passwd, check->passwd)) {
- /* one of them was different */
- continue;
- }
- credentialsMatch = TRUE;
+ if((!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) ||
+ wantNTLMhttp) {
+ /* This protocol requires credentials per connection or is HTTP+NTLM,
+ so verify that we're using the same name and password as well */
+ if(!strequal(needle->user, check->user) ||
+ !strequal(needle->passwd, check->passwd)) {
+ /* one of them was different */
+ continue;
+ }
+ credentialsMatch = TRUE;
}
if(!needle->bits.httpproxy || needle->handler->flags&PROTOPT_SSL ||
}
if(match) {
- /* If we are looking for an NTLM connection, check if this is already
- authenticating with the right credentials. If not, keep looking so
- that we can reuse NTLM connections if possible. (Especially we
- must not reuse the same connection if partway through
- a handshake!) */
- if(wantNTLM) {
+ /* If we are looking for an HTTP+NTLM connection, check if this is
+ already authenticating with the right credentials. If not, keep
+ looking so that we can reuse NTLM connections if
+ possible. (Especially we must not reuse the same connection if
+ partway through a handshake!) */
+ if(wantNTLMhttp) {
if(credentialsMatch && check->ntlm.state != NTLMSTATE_NONE) {
chosen = check;
*force_reuse = TRUE;
break;
}
- else
- continue;
+ else if(credentialsMatch)
+ /* this is a backup choice */
+ chosen = check;
+ continue;
}
if(canPipeline) {
{
/* data->multi->maxconnects can be negative, deal with it. */
size_t maxconnects =
- (data->multi->maxconnects < 0) ? 0 : data->multi->maxconnects;
+ (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
+ data->multi->maxconnects;
struct connectdata *conn_candidate = NULL;
/* Mark the current connection as 'unused' */
static CURLcode ConnectionStore(struct SessionHandle *data,
struct connectdata *conn)
{
- static int connection_id_counter = 0;
-
- CURLcode result;
-
- /* Assign a number to the connection for easier tracking in the log
- output */
- conn->connection_id = connection_id_counter++;
-
- result = Curl_conncache_add_conn(data->state.conn_cache, conn);
- if(result != CURLE_OK)
- conn->connection_id = -1;
-
- return result;
+ return Curl_conncache_add_conn(data->state.conn_cache, conn);
}
/* after a TCP connection to the proxy has been verified, this function does
static void fix_hostname(struct SessionHandle *data,
struct connectdata *conn, struct hostname *host)
{
+ size_t len;
+
#ifndef USE_LIBIDN
(void)data;
(void)conn;
/* set the name we use to display the host name */
host->dispname = host->name;
+
+ len = strlen(host->name);
+ if(host->name[len-1] == '.')
+ /* strip off a single trailing dot if present, primarily for SNI but
+ there's no use for it */
+ host->name[len-1]=0;
+
if(!is_ASCII_name(host->name)) {
#ifdef USE_LIBIDN
/*************************************************************
conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */
conn->connection_id = -1; /* no ID */
conn->port = -1; /* unknown at this point */
+ conn->remote_port = -1; /* unknown */
/* Default protocol-independent behavior doesn't support persistent
connections, so we set this to force-close. Protocols that support
this need to set this to FALSE in their "curl_do" functions. */
- conn->bits.close = TRUE;
+ connclose(conn, "Default to force-close");
/* Store creation time to help future close decision making */
conn->created = Curl_tvnow();
char *path = data->state.path;
char *query;
int rc;
- char protobuf[16];
- const char *protop;
+ char protobuf[16] = "";
+ const char *protop = "";
CURLcode result;
bool rebuild_url = FALSE;
if(conn->host.name[0] == '[') {
/* This looks like an IPv6 address literal. See if there is an address
- scope. */
- char *percent = strstr (conn->host.name, "%25");
+ scope if there is no location header */
+ char *percent = strchr(conn->host.name, '%');
if(percent) {
+ unsigned int identifier_offset = 3;
char *endp;
- unsigned long scope = strtoul (percent + 3, &endp, 10);
+ unsigned long scope;
+ if(strncmp("%25", percent, 3) != 0) {
+ infof(data,
+ "Please URL encode %% as %%25, see RFC 6874.\n");
+ identifier_offset = 1;
+ }
+ scope = strtoul(percent + identifier_offset, &endp, 10);
if(*endp == ']') {
/* The address scope was well formed. Knock it out of the
hostname. */
memmove(percent, endp, strlen(endp)+1);
- if(!data->state.this_is_a_follow)
- /* Don't honour a scope given in a Location: header */
- conn->scope = (unsigned int)scope;
+ conn->scope = (unsigned int)scope;
+ }
+ else {
+ /* Zone identifier is not numeric */
+#if defined(HAVE_NET_IF_H) && defined(IFNAMSIZ) && defined(HAVE_IF_NAMETOINDEX)
+ char ifname[IFNAMSIZ + 2];
+ char *square_bracket;
+ unsigned int scopeidx = 0;
+ strncpy(ifname, percent + identifier_offset, IFNAMSIZ + 2);
+ /* Ensure nullbyte termination */
+ ifname[IFNAMSIZ + 1] = '\0';
+ square_bracket = strchr(ifname, ']');
+ if(square_bracket) {
+ /* Remove ']' */
+ *square_bracket = '\0';
+ scopeidx = if_nametoindex(ifname);
+ if(scopeidx == 0) {
+ infof(data, "Invalid network interface: %s; %s\n", ifname,
+ strerror(errno));
+ }
+ }
+ if(scopeidx > 0) {
+ /* Remove zone identifier from hostname */
+ memmove(percent,
+ percent + identifier_offset + strlen(ifname),
+ identifier_offset + strlen(ifname));
+ conn->scope = scopeidx;
+ }
+ else
+#endif /* HAVE_NET_IF_H && IFNAMSIZ */
+ infof(data, "Invalid IPv6 address format\n");
}
- else
- infof(data, "Invalid IPv6 address format\n");
}
}
{
const struct Curl_handler * p;
CURLcode result;
+ struct SessionHandle *data = conn->data;
/* in some case in the multi state-machine, we go back to the CONNECT state
and then a second (or third or...) call to this function will be made
without doing a DISCONNECT or DONE in between (since the connection is
yet in place) and therefore this function needs to first make sure
there's no lingering previous data allocated. */
- Curl_free_request_state(conn->data);
+ Curl_free_request_state(data);
- memset(&conn->data->req, 0, sizeof(struct SingleRequest));
- conn->data->req.maxdownload = -1;
+ memset(&data->req, 0, sizeof(struct SingleRequest));
+ data->req.maxdownload = -1;
conn->socktype = SOCK_STREAM; /* most of them are TCP streams */
/* only if remote_port was not already parsed off the URL we use the
default port number */
- if(!conn->remote_port)
+ if(conn->remote_port < 0)
conn->remote_port = (unsigned short)conn->given->defport;
return CURLE_OK;
void Curl_free_request_state(struct SessionHandle *data)
{
Curl_safefree(data->req.protop);
+ Curl_safefree(data->req.newurl);
}
/* start scanning for port number at this point */
portptr = proxyptr;
- /* detect and extract RFC2732-style IPv6-addresses */
+ /* detect and extract RFC6874-style IPv6-addresses */
if(*proxyptr == '[') {
char *ptr = ++proxyptr; /* advance beyond the initial bracket */
- while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '%') ||
- (*ptr == '.')))
+ while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.')))
+ ptr++;
+ if(*ptr == '%') {
+ /* There might be a zone identifier */
+ if(strncmp("%25", ptr, 3))
+ infof(data, "Please URL encode %% as %%25, see RFC 6874.\n");
ptr++;
+ /* Allow unresered characters as defined in RFC 3986 */
+ while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
+ (*ptr == '.') || (*ptr == '_') || (*ptr == '~')))
+ ptr++;
+ }
if(*ptr == ']')
/* yeps, it ended nicely with a bracket as well */
*ptr++ = 0;
/* no CURLOPT_PORT given, extract the one from the URL */
char *rest;
- unsigned long port;
-
- port=strtoul(portptr+1, &rest, 10); /* Port number must be decimal */
+ long port;
- if(rest != (portptr+1) && *rest == '\0') {
- /* The colon really did have only digits after it,
- * so it is either a port number or a mistake */
+ port=strtol(portptr+1, &rest, 10); /* Port number must be decimal */
- if(port > 0xffff) { /* Single unix standard says port numbers are
- * 16 bits long */
- failf(data, "Port number too large: %lu", port);
- return CURLE_URL_MALFORMAT;
- }
+ if((port < 0) || (port > 0xffff)) {
+ /* Single unix standard says port numbers are 16 bits long */
+ failf(data, "Port number out of range");
+ return CURLE_URL_MALFORMAT;
+ }
+ else if(rest != &portptr[1]) {
*portptr = '\0'; /* cut off the name there */
conn->remote_port = curlx_ultous(port);
}
- else if(!port)
+ else
/* Browser behavior adaptation. If there's a colon with no digits after,
just cut off the name there which makes us ignore the colon and just
use the default port. Firefox and Chrome both do that. */
char *proxy = NULL;
bool prot_missing = FALSE;
bool no_connections_available = FALSE;
- bool force_reuse;
+ bool force_reuse = FALSE;
size_t max_host_connections = Curl_multi_max_host_connections(data->multi);
size_t max_total_connections = Curl_multi_max_total_connections(data->multi);
/*************************************************************
* If the protocol can't handle url query strings, then cut
- * of the unhandable part
+ * off the unhandable part
*************************************************************/
if((conn->given->flags&PROTOPT_NOURLQUERY)) {
char *path_q_sep = strchr(conn->data->state.path, '?');
#else
/* force this connection's protocol to become HTTP if not already
compatible - if it isn't tunneling through */
- if(!(conn->handler->protocol & CURLPROTO_HTTP) &&
+ if(!(conn->handler->protocol & PROTO_FAMILY_HTTP) &&
!conn->bits.tunnel_proxy)
conn->handler = &Curl_handler_http;
if(conn->handler->done)
result = conn->handler->done(conn, status, premature);
else
- result = CURLE_OK;
+ result = status;
- if(Curl_pgrsDone(conn) && !result)
+ if(!result && Curl_pgrsDone(conn))
result = CURLE_ABORTED_BY_CALLBACK;
/* if the transfer was completed in a paused state there can be buffered