mirror of
https://github.com/moparisthebest/curl
synced 2024-12-21 23:58:49 -05:00
First curl_multi_socket() commit. Should primarily be considered as an internal
code rearrange to fit the future better.
This commit is contained in:
parent
5dc02d53c3
commit
686d90745b
@ -8,7 +8,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
|
||||
content_encoding.c share.c http_digest.c md5.c http_negotiate.c \
|
||||
http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \
|
||||
hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c \
|
||||
select.c gtls.c sslgen.c tftp.c
|
||||
select.c gtls.c sslgen.c tftp.c splay.c
|
||||
|
||||
HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
|
||||
progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \
|
||||
@ -18,6 +18,6 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
|
||||
share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \
|
||||
inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \
|
||||
setup.h transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h \
|
||||
gtls.h tftp.h sockaddr.h
|
||||
gtls.h tftp.h sockaddr.h splay.h
|
||||
|
||||
|
||||
|
@ -98,6 +98,7 @@
|
||||
#include "memory.h"
|
||||
#include "select.h"
|
||||
#include "url.h" /* for Curl_safefree() */
|
||||
#include "multiif.h"
|
||||
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
|
||||
|
||||
/* The last #include file should be: */
|
||||
@ -534,6 +535,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
||||
CURLcode code = CURLE_OK;
|
||||
curl_socket_t sockfd = conn->sock[sockindex];
|
||||
long allow = DEFAULT_CONNECT_TIMEOUT;
|
||||
long allow_total = 0;
|
||||
long has_passed;
|
||||
|
||||
curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
|
||||
@ -546,12 +548,12 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
||||
/* subtract the most strict timeout of the ones */
|
||||
if(data->set.timeout && data->set.connecttimeout) {
|
||||
if (data->set.timeout < data->set.connecttimeout)
|
||||
allow = data->set.timeout*1000;
|
||||
allow_total = allow = data->set.timeout*1000;
|
||||
else
|
||||
allow = data->set.connecttimeout*1000;
|
||||
}
|
||||
else if(data->set.timeout) {
|
||||
allow = data->set.timeout*1000;
|
||||
allow_total = allow = data->set.timeout*1000;
|
||||
}
|
||||
else if(data->set.connecttimeout) {
|
||||
allow = data->set.connecttimeout*1000;
|
||||
@ -564,10 +566,13 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
||||
}
|
||||
if(conn->bits.tcpconnect) {
|
||||
/* we are connected already! */
|
||||
Curl_expire(data, allow_total);
|
||||
*connected = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
Curl_expire(data, allow);
|
||||
|
||||
/* check for connect without timeout as we want to return immediately */
|
||||
rc = waitconnect(sockfd, 0);
|
||||
|
||||
@ -818,6 +823,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
|
||||
return CURLE_OPERATION_TIMEOUTED;
|
||||
}
|
||||
}
|
||||
Curl_expire(data, timeout_ms);
|
||||
|
||||
/* Max time for each address */
|
||||
num_addr = Curl_num_addresses(remotehost->addr);
|
||||
|
26
lib/ftp.c
26
lib/ftp.c
@ -96,6 +96,7 @@
|
||||
#include "select.h"
|
||||
#include "parsedate.h" /* for the week day and month names */
|
||||
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
|
||||
#include "multiif.h"
|
||||
|
||||
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
|
||||
#include "inet_ntoa_r.h"
|
||||
@ -718,27 +719,24 @@ static CURLcode ftp_state_pwd(struct connectdata *conn)
|
||||
}
|
||||
|
||||
/* For the FTP "protocol connect" and "doing" phases only */
|
||||
CURLcode Curl_ftp_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp)
|
||||
int Curl_ftp_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks)
|
||||
{
|
||||
struct FTP *ftp = conn->proto.ftp;
|
||||
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
|
||||
|
||||
if(!numsocks)
|
||||
return GETSOCK_BLANK;
|
||||
|
||||
socks[0] = conn->sock[FIRSTSOCKET];
|
||||
|
||||
if(ftp->sendleft) {
|
||||
/* write mode */
|
||||
FD_SET(sockfd, write_fd_set);
|
||||
return GETSOCK_WRITESOCK(0);
|
||||
}
|
||||
else {
|
||||
|
||||
/* read mode */
|
||||
FD_SET(sockfd, read_fd_set);
|
||||
}
|
||||
|
||||
if((int)sockfd > *max_fdp)
|
||||
*max_fdp = (int)sockfd;
|
||||
|
||||
return CURLE_OK;
|
||||
return GETSOCK_READSOCK(0);
|
||||
}
|
||||
|
||||
/* This is called after the FTP_QUOTE state is passed.
|
||||
|
@ -34,10 +34,9 @@ CURLcode Curl_GetFTPResponse(ssize_t *nread, struct connectdata *conn,
|
||||
int *ftpcode);
|
||||
CURLcode Curl_ftp_nextconnect(struct connectdata *conn);
|
||||
CURLcode Curl_ftp_multi_statemach(struct connectdata *conn, bool *done);
|
||||
CURLcode Curl_ftp_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp);
|
||||
int Curl_ftp_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks);
|
||||
CURLcode Curl_ftp_doing(struct connectdata *conn,
|
||||
bool *dophase_done);
|
||||
#endif /* CURL_DISABLE_FTP */
|
||||
|
26
lib/hash.c
26
lib/hash.c
@ -124,8 +124,11 @@ mk_hash_element(char *key, size_t key_len, const void *p)
|
||||
(struct curl_hash_element *) malloc(sizeof(struct curl_hash_element));
|
||||
|
||||
if(he) {
|
||||
char *dup = strdup(key);
|
||||
char *dup = malloc(key_len);
|
||||
if(dup) {
|
||||
/* copy the key */
|
||||
memcpy(dup, key, key_len);
|
||||
|
||||
he->key = dup;
|
||||
he->key_len = key_len;
|
||||
he->ptr = (void *) p;
|
||||
@ -179,6 +182,23 @@ Curl_hash_add(struct curl_hash *h, char *key, size_t key_len, void *p)
|
||||
return NULL; /* failure */
|
||||
}
|
||||
|
||||
/* remove the identified hash entry, returns non-zero on failure */
|
||||
int Curl_hash_delete(struct curl_hash *h, char *key, size_t key_len)
|
||||
{
|
||||
struct curl_llist_element *le;
|
||||
struct curl_hash_element *he;
|
||||
struct curl_llist *l = FETCH_LIST(h, key, key_len);
|
||||
|
||||
for (le = l->head; le; le = le->next) {
|
||||
he = le->ptr;
|
||||
if (hash_key_compare(he->key, he->key_len, key, key_len)) {
|
||||
Curl_llist_remove(l, le, (void *) h);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *
|
||||
Curl_hash_pick(struct curl_hash *h, char *key, size_t key_len)
|
||||
{
|
||||
@ -186,9 +206,7 @@ Curl_hash_pick(struct curl_hash *h, char *key, size_t key_len)
|
||||
struct curl_hash_element *he;
|
||||
struct curl_llist *l = FETCH_LIST(h, key, key_len);
|
||||
|
||||
for (le = l->head;
|
||||
le;
|
||||
le = le->next) {
|
||||
for (le = l->head; le; le = le->next) {
|
||||
he = le->ptr;
|
||||
if (hash_key_compare(he->key, he->key_len, key, key_len)) {
|
||||
return he->ptr;
|
||||
|
@ -105,17 +105,15 @@
|
||||
* Returns: CURLE_OK always!
|
||||
*/
|
||||
|
||||
CURLcode Curl_resolv_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp)
|
||||
int Curl_resolv_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks)
|
||||
|
||||
{
|
||||
int max = ares_fds(conn->data->state.areschannel,
|
||||
read_fd_set, write_fd_set);
|
||||
*max_fdp = max;
|
||||
int max = ares_getsock(conn->data->state.areschannel,
|
||||
(int *)socks, numsocks);
|
||||
|
||||
return CURLE_OK;
|
||||
return max;
|
||||
}
|
||||
|
||||
/*
|
||||
|
13
lib/hostip.h
13
lib/hostip.h
@ -160,6 +160,14 @@ CURLcode Curl_is_resolved(struct connectdata *conn,
|
||||
CURLcode Curl_wait_for_resolv(struct connectdata *conn,
|
||||
struct Curl_dns_entry **dnsentry);
|
||||
|
||||
|
||||
/* Curl_resolv_getsock() is a generic function that exists in multiple versions
|
||||
depending on what name resolve technology we've built to use. The function
|
||||
is called from the multi_getsock() function */
|
||||
int Curl_resolv_getsock(struct connectdata *conn,
|
||||
curl_socket_t *sock,
|
||||
int numsocks);
|
||||
#if 0
|
||||
/* Curl_resolv_fdset() is a generic function that exists in multiple versions
|
||||
depending on what name resolve technology we've built to use. The function
|
||||
is called from the curl_multi_fdset() function */
|
||||
@ -167,8 +175,11 @@ CURLcode Curl_resolv_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp);
|
||||
#endif
|
||||
|
||||
/* unlock a previously resolved dns entry */
|
||||
void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns);
|
||||
void Curl_resolv_unlock(struct SessionHandle *data,
|
||||
struct Curl_dns_entry *dns);
|
||||
|
||||
/* for debugging purposes only: */
|
||||
void Curl_scan_cache_used(void *user, void *ptr);
|
||||
|
@ -126,17 +126,15 @@ CURLcode Curl_is_resolved(struct connectdata *conn,
|
||||
* It is present here to keep #ifdefs out from multi.c
|
||||
*/
|
||||
|
||||
CURLcode Curl_resolv_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp)
|
||||
int Curl_resolv_getsock(struct connectdata *conn,
|
||||
curl_socket_t *sock,
|
||||
int numsocks)
|
||||
{
|
||||
(void)conn;
|
||||
(void)read_fd_set;
|
||||
(void)write_fd_set;
|
||||
(void)max_fdp;
|
||||
(void)sock;
|
||||
(void)numsocks;
|
||||
|
||||
return CURLE_OK;
|
||||
return 0; /* no bits since we don't use any socks */
|
||||
}
|
||||
|
||||
#endif /* truly sync */
|
||||
|
22
lib/http.c
22
lib/http.c
@ -97,6 +97,7 @@
|
||||
#include "select.h"
|
||||
#include "parsedate.h" /* for the week day and month names */
|
||||
#include "strtoofft.h"
|
||||
#include "multiif.h"
|
||||
|
||||
#define _MPRINTF_REPLACE /* use our functions only */
|
||||
#include <curl/mprintf.h>
|
||||
@ -1416,26 +1417,25 @@ CURLcode Curl_https_connecting(struct connectdata *conn, bool *done)
|
||||
}
|
||||
|
||||
#ifdef USE_SSLEAY
|
||||
CURLcode Curl_https_proto_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp)
|
||||
int Curl_https_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks)
|
||||
{
|
||||
if (conn->protocol & PROT_HTTPS) {
|
||||
struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
|
||||
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
|
||||
|
||||
if(!numsocks)
|
||||
return GETSOCK_BLANK;
|
||||
|
||||
if (connssl->connecting_state == ssl_connect_2_writing) {
|
||||
/* write mode */
|
||||
FD_SET(sockfd, write_fd_set);
|
||||
if((int)sockfd > *max_fdp)
|
||||
*max_fdp = (int)sockfd;
|
||||
socks[0] = conn->sock[FIRSTSOCKET];
|
||||
return GETSOCK_WRITESOCK(0);
|
||||
}
|
||||
else if (connssl->connecting_state == ssl_connect_2_reading) {
|
||||
/* read mode */
|
||||
FD_SET(sockfd, read_fd_set);
|
||||
if((int)sockfd > *max_fdp)
|
||||
*max_fdp = (int)sockfd;
|
||||
socks[0] = conn->sock[FIRSTSOCKET];
|
||||
return GETSOCK_READSOCK(0);
|
||||
}
|
||||
}
|
||||
return CURLE_OK;
|
||||
|
@ -8,7 +8,7 @@
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
* Copyright (C) 1998 - 2006, 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
|
||||
@ -38,10 +38,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done);
|
||||
CURLcode Curl_http_done(struct connectdata *, CURLcode);
|
||||
CURLcode Curl_http_connect(struct connectdata *conn, bool *done);
|
||||
CURLcode Curl_https_connecting(struct connectdata *conn, bool *done);
|
||||
CURLcode Curl_https_proto_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp);
|
||||
int Curl_https_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks);
|
||||
|
||||
/* The following functions are defined in http_chunks.c */
|
||||
void Curl_httpchunk_init(struct connectdata *conn);
|
||||
|
705
lib/multi.c
705
lib/multi.c
@ -46,6 +46,7 @@
|
||||
#include "easyif.h"
|
||||
#include "multiif.h"
|
||||
#include "sendf.h"
|
||||
#include "timeval.h"
|
||||
|
||||
/* The last #include file should be: */
|
||||
#include "memdebug.h"
|
||||
@ -73,6 +74,18 @@ typedef enum {
|
||||
CURLM_STATE_LAST /* not a true state, never use this */
|
||||
} CURLMstate;
|
||||
|
||||
/* we support 16 sockets per easy handle. Set the corresponding bit to what
|
||||
action we should wait for */
|
||||
#define MAX_SOCKSPEREASYHANDLE 16
|
||||
#define GETSOCK_READABLE (0x00ff)
|
||||
#define GETSOCK_WRITABLE (0xff00)
|
||||
|
||||
struct socketstate {
|
||||
curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
|
||||
long action; /* socket action bitmap */
|
||||
long timeout[MAX_SOCKSPEREASYHANDLE];
|
||||
};
|
||||
|
||||
struct Curl_one_easy {
|
||||
/* first, two fields for the linked list of these */
|
||||
struct Curl_one_easy *next;
|
||||
@ -90,6 +103,8 @@ struct Curl_one_easy {
|
||||
will be deleted when this handle is removed
|
||||
from the multi-handle */
|
||||
int msg_num; /* number of messages left in 'msg' to return */
|
||||
|
||||
struct socketstate sockstate; /* for the socket API magic */
|
||||
};
|
||||
|
||||
#define CURL_MULTI_HANDLE 0x000bab1e
|
||||
@ -111,8 +126,21 @@ struct Curl_multi {
|
||||
|
||||
int num_msgs; /* total amount of messages in the easy handles */
|
||||
|
||||
/* callback function and user data pointer for the *socket() API */
|
||||
curl_socket_callback socket_cb;
|
||||
void *socket_userp;
|
||||
|
||||
/* Hostname cache */
|
||||
struct curl_hash *hostcache;
|
||||
|
||||
/* timetree points to the splay-tree of time nodes to figure out expire
|
||||
times of all currently set timers */
|
||||
struct Curl_tree *timetree;
|
||||
|
||||
/* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
|
||||
the pluralis form, there can be more than one easy handle waiting on the
|
||||
same actual socket) */
|
||||
struct curl_hash *sockhash;
|
||||
};
|
||||
|
||||
/* always use this function to change state, to make debugging easier */
|
||||
@ -134,6 +162,7 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state)
|
||||
};
|
||||
CURLMstate oldstate = easy->state;
|
||||
#endif
|
||||
|
||||
easy->state = state;
|
||||
|
||||
#ifdef CURLDEBUG
|
||||
@ -143,25 +172,166 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* We add one of these structs to the sockhash, and then if we add more easy
|
||||
* handles for the same socket we just link them with the next/prev pointers
|
||||
* from the node added to the hash. We only remove the node from the hash when
|
||||
* the final easy handle/socket associated with the node is removed.
|
||||
*/
|
||||
|
||||
struct Curl_sh_entry {
|
||||
struct Curl_sh_entry *next;
|
||||
struct Curl_sh_entry *prev;
|
||||
struct SessionHandle *easy;
|
||||
time_t timestamp;
|
||||
long inuse;
|
||||
};
|
||||
|
||||
/* make sure this socket is present in the hash for this handle */
|
||||
static int sh_addentry(struct curl_hash *sh,
|
||||
curl_socket_t s,
|
||||
struct SessionHandle *data)
|
||||
{
|
||||
struct Curl_sh_entry *there =
|
||||
Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
|
||||
struct Curl_sh_entry *check;
|
||||
|
||||
if(there) {
|
||||
/* verify that this particular handle is in here */
|
||||
check = there;
|
||||
while(check) {
|
||||
if(check->easy == data)
|
||||
/* it is, return fine */
|
||||
return 0;
|
||||
check = check->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* not present, add it */
|
||||
check = calloc(sizeof(struct Curl_sh_entry), 1);
|
||||
if(!check)
|
||||
return 1; /* major failure */
|
||||
check->easy = data;
|
||||
|
||||
if(there) {
|
||||
/* the node for this socket is already here, now link in the struct for
|
||||
the new handle */
|
||||
|
||||
check->next = there->next; /* get the previous next to point to */
|
||||
there->next = check; /* make the new next point to the new entry */
|
||||
|
||||
check->next->prev = check; /* make sure the next one points back to the
|
||||
new one */
|
||||
/* check->prev = NULL; is already cleared and we have no previous
|
||||
node */
|
||||
}
|
||||
else {
|
||||
/* make/add new hash entry */
|
||||
if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check))
|
||||
return 1; /* major failure */
|
||||
}
|
||||
return 0; /* things are good in sockhash land */
|
||||
}
|
||||
|
||||
|
||||
/* delete the given socket + handle from the hash */
|
||||
static void sh_delentry(struct curl_hash *sh,
|
||||
curl_socket_t s,
|
||||
struct SessionHandle *data)
|
||||
{
|
||||
struct Curl_sh_entry *there =
|
||||
Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
|
||||
|
||||
while(there) {
|
||||
/* this socket is in the hash, now scan the list at this point and see if
|
||||
the given easy handle is in there and if so remote that singe entry */
|
||||
if(there->easy == data) {
|
||||
/* match! */
|
||||
if(there->next || there->prev) {
|
||||
/* it is not the only handle for this socket, so only unlink this
|
||||
particular easy handle and leave the actional hash entry */
|
||||
|
||||
/* unlink */
|
||||
there->next->prev = there->prev;
|
||||
there->prev->next = there->next;
|
||||
free(there);
|
||||
}
|
||||
else {
|
||||
/* This is the only easy handle for this socket, we must remove the
|
||||
hash entry. (This'll end up in a call to sh_freeentry().) */
|
||||
Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
|
||||
}
|
||||
break;
|
||||
}
|
||||
there = there->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* free a sockhash entry
|
||||
*/
|
||||
static void sh_freeentry(void *freethis)
|
||||
{
|
||||
struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
|
||||
struct Curl_sh_entry *more = p->next;
|
||||
|
||||
/* if there's a chain of more handles, remove that chain first */
|
||||
while(more) {
|
||||
struct Curl_sh_entry *next = more->next;
|
||||
free(more);
|
||||
more = next;
|
||||
}
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* sh_init() creates a new socket hash and returns the handle for it.
|
||||
*
|
||||
* Quote from README.multi_socket:
|
||||
*
|
||||
* "Some tests at 7000 and 9000 connections showed that the socket hash lookup
|
||||
* is somewhat of a bottle neck. Its current implementation may be a bit too
|
||||
* limiting. It simply has a fixed-size array, and on each entry in the array
|
||||
* it has a linked list with entries. So the hash only checks which list to
|
||||
* scan through. The code I had used so for used a list with merely 7 slots
|
||||
* (as that is what the DNS hash uses) but with 7000 connections that would
|
||||
* make an average of 1000 nodes in each list to run through. I upped that to
|
||||
* 97 slots (I believe a prime is suitable) and noticed a significant speed
|
||||
* increase. I need to reconsider the hash implementation or use a rather
|
||||
* large default value like this. At 9000 connections I was still below 10us
|
||||
* per call."
|
||||
*
|
||||
*/
|
||||
static struct curl_hash *sh_init(void)
|
||||
{
|
||||
return Curl_hash_alloc(97, sh_freeentry);
|
||||
}
|
||||
|
||||
CURLM *curl_multi_init(void)
|
||||
{
|
||||
struct Curl_multi *multi;
|
||||
struct Curl_multi *multi = (void *)calloc(sizeof(struct Curl_multi), 1);
|
||||
|
||||
multi = (void *)malloc(sizeof(struct Curl_multi));
|
||||
|
||||
if(multi) {
|
||||
memset(multi, 0, sizeof(struct Curl_multi));
|
||||
multi->type = CURL_MULTI_HANDLE;
|
||||
}
|
||||
else
|
||||
if(!multi)
|
||||
return NULL;
|
||||
|
||||
multi->type = CURL_MULTI_HANDLE;
|
||||
|
||||
multi->hostcache = Curl_mk_dnscache();
|
||||
if(!multi->hostcache) {
|
||||
/* failure, free mem and bail out */
|
||||
free(multi);
|
||||
multi = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
multi->sockhash = sh_init();
|
||||
if(!multi->sockhash) {
|
||||
/* failure, free mem and bail out */
|
||||
Curl_hash_destroy(multi->hostcache);
|
||||
free(multi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (CURLM *) multi;
|
||||
}
|
||||
|
||||
@ -170,6 +340,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
|
||||
{
|
||||
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
|
||||
struct Curl_one_easy *easy;
|
||||
int i;
|
||||
|
||||
/* First, make some basic checks that the CURLM handle is a good handle */
|
||||
if(!GOOD_MULTI_HANDLE(multi))
|
||||
@ -180,12 +351,12 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
|
||||
return CURLM_BAD_EASY_HANDLE;
|
||||
|
||||
/* Now, time to add an easy handle to the multi stack */
|
||||
easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy));
|
||||
easy = (struct Curl_one_easy *)calloc(sizeof(struct Curl_one_easy), 1);
|
||||
if(!easy)
|
||||
return CURLM_OUT_OF_MEMORY;
|
||||
|
||||
/* clean it all first (just to be sure) */
|
||||
memset(easy, 0, sizeof(struct Curl_one_easy));
|
||||
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
|
||||
easy->sockstate.socks[i] = CURL_SOCKET_BAD;
|
||||
|
||||
/* set the easy handle */
|
||||
easy->easy_handle = easy_handle;
|
||||
@ -209,6 +380,9 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
|
||||
|
||||
Curl_easy_addmulti(easy_handle, multi_handle);
|
||||
|
||||
/* make the SessionHandle struct refer back to this struct */
|
||||
easy->easy_handle->set.one_easy = easy;
|
||||
|
||||
/* increase the node-counter */
|
||||
multi->num_easy++;
|
||||
|
||||
@ -257,6 +431,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
|
||||
if(easy->next)
|
||||
easy->next->prev = easy->prev;
|
||||
|
||||
easy->easy_handle->set.one_easy = NULL; /* detached */
|
||||
|
||||
/* NOTE NOTE NOTE
|
||||
We do not touch the easy handle here! */
|
||||
if (easy->msg)
|
||||
@ -271,6 +447,65 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
|
||||
return CURLM_BAD_EASY_HANDLE; /* twasn't found */
|
||||
}
|
||||
|
||||
static int waitconnect_getsock(struct connectdata *conn,
|
||||
curl_socket_t *sock,
|
||||
int numsocks)
|
||||
{
|
||||
if(!numsocks)
|
||||
return GETSOCK_BLANK;
|
||||
|
||||
sock[0] = conn->sock[FIRSTSOCKET];
|
||||
return GETSOCK_WRITESOCK(0);
|
||||
}
|
||||
|
||||
static int domore_getsock(struct connectdata *conn,
|
||||
curl_socket_t *sock,
|
||||
int numsocks)
|
||||
{
|
||||
if(!numsocks)
|
||||
return GETSOCK_BLANK;
|
||||
|
||||
/* When in DO_MORE state, we could be either waiting for us
|
||||
to connect to a remote site, or we could wait for that site
|
||||
to connect to us. It makes a difference in the way: if we
|
||||
connect to the site we wait for the socket to become writable, if
|
||||
the site connects to us we wait for it to become readable */
|
||||
sock[0] = conn->sock[SECONDARYSOCKET];
|
||||
|
||||
return GETSOCK_WRITESOCK(0);
|
||||
}
|
||||
|
||||
/* returns bitmapped flags for this handle and its sockets */
|
||||
static int multi_getsock(struct Curl_one_easy *easy,
|
||||
curl_socket_t *socks, /* points to numsocks number
|
||||
of sockets */
|
||||
int numsocks)
|
||||
{
|
||||
switch(easy->state) {
|
||||
default:
|
||||
return 0;
|
||||
|
||||
case CURLM_STATE_WAITRESOLVE:
|
||||
return Curl_resolv_getsock(easy->easy_conn, socks, numsocks);
|
||||
|
||||
case CURLM_STATE_PROTOCONNECT:
|
||||
return Curl_protocol_getsock(easy->easy_conn, socks, numsocks);
|
||||
|
||||
case CURLM_STATE_DOING:
|
||||
return Curl_doing_getsock(easy->easy_conn, socks, numsocks);
|
||||
|
||||
case CURLM_STATE_WAITCONNECT:
|
||||
return waitconnect_getsock(easy->easy_conn, socks, numsocks);
|
||||
|
||||
case CURLM_STATE_DO_MORE:
|
||||
return domore_getsock(easy->easy_conn, socks, numsocks);
|
||||
|
||||
case CURLM_STATE_PERFORM:
|
||||
return Curl_single_getsock(easy->easy_conn, socks, numsocks);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CURLMcode curl_multi_fdset(CURLM *multi_handle,
|
||||
fd_set *read_fd_set, fd_set *write_fd_set,
|
||||
fd_set *exc_fd_set, int *max_fd)
|
||||
@ -281,104 +516,58 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
|
||||
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
|
||||
struct Curl_one_easy *easy;
|
||||
int this_max_fd=-1;
|
||||
curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
|
||||
int bitmap;
|
||||
int i;
|
||||
(void)exc_fd_set; /* not used */
|
||||
|
||||
if(!GOOD_MULTI_HANDLE(multi))
|
||||
return CURLM_BAD_HANDLE;
|
||||
|
||||
*max_fd = -1; /* so far none! */
|
||||
|
||||
easy=multi->easy.next;
|
||||
while(easy) {
|
||||
switch(easy->state) {
|
||||
default:
|
||||
break;
|
||||
case CURLM_STATE_WAITRESOLVE:
|
||||
/* waiting for a resolve to complete */
|
||||
Curl_resolv_fdset(easy->easy_conn, read_fd_set, write_fd_set,
|
||||
&this_max_fd);
|
||||
if(this_max_fd > *max_fd)
|
||||
*max_fd = this_max_fd;
|
||||
break;
|
||||
bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
|
||||
|
||||
case CURLM_STATE_PROTOCONNECT:
|
||||
Curl_protocol_fdset(easy->easy_conn, read_fd_set, write_fd_set,
|
||||
&this_max_fd);
|
||||
if(this_max_fd > *max_fd)
|
||||
*max_fd = this_max_fd;
|
||||
break;
|
||||
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
|
||||
curl_socket_t s = CURL_SOCKET_BAD;
|
||||
|
||||
case CURLM_STATE_DOING:
|
||||
Curl_doing_fdset(easy->easy_conn, read_fd_set, write_fd_set,
|
||||
&this_max_fd);
|
||||
if(this_max_fd > *max_fd)
|
||||
*max_fd = this_max_fd;
|
||||
break;
|
||||
|
||||
case CURLM_STATE_WAITCONNECT:
|
||||
case CURLM_STATE_DO_MORE:
|
||||
{
|
||||
/* when we're waiting for a connect, we wait for the socket to
|
||||
become writable */
|
||||
struct connectdata *conn = easy->easy_conn;
|
||||
curl_socket_t sockfd;
|
||||
|
||||
if(CURLM_STATE_WAITCONNECT == easy->state) {
|
||||
sockfd = conn->sock[FIRSTSOCKET];
|
||||
FD_SET(sockfd, write_fd_set);
|
||||
if(bitmap & GETSOCK_READSOCK(i)) {
|
||||
FD_SET(sockbunch[i], read_fd_set);
|
||||
s = sockbunch[i];
|
||||
}
|
||||
if(bitmap & GETSOCK_WRITESOCK(i)) {
|
||||
FD_SET(sockbunch[i], write_fd_set);
|
||||
s = sockbunch[i];
|
||||
}
|
||||
if(s == CURL_SOCKET_BAD)
|
||||
/* this socket is unused, break out of loop */
|
||||
break;
|
||||
else {
|
||||
/* When in DO_MORE state, we could be either waiting for us
|
||||
to connect to a remote site, or we could wait for that site
|
||||
to connect to us. It makes a difference in the way: if we
|
||||
connect to the site we wait for the socket to become writable, if
|
||||
the site connects to us we wait for it to become readable */
|
||||
sockfd = conn->sock[SECONDARYSOCKET];
|
||||
FD_SET(sockfd, write_fd_set);
|
||||
if(s > this_max_fd)
|
||||
this_max_fd = s;
|
||||
}
|
||||
}
|
||||
|
||||
if((int)sockfd > *max_fd)
|
||||
*max_fd = (int)sockfd;
|
||||
}
|
||||
break;
|
||||
case CURLM_STATE_PERFORM:
|
||||
/* This should have a set of file descriptors for us to set. */
|
||||
/* after the transfer is done, go DONE */
|
||||
|
||||
Curl_single_fdset(easy->easy_conn,
|
||||
read_fd_set, write_fd_set,
|
||||
exc_fd_set, &this_max_fd);
|
||||
|
||||
/* remember the maximum file descriptor */
|
||||
if(this_max_fd > *max_fd)
|
||||
*max_fd = this_max_fd;
|
||||
|
||||
break;
|
||||
}
|
||||
easy = easy->next; /* check next handle */
|
||||
}
|
||||
|
||||
*max_fd = this_max_fd;
|
||||
|
||||
return CURLM_OK;
|
||||
}
|
||||
|
||||
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
|
||||
static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||
struct Curl_one_easy *easy,
|
||||
int *running_handles)
|
||||
{
|
||||
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
|
||||
struct Curl_one_easy *easy;
|
||||
bool done;
|
||||
CURLMcode result=CURLM_OK;
|
||||
struct Curl_message *msg = NULL;
|
||||
bool connected;
|
||||
bool async;
|
||||
bool protocol_connect;
|
||||
bool dophase_done;
|
||||
bool done;
|
||||
CURLMcode result = CURLM_OK;
|
||||
|
||||
*running_handles = 0; /* bump this once for every living handle */
|
||||
|
||||
if(!GOOD_MULTI_HANDLE(multi))
|
||||
return CURLM_BAD_HANDLE;
|
||||
|
||||
easy=multi->easy.next;
|
||||
while(easy) {
|
||||
do {
|
||||
if (CURLM_STATE_WAITCONNECT <= easy->state &&
|
||||
easy->state <= CURLM_STATE_DO &&
|
||||
@ -731,10 +920,45 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
|
||||
|
||||
multi->num_msgs++; /* increase message counter */
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
|
||||
{
|
||||
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
|
||||
struct Curl_one_easy *easy;
|
||||
CURLMcode returncode=CURLM_OK;
|
||||
struct Curl_tree *t;
|
||||
|
||||
*running_handles = 0; /* bump this once for every living handle */
|
||||
|
||||
if(!GOOD_MULTI_HANDLE(multi))
|
||||
return CURLM_BAD_HANDLE;
|
||||
|
||||
easy=multi->easy.next;
|
||||
while(easy) {
|
||||
CURLMcode result = multi_runsingle(multi, easy, running_handles);
|
||||
if(result)
|
||||
returncode = result;
|
||||
|
||||
easy = easy->next; /* operate on next handle */
|
||||
}
|
||||
|
||||
return result;
|
||||
/*
|
||||
* Simply remove all expired timers from the splay since handles are dealt
|
||||
* with unconditionally by this function and curl_multi_timeout() requires
|
||||
* that already passed/handled expire times are removed from the splay.
|
||||
*/
|
||||
do {
|
||||
struct timeval now = Curl_tvnow();
|
||||
int key = now.tv_sec; /* drop the usec part */
|
||||
|
||||
multi->timetree = Curl_splaygetbest(key, multi->timetree, &t);
|
||||
} while(t);
|
||||
|
||||
return returncode;
|
||||
}
|
||||
|
||||
/* This is called when an easy handle is cleanup'ed that is part of a multi
|
||||
@ -753,6 +977,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
|
||||
if(GOOD_MULTI_HANDLE(multi)) {
|
||||
multi->type = 0; /* not good anymore */
|
||||
Curl_hash_destroy(multi->hostcache);
|
||||
Curl_hash_destroy(multi->sockhash);
|
||||
|
||||
/* remove all easy handles */
|
||||
easy = multi->easy.next;
|
||||
@ -807,3 +1032,311 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check what sockets we deal with and their "action state" and if we have a
|
||||
* difference from last time we call the callback accordingly.
|
||||
*/
|
||||
static void singlesocket(struct Curl_multi *multi,
|
||||
struct Curl_one_easy *easy)
|
||||
{
|
||||
struct socketstate current;
|
||||
int i;
|
||||
|
||||
memset(¤t, 0, sizeof(current));
|
||||
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
|
||||
current.socks[i] = CURL_SOCKET_BAD;
|
||||
|
||||
/* first fill in the 'current' struct with the state as it is now */
|
||||
current.action = multi_getsock(easy, current.socks, MAX_SOCKSPEREASYHANDLE);
|
||||
|
||||
/* when filled in, we compare with the previous round's state */
|
||||
if(memcmp(¤t, &easy->sockstate, sizeof(struct socketstate))) {
|
||||
/* difference, call the callback once for every socket change ! */
|
||||
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
|
||||
int action;
|
||||
curl_socket_t s = current.socks[i];
|
||||
|
||||
/* Ok, this approach is probably too naive and simple-minded but
|
||||
it might work for a start */
|
||||
|
||||
if((easy->sockstate.socks[i] == CURL_SOCKET_BAD) &&
|
||||
(s == CURL_SOCKET_BAD)) {
|
||||
/* no socket now and there was no socket before */
|
||||
break;
|
||||
}
|
||||
|
||||
if(s == CURL_SOCKET_BAD) {
|
||||
/* socket is removed */
|
||||
action = CURL_POLL_REMOVE;
|
||||
s = easy->sockstate.socks[i]; /* this is the removed socket */
|
||||
}
|
||||
else {
|
||||
if(easy->sockstate.socks[i] == s) {
|
||||
/* still the same socket, but are we waiting for the same actions? */
|
||||
unsigned int curr;
|
||||
unsigned int prev;
|
||||
|
||||
/* the current read/write bits for this particular socket */
|
||||
curr = current.action & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i));
|
||||
|
||||
/* the previous read/write bits for this particular socket */
|
||||
prev = easy->sockstate.action &
|
||||
(GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i));
|
||||
|
||||
if(curr == prev)
|
||||
continue;
|
||||
}
|
||||
|
||||
action = (current.action & GETSOCK_READSOCK(i)?CURL_POLL_IN:0) |
|
||||
(current.action & GETSOCK_WRITESOCK(i)?CURL_POLL_OUT:0);
|
||||
}
|
||||
|
||||
/* call the callback with this new info */
|
||||
if(multi->socket_cb) {
|
||||
multi->socket_cb(easy->easy_handle,
|
||||
s,
|
||||
action,
|
||||
multi->socket_userp);
|
||||
}
|
||||
|
||||
/* Update the sockhash accordingly */
|
||||
if(action == CURL_POLL_REMOVE)
|
||||
/* remove from hash for this easy handle */
|
||||
sh_delentry(multi->sockhash, s, easy->easy_handle);
|
||||
else
|
||||
/* make sure this socket is present in the hash for this handle */
|
||||
sh_addentry(multi->sockhash, s, easy->easy_handle);
|
||||
}
|
||||
/* copy the current state to the storage area */
|
||||
memcpy(&easy->sockstate, ¤t, sizeof(struct socketstate));
|
||||
}
|
||||
else {
|
||||
/* identical, nothing new happened so we don't do any callbacks */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static CURLMcode multi_socket(struct Curl_multi *multi,
|
||||
bool checkall,
|
||||
curl_socket_t s)
|
||||
{
|
||||
CURLMcode result = CURLM_OK;
|
||||
int running_handles;
|
||||
struct SessionHandle *data = NULL;
|
||||
struct Curl_tree *t;
|
||||
|
||||
if(checkall) {
|
||||
struct Curl_one_easy *easyp;
|
||||
result = curl_multi_perform(multi, &running_handles);
|
||||
|
||||
/* walk through each easy handle and do the socket state change magic
|
||||
and callbacks */
|
||||
easyp=multi->easy.next;
|
||||
while(easyp) {
|
||||
singlesocket(multi, easyp);
|
||||
easyp = easyp->next;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else if (s != CURL_SOCKET_TIMEOUT) {
|
||||
|
||||
struct Curl_sh_entry *entry =
|
||||
Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
|
||||
|
||||
if(!entry)
|
||||
/* unmatched socket, major problemo! */
|
||||
return CURLM_BAD_SOCKET; /* better return code? */
|
||||
|
||||
/* Now, there is potentially a chain of easy handles in this hash
|
||||
entry struct and we need to deal with all of them */
|
||||
|
||||
do {
|
||||
data = entry->easy;
|
||||
|
||||
result = multi_runsingle(multi, data->set.one_easy, &running_handles);
|
||||
|
||||
if(result == CURLM_OK)
|
||||
/* get the socket(s) and check if the state has been changed since
|
||||
last */
|
||||
singlesocket(multi, data->set.one_easy);
|
||||
|
||||
entry = entry->next;
|
||||
|
||||
} while(entry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* The loop following here will go on as long as there are expire-times left
|
||||
* to process in the splay and 'data' will be re-assigned for every expired
|
||||
* handle we deal with.
|
||||
*/
|
||||
do {
|
||||
int key;
|
||||
struct timeval now;
|
||||
|
||||
/* the first loop lap 'data' can be NULL */
|
||||
if(data) {
|
||||
result = multi_runsingle(multi, data->set.one_easy, &running_handles);
|
||||
|
||||
if(result == CURLM_OK)
|
||||
/* get the socket(s) and check if the state has been changed since
|
||||
last */
|
||||
singlesocket(multi, data->set.one_easy);
|
||||
}
|
||||
|
||||
/* Check if there's one (more) expired timer to deal with! This function
|
||||
extracts a matching node if there is one */
|
||||
|
||||
now = Curl_tvnow();
|
||||
key = now.tv_sec; /* drop the usec part */
|
||||
|
||||
multi->timetree = Curl_splaygetbest(key, multi->timetree, &t);
|
||||
if(t)
|
||||
data = t->payload;
|
||||
|
||||
} while(t);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLMcode curl_multi_setopt(CURLM *multi_handle,
|
||||
CURLMoption option, ...)
|
||||
{
|
||||
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
|
||||
CURLMcode res = CURLM_OK;
|
||||
va_list param;
|
||||
|
||||
if(!GOOD_MULTI_HANDLE(multi))
|
||||
return CURLM_BAD_HANDLE;
|
||||
|
||||
va_start(param, option);
|
||||
|
||||
switch(option) {
|
||||
case CURLMOPT_SOCKETFUNCTION:
|
||||
multi->socket_cb = va_arg(param, curl_socket_callback);
|
||||
break;
|
||||
case CURLMOPT_SOCKETDATA:
|
||||
multi->socket_userp = va_arg(param, void *);
|
||||
break;
|
||||
default:
|
||||
res = CURLM_UNKNOWN_OPTION;
|
||||
}
|
||||
va_end(param);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s)
|
||||
{
|
||||
#if 0
|
||||
printf("multi_socket(%d)\n", (int)s);
|
||||
#endif
|
||||
|
||||
return multi_socket((struct Curl_multi *)multi_handle, FALSE, s);
|
||||
}
|
||||
|
||||
CURLMcode curl_multi_socket_all(CURLM *multi_handle)
|
||||
|
||||
{
|
||||
return multi_socket((struct Curl_multi *)multi_handle,
|
||||
TRUE, CURL_SOCKET_BAD);
|
||||
}
|
||||
|
||||
CURLMcode curl_multi_timeout(CURLM *multi_handle,
|
||||
long *timeout_ms)
|
||||
{
|
||||
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
|
||||
|
||||
/* First, make some basic checks that the CURLM handle is a good handle */
|
||||
if(!GOOD_MULTI_HANDLE(multi))
|
||||
return CURLM_BAD_HANDLE;
|
||||
|
||||
if(multi->timetree) {
|
||||
/* we have a tree of expire times */
|
||||
struct timeval now = Curl_tvnow();
|
||||
|
||||
/* splay the lowest to the bottom */
|
||||
multi->timetree = Curl_splay(0, multi->timetree);
|
||||
|
||||
/* At least currently, the splay key is a time_t for the expire time */
|
||||
*timeout_ms = (multi->timetree->key - now.tv_sec) * 1000 -
|
||||
now.tv_usec/1000;
|
||||
if(*timeout_ms < 0)
|
||||
/* 0 means immediately */
|
||||
*timeout_ms = 0;
|
||||
}
|
||||
else
|
||||
*timeout_ms = -1;
|
||||
|
||||
return CURLM_OK;
|
||||
}
|
||||
|
||||
/* given a number of milliseconds from now to use to set the 'act before
|
||||
this'-time for the transfer, to be extracted by curl_multi_timeout() */
|
||||
void Curl_expire(struct SessionHandle *data, long milli)
|
||||
{
|
||||
struct Curl_multi *multi = data->multi;
|
||||
struct timeval *nowp = &data->state.expiretime;
|
||||
|
||||
/* this is only interesting for multi-interface using libcurl, and only
|
||||
while there is still a multi interface struct remaining! */
|
||||
if(!multi)
|
||||
return;
|
||||
|
||||
if(!milli) {
|
||||
/* No timeout, clear the time data. */
|
||||
if(nowp->tv_sec) {
|
||||
/* Since this is an cleared time, we must remove the previous entry from
|
||||
the splay tree */
|
||||
multi->timetree = Curl_splayremovebyaddr(multi->timetree,
|
||||
&data->state.timenode);
|
||||
infof(data, "Expire cleared\n");
|
||||
}
|
||||
nowp->tv_sec = nowp->tv_usec = 0;
|
||||
}
|
||||
else {
|
||||
struct timeval set;
|
||||
int rest;
|
||||
|
||||
set = Curl_tvnow();
|
||||
set.tv_sec += milli/1000;
|
||||
set.tv_usec += (milli%1000)*1000;
|
||||
|
||||
rest = (int)(set.tv_usec - 1000000);
|
||||
if(rest > 0) {
|
||||
/* bigger than a full microsec */
|
||||
set.tv_sec++;
|
||||
set.tv_usec -= 1000000;
|
||||
}
|
||||
|
||||
if(nowp->tv_sec) {
|
||||
/* compare if the new time is earlier, and only set it if so */
|
||||
long diff = curlx_tvdiff(set, *nowp);
|
||||
if(diff > 0)
|
||||
/* the new expire time was later so we don't change this */
|
||||
return;
|
||||
|
||||
/* Since this is an updated time, we must remove the previous entry from
|
||||
the splay tree first and then re-add the new value */
|
||||
multi->timetree = Curl_splayremovebyaddr(multi->timetree,
|
||||
&data->state.timenode);
|
||||
}
|
||||
|
||||
*nowp = set;
|
||||
infof(data, "Expire at %ld / %ld (%ldms)\n",
|
||||
(long)nowp->tv_sec, (long)nowp->tv_usec, milli);
|
||||
|
||||
data->state.timenode.payload = data;
|
||||
multi->timetree = Curl_splayinsert((int)nowp->tv_sec,
|
||||
multi->timetree,
|
||||
&data->state.timenode);
|
||||
}
|
||||
#if 0
|
||||
Curl_splayprint(multi->timetree, 0, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -26,5 +26,19 @@
|
||||
/*
|
||||
* Prototypes for library-wide functions provided by multi.c
|
||||
*/
|
||||
void Curl_expire(struct SessionHandle *data, long milli);
|
||||
|
||||
void Curl_multi_rmeasy(void *multi, CURL *data);
|
||||
|
||||
/* the write bits start at bit 16 for the *getsock() bitmap */
|
||||
#define GETSOCK_WRITEBITSTART 16
|
||||
|
||||
#define GETSOCK_BLANK 0 /* no bits set */
|
||||
|
||||
/* set the bit for the given sock number to make the bitmap for writable */
|
||||
#define GETSOCK_WRITESOCK(x) (1 << (GETSOCK_WRITEBITSTART + (x)))
|
||||
|
||||
/* set the bit for the given sock number to make the bitmap for readable */
|
||||
#define GETSOCK_READSOCK(x) (1 << (x))
|
||||
|
||||
#endif /* __MULTIIF_H */
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <curl/curl.h>
|
||||
#include "urldata.h"
|
||||
#include "sendf.h"
|
||||
#include "multiif.h"
|
||||
#include "speedcheck.h"
|
||||
|
||||
void Curl_speedinit(struct SessionHandle *data)
|
||||
@ -43,13 +44,13 @@ CURLcode Curl_speedcheck(struct SessionHandle *data,
|
||||
data->set.low_speed_time &&
|
||||
(Curl_tvlong(data->state.keeps_speed) != 0) &&
|
||||
(data->progress.current_speed < data->set.low_speed_limit)) {
|
||||
long howlong = Curl_tvdiff(now, data->state.keeps_speed);
|
||||
|
||||
/* We are now below the "low speed limit". If we are below it
|
||||
for "low speed time" seconds we consider that enough reason
|
||||
to abort the download. */
|
||||
|
||||
if( (Curl_tvdiff(now, data->state.keeps_speed)/1000) >
|
||||
data->set.low_speed_time) {
|
||||
if( (howlong/1000) > data->set.low_speed_time) {
|
||||
/* we have been this slow for long enough, now die */
|
||||
failf(data,
|
||||
"Operation too slow. "
|
||||
@ -58,6 +59,7 @@ CURLcode Curl_speedcheck(struct SessionHandle *data,
|
||||
data->set.low_speed_time);
|
||||
return CURLE_OPERATION_TIMEOUTED;
|
||||
}
|
||||
Curl_expire(data, howlong);
|
||||
}
|
||||
else {
|
||||
/* we keep up the required speed all right */
|
||||
|
406
lib/splay.c
Normal file
406
lib/splay.c
Normal file
@ -0,0 +1,406 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1997 - 2006, 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 http://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.
|
||||
*
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "splay.h"
|
||||
|
||||
#define compare(i,j) ((i)-(j))
|
||||
|
||||
/* Set this to a key value that will *NEVER* appear otherwise */
|
||||
#define KEY_NOTUSED -1
|
||||
|
||||
/*
|
||||
* Splay using the key i (which may or may not be in the tree.) The starting
|
||||
* root is t.
|
||||
*/
|
||||
struct Curl_tree *Curl_splay(int i, struct Curl_tree *t)
|
||||
{
|
||||
struct Curl_tree N, *l, *r, *y;
|
||||
int comp;
|
||||
|
||||
if (t == NULL)
|
||||
return t;
|
||||
N.smaller = N.larger = NULL;
|
||||
l = r = &N;
|
||||
|
||||
for (;;) {
|
||||
comp = compare(i, t->key);
|
||||
if (comp < 0) {
|
||||
if (t->smaller == NULL)
|
||||
break;
|
||||
if (compare(i, t->smaller->key) < 0) {
|
||||
y = t->smaller; /* rotate smaller */
|
||||
t->smaller = y->larger;
|
||||
y->larger = t;
|
||||
t = y;
|
||||
if (t->smaller == NULL)
|
||||
break;
|
||||
}
|
||||
r->smaller = t; /* link smaller */
|
||||
r = t;
|
||||
t = t->smaller;
|
||||
}
|
||||
else if (comp > 0) {
|
||||
if (t->larger == NULL)
|
||||
break;
|
||||
if (compare(i, t->larger->key) > 0) {
|
||||
y = t->larger; /* rotate larger */
|
||||
t->larger = y->smaller;
|
||||
y->smaller = t;
|
||||
t = y;
|
||||
if (t->larger == NULL)
|
||||
break;
|
||||
}
|
||||
l->larger = t; /* link larger */
|
||||
l = t;
|
||||
t = t->larger;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
l->larger = r->smaller = NULL;
|
||||
|
||||
l->larger = t->smaller; /* assemble */
|
||||
r->smaller = t->larger;
|
||||
t->smaller = N.larger;
|
||||
t->larger = N.smaller;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Insert key i into the tree t. Return a pointer to the resulting tree or
|
||||
NULL if something went wrong. */
|
||||
struct Curl_tree *Curl_splayinsert(int i, struct Curl_tree *t,
|
||||
struct Curl_tree *area)
|
||||
{
|
||||
if (area == NULL)
|
||||
return t;
|
||||
|
||||
if (t != NULL) {
|
||||
t = Curl_splay(i,t);
|
||||
if (compare(i, t->key)==0) {
|
||||
/* it already exists one of this size */
|
||||
|
||||
area->same = t;
|
||||
area->key = i;
|
||||
area->smaller = t->smaller;
|
||||
area->larger = t->larger;
|
||||
|
||||
t->smaller = area;
|
||||
t->key = KEY_NOTUSED;
|
||||
|
||||
return area; /* new root node */
|
||||
}
|
||||
}
|
||||
|
||||
if (t == NULL) {
|
||||
area->smaller = area->larger = NULL;
|
||||
}
|
||||
else if (compare(i, t->key) < 0) {
|
||||
area->smaller = t->smaller;
|
||||
area->larger = t;
|
||||
t->smaller = NULL;
|
||||
|
||||
}
|
||||
else {
|
||||
area->larger = t->larger;
|
||||
area->smaller = t;
|
||||
t->larger = NULL;
|
||||
}
|
||||
area->key = i;
|
||||
|
||||
area->same = NULL; /* no identical node (yet) */
|
||||
return area;
|
||||
}
|
||||
|
||||
/* Deletes 'i' from the tree if it's there (with an exact match). Returns a
|
||||
pointer to the resulting tree. */
|
||||
struct Curl_tree *Curl_splayremove(int i, struct Curl_tree *t,
|
||||
struct Curl_tree **removed)
|
||||
{
|
||||
struct Curl_tree *x;
|
||||
|
||||
if (t==NULL)
|
||||
return NULL;
|
||||
|
||||
t = Curl_splay(i,t);
|
||||
if (compare(i, t->key) == 0) { /* found it */
|
||||
|
||||
/* FIRST! Check if there is a list with identical sizes */
|
||||
if((x = t->same)) {
|
||||
/* there is, pick one from the list */
|
||||
|
||||
/* 'x' is the new root node */
|
||||
|
||||
x->key = t->key;
|
||||
x->larger = t->larger;
|
||||
x->smaller = t->smaller;
|
||||
|
||||
*removed = t;
|
||||
return x; /* new root */
|
||||
}
|
||||
|
||||
if (t->smaller == NULL) {
|
||||
x = t->larger;
|
||||
}
|
||||
else {
|
||||
x = Curl_splay(i, t->smaller);
|
||||
x->larger = t->larger;
|
||||
}
|
||||
*removed = t;
|
||||
|
||||
return x;
|
||||
}
|
||||
else {
|
||||
*removed = NULL; /* no match */
|
||||
return t; /* It wasn't there */
|
||||
}
|
||||
}
|
||||
|
||||
/* Finds and deletes the best-fit node from the tree. Return a pointer to the
|
||||
resulting tree. best-fit means the node with the given or lower number */
|
||||
struct Curl_tree *Curl_splaygetbest(int i, struct Curl_tree *t,
|
||||
struct Curl_tree **removed)
|
||||
{
|
||||
struct Curl_tree *x;
|
||||
|
||||
if (!t) {
|
||||
*removed = NULL; /* none removed since there was no root */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t = Curl_splay(i,t);
|
||||
if(compare(i, t->key) < 0) {
|
||||
/* too big node, try the smaller chain */
|
||||
if(t->smaller)
|
||||
t=Curl_splay(t->smaller->key, t);
|
||||
else {
|
||||
/* fail */
|
||||
*removed = NULL;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
if (compare(i, t->key) >= 0) { /* found it */
|
||||
/* FIRST! Check if there is a list with identical sizes */
|
||||
x = t->same;
|
||||
if(x) {
|
||||
/* there is, pick one from the list */
|
||||
|
||||
/* 'x' is the new root node */
|
||||
|
||||
x->key = t->key;
|
||||
x->larger = t->larger;
|
||||
x->smaller = t->smaller;
|
||||
|
||||
*removed = t;
|
||||
return x; /* new root */
|
||||
}
|
||||
|
||||
if (t->smaller == NULL) {
|
||||
x = t->larger;
|
||||
}
|
||||
else {
|
||||
x = Curl_splay(i, t->smaller);
|
||||
x->larger = t->larger;
|
||||
}
|
||||
*removed = t;
|
||||
|
||||
return x;
|
||||
}
|
||||
else {
|
||||
*removed = NULL; /* no match */
|
||||
return t; /* It wasn't there */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Deletes the node we point out from the tree if it's there. Return a pointer
|
||||
to the resulting tree. */
|
||||
struct Curl_tree *Curl_splayremovebyaddr(struct Curl_tree *t,
|
||||
struct Curl_tree *remove)
|
||||
{
|
||||
struct Curl_tree *x;
|
||||
|
||||
if (!t || !remove)
|
||||
return NULL;
|
||||
|
||||
if(KEY_NOTUSED == remove->key) {
|
||||
/* just unlink ourselves nice and quickly: */
|
||||
remove->smaller->same = remove->same;
|
||||
if(remove->same)
|
||||
remove->same->smaller = remove->smaller;
|
||||
/* voila, we're done! */
|
||||
return t;
|
||||
}
|
||||
|
||||
t = Curl_splay(remove->key, t);
|
||||
|
||||
/* Check if there is a list with identical sizes */
|
||||
|
||||
x = t->same;
|
||||
if(x) {
|
||||
/* 'x' is the new root node */
|
||||
|
||||
x->key = t->key;
|
||||
x->larger = t->larger;
|
||||
x->smaller = t->smaller;
|
||||
|
||||
return x; /* new root */
|
||||
}
|
||||
|
||||
/* Remove the actualy root node: */
|
||||
if (t->smaller == NULL)
|
||||
x = t->larger;
|
||||
else {
|
||||
x = Curl_splay(remove->key, t->smaller);
|
||||
x->larger = t->larger;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
#ifdef CURLDEBUG
|
||||
|
||||
int Curl_splayprint(struct Curl_tree * t, int d, char output)
|
||||
{
|
||||
int distance=0;
|
||||
struct Curl_tree *node;
|
||||
int i;
|
||||
if (t == NULL)
|
||||
return 0;
|
||||
distance += Curl_splayprint(t->larger, d+1, output);
|
||||
for (i=0; i<d; i++)
|
||||
if(output)
|
||||
printf(" ");
|
||||
|
||||
if(output) {
|
||||
printf("%d[%d]", t->key, i);
|
||||
}
|
||||
|
||||
for(node = t->same; node; node = node->same) {
|
||||
distance += i; /* this has the same "virtual" distance */
|
||||
|
||||
if(output)
|
||||
printf(" [+]");
|
||||
}
|
||||
if(output)
|
||||
puts("");
|
||||
|
||||
distance += i;
|
||||
|
||||
distance += Curl_splayprint(t->smaller, d+1, output);
|
||||
|
||||
return distance;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST_SPLAY
|
||||
|
||||
/*#define TEST2 */
|
||||
#define MAX 50
|
||||
#define OUTPUT 0 /* 1 enables, 0 disables */
|
||||
|
||||
/* A sample use of these functions. Start with the empty tree, insert some
|
||||
stuff into it, and then delete it */
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct Curl_tree *root, *t;
|
||||
void *ptrs[MAX];
|
||||
|
||||
long sizes[]={
|
||||
50, 60, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200, 300,
|
||||
220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120, 200,
|
||||
300, 220, 80, 90, 50, 100, 60, 200, 120, 300, 400, 200, 256, 122, 60, 120,
|
||||
200, 300, 220, 80, 90};
|
||||
int i;
|
||||
root = NULL; /* the empty tree */
|
||||
|
||||
for (i = 0; i < MAX; i++) {
|
||||
ptrs[i] = t = (struct Curl_tree *)malloc(sizeof(struct Curl_tree));
|
||||
if(!t) {
|
||||
puts("out of memory!");
|
||||
return 0;
|
||||
}
|
||||
#ifdef TEST2
|
||||
root = Curl_splayinsert(sizes[i], root, t);
|
||||
#else
|
||||
root = Curl_splayinsert((541*i)&1023, root, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
puts("Result:");
|
||||
printtree(root, 0, 1);
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
for (i=0; root; i+=30) {
|
||||
Curl_splayprint(root, 0, 1);
|
||||
do {
|
||||
root = Curl_splaygetbest(i, root, &t);
|
||||
if(t)
|
||||
printf("bestfit %d became %d\n", i, t->key);
|
||||
else
|
||||
printf("bestfit %d failed!\n", i);
|
||||
} while(t && root);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
for (i = 0; i < MAX; i++) {
|
||||
printf("remove pointer %d size %d\n", i, sizes[i]);
|
||||
root = removebyaddr(root, (struct Curl_tree *)ptrs[i]);
|
||||
Curl_splayprint(root, 0, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#ifdef WEIGHT
|
||||
for (i = -1; i<=root->weight; i++) {
|
||||
t = find_rank(i, root);
|
||||
if (t == NULL) {
|
||||
printf("could not find a node of rank %d.\n", i);
|
||||
} else {
|
||||
printf("%d is of rank %d\n", t->key, i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#ifdef TEST2
|
||||
for (i = 0; i < MAX; i++) {
|
||||
printf("remove size %d\n", sizes[i]);
|
||||
root = Curl_splayremove(sizes[i], root, &t);
|
||||
free(t);
|
||||
Curl_splayprint(root, 0, 1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* TEST_SPLAY */
|
50
lib/splay.h
Normal file
50
lib/splay.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef __SPLAY_H
|
||||
#define __SPLAY_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1997 - 2006, 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 http://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.
|
||||
*
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
struct Curl_tree {
|
||||
struct Curl_tree *smaller; /* smaller node */
|
||||
struct Curl_tree *larger; /* larger node */
|
||||
struct Curl_tree *same; /* points to a node with identical key */
|
||||
int key; /* the "sort" key */
|
||||
void *payload; /* data the splay code doesn't care about */
|
||||
};
|
||||
|
||||
struct Curl_tree *Curl_splay(int i, struct Curl_tree *t);
|
||||
struct Curl_tree *Curl_splayinsert(int key, struct Curl_tree *t,
|
||||
struct Curl_tree *new);
|
||||
struct Curl_tree *Curl_splayremove(int key, struct Curl_tree *t,
|
||||
struct Curl_tree **removed);
|
||||
struct Curl_tree *Curl_splaygetbest(int key, struct Curl_tree *t,
|
||||
struct Curl_tree **removed);
|
||||
struct Curl_tree *Curl_splayremovebyaddr(struct Curl_tree *t,
|
||||
struct Curl_tree *remove);
|
||||
|
||||
#ifdef CURLDEBUG
|
||||
int Curl_splayprint(struct Curl_tree * t, int d, char output);
|
||||
#else
|
||||
#define Curl_splayprint(x,y,z)
|
||||
#endif
|
||||
|
||||
#endif
|
@ -329,6 +329,12 @@ curl_multi_strerror(CURLMcode error)
|
||||
case CURLM_INTERNAL_ERROR:
|
||||
return "internal error";
|
||||
|
||||
case CURLM_BAD_SOCKET:
|
||||
return "invalid socket argument";
|
||||
|
||||
case CURLM_UNKNOWN_OPTION:
|
||||
return "unknown option";
|
||||
|
||||
case CURLM_LAST:
|
||||
break;
|
||||
}
|
||||
|
@ -101,6 +101,7 @@
|
||||
#include "share.h"
|
||||
#include "memory.h"
|
||||
#include "select.h"
|
||||
#include "multiif.h"
|
||||
#include "easyif.h" /* for Curl_convert_to_network prototype */
|
||||
|
||||
#define _MPRINTF_REPLACE /* use our functions only */
|
||||
@ -1522,34 +1523,42 @@ CURLcode Curl_readwrite_init(struct connectdata *conn)
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_single_fdset() gets called by the multi interface code when the app
|
||||
* has requested to get the fd_sets for the current connection. This function
|
||||
* Curl_single_getsock() gets called by the multi interface code when the app
|
||||
* has requested to get the sockets for the current connection. This function
|
||||
* will then be called once for every connection that the multi interface
|
||||
* keeps track of. This function will only be called for connections that are
|
||||
* in the proper state to have this information available.
|
||||
*/
|
||||
void Curl_single_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
fd_set *exc_fd_set,
|
||||
int *max_fd)
|
||||
int Curl_single_getsock(struct connectdata *conn,
|
||||
curl_socket_t *sock, /* points to numsocks number
|
||||
of sockets */
|
||||
int numsocks)
|
||||
{
|
||||
*max_fd = -1; /* init */
|
||||
int bitmap = GETSOCK_BLANK;
|
||||
int index = 0;
|
||||
|
||||
if(numsocks < 2)
|
||||
/* simple check but we might need two slots */
|
||||
return GETSOCK_BLANK;
|
||||
|
||||
if(conn->keep.keepon & KEEP_READ) {
|
||||
FD_SET(conn->sockfd, read_fd_set);
|
||||
*max_fd = (int)conn->sockfd;
|
||||
bitmap |= GETSOCK_READSOCK(index);
|
||||
sock[index] = conn->sockfd;
|
||||
}
|
||||
if(conn->keep.keepon & KEEP_WRITE) {
|
||||
FD_SET(conn->writesockfd, write_fd_set);
|
||||
|
||||
/* since sockets are curl_socket_t nowadays, we typecast it to int here
|
||||
to compare it nicely */
|
||||
if((int)conn->writesockfd > *max_fd)
|
||||
*max_fd = (int)conn->writesockfd;
|
||||
if((conn->sockfd != conn->writesockfd) &&
|
||||
(conn->keep.keepon & KEEP_READ)) {
|
||||
/* only if they are not the same socket and we had a readable one,
|
||||
we increase index */
|
||||
index++;
|
||||
sock[index] = conn->writesockfd;
|
||||
}
|
||||
/* we don't use exceptions, only touch that one to prevent compiler
|
||||
warnings! */
|
||||
*exc_fd_set = *exc_fd_set;
|
||||
|
||||
bitmap |= GETSOCK_WRITESOCK(index);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,11 +28,9 @@ CURLcode Curl_second_connect(struct connectdata *conn);
|
||||
CURLcode Curl_posttransfer(struct SessionHandle *data);
|
||||
CURLcode Curl_follow(struct SessionHandle *data, char *newurl, bool retry);
|
||||
CURLcode Curl_readwrite(struct connectdata *conn, bool *done);
|
||||
void Curl_single_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
fd_set *exc_fd_set,
|
||||
int *max_fd);
|
||||
int Curl_single_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks);
|
||||
CURLcode Curl_readwrite_init(struct connectdata *conn);
|
||||
CURLcode Curl_readrewind(struct connectdata *conn);
|
||||
CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp);
|
||||
|
37
lib/url.c
37
lib/url.c
@ -1552,6 +1552,7 @@ CURLcode Curl_disconnect(struct connectdata *conn)
|
||||
NULL, Curl_scan_cache_used);
|
||||
#endif
|
||||
|
||||
Curl_expire(data, 0); /* shut off timers */
|
||||
Curl_hostcache_prune(data); /* kill old DNS cache entries */
|
||||
|
||||
/*
|
||||
@ -2318,26 +2319,22 @@ static void verboseconnect(struct connectdata *conn)
|
||||
conn->ip_addr_str, conn->port);
|
||||
}
|
||||
|
||||
CURLcode Curl_protocol_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp)
|
||||
int Curl_protocol_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks)
|
||||
{
|
||||
CURLcode res = CURLE_OK;
|
||||
if(conn->curl_proto_fdset)
|
||||
res = conn->curl_proto_fdset(conn, read_fd_set, write_fd_set, max_fdp);
|
||||
return res;
|
||||
if(conn->curl_proto_getsock)
|
||||
return conn->curl_proto_getsock(conn, socks, numsocks);
|
||||
return GETSOCK_BLANK;
|
||||
}
|
||||
|
||||
CURLcode Curl_doing_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp)
|
||||
int Curl_doing_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks)
|
||||
{
|
||||
CURLcode res = CURLE_OK;
|
||||
if(conn && conn->curl_doing_fdset)
|
||||
res = conn->curl_doing_fdset(conn, read_fd_set, write_fd_set, max_fdp);
|
||||
return res;
|
||||
if(conn && conn->curl_doing_getsock)
|
||||
return conn->curl_doing_getsock(conn, socks, numsocks);
|
||||
return GETSOCK_BLANK;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3034,7 +3031,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
|
||||
conn->curl_done = Curl_http_done;
|
||||
conn->curl_connect = Curl_http_connect;
|
||||
conn->curl_connecting = Curl_https_connecting;
|
||||
conn->curl_proto_fdset = Curl_https_proto_fdset;
|
||||
conn->curl_proto_getsock = Curl_https_getsock;
|
||||
|
||||
#else /* USE_SS */
|
||||
failf(data, LIBCURL_NAME
|
||||
@ -3086,8 +3083,8 @@ static CURLcode CreateConnection(struct SessionHandle *data,
|
||||
conn->curl_connect = Curl_ftp_connect;
|
||||
conn->curl_connecting = Curl_ftp_multi_statemach;
|
||||
conn->curl_doing = Curl_ftp_doing;
|
||||
conn->curl_proto_fdset = Curl_ftp_fdset;
|
||||
conn->curl_doing_fdset = Curl_ftp_fdset;
|
||||
conn->curl_proto_getsock = Curl_ftp_getsock;
|
||||
conn->curl_doing_getsock = Curl_ftp_getsock;
|
||||
conn->curl_disconnect = Curl_ftp_disconnect;
|
||||
}
|
||||
|
||||
@ -4027,6 +4024,8 @@ CURLcode Curl_done(struct connectdata **connp,
|
||||
struct connectdata *conn = *connp;
|
||||
struct SessionHandle *data=conn->data;
|
||||
|
||||
Curl_expire(data, 0); /* stop timer */
|
||||
|
||||
if(conn->bits.done)
|
||||
return CURLE_OK; /* Curl_done() has already been called */
|
||||
|
||||
|
12
lib/url.h
12
lib/url.h
@ -45,6 +45,16 @@ CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done);
|
||||
CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done);
|
||||
CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done);
|
||||
void Curl_safefree(void *ptr);
|
||||
|
||||
|
||||
int Curl_protocol_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks);
|
||||
int Curl_doing_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks);
|
||||
|
||||
#if 0
|
||||
CURLcode Curl_protocol_fdset(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
@ -54,3 +64,5 @@ CURLcode Curl_doing_fdset(struct connectdata *conn,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -96,6 +96,7 @@
|
||||
#include "http_chunks.h" /* for the structs and enum stuff */
|
||||
#include "hostip.h"
|
||||
#include "hash.h"
|
||||
#include "splay.h"
|
||||
|
||||
#ifdef HAVE_GSSAPI
|
||||
# ifdef HAVE_GSSGNU
|
||||
@ -657,17 +658,15 @@ struct connectdata {
|
||||
|
||||
/* Called from the multi interface during the PROTOCONNECT phase, and it
|
||||
should then return a proper fd set */
|
||||
CURLcode (*curl_proto_fdset)(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp);
|
||||
int (*curl_proto_getsock)(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks);
|
||||
|
||||
/* Called from the multi interface during the DOING phase, and it should
|
||||
then return a proper fd set */
|
||||
CURLcode (*curl_doing_fdset)(struct connectdata *conn,
|
||||
fd_set *read_fd_set,
|
||||
fd_set *write_fd_set,
|
||||
int *max_fdp);
|
||||
int (*curl_doing_getsock)(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks);
|
||||
|
||||
/* This function *MAY* be set to a protocol-dependent function that is run
|
||||
* by the curl_disconnect(), as a step in the disconnection.
|
||||
@ -932,10 +931,11 @@ struct UrlState {
|
||||
#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
|
||||
ENGINE *engine;
|
||||
#endif /* USE_SSLEAY */
|
||||
struct timeval expiretime; /* set this with Curl_expire() only */
|
||||
struct Curl_tree timenode; /* for the splay stuff */
|
||||
|
||||
/* a place to store the most recenlty set FTP entrypath */
|
||||
char *most_recent_ftp_entrypath;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -968,6 +968,8 @@ struct DynamicStatic {
|
||||
* 'struct UrlState' instead. The only exceptions MUST note the changes in
|
||||
* the 'DynamicStatic' struct.
|
||||
*/
|
||||
struct Curl_one_easy; /* declared and used only in multi.c */
|
||||
struct Curl_multi; /* declared and used only in multi.c */
|
||||
|
||||
struct UserDefined {
|
||||
FILE *err; /* the stderr user data goes here */
|
||||
@ -1071,6 +1073,12 @@ struct UserDefined {
|
||||
|
||||
char *private_data; /* Private data */
|
||||
|
||||
struct Curl_one_easy *one_easy; /* When adding an easy handle to a multi
|
||||
handle, an internal 'Curl_one_easy'
|
||||
struct is created and this is a pointer
|
||||
to the particular struct associated with
|
||||
this SessionHandle */
|
||||
|
||||
struct curl_slist *http200aliases; /* linked list of aliases for http200 */
|
||||
|
||||
long ip_version;
|
||||
@ -1139,7 +1147,7 @@ struct UserDefined {
|
||||
|
||||
struct SessionHandle {
|
||||
struct curl_hash *hostcache;
|
||||
void *multi; /* if non-NULL, points to the multi handle
|
||||
struct Curl_multi *multi; /* if non-NULL, points to the multi handle
|
||||
struct of which this "belongs" */
|
||||
struct Curl_share *share; /* Share, handles global variable mutexing */
|
||||
struct UserDefined set; /* values set by the libcurl user */
|
||||
|
Loading…
Reference in New Issue
Block a user