Newer
Older
/*
* Set a SSL_CTX callback
*/
data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
break;
case CURLOPT_SSL_CTX_DATA:
/*
* Set a SSL_CTX callback parameter pointer
*/
data->set.ssl.fsslctxp = va_arg(param, void *);
break;
Daniel Stenberg
committed
case CURLOPT_CERTINFO:
data->set.ssl.certinfo = (bool)(0 != va_arg(param, long));
break;
Daniel Stenberg
committed
#endif
Daniel Stenberg
committed
case CURLOPT_CAINFO:
/*
* Set CA info for SSL connection. Specify file name of the CA certificate
*/
result = setstropt(&data->set.str[STRING_SSL_CAFILE],
break;
case CURLOPT_CAPATH:
/*
* Set CA path info for SSL connection. Specify directory name of the CA
* certificates which have been prepared using openssl c_rehash utility.
/* This does not work on windows. */
result = setstropt(&data->set.str[STRING_SSL_CAPATH],
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case CURLOPT_CRLFILE:
/*
* Set CRL file info for SSL connection. Specify file name of the CRL
* to check certificates revocation
*/
result = setstropt(&data->set.str[STRING_SSL_CRLFILE],
va_arg(param, char *));
break;
Daniel Stenberg
committed
case CURLOPT_ISSUERCERT:
/*
* Set Issuer certificate file
* to check certificates issuer
*/
result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
va_arg(param, char *));
break;
case CURLOPT_TELNETOPTIONS:
/*
* Set a linked list of telnet options
*/
Daniel Stenberg
committed
data->set.telnet_options = va_arg(param, struct curl_slist *);
case CURLOPT_BUFFERSIZE:
/*
* The application kindly asks for a differently sized receive buffer.
* If it seems reasonable, we'll use it.
*/
data->set.buffer_size = va_arg(param, long);
Daniel Stenberg
committed
if((data->set.buffer_size> (BUFSIZE -1 )) ||
(data->set.buffer_size < 1))
data->set.buffer_size = 0; /* huge internal default */
break;
case CURLOPT_NOSIGNAL:
/*
* The application asks not to set any signal() or alarm() handlers,
* even when using a timeout.
*/
data->set.no_signal = (bool)(0 != va_arg(param, long));
{
struct Curl_share *set;
set = va_arg(param, struct Curl_share *);
Daniel Stenberg
committed
/* disconnect from old share, if any */
if(data->share) {
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
if(data->dns.hostcachetype == HCACHE_SHARED) {
data->dns.hostcache = NULL;
data->dns.hostcachetype = HCACHE_NONE;
}
Daniel Stenberg
committed
if(data->share->cookies == data->cookies)
data->cookies = NULL;
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
data->share = NULL;
}
/* use new share if it set */
data->share = set;
if(data->share) {
Daniel Stenberg
committed
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
if(data->share->hostcache) {
/* use shared host cache, first free the private one if any */
if(data->dns.hostcachetype == HCACHE_PRIVATE)
Curl_hash_destroy(data->dns.hostcache);
data->dns.hostcache = data->share->hostcache;
data->dns.hostcachetype = HCACHE_SHARED;
}
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
if(data->share->cookies) {
/* use shared cookie list, first free own one if any */
if(data->cookies)
Curl_cookie_cleanup(data->cookies);
/* enable cookies since we now use a share that uses cookies! */
data->cookies = data->share->cookies;
}
#endif /* CURL_DISABLE_HTTP */
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
/* check for host cache not needed,
* it will be done by curl_easy_perform */
}
break;
Daniel Stenberg
committed
case CURLOPT_PRIVATE:
/*
* Set private data pointer.
*/
Daniel Stenberg
committed
data->set.private_data = va_arg(param, void *);
Daniel Stenberg
committed
break;
case CURLOPT_MAXFILESIZE:
/*
* Set the maximum size of a file to download.
*/
data->set.max_filesize = va_arg(param, long);
break;
case CURLOPT_USE_SSL:
Daniel Stenberg
committed
/*
* Make transfers attempt to use SSL/TLS.
Daniel Stenberg
committed
*/
data->set.ftp_ssl = (curl_usessl)va_arg(param, long);
Daniel Stenberg
committed
break;
case CURLOPT_FTPSSLAUTH:
/*
* Set a specific auth for FTP-SSL transfers.
*/
data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long);
break;
case CURLOPT_IPRESOLVE:
data->set.ip_version = va_arg(param, long);
break;
case CURLOPT_MAXFILESIZE_LARGE:
/*
* Set the maximum size of a file to download.
*/
data->set.max_filesize = va_arg(param, curl_off_t);
case CURLOPT_TCP_NODELAY:
/*
* Enable or disable TCP_NODELAY, which will disable/enable the Nagle
* algorithm
*/
data->set.tcp_nodelay = (bool)(0 != va_arg(param, long));
Daniel Stenberg
committed
case CURLOPT_FTP_ACCOUNT:
result = setstropt(&data->set.str[STRING_FTP_ACCOUNT],
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case CURLOPT_IGNORE_CONTENT_LENGTH:
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case CURLOPT_CONNECT_ONLY:
/*
* No data transfer, set up connection and let application use the socket
*/
data->set.connect_only = (bool)(0 != va_arg(param, long));
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case CURLOPT_FTP_ALTERNATIVE_TO_USER:
result = setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER],
Daniel Stenberg
committed
break;
case CURLOPT_SOCKOPTFUNCTION:
/*
* socket callback function: called after socket() but before connect()
*/
data->set.fsockopt = va_arg(param, curl_sockopt_callback);
break;
case CURLOPT_SOCKOPTDATA:
/*
* socket callback data pointer. Might be NULL.
*/
data->set.sockopt_client = va_arg(param, void *);
break;
case CURLOPT_OPENSOCKETFUNCTION:
/*
* open/create socket callback function: called instead of socket(),
* before connect()
*/
data->set.fopensocket = va_arg(param, curl_opensocket_callback);
break;
case CURLOPT_OPENSOCKETDATA:
/*
* socket callback data pointer. Might be NULL.
*/
data->set.opensocket_client = va_arg(param, void *);
break;
Daniel Stenberg
committed
case CURLOPT_SSL_SESSIONID_CACHE:
data->set.ssl.sessionid = (bool)(0 != va_arg(param, long));
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
#ifdef USE_LIBSSH2
/* we only include SSH options if explicitly built to support SSH */
case CURLOPT_SSH_AUTH_TYPES:
data->set.ssh_auth_types = va_arg(param, long);
break;
case CURLOPT_SSH_PUBLIC_KEYFILE:
/*
* Use this file instead of the $HOME/.ssh/id_dsa.pub file
*/
result = setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY],
break;
case CURLOPT_SSH_PRIVATE_KEYFILE:
/*
* Use this file instead of the $HOME/.ssh/id_dsa file
*/
result = setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY],
case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
/*
* Option to allow for the MD5 of the host public key to be checked
* for validation purposes.
*/
result = setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
Daniel Stenberg
committed
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
case CURLOPT_SSH_KNOWNHOSTS:
/*
* Store the file name to read known hosts from.
*/
result = setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
va_arg(param, char *));
break;
case CURLOPT_SSH_KEYFUNCTION:
/* setting to NULL is fine since the ssh.c functions themselves will
then rever to use the internal default */
data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback);
break;
case CURLOPT_SSH_KEYDATA:
/*
* Custom client data to pass to the SSH keyfunc callback
*/
data->set.ssh_keyfunc_userp = va_arg(param, void *);
break;
#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
#endif /* USE_LIBSSH2 */
Daniel Stenberg
committed
case CURLOPT_HTTP_TRANSFER_DECODING:
/*
* disable libcurl transfer encoding is used
*/
data->set.http_te_skip = (bool)(0 == va_arg(param, long));
break;
case CURLOPT_HTTP_CONTENT_DECODING:
/*
* raw data passed to the application when content encoding is used
*/
data->set.http_ce_skip = (bool)(0 == va_arg(param, long));
break;
case CURLOPT_NEW_FILE_PERMS:
/*
* Uses these permissions instead of 0644
*/
data->set.new_file_perms = va_arg(param, long);
break;
case CURLOPT_NEW_DIRECTORY_PERMS:
/*
* Uses these permissions instead of 0755
*/
data->set.new_directory_perms = va_arg(param, long);
break;
Daniel Stenberg
committed
case CURLOPT_ADDRESS_SCOPE:
/*
* We always get longs when passed plain numericals, but for this value we
* know that an unsigned int will always hold the value so we blindly
* typecast to this type
*/
data->set.scope = (unsigned int) va_arg(param, long);
break;
case CURLOPT_PROTOCOLS:
/* set the bitmask for the protocols that are allowed to be used for the
transfer, which thus helps the app which takes URLs from users or other
external inputs and want to restrict what protocol(s) to deal
with. Defaults to CURLPROTO_ALL. */
data->set.allowed_protocols = va_arg(param, long) & PROT_EXTMASK;
break;
case CURLOPT_REDIR_PROTOCOLS:
/* set the bitmask for the protocols that libcurl is allowed to follow to,
as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs
to be set in both bitmasks to be allowed to get redirected to. Defaults
to all protocols except FILE and SCP. */
data->set.redir_protocols = va_arg(param, long) & PROT_EXTMASK;
break;
case CURLOPT_MAIL_FROM:
result = setstropt(&data->set.str[STRING_MAIL_FROM],
va_arg(param, char *));
break;
case CURLOPT_MAIL_RCPT:
Daniel Stenberg
committed
/* get a list of mail recipients */
data->set.mail_rcpt = va_arg(param, struct curl_slist *);
break;
Daniel Stenberg
committed
case CURLOPT_RTSP_REQUEST:
{
/*
* Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...)
* Would this be better if the RTSPREQ_* were just moved into here?
*/
long curl_rtspreq = va_arg(param, long);
Daniel Stenberg
committed
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
switch(curl_rtspreq) {
case CURL_RTSPREQ_OPTIONS:
rtspreq = RTSPREQ_OPTIONS;
break;
case CURL_RTSPREQ_DESCRIBE:
rtspreq = RTSPREQ_DESCRIBE;
break;
case CURL_RTSPREQ_ANNOUNCE:
rtspreq = RTSPREQ_ANNOUNCE;
break;
case CURL_RTSPREQ_SETUP:
rtspreq = RTSPREQ_SETUP;
break;
case CURL_RTSPREQ_PLAY:
rtspreq = RTSPREQ_PLAY;
break;
case CURL_RTSPREQ_PAUSE:
rtspreq = RTSPREQ_PAUSE;
break;
case CURL_RTSPREQ_TEARDOWN:
rtspreq = RTSPREQ_TEARDOWN;
break;
case CURL_RTSPREQ_GET_PARAMETER:
rtspreq = RTSPREQ_GET_PARAMETER;
break;
case CURL_RTSPREQ_SET_PARAMETER:
rtspreq = RTSPREQ_SET_PARAMETER;
break;
case CURL_RTSPREQ_RECORD:
rtspreq = RTSPREQ_RECORD;
break;
case CURL_RTSPREQ_RECEIVE:
rtspreq = RTSPREQ_RECEIVE;
break;
default:
rtspreq = RTSPREQ_NONE;
}
data->set.rtspreq = rtspreq;
break;
}
case CURLOPT_RTSP_SESSION_ID:
/*
* Set the RTSP Session ID manually. Useful if the application is
* resuming a previously established RTSP session
*/
result = setstropt(&data->set.str[STRING_RTSP_SESSION_ID],
va_arg(param, char *));
break;
case CURLOPT_RTSP_STREAM_URI:
/*
* Set the Stream URI for the RTSP request. Unless the request is
* for generic server options, the application will need to set this.
*/
result = setstropt(&data->set.str[STRING_RTSP_STREAM_URI],
va_arg(param, char *));
break;
case CURLOPT_RTSP_TRANSPORT:
/*
* The content of the Transport: header for the RTSP request
*/
result = setstropt(&data->set.str[STRING_RTSP_TRANSPORT],
va_arg(param, char *));
break;
case CURLOPT_RTSP_CLIENT_CSEQ:
/*
* Set the CSEQ number to issue for the next RTSP request. Useful if the
* application is resuming a previously broken connection. The CSEQ
* will increment from this new number henceforth.
*/
data->state.rtsp_next_client_CSeq = va_arg(param, long);
break;
case CURLOPT_RTSP_SERVER_CSEQ:
/* Same as the above, but for server-initiated requests */
data->state.rtsp_next_client_CSeq = va_arg(param, long);
break;
Daniel Stenberg
committed
data->set.rtp_out = va_arg(param, void *);
break;
Daniel Stenberg
committed
/* Set the user defined RTP write function */
data->set.fwrite_rtp = va_arg(param, curl_write_callback);
break;
default:
/* unknown tag and its companion, just ignore: */
Daniel Stenberg
committed
result = CURLE_FAILED_INIT; /* correct this */
break;
Daniel Stenberg
committed
return result;
static void conn_free(struct connectdata *conn)
{
Daniel Stenberg
committed
if(!conn)
return;
Daniel Stenberg
committed
/* close the SSL stuff before we close any sockets since they will/may
write to the sockets */
Curl_ssl_close(conn, FIRSTSOCKET);
Curl_ssl_close(conn, SECONDARYSOCKET);
/* close possibly still open sockets */
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
sclose(conn->sock[SECONDARYSOCKET]);
if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET])
sclose(conn->sock[FIRSTSOCKET]);
Curl_safefree(conn->user);
Curl_safefree(conn->passwd);
Curl_safefree(conn->proxyuser);
Curl_safefree(conn->proxypasswd);
Curl_safefree(conn->allocptr.proxyuserpwd);
Curl_safefree(conn->allocptr.uagent);
Curl_safefree(conn->allocptr.userpwd);
Curl_safefree(conn->allocptr.accept_encoding);
Curl_safefree(conn->allocptr.rangeline);
Curl_safefree(conn->allocptr.ref);
Curl_safefree(conn->allocptr.host);
Curl_safefree(conn->allocptr.cookiehost);
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.rtsp_transport);
Curl_safefree(conn->trailer);
Curl_safefree(conn->host.rawalloc); /* host name buffer */
Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
Daniel Stenberg
committed
Curl_safefree(conn->master_buffer);
Curl_llist_destroy(conn->send_pipe, NULL);
Curl_llist_destroy(conn->recv_pipe, NULL);
Daniel Stenberg
committed
Curl_llist_destroy(conn->pend_pipe, NULL);
Curl_llist_destroy(conn->done_pipe, NULL);
/* possible left-overs from the async name resolvers */
Curl_destroy_thread_data(&conn->async);
#elif defined(CURLRES_ASYNCH)
Curl_safefree(conn->async.hostname);
Curl_safefree(conn->async.os_specific);
#endif
Curl_free_ssl_config(&conn->ssl_config);
free(conn); /* free all the connection oriented data */
}
CURLcode Curl_disconnect(struct connectdata *conn)
struct SessionHandle *data;
if(!conn)
return CURLE_OK; /* this is closed and fine already */
Daniel Stenberg
committed
if(!data) {
DEBUGF(infof(data, "DISCONNECT without easy handle, ignoring\n"));
return CURLE_OK;
}
Daniel Stenberg
committed
if(conn->dns_entry != NULL) {
Curl_resolv_unlock(data, conn->dns_entry);
conn->dns_entry = NULL;
}
#if defined(DEBUGBUILD) && defined(AGGRESIVE_TEST)
Daniel Stenberg
committed
/* scan for DNS cache entries still marked as in use */
Curl_hash_apply(data->hostcache,
NULL, Curl_scan_cache_used);
#endif
Daniel Stenberg
committed
Curl_expire(data, 0); /* shut off timers */
Daniel Stenberg
committed
Curl_hostcache_prune(data); /* kill old DNS cache entries */
{
int has_host_ntlm = (conn->ntlm.state != NTLMSTATE_NONE);
int has_proxy_ntlm = (conn->proxyntlm.state != NTLMSTATE_NONE);
/* Authentication data is a mix of connection-related and sessionhandle-
related stuff. NTLM is connection-related so when we close the shop
we shall forget. */
if (has_host_ntlm) {
data->state.authhost.done = FALSE;
data->state.authhost.picked =
data->state.authhost.want;
}
if (has_proxy_ntlm) {
data->state.authproxy.done = FALSE;
data->state.authproxy.picked =
data->state.authproxy.want;
if (has_host_ntlm || has_proxy_ntlm) {
data->state.authproblem = FALSE;
Curl_ntlm_cleanup(conn);
}
/* Cleanup possible redirect junk */
if(data->req.newurl) {
free(data->req.newurl);
data->req.newurl = NULL;
}
Daniel Stenberg
committed
if(conn->handler->disconnect)
Daniel Stenberg
committed
/* This is set if protocol-specific cleanups should be made */
Patrick Monnerat
committed
conn->handler->disconnect(conn);
Daniel Stenberg
committed
Daniel Stenberg
committed
/* unlink ourselves! */
infof(data, "Closing connection #%ld\n", conn->connectindex);
Daniel Stenberg
committed
if(data->state.connc)
/* only clear the table entry if we still know in which cache we
used to be in */
data->state.connc->connects[conn->connectindex] = NULL;
Daniel Stenberg
committed
if(conn->host.encalloc)
Daniel Stenberg
committed
idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed
with idn_free() since this was allocated
by libidn */
if(conn->proxy.encalloc)
Daniel Stenberg
committed
idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be
freed with idn_free() since this was
allocated by libidn */
Daniel Stenberg
committed
Curl_ssl_close(conn, FIRSTSOCKET);
/* Indicate to all handles on the pipe that we're dead */
Daniel Stenberg
committed
if(Curl_isPipeliningEnabled(data)) {
signalPipeClose(conn->send_pipe, TRUE);
signalPipeClose(conn->recv_pipe, TRUE);
signalPipeClose(conn->pend_pipe, TRUE);
signalPipeClose(conn->done_pipe, FALSE);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
conn_free(conn);
Daniel Stenberg
committed
data->state.current_conn = NULL;
/*
* This function should return TRUE if the socket is to be assumed to
* be dead. Most commonly this happens when the server has closed the
* connection due to inactivity.
*/
static bool SocketIsDead(curl_socket_t sock)
{
int sval;
bool ret_val = TRUE;
sval = Curl_socket_ready(sock, CURL_SOCKET_BAD, 0);
Daniel Stenberg
committed
if(sval == 0)
ret_val = FALSE;
Daniel Stenberg
committed
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
#ifndef CURL_DISABLE_RTSP
/*
* The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
* want to block the application forever while receiving a stream. Therefore,
* we cannot assume that an RTSP socket is dead just because it is readable.
*
* Instead, if it is readable, run Curl_getconnectinfo() to peek at the socket
* and distinguish between closed and data.
*/
static bool RTSPConnIsDead(struct connectdata *check)
{
int sval;
bool ret_val = TRUE;
sval = Curl_socket_ready(check->sock[FIRSTSOCKET], CURL_SOCKET_BAD, 0);
if(sval == 0) {
/* timeout */
ret_val = FALSE;
}
else if (sval & CURL_CSELECT_ERR) {
/* socket is in an error state */
ret_val = TRUE;
}
else if (sval & CURL_CSELECT_IN) {
/* readable with no error. could be closed or could be alive */
long connectinfo = 0;
Curl_getconnectinfo(check->data, &connectinfo, &check);
if(connectinfo != -1) {
ret_val = FALSE;
}
}
return ret_val;
}
#endif /* CURL_DISABLE_RTSP */
static bool IsPipeliningPossible(const struct SessionHandle *handle)
{
Daniel Stenberg
committed
if(handle->multi && Curl_multi_canPipeline(handle->multi) &&
(handle->set.httpreq == HTTPREQ_GET ||
handle->set.httpreq == HTTPREQ_HEAD) &&
handle->set.httpversion != CURL_HTTP_VERSION_1_0)
return TRUE;
return FALSE;
}
Daniel Stenberg
committed
bool Curl_isPipeliningEnabled(const struct SessionHandle *handle)
Daniel Stenberg
committed
{
Daniel Stenberg
committed
if(handle->multi && Curl_multi_canPipeline(handle->multi))
Daniel Stenberg
committed
return TRUE;
return FALSE;
}
CURLcode Curl_addHandleToPipeline(struct SessionHandle *data,
struct curl_llist *pipeline)
{
Daniel Stenberg
committed
if(!IsPipeliningPossible(data)) {
/* when not pipelined, there MUST be no handle in the list already */
if(pipeline->head)
Daniel Stenberg
committed
infof(data, "PIPE when no PIPE supposed!\n");
}
#endif
Daniel Stenberg
committed
if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
Daniel Stenberg
committed
int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
Daniel Stenberg
committed
struct curl_llist *pipeline)
{
struct curl_llist_element *curr;
curr = pipeline->head;
Daniel Stenberg
committed
while(curr) {
if(curr->ptr == handle) {
Curl_llist_remove(pipeline, curr, NULL);
Daniel Stenberg
committed
return 1; /* we removed a handle */
}
curr = curr->next;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
return 0;
}
Daniel Stenberg
committed
#if 0 /* this code is saved here as it is useful for debugging purposes */
static void Curl_printPipeline(struct curl_llist *pipeline)
{
struct curl_llist_element *curr;
curr = pipeline->head;
Daniel Stenberg
committed
while(curr) {
struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
infof(data, "Handle in pipeline: %s\n", data->state.path);
curr = curr->next;
}
}
#endif
static struct SessionHandle* gethandleathead(struct curl_llist *pipeline)
{
struct curl_llist_element *curr = pipeline->head;
Daniel Stenberg
committed
if(curr) {
return (struct SessionHandle *) curr->ptr;
}
return NULL;
}
/* remove the specified connection from all (possible) pipelines and related
queues */
void Curl_getoff_all_pipelines(struct SessionHandle *data,
struct connectdata *conn)
{
bool recv_head = (bool)(conn->readchannel_inuse &&
(gethandleathead(conn->recv_pipe) == data));
bool send_head = (bool)(conn->writechannel_inuse &&
(gethandleathead(conn->send_pipe) == data));
if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) && recv_head)
conn->readchannel_inuse = FALSE;
if(Curl_removeHandleFromPipeline(data, conn->send_pipe) && send_head)
conn->writechannel_inuse = FALSE;
Curl_removeHandleFromPipeline(data, conn->pend_pipe);
Curl_removeHandleFromPipeline(data, conn->done_pipe);
}
static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke)
{
struct curl_llist_element *curr;
Daniel Stenberg
committed
if(!pipeline)
curr = pipeline->head;
Daniel Stenberg
committed
while(curr) {
struct curl_llist_element *next = curr->next;
struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
Daniel Stenberg
committed
if(data->magic != CURLEASY_MAGIC_NUMBER) {
/* MAJOR BADNESS */
infof(data, "signalPipeClose() found BAAD easy handle\n");
Daniel Stenberg
committed
}
#endif
if (pipe_broke)
data->state.pipe_broke = TRUE;
Curl_multi_handlePipeBreak(data);
Curl_llist_remove(pipeline, curr, NULL);
curr = next;
}
}
* Given one filled in connection struct (named needle), this function should
* detect if there already is one that has all the significant details
* exactly the same and thus should be used instead.
*
* If there is a match, this function returns TRUE - and has marked the
* connection as 'in-use'. It must later be called with ConnectionDone() to
* return back to 'idle' (unused) state.
Daniel Stenberg
committed
ConnectionExists(struct SessionHandle *data,
struct connectdata *needle,
struct connectdata **usethis)
struct connectdata *check;
bool canPipeline = IsPipeliningPossible(data);
for(i=0; i< data->state.connc->num; i++) {
bool match = FALSE;
/*
* Note that if we use a HTTP proxy, we check connections to that
* proxy and not to the actual remote server.
*/
check = data->state.connc->connects[i];
if(!check)
/* NULL pointer means not filled-in entry */
continue;
pipeLen = check->send_pipe->size + check->recv_pipe->size;
Daniel Stenberg
committed
if(check->connectindex == -1) {
Daniel Stenberg
committed
check->connectindex = i; /* Set this appropriately since it might have
been set to -1 when the easy was removed
from the multi */
}
Daniel Stenberg
committed
if(!pipeLen && !check->inuse) {
/* The check for a dead socket makes sense only if there are no
handles in pipeline and the connection isn't already marked in
use */
Daniel Stenberg
committed
bool dead;
#ifndef CURL_DISABLE_RTSP
if(check->protocol & PROT_RTSP)
/* RTSP is a special case due to RTP interleaving */
dead = RTSPConnIsDead(check);
else
#endif /*CURL_DISABLE_RTSP*/
dead = SocketIsDead(check->sock[FIRSTSOCKET]);
Daniel Stenberg
committed
if(dead) {
check->data = data;
Daniel Stenberg
committed
Curl_disconnect(check); /* disconnect resources */
data->state.connc->connects[i]=NULL; /* nothing here */
continue;
}
}
Daniel Stenberg
committed
if(canPipeline) {
/* Make sure the pipe has only GET requests */
struct SessionHandle* sh = gethandleathead(check->send_pipe);
struct SessionHandle* rh = gethandleathead(check->recv_pipe);
Daniel Stenberg
committed
if(sh) {
if(!IsPipeliningPossible(sh))
continue;
}
Daniel Stenberg
committed
else if(rh) {
if(!IsPipeliningPossible(rh))
continue;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
if(pipeLen > MAX_PIPELINE_LENGTH) {
infof(data, "BAD! Connection #%ld has too big pipeline!\n",
check->connectindex);
}
#endif
}
else {
if(pipeLen > 0) {
/* can only happen within multi handles, and means that another easy
handle is using this connection */
continue;
}
#ifdef CURLRES_ASYNCH
Daniel Stenberg
committed
/* ip_addr_str[0] is NUL only if the resolving of the name hasn't
completed yet and until then we don't re-use this connection */
if(!check->ip_addr_str[0]) {
Daniel Stenberg
committed
infof(data,
"Connection #%ld hasn't finished name resolve, can't reuse\n",
check->connectindex);
continue;
}
#endif
if((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || check->bits.close) {
/* Don't pick a connection that hasn't connected yet or that is going
to get closed. */
Daniel Stenberg
committed
infof(data, "Connection #%ld isn't open enough, can't reuse\n",
check->connectindex);
Daniel Stenberg
committed
if(check->recv_pipe->size > 0) {
infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
check->connectindex);
}
#endif
continue;
}
}
if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
/* don't do mixed SSL and non-SSL connections */
continue;
if(needle->protocol&PROT_SSL) {
if((data->set.ssl.verifypeer != check->verifypeer) ||
(data->set.ssl.verifyhost != check->verifyhost))
continue;
}
Daniel Stenberg
committed
if(needle->bits.proxy != check->bits.proxy)
/* don't do mixed proxy and non-proxy connections */
continue;
Daniel Stenberg
committed
if(!canPipeline && check->inuse)
/* this request can't be pipelined but the checked connection is already
in use so we skip it */
continue;
Daniel Stenberg
committed
if(!needle->bits.httpproxy || needle->protocol&PROT_SSL ||
(needle->bits.httpproxy && check->bits.httpproxy &&
needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
Daniel Stenberg
committed
Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
Daniel Stenberg
committed
(needle->port == check->port))) {
/* The requested connection does not use a HTTP proxy or it uses SSL or
it is a non-SSL protocol tunneled over the same http proxy name and
port number */
Daniel Stenberg
committed
if(Curl_raw_equal(needle->handler->scheme, check->handler->scheme) &&
Daniel Stenberg
committed
Curl_raw_equal(needle->host.name, check->host.name) &&
(needle->remote_port == check->remote_port) ) {
Daniel Stenberg
committed
if(needle->protocol & PROT_SSL) {
/* This is SSL, verify that we're using the same
ssl options as well */
if(!Curl_ssl_config_matches(&needle->ssl_config,
&check->ssl_config)) {
Daniel Stenberg
committed
DEBUGF(infof(data,
"Connection #%ld has different SSL parameters, "
"can't reuse\n",
check->connectindex));
continue;
}
else if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) {
DEBUGF(infof(data,
"Connection #%ld has not started ssl connect, "
"can't reuse\n",
check->connectindex));
Daniel Stenberg
committed
continue;
}
}
if((needle->protocol & PROT_FTP) ||
((needle->protocol & PROT_HTTP) &&
(data->state.authhost.want==CURLAUTH_NTLM))) {
/* 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;
}
}
match = TRUE;
else { /* The requested needle connection is using a proxy,
Daniel Stenberg
committed
is the checked one using the same host, port and type? */
if(check->bits.proxy &&
(needle->proxytype == check->proxytype) &&
Daniel Stenberg
committed
(needle->bits.tunnel_proxy == check->bits.tunnel_proxy) &&
Daniel Stenberg
committed
Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
needle->port == check->port) {
/* This is the same proxy connection, use it! */
match = TRUE;
}
}
if(match) {
check->inuse = TRUE; /* mark this as being in use so that no other
handle in a multi stack may nick it */
*usethis = check;
return TRUE; /* yes, we found one to use! */
Daniel Stenberg
committed
return FALSE; /* no matching connecting exists */
}
Daniel Stenberg
committed
/*
* This function frees/closes a connection in the connection cache. This