From 15a56b42d6203603eadfe54f2b58f51de73fdac8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 28 Nov 2001 11:09:18 +0000 Subject: used in the new multi interface, not yet actually part of libcurl but added to CVS to make them available to others --- lib/multi.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 lib/multi.c (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c new file mode 100644 index 000000000..7d396c61d --- /dev/null +++ b/lib/multi.c @@ -0,0 +1,244 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2001, Daniel Stenberg, , et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * 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 MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +#include "multi.h" /* will become soon */ + +typedef enum { + CURLM_STATE_INIT, + CURLM_STATE_CONNECT, + CURLM_STATE_DO, + CURLM_STATE_PERFORM, + CURLM_STATE_DONE, + CURLM_STATE_COMPLETED, + + CURLM_STATE_LAST /* not a true state, never use this */ +} CURLMstate; + +struct Curl_one_easy { + /* first, two fields for the linked list of these */ + struct Curl_one_easy *next; + struct Curl_one_easy *prev; + + CURL *easy_handle; /* this is the easy handle for this unit */ + CURLMstate state; /* the handle's state */ +}; + + +#define CURL_MULTI_HANDLE 0x000bab1e + +#define GOOD_MULTI_HANDLE(x) ((x) && ((x)->type == CURL_MULTI_HANDLE)) +#define GOOD_EASY_HANDLE(x) (x) + +/* This is the struct known as CURLM on the outside */ +struct Curl_multi { + /* First a simple identifier to easier detect if a user mix up + this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ + long type; + + /* We have a linked list with easy handles */ + struct Curl_one_easy first; + /* This is the amount of entries in the linked list above. */ + int num_easy; +}; + + +CURLM *curl_multi_init(void) +{ + struct Curl_multi *multi; + + multi = (void *)malloc(sizeof(struct Curl_multi)); + + if(multi) { + memset(multi, 0, sizeof(struct Curl_multi)); + multi->type = CURL_MULTI_HANDLE; + } + + return (CURLM *) multi; +} + +CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *easy_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(easy_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)); + if(!easy) + return CURLM_OUT_OF_MEMORY; + + /* clean it all first (just to be sure) */ + memset(easy, 0, sizeof(struct Curl_one_easy)); + + /* set the easy handle */ + easy->easy_handle = easy_handle; + easy->state = CURLM_STATE_INIT; + + /* We add this new entry first in the list. We make our 'next' point to the + previous next and our 'prev' point back to the 'first' struct */ + easy->next = multi->first.next; + easy->prev = &multi->first; + + /* make 'easy' the first node in the chain */ + multi->first.next = easy; + + /* if there was a next node, make sure its 'prev' pointer links back to + the new node */ + if(easy->next) + easy->next->prev = easy; + + /* increase the node-counter */ + multi->num_easy++; + + return CURLM_OK; +} + +CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(curl_handle)) + return CURLM_BAD_EASY_HANDLE; + + /* scan through the list and remove the 'curl_handle' */ + easy = multi->first.next; + while(easy) { + if(easy->easy_handle == curl_handle) + break; + easy=easy->next; + } + if(easy) { + /* If the 'state' is not INIT or COMPLETED, we might need to do something + nice to put the easy_handle in a good known state when this returns. */ + + /* make the previous node point to our next */ + if(easy->prev) + easy->prev->next = easy->next; + /* make our next point to our previous node */ + if(easy->next) + easy->next->prev = easy->prev; + + /* NOTE NOTE NOTE + We do not touch the easy handle here! */ + free(easy); + + return CURLM_OK; + } + else + return CURLM_BAD_EASY_HANDLE; /* twasn't found */ +} + +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) +{ + /* Scan through all the easy handles to get the file descriptors set. + Some easy handles may not have connected to the remote host yet, + and then we must make sure that is done. */ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + easy=multi->first.next; + while(easy) { + switch(easy->state) { + case CURLM_STATE_INIT: + case CURLM_STATE_CONNECT: + case CURLM_STATE_DO: + case CURLM_STATE_DONE: + /* we want curl_multi_perform() to get called, but we don't have any + file descriptors to set */ + break; + case CURLM_STATE_PERFORM: + /* This should have a set of file descriptors for us to set. */ + /* after the transfer is done, go DONE */ + break; + } + + } + + return CURLM_OK; +} + +CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + easy=multi->first.next; + while(easy) { + switch(easy->state) { + case CURLM_STATE_INIT: + /* after init, go CONNECT */ + break; + case CURLM_STATE_CONNECT: + /* after connect, go DO */ + break; + case CURLM_STATE_DO: + /* after do, go PERFORM */ + break; + case CURLM_STATE_PERFORM: + /* read/write data if it is ready to do so */ + /* after the transfer is done, go DONE */ + break; + case CURLM_STATE_DONE: + /* after we have DONE what we're supposed to do, go COMPLETED */ + break; + case CURLM_STATE_COMPLETED: + /* this is a completed transfer */ + break; + } + + } + +} + +CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +int curl_multi_info_open(CURLM *multi_handle, CURLMinfo *info_handle); + +CURLMsg *curl_multi_info_read(CURLMinfo *info_handle); + +void curl_multi_info_close(CURLMinfo *info_handle); -- cgit v1.2.1 From e2844f5e040884b941c4e7af6eccadf4616bb47e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 28 Nov 2001 15:25:01 +0000 Subject: mods --- lib/multi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 7d396c61d..1cd98e1a7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -237,8 +237,5 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) CURLMcode curl_multi_cleanup(CURLM *multi_handle); -int curl_multi_info_open(CURLM *multi_handle, CURLMinfo *info_handle); +CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue); -CURLMsg *curl_multi_info_read(CURLMinfo *info_handle); - -void curl_multi_info_close(CURLMinfo *info_handle); -- cgit v1.2.1 From b93a60daf965a507905fbf9553d35a204fbd05a1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 28 Nov 2001 15:46:25 +0000 Subject: the perform "state machine" is more explained now --- lib/multi.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 1cd98e1a7..983785013 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -42,7 +42,8 @@ struct Curl_one_easy { struct Curl_one_easy *prev; CURL *easy_handle; /* this is the easy handle for this unit */ - CURLMstate state; /* the handle's state */ + CURLMstate state; /* the handle's state */ + CURLcode result; /* previous result */ }; @@ -193,7 +194,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, /* after the transfer is done, go DONE */ break; } - + easy = easy->next; /* check next handle */ } return CURLM_OK; @@ -209,30 +210,62 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->first.next; while(easy) { + switch(easy->state) { case CURLM_STATE_INIT: + /* init this transfer */ + easy->result = Curl_init(easy->easy_handle); /* after init, go CONNECT */ + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_CONNECT; break; case CURLM_STATE_CONNECT: + /* connect */ + easy->result = Curl_connect(easy->easy_handle); /* after connect, go DO */ + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_DO; break; case CURLM_STATE_DO: + /* Do the fetch or put request */ + easy->result = Curl_do(easy->easy_handle); /* after do, go PERFORM */ + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_PERFORM; break; case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ + easy->result = Curl_readwrite(easy->easy_handle, &done); /* after the transfer is done, go DONE */ + if(TRUE == done) + easy->state = CURLM_STATE_DONE; break; case CURLM_STATE_DONE: + /* post-transfer command */ + easy->result = Curl_done(easy->easy_handle); /* after we have DONE what we're supposed to do, go COMPLETED */ + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_COMPLETED; break; case CURLM_STATE_COMPLETED: - /* this is a completed transfer */ + /* this is a completed transfer, it is likely to still be connected */ + + /* This node should be delinked from the list now and we should post + an information message that we are complete. */ break; } + if((CURLM_STATE_COMPLETED != easy->state) && + (CURLE_OK != easy->result)) { + /* + * If an error was returned, and we aren't in completed now, + * then we go to completed and consider this transfer aborted. + */ + easy->state = CURLM_STATE_COMPLETED; + } + easy = easy->next; /* operate on next handle */ } - + return CURLM_OK; } CURLMcode curl_multi_cleanup(CURLM *multi_handle); -- cgit v1.2.1 From a32cd520bd6dd8a72b4e796edb943eb92bc16b3d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 28 Nov 2001 16:00:18 +0000 Subject: more more more MORE --- lib/multi.c | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 983785013..e593c0db4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -22,9 +22,16 @@ *****************************************************************************/ #include "setup.h" +#include #include "multi.h" /* will become soon */ +struct Curl_message { + /* the 'CURLMsg' is the part that is visible to the external user */ + struct CURLMsg extmsg; + struct Curl_message *next; +}; + typedef enum { CURLM_STATE_INIT, CURLM_STATE_CONNECT, @@ -59,9 +66,15 @@ struct Curl_multi { long type; /* We have a linked list with easy handles */ - struct Curl_one_easy first; + struct Curl_one_easy easy; /* This is the amount of entries in the linked list above. */ int num_easy; + + /* this is a linked list of posted messages */ + struct Curl_message *msgs; + /* amount of messages in the queue */ + int num_msgs; + }; @@ -107,11 +120,11 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* We add this new entry first in the list. We make our 'next' point to the previous next and our 'prev' point back to the 'first' struct */ - easy->next = multi->first.next; - easy->prev = &multi->first; + easy->next = multi->easy.next; + easy->prev = &multi->easy; /* make 'easy' the first node in the chain */ - multi->first.next = easy; + multi->easy.next = easy; /* if there was a next node, make sure its 'prev' pointer links back to the new node */ @@ -139,7 +152,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, return CURLM_BAD_EASY_HANDLE; /* scan through the list and remove the 'curl_handle' */ - easy = multi->first.next; + easy = multi->easy.next; while(easy) { if(easy->easy_handle == curl_handle) break; @@ -160,6 +173,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, We do not touch the easy handle here! */ free(easy); + multi->num_easy--; /* one less to care about now */ + return CURLM_OK; } else @@ -179,7 +194,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - easy=multi->first.next; + easy=multi->easy.next; while(easy) { switch(easy->state) { case CURLM_STATE_INIT: @@ -204,11 +219,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; + bool done; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - easy=multi->first.next; + easy=multi->easy.next; while(easy) { switch(easy->state) { @@ -268,7 +284,21 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) return CURLM_OK; } -CURLMcode curl_multi_cleanup(CURLM *multi_handle); +CURLMcode curl_multi_cleanup(CURLM *multi_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + if(GOOD_MULTI_HANDLE(multi)) { + multi->type = 0; /* not good anymore */ + + /* remove all easy handles */ + + free(multi); + + return CURLM_OK; + } + else + return CURLM_BAD_HANDLE; +} CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue); -- cgit v1.2.1 From d52c0b6f059a07c5fc103de16363d7bdbcd2b6f7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 4 Dec 2001 07:47:21 +0000 Subject: more comments --- lib/multi.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e593c0db4..57969d4ee 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -56,7 +56,7 @@ struct Curl_one_easy { #define CURL_MULTI_HANDLE 0x000bab1e -#define GOOD_MULTI_HANDLE(x) ((x) && ((x)->type == CURL_MULTI_HANDLE)) +#define GOOD_MULTI_HANDLE(x) ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE)) #define GOOD_EASY_HANDLE(x) (x) /* This is the struct known as CURLM on the outside */ @@ -229,11 +229,14 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) switch(easy->state) { case CURLM_STATE_INIT: - /* init this transfer */ - easy->result = Curl_init(easy->easy_handle); + /* init this transfer. Hm, uh, I can't think of anything to init + right now, let's skip over to CONNECT at once! + + easy->result = Curl_init(easy->easy_handle); + if(CURLE_OK == easy->result) + */ /* after init, go CONNECT */ - if(CURLE_OK == easy->result) - easy->state = CURLM_STATE_CONNECT; + easy->state = CURLM_STATE_CONNECT; break; case CURLM_STATE_CONNECT: /* connect */ @@ -252,6 +255,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_handle, &done); + /* hm, when we follow redirects, we may need to go back to the CONNECT + state */ /* after the transfer is done, go DONE */ if(TRUE == done) easy->state = CURLM_STATE_DONE; -- cgit v1.2.1 From e66cdacb936976003a4c7a07515c24fe7af918b5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 13 Dec 2001 07:16:27 +0000 Subject: minor changes --- lib/multi.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 57969d4ee..ff9e91fea 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -220,6 +220,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; bool done; + CURLMcode result=CURLM_OK; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -229,28 +230,30 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) switch(easy->state) { case CURLM_STATE_INIT: - /* init this transfer. Hm, uh, I can't think of anything to init - right now, let's skip over to CONNECT at once! - - easy->result = Curl_init(easy->easy_handle); - if(CURLE_OK == easy->result) - */ - /* after init, go CONNECT */ - easy->state = CURLM_STATE_CONNECT; + /* init this transfer. */ + easy->result=Curl_pretransfer(easy->easy_handle); + if(CURLE_OK == easy->result) { + /* after init, go CONNECT */ + easy->state = CURLM_STATE_CONNECT; + result = CURLM_CALL_MULTI_PERFORM; + } break; case CURLM_STATE_CONNECT: /* connect */ easy->result = Curl_connect(easy->easy_handle); /* after connect, go DO */ - if(CURLE_OK == easy->result) + if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_DO; + result = CURLM_CALL_MULTI_PERFORM; + } break; case CURLM_STATE_DO: /* Do the fetch or put request */ easy->result = Curl_do(easy->easy_handle); /* after do, go PERFORM */ - if(CURLE_OK == easy->result) + if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; + } break; case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ @@ -258,8 +261,11 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* hm, when we follow redirects, we may need to go back to the CONNECT state */ /* after the transfer is done, go DONE */ - if(TRUE == done) + if(TRUE == done) { + /* call this even if the readwrite function returned error */ + easy->result = Curl_posttransfer(easy->easy_handle); easy->state = CURLM_STATE_DONE; + } break; case CURLM_STATE_DONE: /* post-transfer command */ -- cgit v1.2.1 From 8b6314ccfbe48bba2cd560812dd1841425f3bd79 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 3 Jan 2002 15:01:22 +0000 Subject: merged the multi-dev branch back into MAIN again --- lib/multi.c | 58 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 15 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ff9e91fea..b7ab209f5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -22,10 +22,16 @@ *****************************************************************************/ #include "setup.h" +#include +#include #include #include "multi.h" /* will become soon */ +#include "urldata.h" +#include "transfer.h" +#include "url.h" + struct Curl_message { /* the 'CURLMsg' is the part that is visible to the external user */ struct CURLMsg extmsg; @@ -48,7 +54,9 @@ struct Curl_one_easy { struct Curl_one_easy *next; struct Curl_one_easy *prev; - CURL *easy_handle; /* this is the easy handle for this unit */ + struct SessionHandle *easy_handle; /* the easy handle for this unit */ + struct connectdata *easy_conn; /* the "unit's" connection */ + CURLMstate state; /* the handle's state */ CURLcode result; /* previous result */ }; @@ -134,7 +142,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* increase the node-counter */ multi->num_easy++; - return CURLM_OK; + return CURLM_CALL_MULTI_PERFORM; } CURLMcode curl_multi_remove_handle(CURLM *multi_handle, @@ -190,23 +198,30 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, and then we must make sure that is done. */ struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; + int this_max_fd=-1; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; + *max_fd = -1; /* so far none! */ + easy=multi->easy.next; while(easy) { switch(easy->state) { - case CURLM_STATE_INIT: - case CURLM_STATE_CONNECT: - case CURLM_STATE_DO: - case CURLM_STATE_DONE: - /* we want curl_multi_perform() to get called, but we don't have any - file descriptors to set */ + default: 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 */ @@ -222,6 +237,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) 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; @@ -239,8 +256,9 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } break; case CURLM_STATE_CONNECT: - /* connect */ - easy->result = Curl_connect(easy->easy_handle); + /* Connect. We get a connection identifier filled in. */ + easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); + /* after connect, go DO */ if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_DO; @@ -249,15 +267,18 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) break; case CURLM_STATE_DO: /* Do the fetch or put request */ - easy->result = Curl_do(easy->easy_handle); + easy->result = Curl_do(&easy->easy_conn); /* after do, go PERFORM */ if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_PERFORM; + if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) { + easy->state = CURLM_STATE_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; + } } break; case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ - easy->result = Curl_readwrite(easy->easy_handle, &done); + easy->result = Curl_readwrite(easy->easy_conn, &done); /* hm, when we follow redirects, we may need to go back to the CONNECT state */ /* after the transfer is done, go DONE */ @@ -265,11 +286,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* call this even if the readwrite function returned error */ easy->result = Curl_posttransfer(easy->easy_handle); easy->state = CURLM_STATE_DONE; + result = CURLM_CALL_MULTI_PERFORM; } break; case CURLM_STATE_DONE: /* post-transfer command */ - easy->result = Curl_done(easy->easy_handle); + easy->result = Curl_done(easy->easy_conn); /* after we have DONE what we're supposed to do, go COMPLETED */ if(CURLE_OK == easy->result) easy->state = CURLM_STATE_COMPLETED; @@ -280,7 +302,10 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* This node should be delinked from the list now and we should post an information message that we are complete. */ break; + default: + return CURLM_INTERNAL_ERROR; } + if((CURLM_STATE_COMPLETED != easy->state) && (CURLE_OK != easy->result)) { /* @@ -289,10 +314,13 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) */ easy->state = CURLM_STATE_COMPLETED; } + else if(CURLM_STATE_COMPLETED != easy->state) + /* this one still lives! */ + (*running_handles)++; easy = easy->next; /* operate on next handle */ } - return CURLM_OK; + return result; } CURLMcode curl_multi_cleanup(CURLM *multi_handle) -- cgit v1.2.1 From 8d7f402efbcace85851c6bb8f6aa2452c15a9595 Mon Sep 17 00:00:00 2001 From: Sterling Hughes Date: Mon, 7 Jan 2002 20:52:32 +0000 Subject: Make cach'ing work with threads now, there are now three cases: - Use a global dns cache (via setting the tentatively named, CURLOPT_DNS_USE_GLOBAL_CACHE option to true) - Use a per-handle dns cache, by default - Use a pooled dns cache when in the "multi" interface --- lib/multi.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b7ab209f5..bfd3949cb 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -82,7 +82,8 @@ struct Curl_multi { struct Curl_message *msgs; /* amount of messages in the queue */ int num_msgs; - + /* Hostname cache */ + curl_hash *hostcache; }; @@ -244,7 +245,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { - switch(easy->state) { case CURLM_STATE_INIT: /* init this transfer. */ @@ -256,6 +256,17 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } break; case CURLM_STATE_CONNECT: + if (Curl_global_host_cache_use(easy->easy_handle)) { + easy->easy_handle->hostcache = Curl_global_host_cache_get(); + } + else { + if (multi->hostcache == NULL) { + multi->hostcache = curl_hash_alloc(7, Curl_freeaddrinfo); + } + + easy->easy_handle->hostcache = multi->hostcache; + } + /* Connect. We get a connection identifier filled in. */ easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); @@ -328,7 +339,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) struct Curl_multi *multi=(struct Curl_multi *)multi_handle; if(GOOD_MULTI_HANDLE(multi)) { multi->type = 0; /* not good anymore */ - + curl_hash_destroy(multi->hostcache); /* remove all easy handles */ free(multi); @@ -341,3 +352,10 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue); +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ -- cgit v1.2.1 From 974f314f5785156af6983675aeb28313cc8ba2ea Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 19 Mar 2002 07:54:55 +0000 Subject: copyright string (year) update --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index bfd3949cb..8734ade28 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2001, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2002, Daniel Stenberg, , et al. * * In order to be useful for every potential user, curl and libcurl are * dual-licensed under the MPL and the MIT/X-derivate licenses. -- cgit v1.2.1 From 67b0f9aacd36f9d205f38679bed7acb7eb7f4aad Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 20 Mar 2002 10:54:17 +0000 Subject: no longer include "multi.h", it comes with the regular curl/curl.h now --- lib/multi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 8734ade28..449469c88 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -26,8 +26,6 @@ #include #include -#include "multi.h" /* will become soon */ - #include "urldata.h" #include "transfer.h" #include "url.h" -- cgit v1.2.1 From 969a25d1b20520e7141c9655d24ff07752856907 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 27 Apr 2002 22:21:51 +0000 Subject: implemented curl_multi_info_read() which I had forgotten before! --- lib/multi.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 90 insertions(+), 9 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 449469c88..a21fc5674 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -30,6 +30,11 @@ #include "transfer.h" #include "url.h" +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + struct Curl_message { /* the 'CURLMsg' is the part that is visible to the external user */ struct CURLMsg extmsg; @@ -77,9 +82,14 @@ struct Curl_multi { int num_easy; /* this is a linked list of posted messages */ - struct Curl_message *msgs; - /* amount of messages in the queue */ - int num_msgs; + struct Curl_message *msgs; /* the messages remain here until the handle is + closed */ + struct Curl_message *lastmsg; /* points to the last entry */ + struct Curl_message *readptr; /* NULL before no one read anything */ + + int num_msgs; /* amount of messages in the queue */ + int num_read; /* amount of read messages */ + /* Hostname cache */ curl_hash *hostcache; }; @@ -259,7 +269,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } else { if (multi->hostcache == NULL) { - multi->hostcache = curl_hash_alloc(7, Curl_freeaddrinfo); + multi->hostcache = Curl_hash_alloc(7, Curl_freeaddrinfo); } easy->easy_handle->hostcache = multi->hostcache; @@ -301,9 +311,34 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) case CURLM_STATE_DONE: /* post-transfer command */ easy->result = Curl_done(easy->easy_conn); - /* after we have DONE what we're supposed to do, go COMPLETED */ - if(CURLE_OK == easy->result) - easy->state = CURLM_STATE_COMPLETED; + + /* after we have DONE what we're supposed to do, go COMPLETED, and + it doesn't matter what the Curl_done() returned! */ + easy->state = CURLM_STATE_COMPLETED; + + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->hostcache = NULL; + + /* now add a node to the Curl_message linked list with this info */ + { + struct Curl_message *msg = (struct Curl_message *) + malloc(sizeof(struct Curl_message)); + msg->extmsg.msg = CURLMSG_DONE; + msg->extmsg.easy_handle = easy->easy_handle; + msg->extmsg.data.result = easy->result; + msg->next=NULL; + + if(multi->lastmsg) { + multi->lastmsg->next = msg; + multi->lastmsg = msg; + } + else { + multi->msgs = msg; + multi->lastmsg = msg; + } + multi->num_msgs++; /* increase message counter */ + + } break; case CURLM_STATE_COMPLETED: /* this is a completed transfer, it is likely to still be connected */ @@ -335,10 +370,30 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) CURLMcode curl_multi_cleanup(CURLM *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + struct Curl_one_easy *nexteasy; + struct Curl_message *msg; + struct Curl_message *nextmsg; + if(GOOD_MULTI_HANDLE(multi)) { multi->type = 0; /* not good anymore */ - curl_hash_destroy(multi->hostcache); + Curl_hash_destroy(multi->hostcache); + /* remove all easy handles */ + easy = multi->easy.next; + while(easy) { + nexteasy=easy->next; + free(easy); + easy = nexteasy; + } + + /* remove all struct Curl_message nodes left */ + msg = multi->msgs; + while(msg) { + nextmsg = msg->next; + free(msg); + msg = nextmsg; + } free(multi); @@ -348,7 +403,33 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) return CURLM_BAD_HANDLE; } -CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue); +CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + if(GOOD_MULTI_HANDLE(multi)) { + CURLMsg *msg; + + if(!multi->readptr && !multi->num_read) + multi->readptr = multi->msgs; + + if(!multi->readptr) { + *msgs_in_queue = 0; + return NULL; + } + + multi->num_read++; + + *msgs_in_queue = multi->num_msgs - multi->num_read; + msg = &multi->readptr->extmsg; + + /* advance read pointer */ + multi->readptr = multi->readptr->next; + + return msg; + } + else + return CURLM_BAD_HANDLE; +} /* * local variables: -- cgit v1.2.1 From 2de00283495677728941a820597f9fdc2e1d4e27 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 2 May 2002 18:07:38 +0000 Subject: make sure the dns cache pointers in the easy handles are NULLed --- lib/multi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a21fc5674..a1111ccd5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -383,6 +383,9 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) easy = multi->easy.next; while(easy) { nexteasy=easy->next; + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->hostcache = NULL; + free(easy); easy = nexteasy; } -- cgit v1.2.1 From 2db0744a7bf18bd85061d1f3b050368ed4fbe32f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 2 May 2002 22:12:14 +0000 Subject: return CURLM_CALL_MULTI_PERFORM in one more case, and check return code from malloc() --- lib/multi.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a1111ccd5..47bf1c005 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -323,6 +323,10 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) { struct Curl_message *msg = (struct Curl_message *) malloc(sizeof(struct Curl_message)); + + if(!msg) + return CURLM_OUT_OF_MEMORY; + msg->extmsg.msg = CURLMSG_DONE; msg->extmsg.easy_handle = easy->easy_handle; msg->extmsg.data.result = easy->result; @@ -339,7 +343,9 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) multi->num_msgs++; /* increase message counter */ } + result = CURLM_CALL_MULTI_PERFORM; break; + case CURLM_STATE_COMPLETED: /* this is a completed transfer, it is likely to still be connected */ @@ -364,6 +370,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy = easy->next; /* operate on next handle */ } + return result; } -- cgit v1.2.1 From 0b898b5a8a1998a529bcab8808540a741dcc7ef7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 3 May 2002 12:40:37 +0000 Subject: fixed return code --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 47bf1c005..e8e079c6a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -438,7 +438,7 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) return msg; } else - return CURLM_BAD_HANDLE; + return NULL; } /* -- cgit v1.2.1 From 775645f29b2386efb386d0ae99fdfdfc1ad7b45c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 28 May 2002 14:45:50 +0000 Subject: Gustaf Hui provided new code that changes how curl_multi_info_read() messages are stored, so that they don't have to be kept around for the multi handle's entire life time. He also made it return failure codes properly which it didn't do before. I made the messages only get stored per easy-handle so that they can be independently killed easier without ruining the "master list". It makes the info_read() function slightly less beautiful as it has to scan for messages to return, but it makes removing individual handles a lot easier and less error prone. --- lib/multi.c | 132 +++++++++++++++++++++++++++--------------------------------- 1 file changed, 60 insertions(+), 72 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e8e079c6a..3fcfb8be0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -62,6 +62,13 @@ struct Curl_one_easy { CURLMstate state; /* the handle's state */ CURLcode result; /* previous result */ + + struct Curl_message *msg; /* A pointer to one single posted message. + Cleanup should be done on this pointer NOT on + the linked list in Curl_multi. This message + will be deleted when this handle is removed + from the multi-handle */ + int msg_num; /* number of messages left in 'msg' to return */ }; @@ -81,14 +88,7 @@ struct Curl_multi { /* This is the amount of entries in the linked list above. */ int num_easy; - /* this is a linked list of posted messages */ - struct Curl_message *msgs; /* the messages remain here until the handle is - closed */ - struct Curl_message *lastmsg; /* points to the last entry */ - struct Curl_message *readptr; /* NULL before no one read anything */ - - int num_msgs; /* amount of messages in the queue */ - int num_read; /* amount of read messages */ + int num_msgs; /* total amount of messages in the easy handles */ /* Hostname cache */ curl_hash *hostcache; @@ -188,6 +188,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* NOTE NOTE NOTE We do not touch the easy handle here! */ + if (easy->msg) + free(easy->msg); free(easy); multi->num_easy--; /* one less to care about now */ @@ -245,6 +247,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) struct Curl_one_easy *easy; bool done; CURLMcode result=CURLM_OK; + struct Curl_message *msg = NULL; *running_handles = 0; /* bump this once for every living handle */ @@ -315,35 +318,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the Curl_done() returned! */ easy->state = CURLM_STATE_COMPLETED; - - /* clear out the usage of the shared DNS cache */ - easy->easy_handle->hostcache = NULL; - - /* now add a node to the Curl_message linked list with this info */ - { - struct Curl_message *msg = (struct Curl_message *) - malloc(sizeof(struct Curl_message)); - - if(!msg) - return CURLM_OUT_OF_MEMORY; - - msg->extmsg.msg = CURLMSG_DONE; - msg->extmsg.easy_handle = easy->easy_handle; - msg->extmsg.data.result = easy->result; - msg->next=NULL; - - if(multi->lastmsg) { - multi->lastmsg->next = msg; - multi->lastmsg = msg; - } - else { - multi->msgs = msg; - multi->lastmsg = msg; - } - multi->num_msgs++; /* increase message counter */ - - } - result = CURLM_CALL_MULTI_PERFORM; break; case CURLM_STATE_COMPLETED: @@ -356,17 +330,37 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) return CURLM_INTERNAL_ERROR; } - if((CURLM_STATE_COMPLETED != easy->state) && - (CURLE_OK != easy->result)) { - /* - * If an error was returned, and we aren't in completed now, - * then we go to completed and consider this transfer aborted. - */ - easy->state = CURLM_STATE_COMPLETED; + if(CURLM_STATE_COMPLETED != easy->state) { + if(CURLE_OK != easy->result) + /* + * If an error was returned, and we aren't in completed state now, + * then we go to completed and consider this transfer aborted. */ + easy->state = CURLM_STATE_COMPLETED; + else + /* this one still lives! */ + (*running_handles)++; + } + + if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->hostcache = NULL; + + /* now add a node to the Curl_message linked list with this info */ + msg = (struct Curl_message *)malloc(sizeof(struct Curl_message)); + + if(!msg) + return CURLM_OUT_OF_MEMORY; + + msg->extmsg.msg = CURLMSG_DONE; + msg->extmsg.easy_handle = easy->easy_handle; + msg->extmsg.data.result = easy->result; + msg->next=NULL; + + easy->msg = msg; + easy->msg_num = 1; /* there is one unread message here */ + + multi->num_msgs++; /* increase message counter */ } - else if(CURLM_STATE_COMPLETED != easy->state) - /* this one still lives! */ - (*running_handles)++; easy = easy->next; /* operate on next handle */ } @@ -379,8 +373,6 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; struct Curl_one_easy *nexteasy; - struct Curl_message *msg; - struct Curl_message *nextmsg; if(GOOD_MULTI_HANDLE(multi)) { multi->type = 0; /* not good anymore */ @@ -393,18 +385,12 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) /* clear out the usage of the shared DNS cache */ easy->easy_handle->hostcache = NULL; + if (easy->msg) + free(easy->msg); free(easy); easy = nexteasy; } - /* remove all struct Curl_message nodes left */ - msg = multi->msgs; - while(msg) { - nextmsg = msg->next; - free(msg); - msg = nextmsg; - } - free(multi); return CURLM_OK; @@ -416,26 +402,28 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; - if(GOOD_MULTI_HANDLE(multi)) { - CURLMsg *msg; - - if(!multi->readptr && !multi->num_read) - multi->readptr = multi->msgs; - if(!multi->readptr) { - *msgs_in_queue = 0; - return NULL; - } + if(GOOD_MULTI_HANDLE(multi)) { + struct Curl_one_easy *easy; - multi->num_read++; + if(!multi->num_msgs) + return NULL; /* no messages left to return */ - *msgs_in_queue = multi->num_msgs - multi->num_read; - msg = &multi->readptr->extmsg; + easy=multi->easy.next; + while(easy) { + if(easy->msg_num) { + easy->msg_num--; + break; + } + easy = easy->next; + } + if(!easy) + return NULL; /* this means internal count confusion really */ - /* advance read pointer */ - multi->readptr = multi->readptr->next; + multi->num_msgs--; + *msgs_in_queue = multi->num_msgs; - return msg; + return &easy->msg->extmsg; } else return NULL; -- cgit v1.2.1 From b47b053e54e4e6cfa49c06435b76190ebd00d860 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 5 Jun 2002 21:29:20 +0000 Subject: Gustaf Hui fixed curl_multi_remove_handle() to prevent a potential crash --- lib/multi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3fcfb8be0..d6ede8235 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -179,6 +179,9 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->hostcache = NULL; + /* make the previous node point to our next */ if(easy->prev) easy->prev->next = easy->next; -- cgit v1.2.1 From 108cb14d1f01deb5e5704c70c0feb8e2fd8d2175 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 5 Aug 2002 17:04:39 +0000 Subject: Make SessionHandle keep record if it is used with the multi interface or the easy interface, it CANNOT be used by a mixture. --- lib/multi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d6ede8235..2e6a408b4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -263,10 +263,13 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) case CURLM_STATE_INIT: /* init this transfer. */ easy->result=Curl_pretransfer(easy->easy_handle); + if(CURLE_OK == easy->result) { /* after init, go CONNECT */ easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; + + easy->easy_handle->state.used_interface = Curl_if_multi; } break; case CURLM_STATE_CONNECT: -- cgit v1.2.1 From cb895ec3356822df72eb91171a1cc63ad1845d93 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 12 Aug 2002 09:43:20 +0000 Subject: Initial fix to make the multi interface return control while waiting for the initial connect to "come through". This should work fine for connect and for FTP-PASV connects. Needs massive testing. --- lib/multi.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 9 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2e6a408b4..0830aa4ae 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -29,6 +29,7 @@ #include "urldata.h" #include "transfer.h" #include "url.h" +#include "connect.h" /* The last #include file should be: */ #ifdef MALLOCDEBUG @@ -43,11 +44,13 @@ struct Curl_message { typedef enum { CURLM_STATE_INIT, - CURLM_STATE_CONNECT, - CURLM_STATE_DO, - CURLM_STATE_PERFORM, - CURLM_STATE_DONE, - CURLM_STATE_COMPLETED, + CURLM_STATE_CONNECT, /* connect has been sent off */ + CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */ + CURLM_STATE_DO, /* send off the request (part 1) */ + CURLM_STATE_DO_MORE, /* send off the request (part 2) */ + CURLM_STATE_PERFORM, /* transfer data */ + CURLM_STATE_DONE, /* post data transfer operation */ + CURLM_STATE_COMPLETED, /* operation complete */ CURLM_STATE_LAST /* not a true state, never use this */ } CURLMstate; @@ -224,6 +227,32 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, switch(easy->state) { default: 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; + int sockfd; + + if(CURLM_STATE_WAITCONNECT == easy->state) { + sockfd = conn->firstsocket; + FD_SET(sockfd, write_fd_set); + } + 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->secondarysocket; + FD_SET(sockfd, write_fd_set); + } + + if(sockfd > *max_fd) + *max_fd = 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 */ @@ -251,6 +280,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) bool done; CURLMcode result=CURLM_OK; struct Curl_message *msg = NULL; + bool connected; *running_handles = 0; /* bump this once for every living handle */ @@ -259,6 +289,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { + +#ifdef MALLOCDEBUG + fprintf(stderr, "HANDLE %p: State: %x\n", + (char *)easy, easy->state); +#endif + switch(easy->state) { case CURLM_STATE_INIT: /* init this transfer. */ @@ -287,23 +323,80 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* Connect. We get a connection identifier filled in. */ easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); - /* after connect, go DO */ + /* after the connect has been sent off, go WAITCONNECT */ if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_DO; + easy->state = CURLM_STATE_WAITCONNECT; result = CURLM_CALL_MULTI_PERFORM; } break; + + case CURLM_STATE_WAITCONNECT: + { + bool connected; + easy->result = Curl_is_connected(easy->easy_conn, + easy->easy_conn->firstsocket, + &connected); + if(connected) + easy->result = Curl_protocol_connect(easy->easy_conn, NULL); + + if(CURLE_OK != easy->result) + /* failure detected */ + break; + + if(connected) { + /* after the connect has completed, go DO */ + easy->state = CURLM_STATE_DO; + result = CURLM_CALL_MULTI_PERFORM; + } + } + break; + case CURLM_STATE_DO: /* Do the fetch or put request */ easy->result = Curl_do(&easy->easy_conn); - /* after do, go PERFORM */ if(CURLE_OK == easy->result) { - if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) { + + /* after do, go PERFORM... or DO_MORE */ + if(easy->easy_conn->do_more) { + /* we're supposed to do more, but we need to sit down, relax + and wait a little while first */ + easy->state = CURLM_STATE_DO_MORE; + result = CURLM_OK; + } + else { + /* we're done with the DO, now PERFORM */ + easy->result = Curl_readwrite_init(easy->easy_conn); + if(CURLE_OK == easy->result) { + easy->state = CURLM_STATE_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; + } + } + } + break; + + case CURLM_STATE_DO_MORE: + /* + * First, check if we really are ready to do more. + */ + easy->result = Curl_is_connected(easy->easy_conn, + easy->easy_conn->secondarysocket, + &connected); + if(connected) { + /* + * When we are connected, DO MORE and then go PERFORM + */ + easy->result = Curl_do_more(easy->easy_conn); + + if(CURLE_OK == easy->result) + easy->result = Curl_readwrite_init(easy->easy_conn); + + if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } break; + case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); -- cgit v1.2.1 From ba4e69bebc8f7f32f3bc7faa1e13e7580754075b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 3 Sep 2002 11:52:59 +0000 Subject: updated source code boilerplate/header --- lib/multi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0830aa4ae..21ed99c74 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1,4 +1,4 @@ -/***************************************************************************** +/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | @@ -7,19 +7,19 @@ * * Copyright (C) 1998 - 2002, Daniel Stenberg, , et al. * - * In order to be useful for every potential user, curl and libcurl are - * dual-licensed under the MPL and the MIT/X-derivate licenses. - * + * 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 MPL or the MIT/X-derivate - * licenses. You may pick one of these licenses. + * 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 "setup.h" #include -- cgit v1.2.1 From 35089a4289747bb4b15f00cf602d28fa3c913fdf Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 23 Sep 2002 12:44:45 +0000 Subject: properly disconnect failed connections --- lib/multi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 21ed99c74..f97345a6f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -339,9 +339,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(connected) easy->result = Curl_protocol_connect(easy->easy_conn, NULL); - if(CURLE_OK != easy->result) + if(CURLE_OK != easy->result) { /* failure detected */ + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ break; + } if(connected) { /* after the connect has completed, go DO */ -- cgit v1.2.1 From 9b296e65bd5aa527d306b37c152057020734979b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 7 Oct 2002 13:38:59 +0000 Subject: Following locations properly, if told to do so. --- lib/multi.c | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f97345a6f..c9861c24a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -403,14 +403,40 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); - /* hm, when we follow redirects, we may need to go back to the CONNECT - state */ + + if(easy->result) { + /* The transfer phase returned error, we mark the connection to get + * closed to prevent being re-used. This is becasue we can't + * possibly know if the connection is in a good shape or not now. */ + easy->easy_conn->bits.close = TRUE; + + if(-1 !=easy->easy_conn->secondarysocket) { + /* if we failed anywhere, we must clean up the secondary socket if + it was used */ + sclose(easy->easy_conn->secondarysocket); + easy->easy_conn->secondarysocket=-1; + } + Curl_posttransfer(easy->easy_handle); + Curl_done(easy->easy_conn); + } + /* after the transfer is done, go DONE */ - if(TRUE == done) { + else if(TRUE == done) { + /* call this even if the readwrite function returned error */ - easy->result = Curl_posttransfer(easy->easy_handle); - easy->state = CURLM_STATE_DONE; - result = CURLM_CALL_MULTI_PERFORM; + Curl_posttransfer(easy->easy_handle); + + /* When we follow redirects, must to go back to the CONNECT state */ + if(easy->easy_conn->newurl) { + easy->result = Curl_follow(easy->easy_handle, + strdup(easy->easy_conn->newurl)); + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_CONNECT; + } + else { + easy->state = CURLM_STATE_DONE; + result = CURLM_CALL_MULTI_PERFORM; + } } break; case CURLM_STATE_DONE: -- cgit v1.2.1 From 203633d34d31ffd2a10ac8ed7a81daed4de31f7a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 18 Oct 2002 15:27:49 +0000 Subject: return call_multi when we follow a location --- lib/multi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c9861c24a..98700742d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -430,8 +430,10 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(easy->easy_conn->newurl) { easy->result = Curl_follow(easy->easy_handle, strdup(easy->easy_conn->newurl)); - if(CURLE_OK == easy->result) + if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_CONNECT; + result = CURLM_CALL_MULTI_PERFORM; + } } else { easy->state = CURLM_STATE_DONE; -- cgit v1.2.1 From 42acb00c815f2cca5bd1e3653c9f3a47e9256572 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 11 Nov 2002 23:03:03 +0000 Subject: moved the bools in the connectdata struct into the substruct named ConnectBits where the other bools already are --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 98700742d..f901f3a0f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -360,7 +360,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(CURLE_OK == easy->result) { /* after do, go PERFORM... or DO_MORE */ - if(easy->easy_conn->do_more) { + if(easy->easy_conn->bits.do_more) { /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ easy->state = CURLM_STATE_DO_MORE; -- cgit v1.2.1 From d64dd779931482c614c7cb71d8f9ad81f2417479 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 28 Nov 2002 15:48:54 +0000 Subject: fix the hash init to call the correct dns cleanup function --- lib/multi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f901f3a0f..8548354bd 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -313,9 +313,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->easy_handle->hostcache = Curl_global_host_cache_get(); } else { - if (multi->hostcache == NULL) { - multi->hostcache = Curl_hash_alloc(7, Curl_freeaddrinfo); - } + if (multi->hostcache == NULL) + multi->hostcache = Curl_hash_alloc(7, Curl_freednsinfo); easy->easy_handle->hostcache = multi->hostcache; } -- cgit v1.2.1 From f26a338a54e04d0a6907f5d2479d8b0fa9daf297 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 16 Jan 2003 21:08:12 +0000 Subject: copyright year update in the source header --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 8548354bd..a9a9d8814 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2002, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2003, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms -- cgit v1.2.1 From a7c72b7abf1213c471f3fd11e6b8e3a37d526f60 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 29 Jan 2003 10:14:20 +0000 Subject: removed the local variables for emacs and vim, use the new sample.emacs way for emacs, and vim users should provide a similar non-polluting style --- lib/multi.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a9a9d8814..7ac984f7f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -557,11 +557,3 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) else return NULL; } - -/* - * local variables: - * eval: (load-file "../curl-mode.el") - * end: - * vim600: fdm=marker - * vim: et sw=2 ts=2 sts=2 tw=78 - */ -- cgit v1.2.1 From 69ab4cd391f2d0b4a60916aa707ac8fc5bc478e1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 14 Feb 2003 09:03:03 +0000 Subject: include to compile the fd_set stuff properly on all systems --- lib/multi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 7ac984f7f..0dd699432 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -24,6 +24,11 @@ #include "setup.h" #include #include + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + #include #include "urldata.h" -- cgit v1.2.1 From afffce80f0f7bd5faf55b88bbe335b080f2df664 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 13 Mar 2003 21:41:02 +0000 Subject: Philippe Raoult needed this to build on FreeBSD --- lib/multi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0dd699432..c15c2026a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -25,6 +25,9 @@ #include #include +#ifdef HAVE_SYS_TYPES_H +#include +#endif #ifdef HAVE_SYS_SOCKET_H #include #endif -- cgit v1.2.1 From 2f9cabc30b6a880470ce17b6338e71a8986667a4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 29 Apr 2003 18:03:30 +0000 Subject: Peter Kovacs provided a patch that makes the CURLINFO_CONNECT_TIME work fine when using the multi interface (too). --- lib/multi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c15c2026a..78763b27f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -38,6 +38,7 @@ #include "transfer.h" #include "url.h" #include "connect.h" +#include "progress.h" /* The last #include file should be: */ #ifdef MALLOCDEBUG @@ -328,6 +329,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } /* Connect. We get a connection identifier filled in. */ + Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); /* after the connect has been sent off, go WAITCONNECT */ @@ -468,11 +470,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } if(CURLM_STATE_COMPLETED != easy->state) { - if(CURLE_OK != easy->result) + if(CURLE_OK != easy->result) { /* * If an error was returned, and we aren't in completed state now, * then we go to completed and consider this transfer aborted. */ easy->state = CURLM_STATE_COMPLETED; + } else /* this one still lives! */ (*running_handles)++; -- cgit v1.2.1 From e727fb82f2cfb7fe01a2d03c7d39af60b23f842f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 13 Jun 2003 06:48:04 +0000 Subject: Marty Kuhrt's #include fixes for VMS --- lib/multi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 78763b27f..3dfccd028 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -31,6 +31,9 @@ #ifdef HAVE_SYS_SOCKET_H #include #endif +#ifdef HAVE_UNISTD_H +#include +#endif #include -- cgit v1.2.1 From 308bc9d919d57388f269c473778ea7f6a331d1c5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 26 Jun 2003 11:22:12 +0000 Subject: use CURLDEBUG instead of MALLOCDEBUG for preprocessor conditions --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3dfccd028..651a47d1b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -44,7 +44,7 @@ #include "progress.h" /* The last #include file should be: */ -#ifdef MALLOCDEBUG +#ifdef CURLDEBUG #include "memdebug.h" #endif @@ -302,7 +302,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { -#ifdef MALLOCDEBUG +#ifdef CURLDEBUG fprintf(stderr, "HANDLE %p: State: %x\n", (char *)easy, easy->state); #endif -- cgit v1.2.1 From 938f1d1da7b6e4a578afbb17b1bb0795138e11fb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 15 Jul 2003 22:46:01 +0000 Subject: Dan Winship's fix to make the new auth stuff such as NTLM to work with the multi interface --- lib/multi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 651a47d1b..f6749f18f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -440,8 +440,9 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* When we follow redirects, must to go back to the CONNECT state */ if(easy->easy_conn->newurl) { - easy->result = Curl_follow(easy->easy_handle, - strdup(easy->easy_conn->newurl)); + char *newurl = easy->easy_conn->newurl; + easy->easy_conn->newurl = NULL; + easy->result = Curl_follow(easy->easy_handle, newurl); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; -- cgit v1.2.1 From b73612392d7afbf9835a119446e2a58e72384b00 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 Aug 2003 14:40:59 +0000 Subject: ares awareness/usage/support added. If configure --enable-ares is used, we build libcurl to use ares for asynch name resolves. --- lib/multi.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f6749f18f..0d2fcf7ff 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -56,7 +56,8 @@ struct Curl_message { typedef enum { CURLM_STATE_INIT, - CURLM_STATE_CONNECT, /* connect has been sent off */ + CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ + CURLM_STATE_WAITRESOLVE, /* we're awaiting the resolve to finalize */ CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */ CURLM_STATE_DO, /* send off the request (part 1) */ CURLM_STATE_DO_MORE, /* send off the request (part 2) */ @@ -239,6 +240,14 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, switch(easy->state) { default: break; + case CURLM_STATE_WAITRESOLVE: + /* waiting for a resolve to complete */ + Curl_multi_ares_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: { @@ -293,6 +302,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) CURLMcode result=CURLM_OK; struct Curl_message *msg = NULL; bool connected; + bool async; *running_handles = 0; /* bump this once for every living handle */ @@ -320,6 +330,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->easy_handle->state.used_interface = Curl_if_multi; } break; + case CURLM_STATE_CONNECT: if (Curl_global_host_cache_use(easy->easy_handle)) { easy->easy_handle->hostcache = Curl_global_host_cache_get(); @@ -333,16 +344,46 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* Connect. We get a connection identifier filled in. */ Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); - easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); + easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, &async); - /* after the connect has been sent off, go WAITCONNECT */ if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_WAITCONNECT; - result = CURLM_CALL_MULTI_PERFORM; + if(async) + /* We're now waiting for an asynchronous name lookup */ + easy->state = CURLM_STATE_WAITRESOLVE; + else { + /* after the connect has been sent off, go WAITCONNECT */ + easy->state = CURLM_STATE_WAITCONNECT; + result = CURLM_CALL_MULTI_PERFORM; + } + } + break; + + case CURLM_STATE_WAITRESOLVE: + /* awaiting an asynch name resolve to complete */ + { + bool done; + + /* check if we have the name resolved by now */ + easy->result = Curl_is_resolved(easy->easy_conn, &done); + + if(done) { + /* Perform the next step in the connection phase, and then move on + to the WAITCONNECT state */ + easy->result = Curl_async_resolved(easy->easy_conn); + + easy->state = CURLM_STATE_WAITCONNECT; + } + + if(CURLE_OK != easy->result) { + /* failure detected */ + easy->easy_conn = NULL; /* no more connection */ + break; + } } break; case CURLM_STATE_WAITCONNECT: + /* awaiting a completion of an asynch connect */ { bool connected; easy->result = Curl_is_connected(easy->easy_conn, -- cgit v1.2.1 From 61629d2c86aacd879cac5e5db619251a5eefdbf3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 20 Aug 2003 13:49:46 +0000 Subject: made curl_multi_info_read() set 'msgs_in_queue' to 0 even when it returns NULL! --- lib/multi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0d2fcf7ff..93891bf04 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -588,6 +588,8 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + *msgs_in_queue = 0; /* default to none */ + if(GOOD_MULTI_HANDLE(multi)) { struct Curl_one_easy *easy; -- cgit v1.2.1 From 0efcb57623189dab5b772ca755841ed7494f04ca Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Sep 2003 22:14:29 +0000 Subject: For easy handles within multi handles, we share the DNS cache always. --- lib/multi.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 93891bf04..ddefa9ec8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -121,7 +121,13 @@ CURLM *curl_multi_init(void) memset(multi, 0, sizeof(struct Curl_multi)); multi->type = CURL_MULTI_HANDLE; } - + + multi->hostcache = Curl_hash_alloc(7, Curl_freednsinfo); + if(!multi->hostcache) { + /* failure, free mem and bail out */ + free(multi); + multi = NULL; + } return (CURLM *) multi; } @@ -150,6 +156,9 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* set the easy handle */ easy->easy_handle = easy_handle; easy->state = CURLM_STATE_INIT; + + /* for multi interface connections, we share DNS cache automaticly */ + easy->easy_handle->hostcache = multi->hostcache; /* We add this new entry first in the list. We make our 'next' point to the previous next and our 'prev' point back to the 'first' struct */ @@ -332,16 +341,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) break; case CURLM_STATE_CONNECT: - if (Curl_global_host_cache_use(easy->easy_handle)) { - easy->easy_handle->hostcache = Curl_global_host_cache_get(); - } - else { - if (multi->hostcache == NULL) - multi->hostcache = Curl_hash_alloc(7, Curl_freednsinfo); - - easy->easy_handle->hostcache = multi->hostcache; - } - /* Connect. We get a connection identifier filled in. */ Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, &async); -- cgit v1.2.1 From 8aa2894bfb9a379ebb47ada638736b00ba46a1ea Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 21 Sep 2003 23:10:47 +0000 Subject: failing to resolve a name using ares must Curl_disconnect() the handle properly or risk getting into trouble! --- lib/multi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ddefa9ec8..a1e10d59b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -375,6 +375,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(CURLE_OK != easy->result) { /* failure detected */ + Curl_disconnect(easy->easy_conn); /* disconnect properly */ easy->easy_conn = NULL; /* no more connection */ break; } -- cgit v1.2.1 From 343291ce37acbeece395734a80f3d7dc771f610f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 4 Oct 2003 14:50:45 +0000 Subject: Based on a patch provided by Siddhartha Prakash Jain. In Curl_resolv() when my_getaddrinfo() has been called (and wait has been set to TRUE), we check if the name already is resolved and if so don't return wait status to the parent. This can happen with IP-only names. --- lib/multi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a1e10d59b..39ab97799 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -360,12 +360,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) case CURLM_STATE_WAITRESOLVE: /* awaiting an asynch name resolve to complete */ { - bool done; + struct Curl_dns_entry *dns; /* check if we have the name resolved by now */ - easy->result = Curl_is_resolved(easy->easy_conn, &done); + easy->result = Curl_is_resolved(easy->easy_conn, &dns); - if(done) { + if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ easy->result = Curl_async_resolved(easy->easy_conn); -- cgit v1.2.1 From 8341e8e50253784940f085f1973c28a1ad84594a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 13 Oct 2003 12:21:56 +0000 Subject: Clear the connection pointer after the async resolve has failed. This cures the problem reported by Giuseppe Attardi on October 12, 2003. --- lib/multi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 39ab97799..c0e4760b8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -370,6 +370,11 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) to the WAITCONNECT state */ easy->result = Curl_async_resolved(easy->easy_conn); + if(CURLE_OK != easy->result) + /* if Curl_async_resolved() returns failure, the connection struct + is already freed and gone */ + easy->easy_conn = NULL; /* no more connection */ + easy->state = CURLM_STATE_WAITCONNECT; } -- cgit v1.2.1 From 381c6c5d520a3250a0385f7f76e872e779d7aaac Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 18 Oct 2003 20:38:18 +0000 Subject: minor fix to not shadow a variable --- lib/multi.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c0e4760b8..a89c2b3de 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -389,26 +389,23 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch connect */ - { - bool connected; - easy->result = Curl_is_connected(easy->easy_conn, - easy->easy_conn->firstsocket, - &connected); - if(connected) - easy->result = Curl_protocol_connect(easy->easy_conn, NULL); + easy->result = Curl_is_connected(easy->easy_conn, + easy->easy_conn->firstsocket, + &connected); + if(connected) + easy->result = Curl_protocol_connect(easy->easy_conn, NULL); - if(CURLE_OK != easy->result) { - /* failure detected */ - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ - break; - } + if(CURLE_OK != easy->result) { + /* failure detected */ + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + break; + } - if(connected) { - /* after the connect has completed, go DO */ - easy->state = CURLM_STATE_DO; - result = CURLM_CALL_MULTI_PERFORM; - } + if(connected) { + /* after the connect has completed, go DO */ + easy->state = CURLM_STATE_DO; + result = CURLM_CALL_MULTI_PERFORM; } break; -- cgit v1.2.1 From 1e98727c552ced5f8c7587f64ab69c6eaab743dd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 24 Nov 2003 07:15:37 +0000 Subject: FTPS support added as RFC2228 and the murray-ftp-auth-ssl draft describe it --- lib/multi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a89c2b3de..54f8ab5da 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -266,7 +266,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, int sockfd; if(CURLM_STATE_WAITCONNECT == easy->state) { - sockfd = conn->firstsocket; + sockfd = conn->sock[FIRSTSOCKET]; FD_SET(sockfd, write_fd_set); } else { @@ -275,7 +275,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, 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->secondarysocket; + sockfd = conn->sock[SECONDARYSOCKET]; FD_SET(sockfd, write_fd_set); } @@ -390,7 +390,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch connect */ easy->result = Curl_is_connected(easy->easy_conn, - easy->easy_conn->firstsocket, + easy->easy_conn->sock[FIRSTSOCKET], &connected); if(connected) easy->result = Curl_protocol_connect(easy->easy_conn, NULL); @@ -437,7 +437,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) * First, check if we really are ready to do more. */ easy->result = Curl_is_connected(easy->easy_conn, - easy->easy_conn->secondarysocket, + easy->easy_conn->sock[SECONDARYSOCKET], &connected); if(connected) { /* @@ -465,11 +465,11 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) * possibly know if the connection is in a good shape or not now. */ easy->easy_conn->bits.close = TRUE; - if(-1 !=easy->easy_conn->secondarysocket) { + if(-1 !=easy->easy_conn->sock[SECONDARYSOCKET]) { /* if we failed anywhere, we must clean up the secondary socket if it was used */ - sclose(easy->easy_conn->secondarysocket); - easy->easy_conn->secondarysocket=-1; + sclose(easy->easy_conn->sock[SECONDARYSOCKET]); + easy->easy_conn->sock[SECONDARYSOCKET]=-1; } Curl_posttransfer(easy->easy_handle); Curl_done(easy->easy_conn); -- cgit v1.2.1 From 053f6c85efd0bf698f73343989474d672d0563a8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 7 Jan 2004 09:19:33 +0000 Subject: updated year in the copyright string --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 54f8ab5da..e7a6a1d5b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2003, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms -- cgit v1.2.1 From 3a61c98b6537b69db9b225caa30ec52fc7101aa6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 12 Jan 2004 15:26:32 +0000 Subject: Peter Sylvester brought code that now allows a callback to modified the URL even when the multi interface is used, and then libcurl will simulate a "follow location" to that new URL. Test 509 was added to test this feature. --- lib/multi.c | 318 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 169 insertions(+), 149 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e7a6a1d5b..a16359ce8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -320,45 +320,62 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { - #ifdef CURLDEBUG fprintf(stderr, "HANDLE %p: State: %x\n", (char *)easy, easy->state); #endif + do { + if (CURLM_STATE_WAITCONNECT <= easy->state && + easy->state <= CURLM_STATE_DO && + easy->easy_handle->change.url_changed) { + char *gotourl; + Curl_posttransfer(easy->easy_handle); - switch(easy->state) { - case CURLM_STATE_INIT: - /* init this transfer. */ - easy->result=Curl_pretransfer(easy->easy_handle); - - if(CURLE_OK == easy->result) { - /* after init, go CONNECT */ - easy->state = CURLM_STATE_CONNECT; - result = CURLM_CALL_MULTI_PERFORM; - - easy->easy_handle->state.used_interface = Curl_if_multi; + gotourl = strdup(easy->easy_handle->change.url); + easy->easy_handle->change.url_changed = FALSE; + easy->result = Curl_follow(easy->easy_handle, gotourl); + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_CONNECT; + else + free(gotourl); } - break; + + easy->easy_handle->change.url_changed = FALSE; - case CURLM_STATE_CONNECT: - /* Connect. We get a connection identifier filled in. */ - Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); - easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, &async); + switch(easy->state) { + case CURLM_STATE_INIT: + /* init this transfer. */ + easy->result=Curl_pretransfer(easy->easy_handle); - if(CURLE_OK == easy->result) { - if(async) - /* We're now waiting for an asynchronous name lookup */ - easy->state = CURLM_STATE_WAITRESOLVE; - else { - /* after the connect has been sent off, go WAITCONNECT */ - easy->state = CURLM_STATE_WAITCONNECT; - result = CURLM_CALL_MULTI_PERFORM; + if(CURLE_OK == easy->result) { + /* after init, go CONNECT */ + easy->state = CURLM_STATE_CONNECT; + result = CURLM_CALL_MULTI_PERFORM; + + easy->easy_handle->state.used_interface = Curl_if_multi; } - } - break; + break; - case CURLM_STATE_WAITRESOLVE: - /* awaiting an asynch name resolve to complete */ + case CURLM_STATE_CONNECT: + /* Connect. We get a connection identifier filled in. */ + Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); + easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, + &async); + + if(CURLE_OK == easy->result) { + if(async) + /* We're now waiting for an asynchronous name lookup */ + easy->state = CURLM_STATE_WAITRESOLVE; + else { + /* after the connect has been sent off, go WAITCONNECT */ + easy->state = CURLM_STATE_WAITCONNECT; + result = CURLM_CALL_MULTI_PERFORM; + } + } + break; + + case CURLM_STATE_WAITRESOLVE: + /* awaiting an asynch name resolve to complete */ { struct Curl_dns_entry *dns; @@ -387,146 +404,149 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } break; - case CURLM_STATE_WAITCONNECT: - /* awaiting a completion of an asynch connect */ - easy->result = Curl_is_connected(easy->easy_conn, - easy->easy_conn->sock[FIRSTSOCKET], - &connected); - if(connected) - easy->result = Curl_protocol_connect(easy->easy_conn, NULL); - - if(CURLE_OK != easy->result) { - /* failure detected */ - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ - break; - } + case CURLM_STATE_WAITCONNECT: + /* awaiting a completion of an asynch connect */ + easy->result = Curl_is_connected(easy->easy_conn, + easy->easy_conn->sock[FIRSTSOCKET], + &connected); + if(connected) + easy->result = Curl_protocol_connect(easy->easy_conn, NULL); - if(connected) { - /* after the connect has completed, go DO */ - easy->state = CURLM_STATE_DO; - result = CURLM_CALL_MULTI_PERFORM; - } - break; + if(CURLE_OK != easy->result) { + /* failure detected */ + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + break; + } - case CURLM_STATE_DO: - /* Do the fetch or put request */ - easy->result = Curl_do(&easy->easy_conn); - if(CURLE_OK == easy->result) { - - /* after do, go PERFORM... or DO_MORE */ - if(easy->easy_conn->bits.do_more) { - /* we're supposed to do more, but we need to sit down, relax - and wait a little while first */ - easy->state = CURLM_STATE_DO_MORE; - result = CURLM_OK; + if(connected) { + /* after the connect has completed, go DO */ + easy->state = CURLM_STATE_DO; + result = CURLM_CALL_MULTI_PERFORM; } - else { - /* we're done with the DO, now PERFORM */ - easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_PERFORM; - result = CURLM_CALL_MULTI_PERFORM; + break; + + case CURLM_STATE_DO: + /* Do the fetch or put request */ + easy->result = Curl_do(&easy->easy_conn); + if(CURLE_OK == easy->result) { + + /* after do, go PERFORM... or DO_MORE */ + if(easy->easy_conn->bits.do_more) { + /* we're supposed to do more, but we need to sit down, relax + and wait a little while first */ + easy->state = CURLM_STATE_DO_MORE; + result = CURLM_OK; + } + else { + /* we're done with the DO, now PERFORM */ + easy->result = Curl_readwrite_init(easy->easy_conn); + if(CURLE_OK == easy->result) { + easy->state = CURLM_STATE_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; + } } } - } - break; + break; - case CURLM_STATE_DO_MORE: - /* - * First, check if we really are ready to do more. - */ - easy->result = Curl_is_connected(easy->easy_conn, - easy->easy_conn->sock[SECONDARYSOCKET], - &connected); - if(connected) { + case CURLM_STATE_DO_MORE: /* - * When we are connected, DO MORE and then go PERFORM + * First, check if we really are ready to do more. */ - easy->result = Curl_do_more(easy->easy_conn); - - if(CURLE_OK == easy->result) - easy->result = Curl_readwrite_init(easy->easy_conn); + easy->result = + Curl_is_connected(easy->easy_conn, + easy->easy_conn->sock[SECONDARYSOCKET], + &connected); + if(connected) { + /* + * When we are connected, DO MORE and then go PERFORM + */ + easy->result = Curl_do_more(easy->easy_conn); + + if(CURLE_OK == easy->result) + easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_PERFORM; - result = CURLM_CALL_MULTI_PERFORM; + if(CURLE_OK == easy->result) { + easy->state = CURLM_STATE_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; + } } - } - break; + break; - case CURLM_STATE_PERFORM: - /* read/write data if it is ready to do so */ - easy->result = Curl_readwrite(easy->easy_conn, &done); - - if(easy->result) { - /* The transfer phase returned error, we mark the connection to get - * closed to prevent being re-used. This is becasue we can't - * possibly know if the connection is in a good shape or not now. */ - easy->easy_conn->bits.close = TRUE; - - if(-1 !=easy->easy_conn->sock[SECONDARYSOCKET]) { - /* if we failed anywhere, we must clean up the secondary socket if - it was used */ - sclose(easy->easy_conn->sock[SECONDARYSOCKET]); - easy->easy_conn->sock[SECONDARYSOCKET]=-1; + case CURLM_STATE_PERFORM: + /* read/write data if it is ready to do so */ + easy->result = Curl_readwrite(easy->easy_conn, &done); + + if(easy->result) { + /* The transfer phase returned error, we mark the connection to get + * closed to prevent being re-used. This is becasue we can't + * possibly know if the connection is in a good shape or not now. */ + easy->easy_conn->bits.close = TRUE; + + if(-1 !=easy->easy_conn->sock[SECONDARYSOCKET]) { + /* if we failed anywhere, we must clean up the secondary socket if + it was used */ + sclose(easy->easy_conn->sock[SECONDARYSOCKET]); + easy->easy_conn->sock[SECONDARYSOCKET]=-1; + } + Curl_posttransfer(easy->easy_handle); + Curl_done(easy->easy_conn); } - Curl_posttransfer(easy->easy_handle); - Curl_done(easy->easy_conn); - } - /* after the transfer is done, go DONE */ - else if(TRUE == done) { - - /* call this even if the readwrite function returned error */ - Curl_posttransfer(easy->easy_handle); - - /* When we follow redirects, must to go back to the CONNECT state */ - if(easy->easy_conn->newurl) { - char *newurl = easy->easy_conn->newurl; - easy->easy_conn->newurl = NULL; - easy->result = Curl_follow(easy->easy_handle, newurl); - if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_CONNECT; - result = CURLM_CALL_MULTI_PERFORM; + /* after the transfer is done, go DONE */ + else if(TRUE == done) { + + /* call this even if the readwrite function returned error */ + Curl_posttransfer(easy->easy_handle); + + /* When we follow redirects, must to go back to the CONNECT state */ + if(easy->easy_conn->newurl) { + char *newurl = easy->easy_conn->newurl; + easy->easy_conn->newurl = NULL; + easy->result = Curl_follow(easy->easy_handle, newurl); + if(CURLE_OK == easy->result) { + easy->state = CURLM_STATE_CONNECT; + result = CURLM_CALL_MULTI_PERFORM; + } + } + else { + easy->state = CURLM_STATE_DONE; + result = CURLM_CALL_MULTI_PERFORM; } } - else { - easy->state = CURLM_STATE_DONE; - result = CURLM_CALL_MULTI_PERFORM; - } - } - break; - case CURLM_STATE_DONE: - /* post-transfer command */ - easy->result = Curl_done(easy->easy_conn); + break; + case CURLM_STATE_DONE: + /* post-transfer command */ + easy->result = Curl_done(easy->easy_conn); - /* after we have DONE what we're supposed to do, go COMPLETED, and - it doesn't matter what the Curl_done() returned! */ - easy->state = CURLM_STATE_COMPLETED; - break; + /* after we have DONE what we're supposed to do, go COMPLETED, and + it doesn't matter what the Curl_done() returned! */ + easy->state = CURLM_STATE_COMPLETED; + break; - case CURLM_STATE_COMPLETED: - /* this is a completed transfer, it is likely to still be connected */ + case CURLM_STATE_COMPLETED: + /* this is a completed transfer, it is likely to still be connected */ - /* This node should be delinked from the list now and we should post - an information message that we are complete. */ - break; - default: - return CURLM_INTERNAL_ERROR; - } + /* This node should be delinked from the list now and we should post + an information message that we are complete. */ + break; + default: + return CURLM_INTERNAL_ERROR; + } - if(CURLM_STATE_COMPLETED != easy->state) { - if(CURLE_OK != easy->result) { - /* - * If an error was returned, and we aren't in completed state now, - * then we go to completed and consider this transfer aborted. */ - easy->state = CURLM_STATE_COMPLETED; + if(CURLM_STATE_COMPLETED != easy->state) { + if(CURLE_OK != easy->result) { + /* + * If an error was returned, and we aren't in completed state now, + * then we go to completed and consider this transfer aborted. */ + easy->state = CURLM_STATE_COMPLETED; + } + else + /* this one still lives! */ + (*running_handles)++; } - else - /* this one still lives! */ - (*running_handles)++; - } + + } while (easy->easy_handle->change.url_changed); if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { /* clear out the usage of the shared DNS cache */ -- cgit v1.2.1 From 4816294f5294bf0efe83aefa07560217d0085a90 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 5 Feb 2004 09:37:04 +0000 Subject: compiler warning fix, compare struct pointers of the same type --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a16359ce8..926506b8a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -196,7 +196,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* scan through the list and remove the 'curl_handle' */ easy = multi->easy.next; while(easy) { - if(easy->easy_handle == curl_handle) + if(easy->easy_handle == (struct SessionHandle *)curl_handle) break; easy=easy->next; } -- cgit v1.2.1 From 465753c2dea28567b54d28eeee05141b3a016829 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 4 Mar 2004 16:13:33 +0000 Subject: When following to a new URL, we must make sure to call Curl_done() first, since the current connection must be taken care of properly before we move on. Christopher R. Palmer reported a problem he found due to this mistake. --- lib/multi.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 926506b8a..83e0b1d8c 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -331,13 +331,16 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) char *gotourl; Curl_posttransfer(easy->easy_handle); - gotourl = strdup(easy->easy_handle->change.url); - easy->easy_handle->change.url_changed = FALSE; - easy->result = Curl_follow(easy->easy_handle, gotourl); - if(CURLE_OK == easy->result) - easy->state = CURLM_STATE_CONNECT; - else - free(gotourl); + easy->result = Curl_done(easy->easy_conn); + if(CURLE_OK == easy->result) { + gotourl = strdup(easy->easy_handle->change.url); + easy->easy_handle->change.url_changed = FALSE; + easy->result = Curl_follow(easy->easy_handle, gotourl); + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_CONNECT; + else + free(gotourl); + } } easy->easy_handle->change.url_changed = FALSE; @@ -503,7 +506,9 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(easy->easy_conn->newurl) { char *newurl = easy->easy_conn->newurl; easy->easy_conn->newurl = NULL; - easy->result = Curl_follow(easy->easy_handle, newurl); + easy->result = Curl_done(easy->easy_conn); + if(easy->result == CURLE_OK) + easy->result = Curl_follow(easy->easy_handle, newurl); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; -- cgit v1.2.1 From ce5805a955c5a79d85792caad47594987f0e0b26 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 Mar 2004 22:52:50 +0000 Subject: Use curl_socket_t instead of int for holding sockets. The typedefs and defines are in setup.h. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 83e0b1d8c..2f038d397 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -263,7 +263,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, /* when we're waiting for a connect, we wait for the socket to become writable */ struct connectdata *conn = easy->easy_conn; - int sockfd; + curl_socket_t sockfd; if(CURLM_STATE_WAITCONNECT == easy->state) { sockfd = conn->sock[FIRSTSOCKET]; -- cgit v1.2.1 From 7225b1400236c786add1516e38676d65a7bbd327 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 10 Mar 2004 16:01:47 +0000 Subject: curl_socket_t mistakes cleanup --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2f038d397..e4ee5c63d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -279,8 +279,8 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, FD_SET(sockfd, write_fd_set); } - if(sockfd > *max_fd) - *max_fd = sockfd; + if((int)sockfd > *max_fd) + *max_fd = (int)sockfd; } break; case CURLM_STATE_PERFORM: -- cgit v1.2.1 From e545e33d5fd4f021220f09cf8fb05c66db2a4bf4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Mar 2004 13:13:35 +0000 Subject: Gisle Vanem's fixes to use CURL_SOCKET_BAD more instead of -1 for sockets. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e4ee5c63d..0da68317a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -486,7 +486,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) * possibly know if the connection is in a good shape or not now. */ easy->easy_conn->bits.close = TRUE; - if(-1 !=easy->easy_conn->sock[SECONDARYSOCKET]) { + if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) { /* if we failed anywhere, we must clean up the secondary socket if it was used */ sclose(easy->easy_conn->sock[SECONDARYSOCKET]); -- cgit v1.2.1 From 6950aeafccbdb493ebc862804a05647d97011387 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 30 Mar 2004 08:14:37 +0000 Subject: init the dns pointer to NULL for clarity --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0da68317a..18bfba2bd 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -380,7 +380,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) case CURLM_STATE_WAITRESOLVE: /* awaiting an asynch name resolve to complete */ { - struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns = NULL; /* check if we have the name resolved by now */ easy->result = Curl_is_resolved(easy->easy_conn, &dns); -- cgit v1.2.1 From 7ea837a18c7d22c790daf2733eaffcc5450b1bd9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 30 Mar 2004 13:02:31 +0000 Subject: adjusted to the new dns cache function to hide more hostip internals --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 18bfba2bd..ddc4b16cb 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -122,7 +122,7 @@ CURLM *curl_multi_init(void) multi->type = CURL_MULTI_HANDLE; } - multi->hostcache = Curl_hash_alloc(7, Curl_freednsinfo); + multi->hostcache = Curl_mk_dnscache(); if(!multi->hostcache) { /* failure, free mem and bail out */ free(multi); -- cgit v1.2.1 From 648e82f05d8bce097f9f81b78d9df40f647f6170 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 26 Apr 2004 07:20:11 +0000 Subject: Major hostip.c cleanup and split into multiple files and easier #ifdef usage. --- lib/multi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ddc4b16cb..7d86d202e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -251,8 +251,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, break; case CURLM_STATE_WAITRESOLVE: /* waiting for a resolve to complete */ - Curl_multi_ares_fdset(easy->easy_conn, read_fd_set, write_fd_set, - &this_max_fd); + Curl_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; @@ -413,7 +412,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->easy_conn->sock[FIRSTSOCKET], &connected); if(connected) - easy->result = Curl_protocol_connect(easy->easy_conn, NULL); + easy->result = Curl_protocol_connect(easy->easy_conn); if(CURLE_OK != easy->result) { /* failure detected */ -- cgit v1.2.1 From bbafb2eb27954c34967f91c705e74cc0c186970d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 11 May 2004 11:30:23 +0000 Subject: curl_global_init_mem() allows the memory functions to be replaced. memory.h is included everywhere for this. --- lib/multi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 7d86d202e..0476b1d19 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -42,11 +42,10 @@ #include "url.h" #include "connect.h" #include "progress.h" +#include "memory.h" /* The last #include file should be: */ -#ifdef CURLDEBUG #include "memdebug.h" -#endif struct Curl_message { /* the 'CURLMsg' is the part that is visible to the external user */ -- cgit v1.2.1 From d60c22572b49cde9b839a59510580df079d2d5e2 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 12 May 2004 12:06:39 +0000 Subject: Curl_done() and the protocol-specific conn->curl_done() functions now all take a CURLcode as a second argument, that is non-zero when Curl_done() is called after an error was returned from Curl_do() (or similar). --- lib/multi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0476b1d19..fe03a5dde 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -329,7 +329,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) char *gotourl; Curl_posttransfer(easy->easy_handle); - easy->result = Curl_done(easy->easy_conn); + easy->result = Curl_done(easy->easy_conn, CURLE_OK); if(CURLE_OK == easy->result) { gotourl = strdup(easy->easy_handle->change.url); easy->easy_handle->change.url_changed = FALSE; @@ -491,7 +491,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->easy_conn->sock[SECONDARYSOCKET]=-1; } Curl_posttransfer(easy->easy_handle); - Curl_done(easy->easy_conn); + Curl_done(easy->easy_conn, easy->result); } /* after the transfer is done, go DONE */ @@ -504,7 +504,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(easy->easy_conn->newurl) { char *newurl = easy->easy_conn->newurl; easy->easy_conn->newurl = NULL; - easy->result = Curl_done(easy->easy_conn); + easy->result = Curl_done(easy->easy_conn, CURLE_OK); if(easy->result == CURLE_OK) easy->result = Curl_follow(easy->easy_handle, newurl); if(CURLE_OK == easy->result) { @@ -520,7 +520,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) break; case CURLM_STATE_DONE: /* post-transfer command */ - easy->result = Curl_done(easy->easy_conn); + easy->result = Curl_done(easy->easy_conn, CURLE_OK); /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the Curl_done() returned! */ -- cgit v1.2.1 From 1c69b15c7c8e019b96bb640d33d6256069a3cfd4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 13 May 2004 15:17:49 +0000 Subject: return on memory alloc fail --- lib/multi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index fe03a5dde..d2a3f9c24 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -120,6 +120,8 @@ CURLM *curl_multi_init(void) memset(multi, 0, sizeof(struct Curl_multi)); multi->type = CURL_MULTI_HANDLE; } + else + return NULL; multi->hostcache = Curl_mk_dnscache(); if(!multi->hostcache) { -- cgit v1.2.1 From de279099e5240115271274b2520cb1fc299f8ec5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 17 May 2004 06:53:41 +0000 Subject: bail out nicely if strdup() returns NULL, removed trailing whitespace --- lib/multi.c | 62 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 29 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d2a3f9c24..66a581f65 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1,8 +1,8 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. @@ -10,7 +10,7 @@ * 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. @@ -71,7 +71,7 @@ struct Curl_one_easy { /* first, two fields for the linked list of these */ struct Curl_one_easy *next; struct Curl_one_easy *prev; - + struct SessionHandle *easy_handle; /* the easy handle for this unit */ struct connectdata *easy_conn; /* the "unit's" connection */ @@ -99,7 +99,7 @@ struct Curl_multi { long type; /* We have a linked list with easy handles */ - struct Curl_one_easy easy; + struct Curl_one_easy easy; /* This is the amount of entries in the linked list above. */ int num_easy; @@ -141,7 +141,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - + /* Verify that we got a somewhat good easy handle too */ if(!GOOD_EASY_HANDLE(easy_handle)) return CURLM_BAD_EASY_HANDLE; @@ -150,7 +150,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy)); if(!easy) return CURLM_OUT_OF_MEMORY; - + /* clean it all first (just to be sure) */ memset(easy, 0, sizeof(struct Curl_one_easy)); @@ -160,11 +160,11 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* for multi interface connections, we share DNS cache automaticly */ easy->easy_handle->hostcache = multi->hostcache; - + /* We add this new entry first in the list. We make our 'next' point to the previous next and our 'prev' point back to the 'first' struct */ easy->next = multi->easy.next; - easy->prev = &multi->easy; + easy->prev = &multi->easy; /* make 'easy' the first node in the chain */ multi->easy.next = easy; @@ -189,7 +189,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - + /* Verify that we got a somewhat good easy handle too */ if(!GOOD_EASY_HANDLE(curl_handle)) return CURLM_BAD_EASY_HANDLE; @@ -207,14 +207,14 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* clear out the usage of the shared DNS cache */ easy->easy_handle->hostcache = NULL; - + /* make the previous node point to our next */ if(easy->prev) easy->prev->next = easy->next; /* make our next point to our previous node */ if(easy->next) easy->next->prev = easy->prev; - + /* NOTE NOTE NOTE We do not touch the easy handle here! */ if (easy->msg) @@ -273,7 +273,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, /* 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 + 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); @@ -334,15 +334,19 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->result = Curl_done(easy->easy_conn, CURLE_OK); if(CURLE_OK == easy->result) { gotourl = strdup(easy->easy_handle->change.url); - easy->easy_handle->change.url_changed = FALSE; - easy->result = Curl_follow(easy->easy_handle, gotourl); - if(CURLE_OK == easy->result) - easy->state = CURLM_STATE_CONNECT; + if(gotourl) { + easy->easy_handle->change.url_changed = FALSE; + easy->result = Curl_follow(easy->easy_handle, gotourl); + if(CURLE_OK == easy->result) + easy->state = CURLM_STATE_CONNECT; + else + free(gotourl); + } else - free(gotourl); + easy->result = CURLE_OUT_OF_MEMORY; } } - + easy->easy_handle->change.url_changed = FALSE; switch(easy->state) { @@ -353,8 +357,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(CURLE_OK == easy->result) { /* after init, go CONNECT */ easy->state = CURLM_STATE_CONNECT; - result = CURLM_CALL_MULTI_PERFORM; - + result = CURLM_CALL_MULTI_PERFORM; + easy->easy_handle->state.used_interface = Curl_if_multi; } break; @@ -397,7 +401,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->state = CURLM_STATE_WAITCONNECT; } - + if(CURLE_OK != easy->result) { /* failure detected */ Curl_disconnect(easy->easy_conn); /* disconnect properly */ @@ -425,7 +429,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(connected) { /* after the connect has completed, go DO */ easy->state = CURLM_STATE_DO; - result = CURLM_CALL_MULTI_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; } break; @@ -446,7 +450,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; - result = CURLM_CALL_MULTI_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; } } } @@ -471,7 +475,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; - result = CURLM_CALL_MULTI_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; } } break; @@ -516,7 +520,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } else { easy->state = CURLM_STATE_DONE; - result = CURLM_CALL_MULTI_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; } } break; @@ -619,7 +623,7 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) if(GOOD_MULTI_HANDLE(multi)) { struct Curl_one_easy *easy; - + if(!multi->num_msgs) return NULL; /* no messages left to return */ -- cgit v1.2.1 From d7cb09bd18f98dd870198e9474f4315f48d28daa Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 17 May 2004 08:04:42 +0000 Subject: better bailing out on memory failure --- lib/multi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 66a581f65..673a0f6cc 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -342,8 +342,11 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) else free(gotourl); } - else + else { easy->result = CURLE_OUT_OF_MEMORY; + easy->state = CURLM_STATE_COMPLETED; + break; + } } } -- cgit v1.2.1 From d70a335dcefbe2930367d45c39a00f1babc1e30b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 17 May 2004 08:07:07 +0000 Subject: new Curl_done() proto --- lib/multi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 673a0f6cc..8d1547d17 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -331,7 +331,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) char *gotourl; Curl_posttransfer(easy->easy_handle); - easy->result = Curl_done(easy->easy_conn, CURLE_OK); + easy->result = Curl_done(&easy->easy_conn, CURLE_OK); if(CURLE_OK == easy->result) { gotourl = strdup(easy->easy_handle->change.url); if(gotourl) { @@ -500,7 +500,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->easy_conn->sock[SECONDARYSOCKET]=-1; } Curl_posttransfer(easy->easy_handle); - Curl_done(easy->easy_conn, easy->result); + Curl_done(&easy->easy_conn, easy->result); } /* after the transfer is done, go DONE */ @@ -513,7 +513,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(easy->easy_conn->newurl) { char *newurl = easy->easy_conn->newurl; easy->easy_conn->newurl = NULL; - easy->result = Curl_done(easy->easy_conn, CURLE_OK); + easy->result = Curl_done(&easy->easy_conn, CURLE_OK); if(easy->result == CURLE_OK) easy->result = Curl_follow(easy->easy_handle, newurl); if(CURLE_OK == easy->result) { @@ -529,7 +529,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) break; case CURLM_STATE_DONE: /* post-transfer command */ - easy->result = Curl_done(easy->easy_conn, CURLE_OK); + easy->result = Curl_done(&easy->easy_conn, CURLE_OK); /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the Curl_done() returned! */ -- cgit v1.2.1 From 6ed5feda2b3fd15c720e04c040e470ecc3cd075c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 29 Jun 2004 11:20:07 +0000 Subject: First attempt at making the multi interface work when connecting to a host that resolves to multiple IP addresses. --- lib/multi.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 8d1547d17..12b987ff0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -320,7 +320,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { -#ifdef CURLDEBUG +#if 0 fprintf(stderr, "HANDLE %p: State: %x\n", (char *)easy, easy->state); #endif @@ -416,8 +416,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch connect */ - easy->result = Curl_is_connected(easy->easy_conn, - easy->easy_conn->sock[FIRSTSOCKET], + easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET, &connected); if(connected) easy->result = Curl_protocol_connect(easy->easy_conn); @@ -463,10 +462,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* * First, check if we really are ready to do more. */ - easy->result = - Curl_is_connected(easy->easy_conn, - easy->easy_conn->sock[SECONDARYSOCKET], - &connected); + easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET, + &connected); if(connected) { /* * When we are connected, DO MORE and then go PERFORM -- cgit v1.2.1 From 92637303db99a73f260413fdaf97da4e5c8f4a61 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 31 Aug 2004 06:04:43 +0000 Subject: fix the return code for curl_multi_add_handle() --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 12b987ff0..72e953eb1 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -177,7 +177,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* increase the node-counter */ multi->num_easy++; - return CURLM_CALL_MULTI_PERFORM; + return CURLM_OK; } CURLMcode curl_multi_remove_handle(CURLM *multi_handle, -- cgit v1.2.1 From 21bb852750d39a51d20e29652d29b0be5fdbef38 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 10 Jan 2005 10:07:07 +0000 Subject: Pavel Orehov reported memory problems with the multi interface in bug report #1098843. In short, a shared DNS cache was setup for a multi handle and when the shared cache was deleted before the individual easy handles, the latter cleanups caused read/writes to already freed memory. --- lib/multi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 72e953eb1..63eb505f5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -43,6 +43,7 @@ #include "connect.h" #include "progress.h" #include "memory.h" +#include "easy.h" /* The last #include file should be: */ #include "memdebug.h" @@ -174,6 +175,8 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(easy->next) easy->next->prev = easy; + Curl_easy_addmulti(easy_handle, multi_handle); + /* increase the node-counter */ multi->num_easy++; @@ -584,6 +587,13 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) return result; } +/* This is called when an easy handle is cleanup'ed that is part of a multi + handle */ +void Curl_multi_rmeasy(void *multi_handle, CURL *easy_handle) +{ + curl_multi_remove_handle(multi_handle, easy_handle); +} + CURLMcode curl_multi_cleanup(CURLM *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; @@ -600,6 +610,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) nexteasy=easy->next; /* clear out the usage of the shared DNS cache */ easy->easy_handle->hostcache = NULL; + easy->easy_handle->multi = NULL; if (easy->msg) free(easy->msg); -- cgit v1.2.1 From 065e466f1a8b947770562ec98bd10ad3ed143d49 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 10 Jan 2005 11:42:20 +0000 Subject: Use Curl_easy_addmulti() to clear associations from easy handles to multi handles. Include multi.h to get proto. --- lib/multi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 63eb505f5..2822b16a4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -44,6 +44,7 @@ #include "progress.h" #include "memory.h" #include "easy.h" +#include "multi.h" /* The last #include file should be: */ #include "memdebug.h" @@ -210,6 +211,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* clear out the usage of the shared DNS cache */ easy->easy_handle->hostcache = NULL; + Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association + to this multi handle */ /* make the previous node point to our next */ if(easy->prev) @@ -610,7 +613,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) nexteasy=easy->next; /* clear out the usage of the shared DNS cache */ easy->easy_handle->hostcache = NULL; - easy->easy_handle->multi = NULL; + Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */ if (easy->msg) free(easy->msg); -- cgit v1.2.1 From 29102befa66e009c668d6a51cc41051a273d4703 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 11 Jan 2005 14:00:45 +0000 Subject: Cyrill Osterwalder posted a detailed analysis about a bug that occurs when using a custom Host: header and curl fails to send a request on a re-used persistent connection and thus creates a new connection and resends it. It then sent two Host: headers. Cyrill's analysis was posted here: http://curl.haxx.se/mail/archive-2005-01/0022.html --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2822b16a4..6d037f098 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -342,7 +342,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) gotourl = strdup(easy->easy_handle->change.url); if(gotourl) { easy->easy_handle->change.url_changed = FALSE; - easy->result = Curl_follow(easy->easy_handle, gotourl); + easy->result = Curl_follow(easy->easy_handle, gotourl, FALSE); if(CURLE_OK == easy->result) easy->state = CURLM_STATE_CONNECT; else @@ -518,7 +518,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->easy_conn->newurl = NULL; easy->result = Curl_done(&easy->easy_conn, CURLE_OK); if(easy->result == CURLE_OK) - easy->result = Curl_follow(easy->easy_handle, newurl); + easy->result = Curl_follow(easy->easy_handle, newurl, FALSE); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; -- cgit v1.2.1 From e3fa7d021e4f37db40229a7c8f93296d4132e4db Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 11 Jan 2005 15:25:29 +0000 Subject: Renamed easy.h and multi.h to easyif.h and multiif.h to make sure they don't shadow our public headers with the former names. --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 6d037f098..a2b230ff5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -43,8 +43,8 @@ #include "connect.h" #include "progress.h" #include "memory.h" -#include "easy.h" -#include "multi.h" +#include "easyif.h" +#include "multiif.h" /* The last #include file should be: */ #include "memdebug.h" -- cgit v1.2.1 From 043d70fcdfa50c93dc826069aa5836206f8e3e2d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 25 Jan 2005 00:06:29 +0000 Subject: Use plain structs and not typedef'ed ones in the hash and linked-list code. --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a2b230ff5..62bffec45 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -108,7 +108,7 @@ struct Curl_multi { int num_msgs; /* total amount of messages in the easy handles */ /* Hostname cache */ - curl_hash *hostcache; + struct curl_hash *hostcache; }; -- cgit v1.2.1 From 59b45a90cc04695d862d59d1d84126ad6cdf5f59 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 29 Jan 2005 12:01:20 +0000 Subject: multi interface: when a request is denied due to "Maximum redirects followed" libcurl leaked the last Location: URL. --- lib/multi.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 62bffec45..f569c281a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -523,6 +523,10 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; } + else + /* Since we "took it", we are in charge of freeing this on + failure */ + free(newurl); } else { easy->state = CURLM_STATE_DONE; -- cgit v1.2.1 From 8dbaf534c89764100028137c3633f9fc6d38a5bd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 29 Jan 2005 22:31:06 +0000 Subject: Using the multi interface, and doing a requsted a re-used connection that gets closed just after the request has been sent failed and did not re-issue a request on a fresh reconnect like the easy interface did. Now it does! (define CURL_MULTIEASY, run test case 160) --- lib/multi.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f569c281a..e4773d06d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -506,19 +506,24 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) Curl_done(&easy->easy_conn, easy->result); } - /* after the transfer is done, go DONE */ else if(TRUE == done) { + char *newurl; + bool retry = Curl_retry_request(easy->easy_conn, &newurl); /* call this even if the readwrite function returned error */ Curl_posttransfer(easy->easy_handle); /* When we follow redirects, must to go back to the CONNECT state */ - if(easy->easy_conn->newurl) { - char *newurl = easy->easy_conn->newurl; - easy->easy_conn->newurl = NULL; + if(easy->easy_conn->newurl || retry) { + if(!retry) { + /* if the URL is a follow-location and not just a retried request + then figure out the URL here */ + newurl = easy->easy_conn->newurl; + easy->easy_conn->newurl = NULL; + } easy->result = Curl_done(&easy->easy_conn, CURLE_OK); if(easy->result == CURLE_OK) - easy->result = Curl_follow(easy->easy_handle, newurl, FALSE); + easy->result = Curl_follow(easy->easy_handle, newurl, retry); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; @@ -529,6 +534,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) free(newurl); } else { + /* after the transfer is done, go DONE */ easy->state = CURLM_STATE_DONE; result = CURLM_CALL_MULTI_PERFORM; } -- cgit v1.2.1 From 686d767053e413feacb95fb7ddd9b3883984a3cb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 30 Jan 2005 22:54:06 +0000 Subject: if the DO operation returns failure, bail out and close down nicely to prevent memory leakage --- lib/multi.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e4773d06d..159e51e53 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -462,6 +462,13 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } } } + else { + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result); + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + } break; case CURLM_STATE_DO_MORE: -- cgit v1.2.1 From 6a2e21ec8cbaf7c719902e06953d9dbec629ad4f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 9 Feb 2005 13:06:40 +0000 Subject: FTP code turned into state machine. Not completely yet, but a good start. The tag 'before_ftp_statemachine' was set just before this commit in case of future need. --- lib/multi.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 154 insertions(+), 37 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 159e51e53..643f6362f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -45,6 +45,7 @@ #include "memory.h" #include "easyif.h" #include "multiif.h" +#include "sendf.h" /* The last #include file should be: */ #include "memdebug.h" @@ -56,11 +57,14 @@ struct Curl_message { }; typedef enum { - CURLM_STATE_INIT, + CURLM_STATE_INIT, /* start in this state */ CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ - CURLM_STATE_WAITRESOLVE, /* we're awaiting the resolve to finalize */ - CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */ - CURLM_STATE_DO, /* send off the request (part 1) */ + CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */ + CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */ + CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect + phase */ + CURLM_STATE_DO, /* start send off the request (part 1) */ + CURLM_STATE_DOING, /* sending off the request (part 1) */ CURLM_STATE_DO_MORE, /* send off the request (part 2) */ CURLM_STATE_PERFORM, /* transfer data */ CURLM_STATE_DONE, /* post data transfer operation */ @@ -111,6 +115,33 @@ struct Curl_multi { struct curl_hash *hostcache; }; +/* always use this function to change state, to make debugging easier */ +static void multistate(struct Curl_one_easy *easy, CURLMstate state) +{ +#ifdef CURLDEBUG + const char *statename[]={ + "INIT", + "CONNECT", + "WAITRESOLVE", + "WAITCONNECT", + "PROTOCONNECT", + "DO", + "DOING", + "DO_MORE", + "PERFORM", + "DONE", + "COMPLETED", + }; + CURLMstate oldstate = easy->state; +#endif + easy->state = state; + +#ifdef CURLDEBUG + infof(easy->easy_handle, + "STATE: %s => %s handle %p: \n", + statename[oldstate], statename[easy->state], (char *)easy); +#endif +} CURLM *curl_multi_init(void) { @@ -158,7 +189,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* set the easy handle */ easy->easy_handle = easy_handle; - easy->state = CURLM_STATE_INIT; + multistate(easy, CURLM_STATE_INIT); /* for multi interface connections, we share DNS cache automaticly */ easy->easy_handle->hostcache = multi->hostcache; @@ -258,7 +289,22 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, break; case CURLM_STATE_WAITRESOLVE: /* waiting for a resolve to complete */ - Curl_fdset(easy->easy_conn, read_fd_set, write_fd_set, &this_max_fd); + 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; + + 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; + + 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; @@ -318,6 +364,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) struct Curl_message *msg = NULL; bool connected; bool async; + bool protocol_connect; + bool dophase_done; *running_handles = 0; /* bump this once for every living handle */ @@ -326,10 +374,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { -#if 0 - fprintf(stderr, "HANDLE %p: State: %x\n", - (char *)easy, easy->state); -#endif do { if (CURLM_STATE_WAITCONNECT <= easy->state && easy->state <= CURLM_STATE_DO && @@ -344,13 +388,13 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->easy_handle->change.url_changed = FALSE; easy->result = Curl_follow(easy->easy_handle, gotourl, FALSE); if(CURLE_OK == easy->result) - easy->state = CURLM_STATE_CONNECT; + multistate(easy, CURLM_STATE_CONNECT); else free(gotourl); } else { easy->result = CURLE_OUT_OF_MEMORY; - easy->state = CURLM_STATE_COMPLETED; + multistate(easy, CURLM_STATE_COMPLETED); break; } } @@ -365,7 +409,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(CURLE_OK == easy->result) { /* after init, go CONNECT */ - easy->state = CURLM_STATE_CONNECT; + multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; easy->easy_handle->state.used_interface = Curl_if_multi; @@ -376,16 +420,22 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* Connect. We get a connection identifier filled in. */ Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, - &async); + &async, &protocol_connect); if(CURLE_OK == easy->result) { if(async) /* We're now waiting for an asynchronous name lookup */ - easy->state = CURLM_STATE_WAITRESOLVE; + multistate(easy, CURLM_STATE_WAITRESOLVE); else { - /* after the connect has been sent off, go WAITCONNECT */ - easy->state = CURLM_STATE_WAITCONNECT; + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to + DO! */ result = CURLM_CALL_MULTI_PERFORM; + + if(protocol_connect) + multistate(easy, CURLM_STATE_DO); + else + multistate(easy, CURLM_STATE_WAITCONNECT); } } break; @@ -401,14 +451,17 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ - easy->result = Curl_async_resolved(easy->easy_conn); + easy->result = Curl_async_resolved(easy->easy_conn, + &protocol_connect); if(CURLE_OK != easy->result) /* if Curl_async_resolved() returns failure, the connection struct is already freed and gone */ easy->easy_conn = NULL; /* no more connection */ - - easy->state = CURLM_STATE_WAITCONNECT; + else { + /* FIX: what if protocol_connect is TRUE here?! */ + multistate(easy, CURLM_STATE_WAITCONNECT); + } } if(CURLE_OK != easy->result) { @@ -425,7 +478,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET, &connected); if(connected) - easy->result = Curl_protocol_connect(easy->easy_conn); + easy->result = Curl_protocol_connect(easy->easy_conn, + &protocol_connect); if(CURLE_OK != easy->result) { /* failure detected */ @@ -435,29 +489,64 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } if(connected) { + if(!protocol_connect) { + /* We have a TCP connection, but 'protocol_connect' may be false + and then we continue to 'STATE_PROTOCONNECT'. If protocol + connect is TRUE, we move on to STATE_DO. */ + multistate(easy, CURLM_STATE_PROTOCONNECT); + fprintf(stderr, "WAITCONNECT => PROTOCONNECT\n"); + } + else { + /* after the connect has completed, go DO */ + multistate(easy, CURLM_STATE_DO); + result = CURLM_CALL_MULTI_PERFORM; + } + } + break; + + case CURLM_STATE_PROTOCONNECT: + /* protocol-specific connect phase */ + easy->result = Curl_protocol_connecting(easy->easy_conn, + &protocol_connect); + if(protocol_connect) { /* after the connect has completed, go DO */ - easy->state = CURLM_STATE_DO; + multistate(easy, CURLM_STATE_DO); result = CURLM_CALL_MULTI_PERFORM; } + else if(easy->result) { + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result); + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + } break; case CURLM_STATE_DO: - /* Do the fetch or put request */ - easy->result = Curl_do(&easy->easy_conn); + /* Perform the protocol's DO action */ + easy->result = Curl_do(&easy->easy_conn, &dophase_done); + if(CURLE_OK == easy->result) { - /* after do, go PERFORM... or DO_MORE */ - if(easy->easy_conn->bits.do_more) { + if(!dophase_done) { + /* DO was not completed in one function call, we must continue + DOING... */ + multistate(easy, CURLM_STATE_DOING); + result = CURLM_OK; + } + + /* after DO, go PERFORM... or DO_MORE */ + else if(easy->easy_conn->bits.do_more) { /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ - easy->state = CURLM_STATE_DO_MORE; + multistate(easy, CURLM_STATE_DO_MORE); result = CURLM_OK; } else { /* we're done with the DO, now PERFORM */ easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_PERFORM; + multistate(easy, CURLM_STATE_PERFORM); result = CURLM_CALL_MULTI_PERFORM; } } @@ -471,10 +560,39 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } break; + case CURLM_STATE_DOING: + /* we continue DOING until the DO phase is complete */ + easy->result = Curl_protocol_doing(easy->easy_conn, &dophase_done); + if(CURLE_OK == easy->result) { + if(dophase_done) { + /* after DO, go PERFORM... or DO_MORE */ + if(easy->easy_conn->bits.do_more) { + /* we're supposed to do more, but we need to sit down, relax + and wait a little while first */ + multistate(easy, CURLM_STATE_DO_MORE); + result = CURLM_OK; + } + else { + /* we're done with the DO, now PERFORM */ + easy->result = Curl_readwrite_init(easy->easy_conn); + if(CURLE_OK == easy->result) { + multistate(easy, CURLM_STATE_PERFORM); + result = CURLM_CALL_MULTI_PERFORM; + } + } + } /* dophase_done */ + } + else { + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result); + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + } + break; + case CURLM_STATE_DO_MORE: - /* - * First, check if we really are ready to do more. - */ + /* Ready to do more? */ easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET, &connected); if(connected) { @@ -487,7 +605,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_PERFORM; + multistate(easy, CURLM_STATE_PERFORM); result = CURLM_CALL_MULTI_PERFORM; } } @@ -532,7 +650,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if(easy->result == CURLE_OK) easy->result = Curl_follow(easy->easy_handle, newurl, retry); if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_CONNECT; + multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; } else @@ -542,7 +660,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } else { /* after the transfer is done, go DONE */ - easy->state = CURLM_STATE_DONE; + multistate(easy, CURLM_STATE_DONE); result = CURLM_CALL_MULTI_PERFORM; } } @@ -553,7 +671,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the Curl_done() returned! */ - easy->state = CURLM_STATE_COMPLETED; + multistate(easy, CURLM_STATE_COMPLETED); break; case CURLM_STATE_COMPLETED: @@ -571,7 +689,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* * If an error was returned, and we aren't in completed state now, * then we go to completed and consider this transfer aborted. */ - easy->state = CURLM_STATE_COMPLETED; + multistate(easy, CURLM_STATE_COMPLETED); } else /* this one still lives! */ @@ -600,7 +718,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) multi->num_msgs++; /* increase message counter */ } - easy = easy->next; /* operate on next handle */ } -- cgit v1.2.1 From b7ffc6bb451c1a5f6689db707bc2555167fc6231 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 8 Mar 2005 22:21:59 +0000 Subject: remove old printf() debug leftover --- lib/multi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 643f6362f..a60e1f5a9 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -494,7 +494,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) and then we continue to 'STATE_PROTOCONNECT'. If protocol connect is TRUE, we move on to STATE_DO. */ multistate(easy, CURLM_STATE_PROTOCONNECT); - fprintf(stderr, "WAITCONNECT => PROTOCONNECT\n"); } else { /* after the connect has completed, go DO */ -- cgit v1.2.1 From 87bcb6f3775a437579c7526d0972c714c2e5d31d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 11 Feb 2006 22:35:16 +0000 Subject: Karl M added the CURLOPT_CONNECT_ONLY and CURLINFO_LASTSOCKET options that an app can use to let libcurl only connect to a remote host and then extract the socket from libcurl. libcurl will then not attempt to do any transfer at all after the connect is done. --- lib/multi.c | 67 +++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 29 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a60e1f5a9..a7d1988d6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -522,41 +522,50 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) break; case CURLM_STATE_DO: - /* Perform the protocol's DO action */ - easy->result = Curl_do(&easy->easy_conn, &dophase_done); + if(easy->easy_handle->set.connect_only) { + /* keep connection open for application to use the socket */ + easy->easy_conn->bits.close = FALSE; + multistate(easy, CURLM_STATE_DONE); + easy->result = CURLE_OK; + result = CURLM_OK; + } + else { + /* Perform the protocol's DO action */ + easy->result = Curl_do(&easy->easy_conn, &dophase_done); - if(CURLE_OK == easy->result) { + if(CURLE_OK == easy->result) { - if(!dophase_done) { - /* DO was not completed in one function call, we must continue - DOING... */ - multistate(easy, CURLM_STATE_DOING); - result = CURLM_OK; - } + if(!dophase_done) { + /* DO was not completed in one function call, we must continue + DOING... */ + multistate(easy, CURLM_STATE_DOING); + result = CURLM_OK; + } - /* after DO, go PERFORM... or DO_MORE */ - else if(easy->easy_conn->bits.do_more) { - /* we're supposed to do more, but we need to sit down, relax - and wait a little while first */ - multistate(easy, CURLM_STATE_DO_MORE); - result = CURLM_OK; + /* after DO, go PERFORM... or DO_MORE */ + else if(easy->easy_conn->bits.do_more) { + /* we're supposed to do more, but we need to sit down, relax + and wait a little while first */ + multistate(easy, CURLM_STATE_DO_MORE); + result = CURLM_OK; + } + else { + /* we're done with the DO, now PERFORM */ + easy->result = Curl_readwrite_init(easy->easy_conn); + if(CURLE_OK == easy->result) { + multistate(easy, CURLM_STATE_PERFORM); + result = CURLM_CALL_MULTI_PERFORM; + } + } } else { - /* we're done with the DO, now PERFORM */ - easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_PERFORM); - result = CURLM_CALL_MULTI_PERFORM; - } + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result); + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ } } - else { - /* failure detected */ - Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ - } break; case CURLM_STATE_DOING: -- cgit v1.2.1 From 6fdbb011948cc9fd2cadff04b230427cf02dbd7d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 23 Feb 2006 12:20:48 +0000 Subject: Lots of work and analysis by "xbx___" in bug #1431750 (http://curl.haxx.se/bug/view.cgi?id=1431750) helped me identify and fix two different but related bugs: 1) Removing an easy handle from a multi handle before the transfer is done could leave a connection in the connection cache for that handle that is in a state that isn't suitable for re-use. A subsequent re-use could then read from a NULL pointer and segfault. 2) When an easy handle was removed from the multi handle, there could be an outstanding c-ares DNS name resolve request. When the response arrived, it caused havoc since the connection struct it "belonged" to could've been freed already. Now Curl_done() is called when an easy handle is removed from a multi handle pre-maturely (that is, before the transfer was complteted). Curl_done() also makes sure to cancel all (if any) outstanding c-ares requests. --- lib/multi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a7d1988d6..6213fede4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -92,10 +92,10 @@ struct Curl_one_easy { int msg_num; /* number of messages left in 'msg' to return */ }; - #define CURL_MULTI_HANDLE 0x000bab1e -#define GOOD_MULTI_HANDLE(x) ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE)) +#define GOOD_MULTI_HANDLE(x) \ + ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE)) #define GOOD_EASY_HANDLE(x) (x) /* This is the struct known as CURLM on the outside */ @@ -245,6 +245,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association to this multi handle */ + Curl_done(&easy->easy_conn, easy->result); + /* make the previous node point to our next */ if(easy->prev) easy->prev->next = easy->next; -- cgit v1.2.1 From 4486d336a65b871acd60bb650b65d8afc1177167 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 23 Feb 2006 21:29:48 +0000 Subject: argh, forgot the check for a connection before we call Curl_done --- lib/multi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 6213fede4..f914761bb 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -245,7 +245,10 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association to this multi handle */ - Curl_done(&easy->easy_conn, easy->result); + /* if we have a connection we must call Curl_done() here so that we + don't leave a half-baked one around */ + if(easy->easy_conn) + Curl_done(&easy->easy_conn, easy->result); /* make the previous node point to our next */ if(easy->prev) -- cgit v1.2.1 From 686d90745be4417127050ad4b36d0a5403def200 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 10 Apr 2006 15:00:53 +0000 Subject: First curl_multi_socket() commit. Should primarily be considered as an internal code rearrange to fit the future better. --- lib/multi.c | 1285 ++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 909 insertions(+), 376 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f914761bb..2eed1b2b3 100644 --- a/lib/multi.c +++ b/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 } -CURLM *curl_multi_init(void) +/* + * 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_multi *multi; + 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; + } + } - multi = (void *)malloc(sizeof(struct Curl_multi)); + /* not present, add it */ + check = calloc(sizeof(struct Curl_sh_entry), 1); + if(!check) + return 1; /* major failure */ + check->easy = data; - if(multi) { - memset(multi, 0, sizeof(struct Curl_multi)); - multi->type = CURL_MULTI_HANDLE; + 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 + 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 = (void *)calloc(sizeof(struct Curl_multi), 1); + + 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,319 +516,241 @@ 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; - - 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; - - 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; + bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE); - 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); - } - 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); - } + for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { + curl_socket_t s = CURL_SOCKET_BAD; - if((int)sockfd > *max_fd) - *max_fd = (int)sockfd; + 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 { + if(s > this_max_fd) + this_max_fd = s; } - 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; - - *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 && - easy->easy_handle->change.url_changed) { - char *gotourl; - Curl_posttransfer(easy->easy_handle); - - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); - if(CURLE_OK == easy->result) { - gotourl = strdup(easy->easy_handle->change.url); - if(gotourl) { - easy->easy_handle->change.url_changed = FALSE; - easy->result = Curl_follow(easy->easy_handle, gotourl, FALSE); - if(CURLE_OK == easy->result) - multistate(easy, CURLM_STATE_CONNECT); - else - free(gotourl); - } - else { - easy->result = CURLE_OUT_OF_MEMORY; - multistate(easy, CURLM_STATE_COMPLETED); - break; - } + bool done; + CURLMcode result = CURLM_OK; + + do { + if (CURLM_STATE_WAITCONNECT <= easy->state && + easy->state <= CURLM_STATE_DO && + easy->easy_handle->change.url_changed) { + char *gotourl; + Curl_posttransfer(easy->easy_handle); + + easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + if(CURLE_OK == easy->result) { + gotourl = strdup(easy->easy_handle->change.url); + if(gotourl) { + easy->easy_handle->change.url_changed = FALSE; + easy->result = Curl_follow(easy->easy_handle, gotourl, FALSE); + if(CURLE_OK == easy->result) + multistate(easy, CURLM_STATE_CONNECT); + else + free(gotourl); } - } - - easy->easy_handle->change.url_changed = FALSE; - - switch(easy->state) { - case CURLM_STATE_INIT: - /* init this transfer. */ - easy->result=Curl_pretransfer(easy->easy_handle); - - if(CURLE_OK == easy->result) { - /* after init, go CONNECT */ - multistate(easy, CURLM_STATE_CONNECT); - result = CURLM_CALL_MULTI_PERFORM; - - easy->easy_handle->state.used_interface = Curl_if_multi; + else { + easy->result = CURLE_OUT_OF_MEMORY; + multistate(easy, CURLM_STATE_COMPLETED); + break; } - break; + } + } - case CURLM_STATE_CONNECT: - /* Connect. We get a connection identifier filled in. */ - Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); - easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, - &async, &protocol_connect); + easy->easy_handle->change.url_changed = FALSE; - if(CURLE_OK == easy->result) { - if(async) - /* We're now waiting for an asynchronous name lookup */ - multistate(easy, CURLM_STATE_WAITRESOLVE); - else { - /* after the connect has been sent off, go WAITCONNECT unless the - protocol connect is already done and we can go directly to - DO! */ - result = CURLM_CALL_MULTI_PERFORM; + switch(easy->state) { + case CURLM_STATE_INIT: + /* init this transfer. */ + easy->result=Curl_pretransfer(easy->easy_handle); - if(protocol_connect) - multistate(easy, CURLM_STATE_DO); - else - multistate(easy, CURLM_STATE_WAITCONNECT); - } - } - break; + if(CURLE_OK == easy->result) { + /* after init, go CONNECT */ + multistate(easy, CURLM_STATE_CONNECT); + result = CURLM_CALL_MULTI_PERFORM; - case CURLM_STATE_WAITRESOLVE: - /* awaiting an asynch name resolve to complete */ - { - struct Curl_dns_entry *dns = NULL; + easy->easy_handle->state.used_interface = Curl_if_multi; + } + break; - /* check if we have the name resolved by now */ - easy->result = Curl_is_resolved(easy->easy_conn, &dns); + case CURLM_STATE_CONNECT: + /* Connect. We get a connection identifier filled in. */ + Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); + easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, + &async, &protocol_connect); - if(dns) { - /* Perform the next step in the connection phase, and then move on - to the WAITCONNECT state */ - easy->result = Curl_async_resolved(easy->easy_conn, - &protocol_connect); + if(CURLE_OK == easy->result) { + if(async) + /* We're now waiting for an asynchronous name lookup */ + multistate(easy, CURLM_STATE_WAITRESOLVE); + else { + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to + DO! */ + result = CURLM_CALL_MULTI_PERFORM; - if(CURLE_OK != easy->result) - /* if Curl_async_resolved() returns failure, the connection struct - is already freed and gone */ - easy->easy_conn = NULL; /* no more connection */ - else { - /* FIX: what if protocol_connect is TRUE here?! */ + if(protocol_connect) + multistate(easy, CURLM_STATE_DO); + else multistate(easy, CURLM_STATE_WAITCONNECT); - } } + } + break; - if(CURLE_OK != easy->result) { - /* failure detected */ - Curl_disconnect(easy->easy_conn); /* disconnect properly */ + case CURLM_STATE_WAITRESOLVE: + /* awaiting an asynch name resolve to complete */ + { + struct Curl_dns_entry *dns = NULL; + + /* check if we have the name resolved by now */ + easy->result = Curl_is_resolved(easy->easy_conn, &dns); + + if(dns) { + /* Perform the next step in the connection phase, and then move on + to the WAITCONNECT state */ + easy->result = Curl_async_resolved(easy->easy_conn, + &protocol_connect); + + if(CURLE_OK != easy->result) + /* if Curl_async_resolved() returns failure, the connection struct + is already freed and gone */ easy->easy_conn = NULL; /* no more connection */ - break; + else { + /* FIX: what if protocol_connect is TRUE here?! */ + multistate(easy, CURLM_STATE_WAITCONNECT); } } - break; - case CURLM_STATE_WAITCONNECT: - /* awaiting a completion of an asynch connect */ - easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET, - &connected); - if(connected) - easy->result = Curl_protocol_connect(easy->easy_conn, - &protocol_connect); + if(CURLE_OK != easy->result) { + /* failure detected */ + Curl_disconnect(easy->easy_conn); /* disconnect properly */ + easy->easy_conn = NULL; /* no more connection */ + break; + } + } + break; - if(CURLE_OK != easy->result) { - /* failure detected */ - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ - break; - } + case CURLM_STATE_WAITCONNECT: + /* awaiting a completion of an asynch connect */ + easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET, + &connected); + if(connected) + easy->result = Curl_protocol_connect(easy->easy_conn, + &protocol_connect); - if(connected) { - if(!protocol_connect) { - /* We have a TCP connection, but 'protocol_connect' may be false - and then we continue to 'STATE_PROTOCONNECT'. If protocol - connect is TRUE, we move on to STATE_DO. */ - multistate(easy, CURLM_STATE_PROTOCONNECT); - } - else { - /* after the connect has completed, go DO */ - multistate(easy, CURLM_STATE_DO); - result = CURLM_CALL_MULTI_PERFORM; - } - } + if(CURLE_OK != easy->result) { + /* failure detected */ + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ break; + } - case CURLM_STATE_PROTOCONNECT: - /* protocol-specific connect phase */ - easy->result = Curl_protocol_connecting(easy->easy_conn, - &protocol_connect); - if(protocol_connect) { + if(connected) { + if(!protocol_connect) { + /* We have a TCP connection, but 'protocol_connect' may be false + and then we continue to 'STATE_PROTOCONNECT'. If protocol + connect is TRUE, we move on to STATE_DO. */ + multistate(easy, CURLM_STATE_PROTOCONNECT); + } + else { /* after the connect has completed, go DO */ multistate(easy, CURLM_STATE_DO); result = CURLM_CALL_MULTI_PERFORM; } - else if(easy->result) { - /* failure detected */ - Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ - } - break; + } + break; - case CURLM_STATE_DO: - if(easy->easy_handle->set.connect_only) { - /* keep connection open for application to use the socket */ - easy->easy_conn->bits.close = FALSE; - multistate(easy, CURLM_STATE_DONE); - easy->result = CURLE_OK; - result = CURLM_OK; - } - else { - /* Perform the protocol's DO action */ - easy->result = Curl_do(&easy->easy_conn, &dophase_done); + case CURLM_STATE_PROTOCONNECT: + /* protocol-specific connect phase */ + easy->result = Curl_protocol_connecting(easy->easy_conn, + &protocol_connect); + if(protocol_connect) { + /* after the connect has completed, go DO */ + multistate(easy, CURLM_STATE_DO); + result = CURLM_CALL_MULTI_PERFORM; + } + else if(easy->result) { + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result); + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + } + break; - if(CURLE_OK == easy->result) { + case CURLM_STATE_DO: + if(easy->easy_handle->set.connect_only) { + /* keep connection open for application to use the socket */ + easy->easy_conn->bits.close = FALSE; + multistate(easy, CURLM_STATE_DONE); + easy->result = CURLE_OK; + result = CURLM_OK; + } + else { + /* Perform the protocol's DO action */ + easy->result = Curl_do(&easy->easy_conn, &dophase_done); - if(!dophase_done) { - /* DO was not completed in one function call, we must continue - DOING... */ - multistate(easy, CURLM_STATE_DOING); - result = CURLM_OK; - } + if(CURLE_OK == easy->result) { - /* after DO, go PERFORM... or DO_MORE */ - else if(easy->easy_conn->bits.do_more) { - /* we're supposed to do more, but we need to sit down, relax - and wait a little while first */ - multistate(easy, CURLM_STATE_DO_MORE); - result = CURLM_OK; - } - else { - /* we're done with the DO, now PERFORM */ - easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_PERFORM); - result = CURLM_CALL_MULTI_PERFORM; - } - } - } - else { - /* failure detected */ - Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ + if(!dophase_done) { + /* DO was not completed in one function call, we must continue + DOING... */ + multistate(easy, CURLM_STATE_DOING); + result = CURLM_OK; } - } - break; - case CURLM_STATE_DOING: - /* we continue DOING until the DO phase is complete */ - easy->result = Curl_protocol_doing(easy->easy_conn, &dophase_done); - if(CURLE_OK == easy->result) { - if(dophase_done) { - /* after DO, go PERFORM... or DO_MORE */ - if(easy->easy_conn->bits.do_more) { - /* we're supposed to do more, but we need to sit down, relax - and wait a little while first */ - multistate(easy, CURLM_STATE_DO_MORE); - result = CURLM_OK; - } - else { - /* we're done with the DO, now PERFORM */ - easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_PERFORM); - result = CURLM_CALL_MULTI_PERFORM; - } + /* after DO, go PERFORM... or DO_MORE */ + else if(easy->easy_conn->bits.do_more) { + /* we're supposed to do more, but we need to sit down, relax + and wait a little while first */ + multistate(easy, CURLM_STATE_DO_MORE); + result = CURLM_OK; + } + else { + /* we're done with the DO, now PERFORM */ + easy->result = Curl_readwrite_init(easy->easy_conn); + if(CURLE_OK == easy->result) { + multistate(easy, CURLM_STATE_PERFORM); + result = CURLM_CALL_MULTI_PERFORM; } - } /* dophase_done */ + } } else { /* failure detected */ @@ -602,141 +759,208 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) Curl_disconnect(easy->easy_conn); /* close the connection */ easy->easy_conn = NULL; /* no more connection */ } - break; - - case CURLM_STATE_DO_MORE: - /* Ready to do more? */ - easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET, - &connected); - if(connected) { - /* - * When we are connected, DO MORE and then go PERFORM - */ - easy->result = Curl_do_more(easy->easy_conn); + } + break; - if(CURLE_OK == easy->result) + case CURLM_STATE_DOING: + /* we continue DOING until the DO phase is complete */ + easy->result = Curl_protocol_doing(easy->easy_conn, &dophase_done); + if(CURLE_OK == easy->result) { + if(dophase_done) { + /* after DO, go PERFORM... or DO_MORE */ + if(easy->easy_conn->bits.do_more) { + /* we're supposed to do more, but we need to sit down, relax + and wait a little while first */ + multistate(easy, CURLM_STATE_DO_MORE); + result = CURLM_OK; + } + else { + /* we're done with the DO, now PERFORM */ easy->result = Curl_readwrite_init(easy->easy_conn); - - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_PERFORM); - result = CURLM_CALL_MULTI_PERFORM; + if(CURLE_OK == easy->result) { + multistate(easy, CURLM_STATE_PERFORM); + result = CURLM_CALL_MULTI_PERFORM; + } } + } /* dophase_done */ + } + else { + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result); + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ + } + break; + + case CURLM_STATE_DO_MORE: + /* Ready to do more? */ + easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET, + &connected); + if(connected) { + /* + * When we are connected, DO MORE and then go PERFORM + */ + easy->result = Curl_do_more(easy->easy_conn); + + if(CURLE_OK == easy->result) + easy->result = Curl_readwrite_init(easy->easy_conn); + + if(CURLE_OK == easy->result) { + multistate(easy, CURLM_STATE_PERFORM); + result = CURLM_CALL_MULTI_PERFORM; } - break; + } + break; - case CURLM_STATE_PERFORM: - /* read/write data if it is ready to do so */ - easy->result = Curl_readwrite(easy->easy_conn, &done); - - if(easy->result) { - /* The transfer phase returned error, we mark the connection to get - * closed to prevent being re-used. This is becasue we can't - * possibly know if the connection is in a good shape or not now. */ - easy->easy_conn->bits.close = TRUE; - - if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) { - /* if we failed anywhere, we must clean up the secondary socket if - it was used */ - sclose(easy->easy_conn->sock[SECONDARYSOCKET]); - easy->easy_conn->sock[SECONDARYSOCKET]=-1; - } - Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); + case CURLM_STATE_PERFORM: + /* read/write data if it is ready to do so */ + easy->result = Curl_readwrite(easy->easy_conn, &done); + + if(easy->result) { + /* The transfer phase returned error, we mark the connection to get + * closed to prevent being re-used. This is becasue we can't + * possibly know if the connection is in a good shape or not now. */ + easy->easy_conn->bits.close = TRUE; + + if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) { + /* if we failed anywhere, we must clean up the secondary socket if + it was used */ + sclose(easy->easy_conn->sock[SECONDARYSOCKET]); + easy->easy_conn->sock[SECONDARYSOCKET]=-1; } + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result); + } - else if(TRUE == done) { - char *newurl; - bool retry = Curl_retry_request(easy->easy_conn, &newurl); + else if(TRUE == done) { + char *newurl; + bool retry = Curl_retry_request(easy->easy_conn, &newurl); - /* call this even if the readwrite function returned error */ - Curl_posttransfer(easy->easy_handle); + /* call this even if the readwrite function returned error */ + Curl_posttransfer(easy->easy_handle); - /* When we follow redirects, must to go back to the CONNECT state */ - if(easy->easy_conn->newurl || retry) { - if(!retry) { - /* if the URL is a follow-location and not just a retried request - then figure out the URL here */ - newurl = easy->easy_conn->newurl; - easy->easy_conn->newurl = NULL; - } - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); - if(easy->result == CURLE_OK) - easy->result = Curl_follow(easy->easy_handle, newurl, retry); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_CONNECT); - result = CURLM_CALL_MULTI_PERFORM; - } - else - /* Since we "took it", we are in charge of freeing this on - failure */ - free(newurl); + /* When we follow redirects, must to go back to the CONNECT state */ + if(easy->easy_conn->newurl || retry) { + if(!retry) { + /* if the URL is a follow-location and not just a retried request + then figure out the URL here */ + newurl = easy->easy_conn->newurl; + easy->easy_conn->newurl = NULL; } - else { - /* after the transfer is done, go DONE */ - multistate(easy, CURLM_STATE_DONE); + easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + if(easy->result == CURLE_OK) + easy->result = Curl_follow(easy->easy_handle, newurl, retry); + if(CURLE_OK == easy->result) { + multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; } + else + /* Since we "took it", we are in charge of freeing this on + failure */ + free(newurl); } - break; - case CURLM_STATE_DONE: - /* post-transfer command */ - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + else { + /* after the transfer is done, go DONE */ + multistate(easy, CURLM_STATE_DONE); + result = CURLM_CALL_MULTI_PERFORM; + } + } + break; + case CURLM_STATE_DONE: + /* post-transfer command */ + easy->result = Curl_done(&easy->easy_conn, CURLE_OK); - /* after we have DONE what we're supposed to do, go COMPLETED, and - it doesn't matter what the Curl_done() returned! */ - multistate(easy, CURLM_STATE_COMPLETED); - break; + /* after we have DONE what we're supposed to do, go COMPLETED, and + it doesn't matter what the Curl_done() returned! */ + multistate(easy, CURLM_STATE_COMPLETED); + break; - case CURLM_STATE_COMPLETED: - /* this is a completed transfer, it is likely to still be connected */ + case CURLM_STATE_COMPLETED: + /* this is a completed transfer, it is likely to still be connected */ - /* This node should be delinked from the list now and we should post - an information message that we are complete. */ - break; - default: - return CURLM_INTERNAL_ERROR; - } + /* This node should be delinked from the list now and we should post + an information message that we are complete. */ + break; + default: + return CURLM_INTERNAL_ERROR; + } - if(CURLM_STATE_COMPLETED != easy->state) { - if(CURLE_OK != easy->result) { - /* - * If an error was returned, and we aren't in completed state now, - * then we go to completed and consider this transfer aborted. */ - multistate(easy, CURLM_STATE_COMPLETED); - } - else - /* this one still lives! */ - (*running_handles)++; + if(CURLM_STATE_COMPLETED != easy->state) { + if(CURLE_OK != easy->result) { + /* + * If an error was returned, and we aren't in completed state now, + * then we go to completed and consider this transfer aborted. */ + multistate(easy, CURLM_STATE_COMPLETED); } + else + /* this one still lives! */ + (*running_handles)++; + } - } while (easy->easy_handle->change.url_changed); + } while (easy->easy_handle->change.url_changed); - if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { - /* clear out the usage of the shared DNS cache */ - easy->easy_handle->hostcache = NULL; + if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->hostcache = NULL; - /* now add a node to the Curl_message linked list with this info */ - msg = (struct Curl_message *)malloc(sizeof(struct Curl_message)); + /* now add a node to the Curl_message linked list with this info */ + msg = (struct Curl_message *)malloc(sizeof(struct Curl_message)); - if(!msg) - return CURLM_OUT_OF_MEMORY; + if(!msg) + return CURLM_OUT_OF_MEMORY; - msg->extmsg.msg = CURLMSG_DONE; - msg->extmsg.easy_handle = easy->easy_handle; - msg->extmsg.data.result = easy->result; - msg->next=NULL; + msg->extmsg.msg = CURLMSG_DONE; + msg->extmsg.easy_handle = easy->easy_handle; + msg->extmsg.data.result = easy->result; + msg->next=NULL; - easy->msg = msg; - easy->msg_num = 1; /* there is one unread message here */ + easy->msg = msg; + easy->msg_num = 1; /* there is one unread message here */ - multi->num_msgs++; /* increase message counter */ - } - easy = easy->next; /* operate on next handle */ + 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 */ + } + + /* + * 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 handle */ void Curl_multi_rmeasy(void *multi_handle, CURL *easy_handle) @@ -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 +} + -- cgit v1.2.1 From 48f56d9600d14b963be992e0e25f355a299873bb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 18 Apr 2006 10:55:41 +0000 Subject: attempt to silence the MIPSPro compiler warning --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2eed1b2b3..78cd2af68 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1088,8 +1088,8 @@ static void singlesocket(struct Curl_multi *multi, continue; } - action = (current.action & GETSOCK_READSOCK(i)?CURL_POLL_IN:0) | - (current.action & GETSOCK_WRITESOCK(i)?CURL_POLL_OUT:0); + 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 */ -- cgit v1.2.1 From 0ec96e427987e157dfbee57c016c30b671f336f5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 21 Apr 2006 13:40:07 +0000 Subject: each socket is used by exactly one easy handle, but of course each easy handle can and will use more than one socket --- lib/multi.c | 117 ++++++++++++++++++------------------------------------------ 1 file changed, 35 insertions(+), 82 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 78cd2af68..7a75eb3dc 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -83,7 +83,6 @@ typedef enum { struct socketstate { curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; long action; /* socket action bitmap */ - long timeout[MAX_SOCKSPEREASYHANDLE]; }; struct Curl_one_easy { @@ -173,19 +172,20 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) } /* - * 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. + * We add one of these structs to the sockhash for a particular socket */ struct Curl_sh_entry { - struct Curl_sh_entry *next; - struct Curl_sh_entry *prev; struct SessionHandle *easy; time_t timestamp; long inuse; + int action; /* what action READ/WRITE this socket waits for */ + void *userp; /* settable by users (not yet decided exactly how) */ }; +/* bits for 'action' having no bits means this socket is not expecting any + action */ +#define SH_READ 1 +#define SG_WRITE 2 /* make sure this socket is present in the hash for this handle */ static int sh_addentry(struct curl_hash *sh, @@ -196,16 +196,9 @@ static int sh_addentry(struct curl_hash *sh, 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; - } - } + if(there) + /* it is present, return fine */ + return 0; /* not present, add it */ check = calloc(sizeof(struct Curl_sh_entry), 1); @@ -213,57 +206,25 @@ static int sh_addentry(struct curl_hash *sh, 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 */ + /* make/add new hash entry */ + if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) + return 1; /* major failure */ - 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) +static void sh_delentry(struct curl_hash *sh, curl_socket_t s) { 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; + if(there) { + /* this socket is in the hash */ + /* We remove the hash entry. (This'll end up in a call to + sh_freeentry().) */ + Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t)); } } @@ -273,14 +234,6 @@ static void sh_delentry(struct curl_hash *sh, 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); } @@ -1034,8 +987,9 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) } /* - * Check what sockets we deal with and their "action state" and if we have a - * difference from last time we call the callback accordingly. + * singlesocket() checks what sockets we deal with and their "action state" + * and if we have a different state in any of those sockets from last time we + * call the callback accordingly. */ static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy) @@ -1050,9 +1004,11 @@ static void singlesocket(struct Curl_multi *multi, /* 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 */ + /* when filled in, we compare with the previous round's state in a first + quick memory compare check */ if(memcmp(¤t, &easy->sockstate, sizeof(struct socketstate))) { - /* difference, call the callback once for every socket change ! */ + + /* there is 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]; @@ -1103,7 +1059,7 @@ static void singlesocket(struct Curl_multi *multi, /* Update the sockhash accordingly */ if(action == CURL_POLL_REMOVE) /* remove from hash for this easy handle */ - sh_delentry(multi->sockhash, s, easy->easy_handle); + sh_delentry(multi->sockhash, s); else /* make sure this socket is present in the hash for this handle */ sh_addentry(multi->sockhash, s, easy->easy_handle); @@ -1138,6 +1094,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, easyp = easyp->next; } + /* or should we fall-through and do the timer-based stuff? */ return result; } else if (s != CURL_SOCKET_TIMEOUT) { @@ -1152,20 +1109,16 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* 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); + data = entry->easy; - entry = entry->next; + result = multi_runsingle(multi, data->set.one_easy, &running_handles); - } while(entry); + if(result == CURLM_OK) + /* get the socket(s) and check if the state has been changed since + last */ + singlesocket(multi, data->set.one_easy); + /* or should we fall-through and do the timer-based stuff? */ return result; } -- cgit v1.2.1 From ecc6c1f501ae835471b8844cba393835a12bb803 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 25 Apr 2006 05:32:05 +0000 Subject: prevent signed/unsigned warnings --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 7a75eb3dc..ee7e388fa 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -496,8 +496,8 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, /* this socket is unused, break out of loop */ break; else { - if(s > this_max_fd) - this_max_fd = s; + if(s > (curl_socket_t)this_max_fd) + this_max_fd = (int)s; } } -- cgit v1.2.1 From 12db20be4e8b0130aba7342a1124eb85e3115822 Mon Sep 17 00:00:00 2001 From: Gisle Vanem Date: Wed, 26 Apr 2006 17:26:22 +0000 Subject: Fixed signed/unsigned convertion errors in Salford-C. #ifdef around WSAEDISCON in strerror.c. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ee7e388fa..c0f9a7918 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -780,7 +780,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* if we failed anywhere, we must clean up the secondary socket if it was used */ sclose(easy->easy_conn->sock[SECONDARYSOCKET]); - easy->easy_conn->sock[SECONDARYSOCKET]=-1; + easy->easy_conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; } Curl_posttransfer(easy->easy_handle); Curl_done(&easy->easy_conn, easy->result); -- cgit v1.2.1 From 73daf8ce334dc3d3dc8215cd898cfbb9fe3267de Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 May 2006 11:33:00 +0000 Subject: Robson Braga Araujo fixed a memory leak when you added an easy handle to a multi stack and that easy handle had already been used to do one or more easy interface transfers, as then the code threw away the previously used DNS cache without properly freeing it. --- lib/multi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c0f9a7918..c3463b0ce 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -315,7 +315,12 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, easy->easy_handle = easy_handle; multistate(easy, CURLM_STATE_INIT); - /* for multi interface connections, we share DNS cache automaticly */ + /* for multi interface connections, we share DNS cache automaticly. + First kill the existing one if there is any. */ + if (easy->easy_handle->hostcache && + easy->easy_handle->hostcache != multi->hostcache) + Curl_hash_destroy(easy->easy_handle->hostcache); + easy->easy_handle->hostcache = multi->hostcache; /* We add this new entry first in the list. We make our 'next' point to the -- cgit v1.2.1 From 482b3ba70273f2f3cde02e7e57907e1dcc5d9d31 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 26 May 2006 11:26:42 +0000 Subject: long/int cleanup to silence picky compiler warnings --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c3463b0ce..92bf28151 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -82,7 +82,7 @@ typedef enum { struct socketstate { curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; - long action; /* socket action bitmap */ + unsigned int action; /* socket action bitmap */ }; struct Curl_one_easy { @@ -185,7 +185,7 @@ struct Curl_sh_entry { /* bits for 'action' having no bits means this socket is not expecting any action */ #define SH_READ 1 -#define SG_WRITE 2 +#define SH_WRITE 2 /* make sure this socket is present in the hash for this handle */ static int sh_addentry(struct curl_hash *sh, -- cgit v1.2.1 From 405d98ee6391d8bdb1c0d3efcc7f6e4358c6465f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 27 May 2006 22:26:16 +0000 Subject: adapted to the new Curl_splayremovebyaddr() proto --- lib/multi.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 92bf28151..bafadd446 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1239,6 +1239,7 @@ void Curl_expire(struct SessionHandle *data, long milli) { struct Curl_multi *multi = data->multi; struct timeval *nowp = &data->state.expiretime; + int rc; /* this is only interesting for multi-interface using libcurl, and only while there is still a multi interface struct remaining! */ @@ -1250,11 +1251,14 @@ void Curl_expire(struct SessionHandle *data, long milli) 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); + rc = Curl_splayremovebyaddr(multi->timetree, + &data->state.timenode, + &multi->timetree); + if(rc) + infof(data, "Internal error clearing splay node = %d\n", rc); infof(data, "Expire cleared\n"); + nowp->tv_sec = nowp->tv_usec = 0; } - nowp->tv_sec = nowp->tv_usec = 0; } else { struct timeval set; @@ -1272,7 +1276,9 @@ void Curl_expire(struct SessionHandle *data, long milli) } if(nowp->tv_sec) { - /* compare if the new time is earlier, and only set it if so */ + /* This means that the struct is added as a node in the splay tree. + Compare if the new time is earlier, and only remove-old/add-new if it + is. */ long diff = curlx_tvdiff(set, *nowp); if(diff > 0) /* the new expire time was later so we don't change this */ @@ -1280,8 +1286,11 @@ void Curl_expire(struct SessionHandle *data, long milli) /* 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); + rc = Curl_splayremovebyaddr(multi->timetree, + &data->state.timenode, + &multi->timetree); + if(rc) + infof(data, "Internal error removing splay node = %d\n", rc); } *nowp = set; -- cgit v1.2.1 From dfe1884c2529d728750d0824f73055627673cd72 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 22 Jun 2006 21:36:53 +0000 Subject: Peter Silva introduced CURLOPT_MAX_SEND_SPEED_LARGE and CURLOPT_MAX_RECV_SPEED_LARGE that limit tha maximum rate libcurl is allowed to send or receive data. This kind of adds the the command line tool's option --limit-rate to the library. The rate limiting logic in the curl app is now removed and is instead provided by libcurl itself. Transfer rate limiting will now also work for -d and -F, which it didn't before. --- lib/multi.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index bafadd446..9201402a2 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -68,6 +68,7 @@ typedef enum { CURLM_STATE_DOING, /* sending off the request (part 1) */ CURLM_STATE_DO_MORE, /* send off the request (part 2) */ CURLM_STATE_PERFORM, /* transfer data */ + CURLM_STATE_TOOFAST, /* wait because limit-rate exceeded */ CURLM_STATE_DONE, /* post data transfer operation */ CURLM_STATE_COMPLETED, /* operation complete */ @@ -156,6 +157,7 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) "DOING", "DO_MORE", "PERFORM", + "TOOFAST", "DONE", "COMPLETED", }; @@ -440,6 +442,7 @@ static int multi_getsock(struct Curl_one_easy *easy, int numsocks) { switch(easy->state) { + case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */ default: return 0; @@ -771,7 +774,37 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } break; + case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ + /* if both rates are within spec, resume transfer */ + Curl_pgrsUpdate(easy->easy_conn); + if ( ( ( easy->easy_handle->set.max_send_speed == 0 ) || + ( easy->easy_handle->progress.ulspeed < + easy->easy_handle->set.max_send_speed ) ) && + ( ( easy->easy_handle->set.max_recv_speed == 0 ) || + ( easy->easy_handle->progress.dlspeed < + easy->easy_handle->set.max_recv_speed ) ) + ) + multistate(easy, CURLM_STATE_PERFORM); + + break; + case CURLM_STATE_PERFORM: + + /* check if over speed */ + if ( ( ( easy->easy_handle->set.max_send_speed > 0 ) && + ( easy->easy_handle->progress.ulspeed > + easy->easy_handle->set.max_send_speed ) ) || + ( ( easy->easy_handle->set.max_recv_speed > 0 ) && + ( easy->easy_handle->progress.dlspeed > + easy->easy_handle->set.max_recv_speed ) ) + ) { + /* Transfer is over the speed limit. Change state. TODO: Call + * Curl_expire() with the time left until we're targeted to be below + * the speed limit again. */ + multistate(easy, CURLM_STATE_TOOFAST ); + break; + } + /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); @@ -825,6 +858,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } break; + case CURLM_STATE_DONE: /* post-transfer command */ easy->result = Curl_done(&easy->easy_conn, CURLE_OK); -- cgit v1.2.1 From a8ac6f1dc15056d39668bbed48fa9e7fee5e789a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 23 Jun 2006 22:07:06 +0000 Subject: Arve Knudsen found a flaw in curl_multi_fdset() for systems where curl_socket_t is unsigned (like Windows) that could cause it to wrongly return a max fd of -1. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9201402a2..cfb749dfd 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -504,7 +504,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, /* this socket is unused, break out of loop */ break; else { - if(s > (curl_socket_t)this_max_fd) + if((int)s > this_max_fd) this_max_fd = (int)s; } } -- cgit v1.2.1 From 64f72c22b99e5b130eee08c704d80b99c48622ad Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Wed, 28 Jun 2006 04:17:04 +0000 Subject: fix minor compiler warning --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index cfb749dfd..9b9929dc9 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1083,7 +1083,7 @@ static void singlesocket(struct Curl_multi *multi, continue; } - action = ((current.action & GETSOCK_READSOCK(i))?CURL_POLL_IN:0) | + action = ((current.action & GETSOCK_READSOCK(i))?CURL_POLL_IN:0) + ((current.action & GETSOCK_WRITESOCK(i))?CURL_POLL_OUT:0); } -- cgit v1.2.1 From b01286d28049907def2b9b943c5c337cb06048b3 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Wed, 28 Jun 2006 05:22:47 +0000 Subject: fix better minor compiler warning --- lib/multi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9b9929dc9..9aee31ddd 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1083,8 +1083,11 @@ static void singlesocket(struct Curl_multi *multi, continue; } - action = ((current.action & GETSOCK_READSOCK(i))?CURL_POLL_IN:0) + - ((current.action & GETSOCK_WRITESOCK(i))?CURL_POLL_OUT:0); + action = CURL_POLL_NONE; + if(current.action & GETSOCK_READSOCK(i)) + action |= CURL_POLL_IN; + if(current.action & GETSOCK_WRITESOCK(i)) + action |= CURL_POLL_OUT; } /* call the callback with this new info */ -- cgit v1.2.1 From ca319f63ad757a69f14105ab4a8bbf10987b8ad0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 7 Jul 2006 22:58:06 +0000 Subject: Ingmar Runge provided a source snippet that caused a crash. The reason for the crash was that libcurl internally was a bit confused about who owned the DNS cache at all times so if you created an easy handle that uses a shared DNS cache and added that to a multi handle it would crash. Now we keep more careful internal track of exactly what kind of DNS cache each easy handle uses: None, Private (allocated for and used only by this single handle), Shared (points to a cache held by a shared object), Global (points to the global cache) or Multi (points to the cache within the multi handle that is automatically shared between all easy handles that are added with private caches). --- lib/multi.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9aee31ddd..5f98c2eaf 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -317,13 +317,14 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, easy->easy_handle = easy_handle; multistate(easy, CURLM_STATE_INIT); - /* for multi interface connections, we share DNS cache automaticly. - First kill the existing one if there is any. */ - if (easy->easy_handle->hostcache && - easy->easy_handle->hostcache != multi->hostcache) - Curl_hash_destroy(easy->easy_handle->hostcache); - - easy->easy_handle->hostcache = multi->hostcache; + /* for multi interface connections, we share DNS cache automaticly if the + easy handle's one is currently private. */ + if (easy->easy_handle->dns.hostcache && + (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) { + Curl_hash_destroy(easy->easy_handle->dns.hostcache); + easy->easy_handle->dns.hostcache = multi->hostcache; + easy->easy_handle->dns.hostcachetype = HCACHE_MULTI; + } /* We add this new entry first in the list. We make our 'next' point to the previous next and our 'prev' point back to the 'first' struct */ @@ -374,8 +375,12 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ - /* clear out the usage of the shared DNS cache */ - easy->easy_handle->hostcache = NULL; + if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->dns.hostcache = NULL; + easy->easy_handle->dns.hostcachetype = HCACHE_NONE; + } + Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association to this multi handle */ @@ -893,8 +898,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } while (easy->easy_handle->change.url_changed); if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { - /* clear out the usage of the shared DNS cache */ - easy->easy_handle->hostcache = NULL; + if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->dns.hostcache = NULL; + easy->easy_handle->dns.hostcachetype = HCACHE_NONE; + } /* now add a node to the Curl_message linked list with this info */ msg = (struct Curl_message *)malloc(sizeof(struct Curl_message)); @@ -975,8 +983,11 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) easy = multi->easy.next; while(easy) { nexteasy=easy->next; - /* clear out the usage of the shared DNS cache */ - easy->easy_handle->hostcache = NULL; + if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { + /* clear out the usage of the shared DNS cache */ + easy->easy_handle->dns.hostcache = NULL; + easy->easy_handle->dns.hostcachetype = HCACHE_NONE; + } Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */ if (easy->msg) -- cgit v1.2.1 From 34f5e8ad0ed29d4dd7c59768339e37c9be2e533a Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Mon, 10 Jul 2006 16:14:36 +0000 Subject: DNS cache must use the multi DNS cache if the easy handle's one is not using anyone in curl_multi_add_handle. --- lib/multi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 5f98c2eaf..c59145414 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -322,6 +322,12 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if (easy->easy_handle->dns.hostcache && (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) { Curl_hash_destroy(easy->easy_handle->dns.hostcache); + easy->easy_handle->dns.hostcache = NULL; + easy->easy_handle->dns.hostcachetype = HCACHE_NONE; + } + + if (!easy->easy_handle->dns.hostcache || + (easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) { easy->easy_handle->dns.hostcache = multi->hostcache; easy->easy_handle->dns.hostcachetype = HCACHE_MULTI; } -- cgit v1.2.1 From 73f407b7ae8dd5810ceeec5e98434191c5bd0edc Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 13 Jul 2006 18:44:24 +0000 Subject: Fix compiler warning. --- lib/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c59145414..674f7a913 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1311,7 +1311,8 @@ void Curl_expire(struct SessionHandle *data, long milli) if(rc) infof(data, "Internal error clearing splay node = %d\n", rc); infof(data, "Expire cleared\n"); - nowp->tv_sec = nowp->tv_usec = 0; + nowp->tv_sec = 0; + nowp->tv_sec = 0; } } else { -- cgit v1.2.1 From 700cd5805ce8b02a63d7efaaecd06e81febf0eeb Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 13 Jul 2006 18:57:34 +0000 Subject: Oops, missing "u" --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 674f7a913..8d0de7628 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1312,7 +1312,7 @@ void Curl_expire(struct SessionHandle *data, long milli) infof(data, "Internal error clearing splay node = %d\n", rc); infof(data, "Expire cleared\n"); nowp->tv_sec = 0; - nowp->tv_sec = 0; + nowp->tv_usec = 0; } } else { -- cgit v1.2.1 From 06d05b18b28d67b8745b63266f253276a24b901e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 17 Jul 2006 18:35:58 +0000 Subject: Jari Sundell did some excellent research and bug tracking, figured out that we did wrong and patched it: When nodes were removed from the splay tree, and we didn't properly remove it from the splay tree when an easy handle was removed from a multi stack and thus we could wrongly leave a node in the splay tree pointing to (bad) memory. --- lib/multi.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 8d0de7628..44c742476 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -381,6 +381,11 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ + /* The timer must be shut down before easy->multi is set to NULL, + else the timenode will remain in the splay tree after + curl_easy_cleanup is called. */ + Curl_expire(easy->easy_handle, 0); + if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ easy->easy_handle->dns.hostcache = NULL; @@ -962,6 +967,17 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) int key = now.tv_sec; /* drop the usec part */ multi->timetree = Curl_splaygetbest(key, multi->timetree, &t); + + if (t) { + struct SessionHandle *d = t->payload; + struct timeval* tv = &d->state.expiretime; + + /* clear the expire times within the handles that we remove from the + splay tree */ + tv->tv_sec = 0; + tv->tv_usec = 0; + } + } while(t); return returncode; @@ -1207,8 +1223,15 @@ static CURLMcode multi_socket(struct Curl_multi *multi, key = now.tv_sec; /* drop the usec part */ multi->timetree = Curl_splaygetbest(key, multi->timetree, &t); - if(t) + if(t) { + /* assign 'data' to be the easy handle we just removed from the splay + tree */ data = t->payload; + /* clear the expire time within the handle we removed from the + splay tree */ + data->state.expiretime.tv_sec = 0; + data->state.expiretime.tv_usec = 0; + } } while(t); -- cgit v1.2.1 From 6f6b93da02019141812b81bfdbb6bcda430c3b4d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 26 Jul 2006 22:19:42 +0000 Subject: [Hiper-related work] Added a function called curl_multi_assign() that will set a private pointer added to the internal libcurl hash table for the particular socket passed in to this function. --- lib/multi.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 44c742476..3296f09e5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -182,7 +182,7 @@ struct Curl_sh_entry { time_t timestamp; long inuse; int action; /* what action READ/WRITE this socket waits for */ - void *userp; /* settable by users (not yet decided exactly how) */ + void *socketp; /* settable by users with curl_multi_assign() */ }; /* bits for 'action' having no bits means this socket is not expecting any action */ @@ -1125,10 +1125,14 @@ static void singlesocket(struct Curl_multi *multi, /* call the callback with this new info */ if(multi->socket_cb) { + struct Curl_sh_entry *entry = + Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + multi->socket_cb(easy->easy_handle, s, action, - multi->socket_userp); + multi->socket_userp, + entry->socketp); } /* Update the sockhash accordingly */ @@ -1385,3 +1389,19 @@ void Curl_expire(struct SessionHandle *data, long milli) #endif } +CURLMcode curl_multi_assign(CURLM *multi_handle, + curl_socket_t s, void *hashp) +{ + struct Curl_sh_entry *there = NULL; + struct Curl_multi *multi = (struct Curl_multi *)multi_handle; + + if(s != CURL_SOCKET_BAD) + there = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(curl_socket_t)); + + if(!there) + return CURLM_BAD_SOCKET; + + there->socketp = hashp; + + return CURLM_OK; +} -- cgit v1.2.1 From 01b2cf82ec9495f36976710af0015d4cf7f529cd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 30 Jul 2006 22:44:07 +0000 Subject: curl_multi_socket() and curl_multi_socket_all() got modified prototypes: they both now provide the number of running handles back to the calling function. --- lib/multi.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3296f09e5..7f553bda5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1123,6 +1123,15 @@ static void singlesocket(struct Curl_multi *multi, action |= CURL_POLL_OUT; } + /* Update the sockhash accordingly BEFORE the callback of not a removal, + in case the callback wants to use curl_multi_assign(), but do the + removal AFTER the callback for the very same reason (but then to be + able to pass the correct entry->socketp) */ + + if(action != CURL_POLL_REMOVE) + /* make sure this socket is present in the hash for this handle */ + sh_addentry(multi->sockhash, s, easy->easy_handle); + /* call the callback with this new info */ if(multi->socket_cb) { struct Curl_sh_entry *entry = @@ -1132,16 +1141,13 @@ static void singlesocket(struct Curl_multi *multi, s, action, multi->socket_userp, - entry->socketp); + entry ? entry->socketp : NULL); } - /* Update the sockhash accordingly */ if(action == CURL_POLL_REMOVE) /* remove from hash for this easy handle */ sh_delentry(multi->sockhash, s); - 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)); @@ -1154,16 +1160,16 @@ static void singlesocket(struct Curl_multi *multi, static CURLMcode multi_socket(struct Curl_multi *multi, bool checkall, - curl_socket_t s) + curl_socket_t s, + int *running_handles) { 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); + result = curl_multi_perform(multi, running_handles); /* walk through each easy handle and do the socket state change magic and callbacks */ @@ -1190,7 +1196,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data = entry->easy; - result = multi_runsingle(multi, data->set.one_easy, &running_handles); + 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 @@ -1212,7 +1218,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* the first loop lap 'data' can be NULL */ if(data) { - result = multi_runsingle(multi, data->set.one_easy, &running_handles); + 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 @@ -1269,20 +1275,18 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, } -CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s) +CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, + int *running_handles) { -#if 0 - printf("multi_socket(%d)\n", (int)s); -#endif - - return multi_socket((struct Curl_multi *)multi_handle, FALSE, s); + return multi_socket((struct Curl_multi *)multi_handle, FALSE, s, + running_handles); } -CURLMcode curl_multi_socket_all(CURLM *multi_handle) +CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) { return multi_socket((struct Curl_multi *)multi_handle, - TRUE, CURL_SOCKET_BAD); + TRUE, CURL_SOCKET_BAD, running_handles); } CURLMcode curl_multi_timeout(CURLM *multi_handle, -- cgit v1.2.1 From 9f579f12fcda284c5b9f5dd8c1581fc5cd7fa8fb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 1 Aug 2006 09:38:35 +0000 Subject: spell-fixed a comment --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 7f553bda5..3229768f0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1123,7 +1123,7 @@ static void singlesocket(struct Curl_multi *multi, action |= CURL_POLL_OUT; } - /* Update the sockhash accordingly BEFORE the callback of not a removal, + /* Update the sockhash accordingly BEFORE the callback if not a removal, in case the callback wants to use curl_multi_assign(), but do the removal AFTER the callback for the very same reason (but then to be able to pass the correct entry->socketp) */ -- cgit v1.2.1 From 159834171e0ae174634cd88385b223d0dafec359 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 2 Aug 2006 22:29:29 +0000 Subject: keep count of the number of "alive" handles in a struct member, as otherwise *multi_socket*() can't return the proper number --- lib/multi.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3229768f0..4d8a89fb5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -121,10 +121,11 @@ struct Curl_multi { /* We have a linked list with easy handles */ struct Curl_one_easy easy; - /* This is the amount of entries in the linked list above. */ - int num_easy; - int num_msgs; /* total amount of messages in the easy handles */ + int num_easy; /* amount of entries in the linked list above. */ + int num_msgs; /* amount of messages in the easy handles */ + int num_alive; /* amount of easy handles that are added but have not yet + reached COMPLETE state */ /* callback function and user data pointer for the *socket() API */ curl_socket_callback socket_cb; @@ -171,6 +172,9 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) "STATE: %s => %s handle %p: \n", statename[oldstate], statename[easy->state], (char *)easy); #endif + if(state == CURLM_STATE_COMPLETED) + /* changing to COMPLETED means there's one less easy handle 'alive' */ + easy->easy_handle->multi->num_alive--; } /* @@ -352,6 +356,8 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* increase the node-counter */ multi->num_easy++; + /* increase the alive-counter */ + multi->num_alive++; return CURLM_OK; } @@ -901,9 +907,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * then we go to completed and consider this transfer aborted. */ multistate(easy, CURLM_STATE_COMPLETED); } - else - /* this one still lives! */ - (*running_handles)++; } } while (easy->easy_handle->change.url_changed); @@ -943,8 +946,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) 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; @@ -980,6 +981,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } while(t); + *running_handles = multi->num_alive; + return returncode; } @@ -1169,6 +1172,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, if(checkall) { struct Curl_one_easy *easyp; + /* *perform() deals with running_handles on its own */ result = curl_multi_perform(multi, running_handles); /* walk through each easy handle and do the socket state change magic @@ -1191,9 +1195,6 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* 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 */ - data = entry->easy; result = multi_runsingle(multi, data->set.one_easy, running_handles); @@ -1203,6 +1204,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, last */ singlesocket(multi, data->set.one_easy); + *running_handles = multi->num_alive; + /* or should we fall-through and do the timer-based stuff? */ return result; } @@ -1245,6 +1248,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, } while(t); + *running_handles = multi->num_alive; return result; } -- cgit v1.2.1 From d211fcd34fe1c667abb55f5a1f56da8824faeb52 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 3 Aug 2006 11:41:49 +0000 Subject: Silence compiler warning 'unused parameter running_handles' in function multi_runsingle(). This is done here returning multi->num_alive in the running_handles parameter even when functions that call multi_runsingle() at this moment overwrite the returned value with the one that is valid when those functions curl_multi_perform() and multi_socket() have removed expired timers from the splay. Most probably, parameter 'running_handles' in function multi_runsingle() should be just removed. --- lib/multi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 4d8a89fb5..3190c53b0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -935,6 +935,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multi->num_msgs++; /* increase message counter */ } + *running_handles = multi->num_alive; return result; } -- cgit v1.2.1 From 01a79be2c93af3139303c027e867f462d66c18fd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 3 Aug 2006 11:47:42 +0000 Subject: removed running_handles argument from multi_runsingle() since it wasn't really used anymore since multi->num_alive was introduced --- lib/multi.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3190c53b0..fa88cdaab 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -540,8 +540,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, } static CURLMcode multi_runsingle(struct Curl_multi *multi, - struct Curl_one_easy *easy, - int *running_handles) + struct Curl_one_easy *easy) { struct Curl_message *msg = NULL; bool connected; @@ -935,7 +934,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multi->num_msgs++; /* increase message counter */ } - *running_handles = multi->num_alive; return result; } @@ -952,7 +950,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { - CURLMcode result = multi_runsingle(multi, easy, running_handles); + CURLMcode result = multi_runsingle(multi, easy); if(result) returncode = result; @@ -1198,7 +1196,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data = entry->easy; - result = multi_runsingle(multi, data->set.one_easy, running_handles); + result = multi_runsingle(multi, data->set.one_easy); if(result == CURLM_OK) /* get the socket(s) and check if the state has been changed since @@ -1222,7 +1220,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* the first loop lap 'data' can be NULL */ if(data) { - result = multi_runsingle(multi, data->set.one_easy, running_handles); + result = multi_runsingle(multi, data->set.one_easy); if(result == CURLM_OK) /* get the socket(s) and check if the state has been changed since -- cgit v1.2.1 From 2ac560e58bb92c8225919f782e18fc93b436b9d9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 4 Aug 2006 13:06:36 +0000 Subject: even when we get a single connection to deal with, we must still check for timeout'ed connections and possibly deal with them too --- lib/multi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index fa88cdaab..4e6efecd1 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1203,10 +1203,9 @@ static CURLMcode multi_socket(struct Curl_multi *multi, last */ singlesocket(multi, data->set.one_easy); - *running_handles = multi->num_alive; - - /* or should we fall-through and do the timer-based stuff? */ - return result; + /* Now we fall-through and do the timer-based stuff, since we don't want + to force the user to have to deal with timeouts as long as at least one + connection in fact has traffic. */ } /* -- cgit v1.2.1 From 8709f6c4b30eda92da0cd51cec83a1a6d7db8c32 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 4 Aug 2006 14:39:19 +0000 Subject: oops, the previous commit was incomplete as we made an unconditional call to multi_runsingle() without it being really necessary or good --- lib/multi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 4e6efecd1..d822bda52 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1206,6 +1206,9 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* Now we fall-through and do the timer-based stuff, since we don't want to force the user to have to deal with timeouts as long as at least one connection in fact has traffic. */ + + data = NULL; /* set data to NULL again to avoid calling multi_runsingle() + in case there's no need to */ } /* -- cgit v1.2.1 From 2ff609dd43cb5c1c0da893c080132a48a2d4c73b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 25 Aug 2006 13:53:20 +0000 Subject: Armel Asselin reported that the 'running_handles' counter wasn't updated properly if you removed a "live" handle from a multi handle with curl_multi_remove_handle(). --- lib/multi.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d822bda52..c1ff12e29 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -386,6 +386,10 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, if(easy) { /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ + if(easy->state != CURLM_STATE_COMPLETED) + /* this handle is "alive" so we need to count down the total number of + alive connections when this is removed */ + multi->num_alive--; /* The timer must be shut down before easy->multi is set to NULL, else the timenode will remain in the splay tree after -- cgit v1.2.1 From d7168a82e2123b6c3eb4db8e890ce7c14e21b32a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 31 Aug 2006 12:53:39 +0000 Subject: Dmitriy Sergeyev found and fixed a multi interface flaw when using asynch name resolves. It could get stuck in the wrong state. --- lib/multi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c1ff12e29..01891b5dd 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -639,8 +639,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, is already freed and gone */ easy->easy_conn = NULL; /* no more connection */ else { - /* FIX: what if protocol_connect is TRUE here?! */ - multistate(easy, CURLM_STATE_WAITCONNECT); + /* call again please so that we get the next socket setup */ + result = CURLM_CALL_MULTI_PERFORM; + if(protocol_connect) + multistate(easy, CURLM_STATE_DO); + else + multistate(easy, CURLM_STATE_WAITCONNECT); } } -- cgit v1.2.1 From b7eeb6e67fca686f840eacd6b8394edb58b07482 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 Sep 2006 21:49:20 +0000 Subject: Major overhaul introducing http pipelining support and shared connection cache within the multi handle. --- lib/multi.c | 431 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 389 insertions(+), 42 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 01891b5dd..e6b188135 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -64,13 +64,17 @@ typedef enum { CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */ CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect phase */ + CURLM_STATE_WAITDO, /* wait for our turn to send the request */ CURLM_STATE_DO, /* start send off the request (part 1) */ CURLM_STATE_DOING, /* sending off the request (part 1) */ CURLM_STATE_DO_MORE, /* send off the request (part 2) */ + CURLM_STATE_DO_DONE, /* done sending off request */ + CURLM_STATE_WAITPERFORM, /* wait for our turn to read the response */ CURLM_STATE_PERFORM, /* transfer data */ CURLM_STATE_TOOFAST, /* wait because limit-rate exceeded */ CURLM_STATE_DONE, /* post data transfer operation */ CURLM_STATE_COMPLETED, /* operation complete */ + CURLM_STATE_CANCELLED, /* cancelled */ CURLM_STATE_LAST /* not a true state, never use this */ } CURLMstate; @@ -86,6 +90,11 @@ struct socketstate { unsigned int action; /* socket action bitmap */ }; +struct closure { + struct closure *next; /* a simple one-way list of structs */ + struct SessionHandle *easy_handle; +}; + struct Curl_one_easy { /* first, two fields for the linked list of these */ struct Curl_one_easy *next; @@ -142,8 +151,20 @@ struct Curl_multi { the pluralis form, there can be more than one easy handle waiting on the same actual socket) */ struct curl_hash *sockhash; + + /* Whether pipelining is enabled for this multi handle */ + bool pipelining_enabled; + + /* shared connection cache */ + struct conncache *connc; + + /* list of easy handles kept around for doing nice connection closures */ + struct closure *closure; }; +static bool multi_conn_using(struct Curl_multi *multi, + struct SessionHandle *data); + /* always use this function to change state, to make debugging easier */ static void multistate(struct Curl_one_easy *easy, CURLMstate state) { @@ -154,23 +175,33 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) "WAITRESOLVE", "WAITCONNECT", "PROTOCONNECT", + "WAITDO", "DO", "DOING", "DO_MORE", + "DO_DONE", + "WAITPERFORM", "PERFORM", "TOOFAST", "DONE", "COMPLETED", + "CANCELLED" }; CURLMstate oldstate = easy->state; + int index = -1; #endif easy->state = state; + if(easy->state > CURLM_STATE_CONNECT && + easy->state < CURLM_STATE_COMPLETED) + index = easy->easy_conn->connectindex; + #ifdef CURLDEBUG infof(easy->easy_handle, - "STATE: %s => %s handle %p: \n", - statename[oldstate], statename[easy->state], (char *)easy); + "STATE: %s => %s handle %p; (connection #%d) \n", + statename[oldstate], statename[easy->state], + (char *)easy, index); #endif if(state == CURLM_STATE_COMPLETED) /* changing to COMPLETED means there's one less easy handle 'alive' */ @@ -291,6 +322,13 @@ CURLM *curl_multi_init(void) return NULL; } + multi->connc = Curl_mk_connc(CONNCACHE_MULTI); + if(!multi->connc) { + Curl_hash_destroy(multi->hostcache); + free(multi); + return NULL; + } + return (CURLM *) multi; } @@ -309,6 +347,9 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(!GOOD_EASY_HANDLE(easy_handle)) return CURLM_BAD_EASY_HANDLE; + /* TODO: add some kind of code that prevents a user from being able to + add the same handle more than once! */ + /* Now, time to add an easy handle to the multi stack */ easy = (struct Curl_one_easy *)calloc(sizeof(struct Curl_one_easy), 1); if(!easy) @@ -321,7 +362,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, easy->easy_handle = easy_handle; multistate(easy, CURLM_STATE_INIT); - /* for multi interface connections, we share DNS cache automaticly if the + /* for multi interface connections, we share DNS cache automatically if the easy handle's one is currently private. */ if (easy->easy_handle->dns.hostcache && (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) { @@ -336,6 +377,23 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, easy->easy_handle->dns.hostcachetype = HCACHE_MULTI; } + if(easy->easy_handle->state.connc) { + if(easy->easy_handle->state.connc->type == CONNCACHE_PRIVATE) { + /* kill old private version */ + Curl_rm_connc(easy->easy_handle->state.connc); + /* point out our shared one instead */ + easy->easy_handle->state.connc = multi->connc; + } + /* else it is already using multi? */ + } + else + /* point out our shared one */ + easy->easy_handle->state.connc = multi->connc; + + /* Make sure the type is setup correctly */ + easy->easy_handle->state.connc->type = CONNCACHE_MULTI; + + /* We add this new entry first in the list. We make our 'next' point to the previous next and our 'prev' point back to the 'first' struct */ easy->next = multi->easy.next; @@ -383,6 +441,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, break; easy=easy->next; } + if(easy) { /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ @@ -391,6 +450,15 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, alive connections when this is removed */ multi->num_alive--; + if (easy->easy_handle->state.is_in_pipeline && + easy->state > CURLM_STATE_DO) { + /* If the handle is in a pipeline and has finished sending off its + request, we need to remember the fact that we want to remove this + handle but do the actual removal at a later time */ + easy->easy_handle->state.cancelled = TRUE; + return CURLM_OK; + } + /* The timer must be shut down before easy->multi is set to NULL, else the timenode will remain in the splay tree after curl_easy_cleanup is called. */ @@ -402,13 +470,44 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, easy->easy_handle->dns.hostcachetype = HCACHE_NONE; } - Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association - to this multi handle */ - /* if we have a connection we must call Curl_done() here so that we don't leave a half-baked one around */ - if(easy->easy_conn) + if(easy->easy_conn) { + /* Set up the association right */ + easy->easy_conn->data = easy->easy_handle; Curl_done(&easy->easy_conn, easy->result); + } + + /* If this easy_handle was the last one in charge for one or more + connections a the shared connection cache, we might need to keep this + handle around until either A) the connection is closed and killed + properly, or B) another easy_handle uses the connection. + + The reason why we need to have a easy_handle associated with a live + connection is simply that some connections will need a handle to get + closed down properly. Currently, the only connections that need to keep + a easy_handle handle around are using FTP(S). Such connections have + the PROT_CLOSEACTION bit set. + + Thus, we need to check for all connections in the shared cache that + points to this handle and are using PROT_CLOSEACTION. If there's any, + we need to add this handle to the list of "easy_handls kept around for + nice closure". + */ + if(multi_conn_using(multi, easy->easy_handle)) + /* There's at least one connection using this handle so we must keep + this handle around. We also keep the connection cache pointer + pointing to the shared one since that will be used on close as + well. */ + easy->easy_handle->state.shared_conn = multi; + else + if(easy->easy_handle->state.connc->type == CONNCACHE_MULTI) + /* if this was using the shared connection cache we clear the pointer + to that */ + easy->easy_handle->state.connc = NULL; + + Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association + to this multi handle */ /* make the previous node point to our next */ if(easy->prev) @@ -433,6 +532,11 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, return CURLM_BAD_EASY_HANDLE; /* twasn't found */ } +bool Curl_multi_canPipeline(struct Curl_multi* multi) +{ + return multi->pipelining_enabled; +} + static int waitconnect_getsock(struct connectdata *conn, curl_socket_t *sock, int numsocks) @@ -467,6 +571,16 @@ static int multi_getsock(struct Curl_one_easy *easy, of sockets */ int numsocks) { + if (easy->easy_handle->state.pipe_broke) { + return 0; + } + + if (easy->state > CURLM_STATE_CONNECT && + easy->state < CURLM_STATE_COMPLETED) { + /* Set up ownership correctly */ + easy->easy_conn->data = easy->easy_handle; + } + switch(easy->state) { case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */ default: @@ -488,6 +602,7 @@ static int multi_getsock(struct Curl_one_easy *easy, return domore_getsock(easy->easy_conn, socks, numsocks); case CURLM_STATE_PERFORM: + case CURLM_STATE_WAITPERFORM: return Curl_single_getsock(easy->easy_conn, socks, numsocks); } @@ -553,8 +668,33 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, bool dophase_done; bool done; CURLMcode result = CURLM_OK; + struct Curl_transfer_keeper *k; do { + + if (easy->easy_handle->state.pipe_broke) { + infof(easy->easy_handle, "Pipe broke: handle 0x%x\n", easy); + if(easy->easy_handle->state.is_in_pipeline) { + /* Head back to the CONNECT state */ + multistate(easy, CURLM_STATE_CONNECT); + result = CURLM_CALL_MULTI_PERFORM; + easy->result = CURLE_OK; + } else { + easy->result = CURLE_COULDNT_CONNECT; + multistate(easy, CURLM_STATE_COMPLETED); + } + + easy->easy_handle->state.pipe_broke = FALSE; + easy->easy_conn = NULL; + break; + } + + if (easy->state > CURLM_STATE_CONNECT && + easy->state < CURLM_STATE_COMPLETED) { + /* Make sure we set the connection's current owner */ + easy->easy_conn->data = easy->easy_handle; + } + if (CURLM_STATE_WAITCONNECT <= easy->state && easy->state <= CURLM_STATE_DO && easy->easy_handle->change.url_changed) { @@ -562,6 +702,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_posttransfer(easy->easy_handle); easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + /* We make sure that the pipe broken flag is reset + because in this case, it isn't an actual break */ + easy->easy_handle->state.pipe_broke = FALSE; if(CURLE_OK == easy->result) { gotourl = strdup(easy->easy_handle->change.url); if(gotourl) { @@ -603,19 +746,24 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, &async, &protocol_connect); if(CURLE_OK == easy->result) { + /* Add this handle to the send pipeline */ + Curl_addHandleToPipeline(easy->easy_handle, + easy->easy_conn->send_pipe); + if(async) /* We're now waiting for an asynchronous name lookup */ multistate(easy, CURLM_STATE_WAITRESOLVE); else { /* after the connect has been sent off, go WAITCONNECT unless the protocol connect is already done and we can go directly to - DO! */ + WAITDO! */ result = CURLM_CALL_MULTI_PERFORM; - if(protocol_connect) - multistate(easy, CURLM_STATE_DO); - else + if(protocol_connect) { + multistate(easy, CURLM_STATE_WAITDO); + } else { multistate(easy, CURLM_STATE_WAITCONNECT); + } } } break; @@ -659,7 +807,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch connect */ - easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET, + easy->result = Curl_is_connected(easy->easy_conn, + FIRSTSOCKET, &connected); if(connected) easy->result = Curl_protocol_connect(easy->easy_conn, @@ -680,8 +829,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_PROTOCONNECT); } else { - /* after the connect has completed, go DO */ - multistate(easy, CURLM_STATE_DO); + /* after the connect has completed, go WAITDO */ + multistate(easy, CURLM_STATE_WAITDO); + result = CURLM_CALL_MULTI_PERFORM; } } @@ -692,8 +842,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = Curl_protocol_connecting(easy->easy_conn, &protocol_connect); if(protocol_connect) { - /* after the connect has completed, go DO */ - multistate(easy, CURLM_STATE_DO); + /* after the connect has completed, go WAITDO */ + multistate(easy, CURLM_STATE_WAITDO); result = CURLM_CALL_MULTI_PERFORM; } else if(easy->result) { @@ -705,6 +855,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } break; + case CURLM_STATE_WAITDO: + /* Wait for our turn to DO when we're pipelining requests */ + infof(easy->easy_handle, "Connection #%d: send pipe size = %d\n", + easy->easy_conn->connectindex, + easy->easy_conn->send_pipe->size); + if (!easy->easy_conn->writechannel_inuse && + Curl_isHandleAtHead(easy->easy_handle, + easy->easy_conn->send_pipe)) { + /* Grab the channel */ + easy->easy_conn->writechannel_inuse = TRUE; + multistate(easy, CURLM_STATE_DO); + result = CURLM_CALL_MULTI_PERFORM; + } + break; + case CURLM_STATE_DO: if(easy->easy_handle->set.connect_only) { /* keep connection open for application to use the socket */ @@ -715,7 +880,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* Perform the protocol's DO action */ - easy->result = Curl_do(&easy->easy_conn, &dophase_done); + easy->result = Curl_do(&easy->easy_conn, + &dophase_done); if(CURLE_OK == easy->result) { @@ -726,7 +892,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_OK; } - /* after DO, go PERFORM... or DO_MORE */ + /* after DO, go DO_DONE... or DO_MORE */ else if(easy->easy_conn->bits.do_more) { /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ @@ -734,10 +900,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_OK; } else { - /* we're done with the DO, now PERFORM */ + /* we're done with the DO, now DO_DONE */ easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_PERFORM); + multistate(easy, CURLM_STATE_DO_DONE); result = CURLM_CALL_MULTI_PERFORM; } } @@ -754,7 +920,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DOING: /* we continue DOING until the DO phase is complete */ - easy->result = Curl_protocol_doing(easy->easy_conn, &dophase_done); + easy->result = Curl_protocol_doing(easy->easy_conn, + &dophase_done); if(CURLE_OK == easy->result) { if(dophase_done) { /* after DO, go PERFORM... or DO_MORE */ @@ -765,10 +932,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_OK; } else { - /* we're done with the DO, now PERFORM */ + /* we're done with the DO, now DO_DONE */ easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_PERFORM); + multistate(easy, CURLM_STATE_DO_DONE); result = CURLM_CALL_MULTI_PERFORM; } } @@ -785,11 +952,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DO_MORE: /* Ready to do more? */ - easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET, + easy->result = Curl_is_connected(easy->easy_conn, + SECONDARYSOCKET, &connected); if(connected) { /* - * When we are connected, DO MORE and then go PERFORM + * When we are connected, DO MORE and then go DO_DONE */ easy->result = Curl_do_more(easy->easy_conn); @@ -797,12 +965,38 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_PERFORM); + multistate(easy, CURLM_STATE_DO_DONE); result = CURLM_CALL_MULTI_PERFORM; } } break; + case CURLM_STATE_DO_DONE: + /* Remove ourselves from the send pipeline */ + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->send_pipe); + /* Add ourselves to the recv pipeline */ + Curl_addHandleToPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); + multistate(easy, CURLM_STATE_WAITPERFORM); + result = CURLM_CALL_MULTI_PERFORM; + break; + + case CURLM_STATE_WAITPERFORM: + infof(easy->easy_handle, "Connection #%d: recv pipe size = %d\n", + easy->easy_conn->connectindex, + easy->easy_conn->recv_pipe->size); + /* Wait for our turn to PERFORM */ + if (!easy->easy_conn->readchannel_inuse && + Curl_isHandleAtHead(easy->easy_handle, + easy->easy_conn->recv_pipe)) { + /* Grab the channel */ + easy->easy_conn->readchannel_inuse = TRUE; + multistate(easy, CURLM_STATE_PERFORM); + result = CURLM_CALL_MULTI_PERFORM; + } + break; + case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ Curl_pgrsUpdate(easy->easy_conn); @@ -813,12 +1007,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, ( easy->easy_handle->progress.dlspeed < easy->easy_handle->set.max_recv_speed ) ) ) - multistate(easy, CURLM_STATE_PERFORM); - + multistate(easy, CURLM_STATE_PERFORM); break; case CURLM_STATE_PERFORM: - /* check if over speed */ if ( ( ( easy->easy_handle->set.max_send_speed > 0 ) && ( easy->easy_handle->progress.ulspeed > @@ -837,6 +1029,18 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); + k = &easy->easy_handle->reqdata.keep; + + if (!(k->keepon & KEEP_READ)) { + /* We're done reading */ + easy->easy_conn->readchannel_inuse = FALSE; + } + + if (!(k->keepon & KEEP_WRITE)) { + /* We're done writing */ + easy->easy_conn->writechannel_inuse = FALSE; + } + if(easy->result) { /* The transfer phase returned error, we mark the connection to get * closed to prevent being re-used. This is becasue we can't @@ -852,7 +1056,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_posttransfer(easy->easy_handle); Curl_done(&easy->easy_conn, easy->result); } - else if(TRUE == done) { char *newurl; bool retry = Curl_retry_request(easy->easy_conn, &newurl); @@ -860,13 +1063,18 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* call this even if the readwrite function returned error */ Curl_posttransfer(easy->easy_handle); + if (retry) { + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); + } + /* When we follow redirects, must to go back to the CONNECT state */ - if(easy->easy_conn->newurl || retry) { + if(easy->easy_handle->reqdata.newurl || retry) { if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ - newurl = easy->easy_conn->newurl; - easy->easy_conn->newurl = NULL; + newurl = easy->easy_handle->reqdata.newurl; + easy->easy_handle->reqdata.newurl = NULL; } easy->result = Curl_done(&easy->easy_conn, CURLE_OK); if(easy->result == CURLE_OK) @@ -886,23 +1094,49 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_CALL_MULTI_PERFORM; } } + break; case CURLM_STATE_DONE: - /* post-transfer command */ - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + /* Remove ourselves from the receive pipeline */ + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); + easy->easy_handle->state.is_in_pipeline = FALSE; + + if (easy->easy_conn->bits.stream_was_rewound) { + /* This request read past its response boundary so we quickly + let the other requests consume those bytes since there is no + guarantee that the socket will become active again */ + result = CURLM_CALL_MULTI_PERFORM; + } + + if (!easy->easy_handle->state.cancelled) { + /* post-transfer command */ + easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + + /* after we have DONE what we're supposed to do, go COMPLETED, and + it doesn't matter what the Curl_done() returned! */ + multistate(easy, CURLM_STATE_COMPLETED); + } - /* after we have DONE what we're supposed to do, go COMPLETED, and - it doesn't matter what the Curl_done() returned! */ - multistate(easy, CURLM_STATE_COMPLETED); break; case CURLM_STATE_COMPLETED: + if (easy->easy_handle->state.cancelled) { + /* Go into the CANCELLED state if we were cancelled */ + multistate(easy, CURLM_STATE_CANCELLED); + } + /* this is a completed transfer, it is likely to still be connected */ /* This node should be delinked from the list now and we should post an information message that we are complete. */ break; + + case CURLM_STATE_CANCELLED: + /* Cancelled transfer, wait to be cleaned up */ + break; + default: return CURLM_INTERNAL_ERROR; } @@ -911,7 +1145,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK != easy->result) { /* * If an error was returned, and we aren't in completed state now, - * then we go to completed and consider this transfer aborted. */ + * then we go to completed and consider this transfer aborted. + */ multistate(easy, CURLM_STATE_COMPLETED); } } @@ -934,7 +1169,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, msg->extmsg.msg = CURLMSG_DONE; msg->extmsg.easy_handle = easy->easy_handle; msg->extmsg.data.result = easy->result; - msg->next=NULL; + msg->next = NULL; easy->msg = msg; easy->msg_num = 1; /* there is one unread message here */ @@ -958,7 +1193,17 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { - CURLMcode result = multi_runsingle(multi, easy); + CURLMcode result; + + if (easy->easy_handle->state.cancelled && + easy->state == CURLM_STATE_CANCELLED) { + /* Remove cancelled handles once it's safe to do so */ + easy = easy->next; + Curl_multi_rmeasy(multi_handle, easy->easy_handle); + continue; + } + + result = multi_runsingle(multi, easy); if(result) returncode = result; @@ -975,7 +1220,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) int key = now.tv_sec; /* drop the usec part */ multi->timetree = Curl_splaygetbest(key, multi->timetree, &t); - if (t) { struct SessionHandle *d = t->payload; struct timeval* tv = &d->state.expiretime; @@ -1000,17 +1244,42 @@ void Curl_multi_rmeasy(void *multi_handle, CURL *easy_handle) curl_multi_remove_handle(multi_handle, easy_handle); } + CURLMcode curl_multi_cleanup(CURLM *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; struct Curl_one_easy *nexteasy; + int i; + struct closure *cl; + struct closure *n; if(GOOD_MULTI_HANDLE(multi)) { multi->type = 0; /* not good anymore */ Curl_hash_destroy(multi->hostcache); Curl_hash_destroy(multi->sockhash); +#if 1 + /* go over all connections that have close actions */ + for(i=0; i< multi->connc->num; i++) { + if(multi->connc->connects[i] && + multi->connc->connects[i]->protocol & PROT_CLOSEACTION) + Curl_disconnect(multi->connc->connects[i]); + } + /* now walk through the list of handles we kept around only to be + able to close connections "properly" */ + cl = multi->closure; + while(cl) { + cl->easy_handle->state.shared_conn = NULL; /* no more shared */ + Curl_close(cl->easy_handle); /* close handle */ + n = cl->next; + free(cl); + cl= n; + } +#endif + + Curl_rm_connc(multi->connc); + /* remove all easy handles */ easy = multi->easy.next; while(easy) { @@ -1020,6 +1289,10 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) easy->easy_handle->dns.hostcache = NULL; easy->easy_handle->dns.hostcachetype = HCACHE_NONE; } + + /* Clear the pointer to the connection cache */ + easy->easy_handle->state.connc = NULL; + Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */ if (easy->msg) @@ -1280,6 +1553,9 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, case CURLMOPT_SOCKETDATA: multi->socket_userp = va_arg(param, void *); break; + case CURLMOPT_PIPELINING: + multi->pipelining_enabled = va_arg(param, long); + break; default: res = CURLM_UNKNOWN_OPTION; } @@ -1422,3 +1698,74 @@ CURLMcode curl_multi_assign(CURLM *multi_handle, return CURLM_OK; } + +static bool multi_conn_using(struct Curl_multi *multi, + struct SessionHandle *data) +{ + /* any live CLOSEACTION-connections pointing to the give 'data' ? */ + int i; + + for(i=0; i< multi->connc->num; i++) { + if(multi->connc->connects[i] && + (multi->connc->connects[i]->data == data) && + multi->connc->connects[i]->protocol & PROT_CLOSEACTION) + return TRUE; + } + + return FALSE; +} + +/* add the given data pointer to the list of 'closure handles' that are + kept around only to be able to close some connections nicely */ +void Curl_multi_add_closure(struct Curl_multi *multi, + struct SessionHandle *data) +{ + int i; + struct closure *cl = (struct closure *)calloc(sizeof(struct closure), 1); + struct closure *p=NULL; + struct closure *n; + if(cl) { + cl->easy_handle = data; + cl->next = multi->closure; + multi->closure = cl; + } + + p = multi->closure; + cl = p->next; /* start immediately on the second since the first is the one + we just added and it is _very_ likely to actually exist + used in the cache since that's the whole purpose of adding + it to this list! */ + + /* When adding, scan through all the other currently kept handles and see if + there are any connections still referring to them and kill them if not. */ + while(cl) { + bool inuse = FALSE; + for(i=0; i< multi->connc->num; i++) { + if(multi->connc->connects[i] && + (multi->connc->connects[i]->data == cl->easy_handle)) { + inuse = TRUE; + break; + } + } + + n = cl->next; + + if(!inuse) { + /* cl->easy_handle is now killable */ + infof(data, "Delayed kill of easy handle %p\n", cl->easy_handle); + /* unmark it as not having a connection around that uses it anymore */ + cl->easy_handle->state.shared_conn= NULL; + Curl_close(cl->easy_handle); + if(p) + p->next = n; + else + multi->closure = n; + free(cl); + } + else + p = cl; + + cl = n; + } + +} -- cgit v1.2.1 From dc7c9155531195d8f346e52d753a857891460793 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 8 Sep 2006 05:18:07 +0000 Subject: Compilation fix --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e6b188135..24931a1b6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -193,11 +193,11 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) easy->state = state; +#ifdef CURLDEBUG if(easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) index = easy->easy_conn->connectindex; -#ifdef CURLDEBUG infof(easy->easy_handle, "STATE: %s => %s handle %p; (connection #%d) \n", statename[oldstate], statename[easy->state], -- cgit v1.2.1 From bb87b65f083fdace9301eea95376f62b108405a0 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 9 Sep 2006 13:24:42 +0000 Subject: Compiler warning fix --- lib/multi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 24931a1b6..afbf271f5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -188,7 +188,7 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) "CANCELLED" }; CURLMstate oldstate = easy->state; - int index = -1; + long index = -1; #endif easy->state = state; @@ -199,7 +199,7 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) index = easy->easy_conn->connectindex; infof(easy->easy_handle, - "STATE: %s => %s handle %p; (connection #%d) \n", + "STATE: %s => %s handle %p; (connection #%ld) \n", statename[oldstate], statename[easy->state], (char *)easy, index); #endif @@ -1554,7 +1554,7 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, multi->socket_userp = va_arg(param, void *); break; case CURLMOPT_PIPELINING: - multi->pipelining_enabled = va_arg(param, long); + multi->pipelining_enabled = (bool)(0 != va_arg(param, long)); break; default: res = CURLM_UNKNOWN_OPTION; -- cgit v1.2.1 From 8240cea628cfcfdd962d1dfa4ae40e27ed2e9bfb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 10 Sep 2006 22:15:32 +0000 Subject: Jeff Pohlmeyer presented a *multi_socket()-using program that exposed a problem with it (SIGSEGV-style). It clearly showed that the existing socket-state and state-difference function wasn't good enough so I rewrote it and could then re-run Jeff's program without any crash. The previous version clearly could miss to tell the application when a handle changed from using one socket to using another. While I was at it (as I could use this as a means to track this problem down), I've now added a 'magic' number to the easy handle struct that is inited at curl_easy_init() time and cleared at curl_easy_cleanup() time that we can use internally to detect that an easy handle seems to be fine, or at least not closed or freed (freeing in debug builds fill the area with 0x13 bytes but in normal builds we can of course not assume any particular data in the freed areas). --- lib/multi.c | 197 ++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 118 insertions(+), 79 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index afbf271f5..9e7782af8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -79,9 +79,9 @@ 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 +/* we support N sockets per easy handle. Set the corresponding bit to what action we should wait for */ -#define MAX_SOCKSPEREASYHANDLE 16 +#define MAX_SOCKSPEREASYHANDLE 5 #define GETSOCK_READABLE (0x00ff) #define GETSOCK_WRITABLE (0xff00) @@ -113,14 +113,20 @@ struct Curl_one_easy { from the multi-handle */ int msg_num; /* number of messages left in 'msg' to return */ - struct socketstate sockstate; /* for the socket API magic */ + /* Array with the plain socket numbers this handle takes care of, in no + particular order. Note that all sockets are added to the sockhash, where + the state etc are also kept. This array is mostly used to detect when a + socket is to be removed from the hash. See singlesocket(). */ + curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; + int numsocks; }; #define CURL_MULTI_HANDLE 0x000bab1e #define GOOD_MULTI_HANDLE(x) \ ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE)) -#define GOOD_EASY_HANDLE(x) (x) +#define GOOD_EASY_HANDLE(x) \ + (((struct SessionHandle *)x)->magic == CURLEASY_MAGIC_NUMBER) /* This is the struct known as CURLM on the outside */ struct Curl_multi { @@ -164,6 +170,8 @@ struct Curl_multi { static bool multi_conn_using(struct Curl_multi *multi, struct SessionHandle *data); +static void singlesocket(struct Curl_multi *multi, + struct Curl_one_easy *easy); /* always use this function to change state, to make debugging easier */ static void multistate(struct Curl_one_easy *easy, CURLMstate state) @@ -217,6 +225,7 @@ struct Curl_sh_entry { time_t timestamp; long inuse; int action; /* what action READ/WRITE this socket waits for */ + curl_socket_t socket; /* mainly to ease debugging */ void *socketp; /* settable by users with curl_multi_assign() */ }; /* bits for 'action' having no bits means this socket is not expecting any @@ -225,9 +234,9 @@ struct Curl_sh_entry { #define SH_WRITE 2 /* 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) +static struct Curl_sh_entry *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)); @@ -235,19 +244,22 @@ static int sh_addentry(struct curl_hash *sh, if(there) /* it is present, return fine */ - return 0; + return there; /* not present, add it */ check = calloc(sizeof(struct Curl_sh_entry), 1); if(!check) - return 1; /* major failure */ + return NULL; /* major failure */ check->easy = data; + check->socket = s; /* make/add new hash entry */ - if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) - return 1; /* major failure */ + if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { + free(check); + return NULL; /* major failure */ + } - return 0; /* things are good in sockhash land */ + return check; /* things are good in sockhash land */ } @@ -337,7 +349,6 @@ 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)) @@ -355,8 +366,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(!easy) return CURLM_OUT_OF_MEMORY; - for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) - easy->sockstate.socks[i] = CURL_SOCKET_BAD; + easy->numsocks=0; /* set the easy handle */ easy->easy_handle = easy_handle; @@ -393,7 +403,6 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* Make sure the type is setup correctly */ easy->easy_handle->state.connc->type = CONNCACHE_MULTI; - /* We add this new entry first in the list. We make our 'next' point to the previous next and our 'prev' point back to the 'first' struct */ easy->next = multi->easy.next; @@ -420,6 +429,22 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, return CURLM_OK; } +#if 0 +/* Debug-function, used like this: + * + * Curl_hash_print(multi->sockhash, debug_print_sock_hash); + * + * Enable the hash print function first by editing hash.c + */ +static void debug_print_sock_hash(void *p) +{ + struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; + + fprintf(stderr, " [easy %p/magic %x/socket %d]", + (void *)sh->easy, sh->easy->magic, sh->socket); +} +#endif + CURLMcode curl_multi_remove_handle(CURLM *multi_handle, CURL *curl_handle) { @@ -506,6 +531,12 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, to that */ easy->easy_handle->state.connc = NULL; + /* change state without using multistate(), only to make singlesocket() do + what we want */ + easy->state = CURLM_STATE_COMPLETED; + singlesocket(multi, easy); /* to let the application know what sockets + that vanish with this handle */ + Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association to this multi handle */ @@ -584,6 +615,8 @@ static int multi_getsock(struct Curl_one_easy *easy, switch(easy->state) { case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */ default: + /* this will get called with CURLM_STATE_COMPLETED when a handle is + removed */ return 0; case CURLM_STATE_WAITRESOLVE: @@ -1351,94 +1384,96 @@ static void singlesocket(struct Curl_multi *multi, { struct socketstate current; int i; + struct Curl_sh_entry *entry; + curl_socket_t s; + int num; 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 */ + /* Fill in the 'current' struct with the state as it is now: what sockets to + supervise and for what actions */ current.action = multi_getsock(easy, current.socks, MAX_SOCKSPEREASYHANDLE); - /* when filled in, we compare with the previous round's state in a first - quick memory compare check */ - if(memcmp(¤t, &easy->sockstate, sizeof(struct socketstate))) { + /* We have 0 .. N sockets already and we get to know about the 0 .. M + sockets we should have from now on. Detect the differences, remove no + longer supervised ones and add new ones */ - /* there is 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]; + /* walk over the sockets we got right now */ + for(i=0; (i< MAX_SOCKSPEREASYHANDLE) && + (current.action & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); + i++) { + int action = CURL_POLL_NONE; - /* Ok, this approach is probably too naive and simple-minded but - it might work for a start */ + s = current.socks[i]; - if((easy->sockstate.socks[i] == CURL_SOCKET_BAD) && - (s == CURL_SOCKET_BAD)) { - /* no socket now and there was no socket before */ - break; - } + /* get it from the hash */ + entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); - 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; + if(current.action & GETSOCK_READSOCK(i)) + action |= CURL_POLL_IN; + if(current.action & GETSOCK_WRITESOCK(i)) + action |= CURL_POLL_OUT; - /* the current read/write bits for this particular socket */ - curr = current.action & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)); + if(entry) { + /* yeps, already present so check if it has the same action set */ + if(entry->action == action) + /* same, continue */ + continue; + } + else { + /* this is a socket we didn't have before, add it! */ + entry = sh_addentry(multi->sockhash, s, easy->easy_handle); + if(!entry) + /* fatal */ + return; + } - /* the previous read/write bits for this particular socket */ - prev = easy->sockstate.action & - (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)); + multi->socket_cb(easy->easy_handle, + s, + action, + multi->socket_userp, + entry ? entry->socketp : NULL); - if(curr == prev) - continue; - } + entry->action = action; /* store the current action state */ + } - action = CURL_POLL_NONE; - if(current.action & GETSOCK_READSOCK(i)) - action |= CURL_POLL_IN; - if(current.action & GETSOCK_WRITESOCK(i)) - action |= CURL_POLL_OUT; + num = i; /* number of sockets */ + + /* when we've walked over all the sockets we should have right now, we must + make sure to detect sockets that are removed */ + for(i=0; i< easy->numsocks; i++) { + int j; + s = easy->sockets[i]; + for(j=0; jsocketp) */ - - if(action != CURL_POLL_REMOVE) - /* make sure this socket is present in the hash for this handle */ - sh_addentry(multi->sockhash, s, easy->easy_handle); - - /* call the callback with this new info */ - if(multi->socket_cb) { - struct Curl_sh_entry *entry = - Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); - + } + if(s != CURL_SOCKET_BAD) { + /* this socket has been removed. Remove it */ + + entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + if(entry) { + /* just a precaution, this socket really SHOULD be in the hash already + but in case it isn't, we don't have to tell the app to remove it + either since it never got to know about it */ multi->socket_cb(easy->easy_handle, s, - action, + CURL_POLL_REMOVE, multi->socket_userp, entry ? entry->socketp : NULL); - } - if(action == CURL_POLL_REMOVE) - /* remove from hash for this easy handle */ sh_delentry(multi->sockhash, s); - + } } - /* 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 */ } + memcpy(easy->sockets, current.socks, num*sizeof(curl_socket_t)); + easy->numsocks = num; } static CURLMcode multi_socket(struct Curl_multi *multi, @@ -1477,6 +1512,10 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data = entry->easy; + if(data->magic != CURLEASY_MAGIC_NUMBER) + /* bad bad bad bad bad bad bad */ + return CURLM_INTERNAL_ERROR; + result = multi_runsingle(multi, data->set.one_easy); if(result == CURLM_OK) -- cgit v1.2.1 From 29dc39fce1126265d8526be15beec3e3fdc1c11d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 11 Sep 2006 17:18:18 +0000 Subject: - Fixed my breakage from earlier today so that doing curl_easy_cleanup() on a handle that is part of a multi handle first removes the handle from the stack. - Added CURLOPT_SSL_SESSIONID_CACHE and --no-sessionid to disable SSL session-ID re-use on demand since there obviously are broken servers out there that misbehave with session-IDs used. --- lib/multi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9e7782af8..312e577d7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -705,6 +705,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, do { + if(!GOOD_EASY_HANDLE(easy->easy_handle)) + return CURLE_BAD_FUNCTION_ARGUMENT; + if (easy->easy_handle->state.pipe_broke) { infof(easy->easy_handle, "Pipe broke: handle 0x%x\n", easy); if(easy->easy_handle->state.is_in_pipeline) { @@ -1231,8 +1234,9 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if (easy->easy_handle->state.cancelled && easy->state == CURLM_STATE_CANCELLED) { /* Remove cancelled handles once it's safe to do so */ - easy = easy->next; Curl_multi_rmeasy(multi_handle, easy->easy_handle); + easy->easy_handle = NULL; + easy = easy->next; continue; } -- cgit v1.2.1 From 733a184ce0747c65fbc634e066cfac0ffae43d80 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 12 Sep 2006 23:51:01 +0000 Subject: Compiler warning fix --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 312e577d7..77c6a9fb4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -706,7 +706,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, do { if(!GOOD_EASY_HANDLE(easy->easy_handle)) - return CURLE_BAD_FUNCTION_ARGUMENT; + return CURLM_BAD_EASY_HANDLE; if (easy->easy_handle->state.pipe_broke) { infof(easy->easy_handle, "Pipe broke: handle 0x%x\n", easy); -- cgit v1.2.1 From 2d5fc39d3573c10a460cdeb2139631da62b8e391 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 16 Sep 2006 21:50:29 +0000 Subject: Resize the connection cache upwards when adding more handles than what currently fits in the cache, to make the cache work better especially for pipelining cases but also for "mere" (persistent) connection re-use. --- lib/multi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 77c6a9fb4..2f7f32870 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -423,6 +423,18 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* increase the node-counter */ multi->num_easy++; + + if((multi->num_easy+5) > multi->connc->num) { + /* we want the connection cache to have room for all easy transfers, and + some more so we have a margin of 5 for now, but we add the new amount + plus 10 to not have to do it for every new handle added */ + CURLcode res = Curl_ch_connc(easy_handle, multi->connc, + multi->num_easy + 10); + if(res) + /* TODO: we need to do some cleaning up here! */ + return res; + } + /* increase the alive-counter */ multi->num_alive++; -- cgit v1.2.1 From 71920d61e6c0f38f91a7ba471505de031d568ff7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 20 Sep 2006 12:03:50 +0000 Subject: Michael Wallner's test program again help me track down a problem. This time it basically was that we didn't remove the current connection from the pipe list when following a redirect. Also in this commit: several cases of additional debug code for debug builds helping to check and track down some signs of run-time trouble. --- lib/multi.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2f7f32870..2da17a3f0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -424,12 +424,12 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* increase the node-counter */ multi->num_easy++; - if((multi->num_easy+5) > multi->connc->num) { - /* we want the connection cache to have room for all easy transfers, and - some more so we have a margin of 5 for now, but we add the new amount - plus 10 to not have to do it for every new handle added */ + if((multi->num_easy * 4) > multi->connc->num) { + /* We want the connection cache to have plenty room. Before we supported + the shared cache every single easy handle had 5 entries in their cache + by default. */ CURLcode res = Curl_ch_connc(easy_handle, multi->connc, - multi->num_easy + 10); + multi->connc->num*4); if(res) /* TODO: we need to do some cleaning up here! */ return res; @@ -1111,13 +1111,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* call this even if the readwrite function returned error */ Curl_posttransfer(easy->easy_handle); - if (retry) { - Curl_removeHandleFromPipeline(easy->easy_handle, - easy->easy_conn->recv_pipe); - } - /* When we follow redirects, must to go back to the CONNECT state */ if(easy->easy_handle->reqdata.newurl || retry) { + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ -- cgit v1.2.1 From ab798fe5ba15c34a890f022b527a3d2a6f37dce3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 21 Sep 2006 20:52:58 +0000 Subject: (FTP) a failed upload does not invalidate the control connection --- lib/multi.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2da17a3f0..0a19c129e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -512,7 +512,15 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, if(easy->easy_conn) { /* Set up the association right */ easy->easy_conn->data = easy->easy_handle; + + /* Curl_done() clears the conn->data field to lose the association + between the easy handle and the connection */ Curl_done(&easy->easy_conn, easy->result); + + if(easy->easy_conn) + /* the connection is still alive, set back the association to enable + the check below to trigger TRUE */ + easy->easy_conn->data = easy->easy_handle; } /* If this easy_handle was the last one in charge for one or more @@ -528,8 +536,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, Thus, we need to check for all connections in the shared cache that points to this handle and are using PROT_CLOSEACTION. If there's any, - we need to add this handle to the list of "easy_handls kept around for - nice closure". + we need to add this handle to the list of "easy handles kept around for + nice connection closures". */ if(multi_conn_using(multi, easy->easy_handle)) /* There's at least one connection using this handle so we must keep @@ -1011,6 +1019,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK == easy->result) easy->result = Curl_readwrite_init(easy->easy_conn); + else + /* Remove ourselves from the send pipeline */ + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->send_pipe); if(CURLE_OK == easy->result) { multistate(easy, CURLM_STATE_DO_DONE); @@ -1167,10 +1179,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_COMPLETED: - if (easy->easy_handle->state.cancelled) { + if (easy->easy_handle->state.cancelled) /* Go into the CANCELLED state if we were cancelled */ multistate(easy, CURLM_STATE_CANCELLED); - } /* this is a completed transfer, it is likely to still be connected */ -- cgit v1.2.1 From b2ca777a08c9a23a9d9b90fc8106f3305744fec2 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Mon, 25 Sep 2006 00:16:23 +0000 Subject: Compiler warning fix --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0a19c129e..244994cfe 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -430,9 +430,9 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, by default. */ CURLcode res = Curl_ch_connc(easy_handle, multi->connc, multi->connc->num*4); - if(res) + if(res != CURLE_OK) /* TODO: we need to do some cleaning up here! */ - return res; + return CURLM_OUT_OF_MEMORY; } /* increase the alive-counter */ -- cgit v1.2.1 From ae13c93b7db9f9c68eaf95150ed551b3b649d8c4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 28 Sep 2006 21:26:06 +0000 Subject: Reported in #1561470 (http://curl.haxx.se/bug/view.cgi?id=1561470), libcurl would crash if a bad function sequence was used when shutting down after using the multi interface (i.e using easy_cleanup after multi_cleanup) so precautions have been added to make sure it doesn't any more - test case 529 was added to verify. --- lib/multi.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 244994cfe..9d596401a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -172,6 +172,8 @@ static bool multi_conn_using(struct Curl_multi *multi, struct SessionHandle *data); static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy); +static void add_closure(struct Curl_multi *multi, + struct SessionHandle *data); /* always use this function to change state, to make debugging easier */ static void multistate(struct Curl_one_easy *easy, CURLMstate state) @@ -539,17 +541,28 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, we need to add this handle to the list of "easy handles kept around for nice connection closures". */ - if(multi_conn_using(multi, easy->easy_handle)) + if(multi_conn_using(multi, easy->easy_handle)) { /* There's at least one connection using this handle so we must keep this handle around. We also keep the connection cache pointer pointing to the shared one since that will be used on close as well. */ easy->easy_handle->state.shared_conn = multi; - else - if(easy->easy_handle->state.connc->type == CONNCACHE_MULTI) - /* if this was using the shared connection cache we clear the pointer - to that */ - easy->easy_handle->state.connc = NULL; + + /* this handle is still being used by a shared connection cache and + thus we leave it around for now */ + add_closure(multi, easy->easy_handle); + } + + if(easy->easy_handle->state.connc->type == CONNCACHE_MULTI) { + /* if this was using the shared connection cache we clear the pointer + to that since we're not part of that handle anymore */ + easy->easy_handle->state.connc = NULL; + + /* and modify the connectindex since this handle can't point to the + connection cache anymore */ + if(easy->easy_conn) + easy->easy_conn->connectindex = -1; + } /* change state without using multistate(), only to make singlesocket() do what we want */ @@ -1320,15 +1333,20 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) /* go over all connections that have close actions */ for(i=0; i< multi->connc->num; i++) { if(multi->connc->connects[i] && - multi->connc->connects[i]->protocol & PROT_CLOSEACTION) + multi->connc->connects[i]->protocol & PROT_CLOSEACTION) { Curl_disconnect(multi->connc->connects[i]); + multi->connc->connects[i] = NULL; + } } /* now walk through the list of handles we kept around only to be able to close connections "properly" */ cl = multi->closure; while(cl) { cl->easy_handle->state.shared_conn = NULL; /* no more shared */ - Curl_close(cl->easy_handle); /* close handle */ + if(cl->easy_handle->state.closed) + /* close handle only if curl_easy_cleanup() already has been called + for this easy handle */ + Curl_close(cl->easy_handle); n = cl->next; free(cl); cl= n; @@ -1780,8 +1798,8 @@ static bool multi_conn_using(struct Curl_multi *multi, /* add the given data pointer to the list of 'closure handles' that are kept around only to be able to close some connections nicely */ -void Curl_multi_add_closure(struct Curl_multi *multi, - struct SessionHandle *data) +static void add_closure(struct Curl_multi *multi, + struct SessionHandle *data) { int i; struct closure *cl = (struct closure *)calloc(sizeof(struct closure), 1); -- cgit v1.2.1 From 552b963e6defebd6d0d6d41f8c74798d856c313c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 4 Oct 2006 21:11:08 +0000 Subject: Dmitriy Sergeyev provided an example source code that crashed CVS libcurl but that worked nicely in 7.15.5. I converted it into test case 532 and fixed the problem. --- lib/multi.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9d596401a..46fd255f3 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -351,6 +351,8 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; + struct closure *cl; + struct closure *prev=NULL; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -368,7 +370,21 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(!easy) return CURLM_OUT_OF_MEMORY; - easy->numsocks=0; + cl = multi->closure; + while(cl) { + struct closure *next = cl->next; + if(cl->easy_handle == easy_handle) { + /* remove this handle from the closure list */ + free(cl); + if(prev) + prev->next = next; + else + multi->closure = next; + break; /* no need to continue since this handle can only be present once + in the list */ + } + cl = next; + } /* set the easy handle */ easy->easy_handle = easy_handle; @@ -1796,8 +1812,10 @@ static bool multi_conn_using(struct Curl_multi *multi, return FALSE; } -/* add the given data pointer to the list of 'closure handles' that are - kept around only to be able to close some connections nicely */ +/* Add the given data pointer to the list of 'closure handles' that are kept + around only to be able to close some connections nicely - just make sure + that this handle isn't already added, like for the cases when an easy + handle is removed, added and removed again... */ static void add_closure(struct Curl_multi *multi, struct SessionHandle *data) { -- cgit v1.2.1 From befc30bc55cebe958d652ec4f5d31aa8565773c1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 6 Oct 2006 21:19:57 +0000 Subject: Bogdan Nicula's hanging test case was converted to test case 533 and the test now runs fine. --- lib/multi.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 46fd255f3..620c08277 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -942,9 +942,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITDO: /* Wait for our turn to DO when we're pipelining requests */ - infof(easy->easy_handle, "Connection #%d: send pipe size = %d\n", +#ifdef CURLDEBUG + infof(easy->easy_handle, "Conn %d send pipe %d inuse %d athead %d\n", easy->easy_conn->connectindex, - easy->easy_conn->send_pipe->size); + easy->easy_conn->send_pipe->size, + easy->easy_conn->writechannel_inuse, + Curl_isHandleAtHead(easy->easy_handle, + easy->easy_conn->send_pipe)); +#endif if (!easy->easy_conn->writechannel_inuse && Curl_isHandleAtHead(easy->easy_handle, easy->easy_conn->send_pipe)) { @@ -1232,6 +1237,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * If an error was returned, and we aren't in completed state now, * then we go to completed and consider this transfer aborted. */ + if(easy->easy_conn) { + /* if this has a connection, unsubscribe from the pipelines */ + easy->easy_conn->writechannel_inuse = FALSE; + easy->easy_conn->readchannel_inuse = FALSE; + } multistate(easy, CURLM_STATE_COMPLETED); } } @@ -1345,7 +1355,6 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) Curl_hash_destroy(multi->hostcache); Curl_hash_destroy(multi->sockhash); -#if 1 /* go over all connections that have close actions */ for(i=0; i< multi->connc->num; i++) { if(multi->connc->connects[i] && @@ -1367,7 +1376,6 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) free(cl); cl= n; } -#endif Curl_rm_connc(multi->connc); -- cgit v1.2.1 From 1128029599b1c7e65bf304a3d8bcf2e6aadce72a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 7 Oct 2006 21:04:57 +0000 Subject: don't display or act on state changes that doesn't actually change state --- lib/multi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 620c08277..ad6bebca0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -197,9 +197,13 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) "COMPLETED", "CANCELLED" }; - CURLMstate oldstate = easy->state; long index = -1; #endif + CURLMstate oldstate = easy->state; + + if(oldstate == state) + /* don't bother when the new state is the same as the old state */ + return; easy->state = state; -- cgit v1.2.1 From a1de9367ecc17ae1d77e46b76fb1aba6c9f3ccb2 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 9 Oct 2006 06:58:05 +0000 Subject: Bogdan Nicula's second test case (posted Sun, 08 Oct 2006) converted to test case 535 and it now runs fine. Again a problem with the pipelining code not taking all possible (error) conditions into account. --- lib/multi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ad6bebca0..48863c8e6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1081,9 +1081,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_WAITPERFORM: - infof(easy->easy_handle, "Connection #%d: recv pipe size = %d\n", +#ifdef CURLDEBUG + infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n", easy->easy_conn->connectindex, - easy->easy_conn->recv_pipe->size); + easy->easy_conn->recv_pipe->size, + easy->easy_conn->readchannel_inuse, + Curl_isHandleAtHead(easy->easy_handle, + easy->easy_conn->recv_pipe)); +#endif /* Wait for our turn to PERFORM */ if (!easy->easy_conn->readchannel_inuse && Curl_isHandleAtHead(easy->easy_handle, -- cgit v1.2.1 From 15e3dfe1d34040165d5ef12c69e8b91dc8eda8ef Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Mon, 9 Oct 2006 11:21:40 +0000 Subject: Compiler warning fix --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 48863c8e6..041209c49 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -377,7 +377,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, cl = multi->closure; while(cl) { struct closure *next = cl->next; - if(cl->easy_handle == easy_handle) { + if(cl->easy_handle == (struct SessionHandle *)easy_handle) { /* remove this handle from the closure list */ free(cl); if(prev) -- cgit v1.2.1 From 7d0c58a285b59c085904e9ee388797b3701d0161 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 9 Oct 2006 21:24:34 +0000 Subject: when going to completed due to error, mark the handle as not in a pipeline anymore --- lib/multi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 041209c49..b92c050de 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1246,6 +1246,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * If an error was returned, and we aren't in completed state now, * then we go to completed and consider this transfer aborted. */ + easy->easy_handle->state.is_in_pipeline = FALSE; if(easy->easy_conn) { /* if this has a connection, unsubscribe from the pipelines */ easy->easy_conn->writechannel_inuse = FALSE; -- cgit v1.2.1 From 1ce7b48057cb3aa71e71d841ed584d3414a86ae6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 10 Oct 2006 14:23:34 +0000 Subject: mark the handle as no longer having a broken pipe when a transfer has failed --- lib/multi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b92c050de..e901bddfd 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1247,6 +1247,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * then we go to completed and consider this transfer aborted. */ easy->easy_handle->state.is_in_pipeline = FALSE; + easy->easy_handle->state.pipe_broke = FALSE; + if(easy->easy_conn) { /* if this has a connection, unsubscribe from the pipelines */ easy->easy_conn->writechannel_inuse = FALSE; -- cgit v1.2.1 From b61c06384ab88baf4b3231e84386c4a70126d888 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 12 Oct 2006 08:36:47 +0000 Subject: Jeff Pohlmeyer has been working with the hiperfifo.c example source code, and while doing so it became apparent that the current timeout system for the socket API really was a bit awkward since it become quite some work to be sure we have the correct timeout set. Jeff then provided the new CURLMOPT_TIMERFUNCTION that is yet another callback the app can set to get to know when the general timeout time changes and thus for an application like hiperfifo.c it makes everything a lot easier and nicer. There's a CURLMOPT_TIMERDATA option too of course in good old libcurl tradition. --- lib/multi.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 12 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e901bddfd..aaa80b228 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -166,6 +166,12 @@ struct Curl_multi { /* list of easy handles kept around for doing nice connection closures */ struct closure *closure; + + /* timer callback and user data pointer for the *socket() API */ + curl_multi_timer_callback timer_cb; + void *timer_userp; + time_t timer_lastcall; /* the fixed time for the timeout for the previous + callback */ }; static bool multi_conn_using(struct Curl_multi *multi, @@ -174,6 +180,7 @@ static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy); static void add_closure(struct Curl_multi *multi, struct SessionHandle *data); +static int update_timer(struct Curl_multi *multi); /* always use this function to change state, to make debugging easier */ static void multistate(struct Curl_one_easy *easy, CURLMstate state) @@ -460,6 +467,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* increase the alive-counter */ multi->num_alive++; + update_timer(multi); return CURLM_OK; } @@ -610,6 +618,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, multi->num_easy--; /* one less to care about now */ + update_timer(multi); return CURLM_OK; } else @@ -1342,6 +1351,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) *running_handles = multi->num_alive; + if ( CURLM_OK == returncode ) + update_timer(multi); return returncode; } @@ -1673,8 +1684,15 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, case CURLMOPT_PIPELINING: multi->pipelining_enabled = (bool)(0 != va_arg(param, long)); break; + case CURLMOPT_TIMERFUNCTION: + multi->timer_cb = va_arg(param, curl_multi_timer_callback); + break; + case CURLMOPT_TIMERDATA: + multi->timer_userp = va_arg(param, void *); + break; default: res = CURLM_UNKNOWN_OPTION; + break; } va_end(param); return res; @@ -1684,26 +1702,26 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles) { - return multi_socket((struct Curl_multi *)multi_handle, FALSE, s, - running_handles); + CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, + running_handles); + if (CURLM_OK == result) + update_timer((struct Curl_multi *)multi_handle); + return result; } CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) { - return multi_socket((struct Curl_multi *)multi_handle, - TRUE, CURL_SOCKET_BAD, running_handles); + CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, + TRUE, CURL_SOCKET_BAD, running_handles); + if (CURLM_OK == result) + update_timer((struct Curl_multi *)multi_handle); + return result; } -CURLMcode curl_multi_timeout(CURLM *multi_handle, - long *timeout_ms) +static CURLMcode multi_timeout(struct Curl_multi *multi, + 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(); @@ -1724,6 +1742,44 @@ CURLMcode curl_multi_timeout(CURLM *multi_handle, return CURLM_OK; } +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; + + return multi_timeout(multi, timeout_ms); +} + +/* + * Tell the application it should update its timers, if it subscribes to the + * update timer callback. + */ +static int update_timer(struct Curl_multi *multi) +{ + long timeout_ms; + if (!multi->timer_cb) + return 0; + if ( multi_timeout(multi, &timeout_ms) != CURLM_OK ) + return -1; + if ( timeout_ms < 0 ) + return 0; + + /* When multi_timeout() is done, multi->timetree points to the node with the + * timeout we got the (relative) time-out time for. We can thus easily check + * if this is the same (fixed) time as we got in a previous call and then + * avoid calling the callback again. */ + if(multi->timetree->key == multi->timer_lastcall) + return 0; + + multi->timer_lastcall = multi->timetree->key; + + return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp); +} + /* 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) -- cgit v1.2.1 From ab60a124654dc0326d97271ebd30bb0984f78d8b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 12 Oct 2006 14:30:47 +0000 Subject: Starting now, adding an easy handle to a multi stack that was already added to a multi stack will cause CURLM_BAD_EASY_HANDLE to get returned. --- lib/multi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index aaa80b228..7272f471a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -373,8 +373,10 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(!GOOD_EASY_HANDLE(easy_handle)) return CURLM_BAD_EASY_HANDLE; - /* TODO: add some kind of code that prevents a user from being able to - add the same handle more than once! */ + /* Prevent users to add the same handle more than once! */ + if(((struct SessionHandle *)easy_handle)->multi) + /* possibly we should create a new unique error code for this condition */ + return CURLM_BAD_EASY_HANDLE; /* Now, time to add an easy handle to the multi stack */ easy = (struct Curl_one_easy *)calloc(sizeof(struct Curl_one_easy), 1); -- cgit v1.2.1 From efe3cb6e1ae672a48746c6000b1aedd2742deac7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 13 Oct 2006 07:11:26 +0000 Subject: Added curl_multi_dump() when built with CURLDEBUG - this is not a stable public function, this is only meant to allow easier tracking of the internal handle's state and what sockets they use. Only for research and development. --- lib/multi.c | 101 ++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 33 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 7272f471a..69f8699a0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -85,11 +85,6 @@ typedef enum { #define GETSOCK_READABLE (0x00ff) #define GETSOCK_WRITABLE (0xff00) -struct socketstate { - curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; - unsigned int action; /* socket action bitmap */ -}; - struct closure { struct closure *next; /* a simple one-way list of structs */ struct SessionHandle *easy_handle; @@ -182,28 +177,33 @@ static void add_closure(struct Curl_multi *multi, struct SessionHandle *data); static int update_timer(struct Curl_multi *multi); +#ifdef CURLDEBUG +static const char *statename[]={ + "INIT", + "CONNECT", + "WAITRESOLVE", + "WAITCONNECT", + "PROTOCONNECT", + "WAITDO", + "DO", + "DOING", + "DO_MORE", + "DO_DONE", + "WAITPERFORM", + "PERFORM", + "TOOFAST", + "DONE", + "COMPLETED", + "CANCELLED" +}; + +void curl_multi_dump(CURLM *multi_handle); +#endif + /* always use this function to change state, to make debugging easier */ static void multistate(struct Curl_one_easy *easy, CURLMstate state) { #ifdef CURLDEBUG - const char *statename[]={ - "INIT", - "CONNECT", - "WAITRESOLVE", - "WAITCONNECT", - "PROTOCONNECT", - "WAITDO", - "DO", - "DOING", - "DO_MORE", - "DO_DONE", - "WAITPERFORM", - "PERFORM", - "TOOFAST", - "DONE", - "COMPLETED", - "CANCELLED" - }; long index = -1; #endif CURLMstate oldstate = easy->state; @@ -1473,19 +1473,20 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy) { - struct socketstate current; + curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; int i; struct Curl_sh_entry *entry; curl_socket_t s; int num; + unsigned int curraction; - memset(¤t, 0, sizeof(current)); + memset(&socks, 0, sizeof(socks)); for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) - current.socks[i] = CURL_SOCKET_BAD; + socks[i] = CURL_SOCKET_BAD; /* Fill in the 'current' struct with the state as it is now: what sockets to supervise and for what actions */ - current.action = multi_getsock(easy, current.socks, MAX_SOCKSPEREASYHANDLE); + curraction = multi_getsock(easy, socks, MAX_SOCKSPEREASYHANDLE); /* We have 0 .. N sockets already and we get to know about the 0 .. M sockets we should have from now on. Detect the differences, remove no @@ -1493,18 +1494,18 @@ static void singlesocket(struct Curl_multi *multi, /* walk over the sockets we got right now */ for(i=0; (i< MAX_SOCKSPEREASYHANDLE) && - (current.action & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); + (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); i++) { int action = CURL_POLL_NONE; - s = current.socks[i]; + s = socks[i]; /* get it from the hash */ entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); - if(current.action & GETSOCK_READSOCK(i)) + if(curraction & GETSOCK_READSOCK(i)) action |= CURL_POLL_IN; - if(current.action & GETSOCK_WRITESOCK(i)) + if(curraction & GETSOCK_WRITESOCK(i)) action |= CURL_POLL_OUT; if(entry) { @@ -1538,7 +1539,7 @@ static void singlesocket(struct Curl_multi *multi, int j; s = easy->sockets[i]; for(j=0; jsockets, current.socks, num*sizeof(curl_socket_t)); + memcpy(easy->sockets, socks, num*sizeof(curl_socket_t)); easy->numsocks = num; } @@ -1946,3 +1947,37 @@ static void add_closure(struct Curl_multi *multi, } } + +#ifdef CURLDEBUG +void curl_multi_dump(CURLM *multi_handle) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + int i; + fprintf(stderr, "* Multi status: %d handles, %d alive\n", + multi->num_easy, multi->num_alive); + for(easy=multi->easy.next; easy; easy = easy->next) { + if(easy->state != CURLM_STATE_COMPLETED) { + /* only display handles that are not completed */ + fprintf(stderr, "handle %p, state %s, %d sockets\n", + (void *)easy, statename[easy->state], easy->numsocks); + for(i=0; i < easy->numsocks; i++) { + curl_socket_t s = easy->sockets[i]; + struct Curl_sh_entry *entry = + Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + + fprintf(stderr, "%d ", (int)s); + if(!entry) { + fprintf(stderr, "INTERNAL CONFUSION\n"); + continue; + } + fprintf(stderr, "[%s %s] ", + entry->action&CURL_POLL_IN?"RECVING":"", + entry->action&CURL_POLL_OUT?"SENDING":""); + } + if(easy->numsocks) + fprintf(stderr, "\n"); + } + } +} +#endif -- cgit v1.2.1 From 86f93a53d6a18827668784b98d4fc6824bbaa417 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 13 Oct 2006 14:54:36 +0000 Subject: print the actual (externally known) easy handle and not the internal container for it --- lib/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 69f8699a0..eeb30fba6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1960,7 +1960,8 @@ void curl_multi_dump(CURLM *multi_handle) if(easy->state != CURLM_STATE_COMPLETED) { /* only display handles that are not completed */ fprintf(stderr, "handle %p, state %s, %d sockets\n", - (void *)easy, statename[easy->state], easy->numsocks); + (void *)easy->easy_handle, + statename[easy->state], easy->numsocks); for(i=0; i < easy->numsocks; i++) { curl_socket_t s = easy->sockets[i]; struct Curl_sh_entry *entry = -- cgit v1.2.1 From 4bdd7596d305471a806f247d3a46395d7c7542e7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 18 Oct 2006 15:11:24 +0000 Subject: the expire timer is a bit too annoying to see all the time ;-) --- lib/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index eeb30fba6..6daabd00b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1845,9 +1845,10 @@ void Curl_expire(struct SessionHandle *data, long milli) } *nowp = set; +#if 0 infof(data, "Expire at %ld / %ld (%ldms)\n", (long)nowp->tv_sec, (long)nowp->tv_usec, milli); - +#endif data->state.timenode.payload = data; multi->timetree = Curl_splayinsert((int)nowp->tv_sec, multi->timetree, -- cgit v1.2.1 From e1edd41e1b9357cec2d42762e8ae67e356693941 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 23 Oct 2006 20:34:56 +0000 Subject: Ravi Pratap provided a major update with pipelining fixes. We also no longer re-use connections (for pipelining) before the name resolving is done. --- lib/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 6daabd00b..a7cc25562 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -773,7 +773,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, return CURLM_BAD_EASY_HANDLE; if (easy->easy_handle->state.pipe_broke) { - infof(easy->easy_handle, "Pipe broke: handle 0x%x\n", easy); + infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n", + easy, easy->easy_handle->reqdata.path); if(easy->easy_handle->state.is_in_pipeline) { /* Head back to the CONNECT state */ multistate(easy, CURLM_STATE_CONNECT); -- cgit v1.2.1 From e4505aefd9dc81eb2c51f2739f8dc626f7c3ce93 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 Dec 2006 15:36:26 +0000 Subject: Jared Lundell filed bug report #1604956 (http://curl.haxx.se/bug/view.cgi?id=1604956) which identified setting CURLOPT_MAXCONNECTS to zero caused libcurl to SIGSEGV. Starting now, libcurl will always internally use no less than 1 entry in the connection cache. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a7cc25562..0411cc908 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -347,7 +347,7 @@ CURLM *curl_multi_init(void) return NULL; } - multi->connc = Curl_mk_connc(CONNCACHE_MULTI); + multi->connc = Curl_mk_connc(CONNCACHE_MULTI, -1); if(!multi->connc) { Curl_hash_destroy(multi->hostcache); free(multi); -- cgit v1.2.1 From 385e612fa5b7663fc2bc815677b8c27bec2f0fe4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 16 Jan 2007 22:22:10 +0000 Subject: - Armel Asselin improved libcurl to behave a lot better when an easy handle doing an FTP transfer is removed from a multi handle before completion. The fix also fixed the "alive counter" to be correct on "premature removal" for all protocols. --- lib/multi.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0411cc908..e10f5e434 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -512,9 +512,11 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, } if(easy) { + bool premature = easy->state != CURLM_STATE_COMPLETED; + /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ - if(easy->state != CURLM_STATE_COMPLETED) + if(premature) /* this handle is "alive" so we need to count down the total number of alive connections when this is removed */ multi->num_alive--; @@ -547,7 +549,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* Curl_done() clears the conn->data field to lose the association between the easy handle and the connection */ - Curl_done(&easy->easy_conn, easy->result); + Curl_done(&easy->easy_conn, easy->result, premature); if(easy->easy_conn) /* the connection is still alive, set back the association to enable @@ -802,7 +804,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, char *gotourl; Curl_posttransfer(easy->easy_handle); - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); /* We make sure that the pipe broken flag is reset because in this case, it isn't an actual break */ easy->easy_handle->state.pipe_broke = FALSE; @@ -950,7 +952,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else if(easy->result) { /* failure detected */ Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); + Curl_done(&easy->easy_conn, easy->result, FALSE); Curl_disconnect(easy->easy_conn); /* close the connection */ easy->easy_conn = NULL; /* no more connection */ } @@ -1017,7 +1019,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* failure detected */ Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); + Curl_done(&easy->easy_conn, easy->result, FALSE); Curl_disconnect(easy->easy_conn); /* close the connection */ easy->easy_conn = NULL; /* no more connection */ } @@ -1050,7 +1052,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* failure detected */ Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); + Curl_done(&easy->easy_conn, easy->result, FALSE); Curl_disconnect(easy->easy_conn); /* close the connection */ easy->easy_conn = NULL; /* no more connection */ } @@ -1169,7 +1171,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; } Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); + Curl_done(&easy->easy_conn, easy->result, FALSE); } else if(TRUE == done) { char *newurl; @@ -1188,7 +1190,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, newurl = easy->easy_handle->reqdata.newurl; easy->easy_handle->reqdata.newurl = NULL; } - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); if(easy->result == CURLE_OK) easy->result = Curl_follow(easy->easy_handle, newurl, retry); if(CURLE_OK == easy->result) { @@ -1224,7 +1226,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if (!easy->easy_handle->state.cancelled) { /* post-transfer command */ - easy->result = Curl_done(&easy->easy_conn, CURLE_OK); + easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the Curl_done() returned! */ -- cgit v1.2.1 From cdbbb7d9006bad8608edb794193ffa629da66a53 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 27 Jan 2007 03:14:25 +0000 Subject: Compiler warning fix --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e10f5e434..b501f296f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -512,7 +512,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, } if(easy) { - bool premature = easy->state != CURLM_STATE_COMPLETED; + bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED); /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ -- cgit v1.2.1 From c7d096620138199b06175ae57fd2385d3cb1ee63 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 12 Feb 2007 12:15:41 +0000 Subject: - Jeff Pohlmeyer fixed a flaw in curl_multi_add_handle() when adding a handle that has an easy handle present in the "closure" list pending closure. --- lib/multi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b501f296f..d32172288 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -396,6 +396,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, break; /* no need to continue since this handle can only be present once in the list */ } + prev = cl; cl = next; } -- cgit v1.2.1 From cbf58d88d0a84042d9dd8a7a6b1644f15242cf02 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 18 Feb 2007 23:02:42 +0000 Subject: - Jeff Pohlmeyer identified two problems: first a rather obscure problem with the multi interface and connection re-use that could make a curl_multi_remove_handle() ruin a pointer in another handle. The second problem was less of an actual problem but more of minor quirk: the re-using of connections wasn't properly checking if the connection was marked for closure. --- lib/multi.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d32172288..49db7d064 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -542,11 +542,10 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, easy->easy_handle->dns.hostcachetype = HCACHE_NONE; } - /* if we have a connection we must call Curl_done() here so that we - don't leave a half-baked one around */ - if(easy->easy_conn) { - /* Set up the association right */ - easy->easy_conn->data = easy->easy_handle; + /* we must call Curl_done() here (if we still "own it") so that we don't + leave a half-baked one around */ + if(easy->easy_conn && + (easy->easy_conn->data == easy->easy_handle)) { /* Curl_done() clears the conn->data field to lose the association between the easy handle and the connection */ -- cgit v1.2.1 From f19d333ef6b067809cb2b0c153fbd3f5db4321a1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 21 Feb 2007 21:59:40 +0000 Subject: - Ravi Pratap provided work on libcurl making pipelining more robust and fixing some bugs: o Don't mix GET and POST requests in a pipeline o Fix the order in which requests are dispatched from the pipeline o Fixed several curl bugs with pipelining when the server is returning chunked encoding: * Added states to chunked parsing for final CRLF * Rewind buffer after parsing chunk with data remaining * Moved chunked header initializing to a spot just before receiving headers --- lib/multi.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 49db7d064..2a7f50baa 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -354,6 +354,12 @@ CURLM *curl_multi_init(void) return NULL; } + /* Let's make the doubly-linked list a circular list. This makes + the linked list code simpler and allows inserting at the end + with less work (we didn't keep a tail pointer before). */ + multi->easy.next = &multi->easy; + multi->easy.prev = &multi->easy; + return (CURLM *) multi; } @@ -435,18 +441,21 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* Make sure the type is setup correctly */ easy->easy_handle->state.connc->type = CONNCACHE_MULTI; - /* We add this new entry first in the list. We make our 'next' point to the - previous next and our 'prev' point back to the 'first' struct */ - easy->next = multi->easy.next; - easy->prev = &multi->easy; + /* This adds the new entry at the back of the list + to try and maintain a FIFO queue so the pipelined + requests are in order. */ + + /* We add this new entry last in the list. We make our 'next' point to the + 'first' struct and our 'prev' point to the previous 'prev' */ + easy->next = &multi->easy; + easy->prev = multi->easy.prev; - /* make 'easy' the first node in the chain */ - multi->easy.next = easy; + /* make 'easy' the last node in the chain */ + multi->easy.prev = easy; - /* if there was a next node, make sure its 'prev' pointer links back to + /* if there was a prev node, make sure its 'next' pointer links to the new node */ - if(easy->next) - easy->next->prev = easy; + easy->prev->next = easy; Curl_easy_addmulti(easy_handle, multi_handle); @@ -506,7 +515,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* scan through the list and remove the 'curl_handle' */ easy = multi->easy.next; - while(easy) { + while(easy != &multi->easy) { if(easy->easy_handle == (struct SessionHandle *)curl_handle) break; easy=easy->next; @@ -726,7 +735,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, return CURLM_BAD_HANDLE; easy=multi->easy.next; - while(easy) { + while(easy != &multi->easy) { bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE); for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { @@ -1092,6 +1101,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->recv_pipe); multistate(easy, CURLM_STATE_WAITPERFORM); result = CURLM_CALL_MULTI_PERFORM; + + Curl_pre_readwrite(easy->easy_conn); + break; case CURLM_STATE_WAITPERFORM: @@ -1313,7 +1325,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) return CURLM_BAD_HANDLE; easy=multi->easy.next; - while(easy) { + while(easy != &multi->easy) { CURLMcode result; if (easy->easy_handle->state.cancelled && @@ -1409,7 +1421,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) /* remove all easy handles */ easy = multi->easy.next; - while(easy) { + while(easy != &multi->easy) { nexteasy=easy->next; if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ @@ -1588,7 +1600,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* walk through each easy handle and do the socket state change magic and callbacks */ easyp=multi->easy.next; - while(easyp) { + while(easyp != &multi->easy) { singlesocket(multi, easyp); easyp = easyp->next; } -- cgit v1.2.1 From b819c72700d5f01cab5848f1cd3c880205d01c81 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 25 Feb 2007 11:38:13 +0000 Subject: - Adam D. Moss made the HTTP CONNECT procedure less blocking when used from the multi interface. Note that it still does a part of the connection in a blocking manner. --- lib/multi.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2a7f50baa..e55cb6994 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -47,6 +47,7 @@ #include "multiif.h" #include "sendf.h" #include "timeval.h" +#include "http.h" /* The last #include file should be: */ #include "memdebug.h" @@ -62,6 +63,7 @@ typedef enum { CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */ CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */ + CURLM_STATE_WAITPROXYCONNECT, /* awaiting proxy CONNECT to finalize */ CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect phase */ CURLM_STATE_WAITDO, /* wait for our turn to send the request */ @@ -791,7 +793,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; easy->result = CURLE_OK; - } else { + } + else { easy->result = CURLE_COULDNT_CONNECT; multistate(easy, CURLM_STATE_COMPLETED); } @@ -871,10 +874,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, WAITDO! */ result = CURLM_CALL_MULTI_PERFORM; - if(protocol_connect) { + if(protocol_connect) multistate(easy, CURLM_STATE_WAITDO); - } else { - multistate(easy, CURLM_STATE_WAITCONNECT); + else { + if (easy->easy_conn->bits.tunnel_connecting) + multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + else + multistate(easy, CURLM_STATE_WAITCONNECT); } } } @@ -903,8 +909,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_CALL_MULTI_PERFORM; if(protocol_connect) multistate(easy, CURLM_STATE_DO); - else - multistate(easy, CURLM_STATE_WAITCONNECT); + else { + if (easy->easy_conn->bits.tunnel_connecting) + multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + else + multistate(easy, CURLM_STATE_WAITCONNECT); + } } } @@ -917,6 +927,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } break; + case CURLM_STATE_WAITPROXYCONNECT: + /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ + easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect); + + if(CURLE_OK == easy->result) { + if (!easy->easy_conn->bits.tunnel_connecting) + multistate(easy, CURLM_STATE_WAITCONNECT); + } + break; + case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch connect */ easy->result = Curl_is_connected(easy->easy_conn, -- cgit v1.2.1 From c514a2a89aa1c1e06b70405eedb4e1f70b27fd10 Mon Sep 17 00:00:00 2001 From: Gisle Vanem Date: Mon, 26 Feb 2007 04:24:26 +0000 Subject: Removed inclusion of and in .c-files since they're already included through "setup.h". --- lib/multi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e55cb6994..c04cad142 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -22,12 +22,10 @@ ***************************************************************************/ #include "setup.h" + #include #include -#ifdef HAVE_SYS_TYPES_H -#include -#endif #ifdef HAVE_SYS_SOCKET_H #include #endif -- cgit v1.2.1 From acc4cf87cda36d4c82e074e9ee8cc9d4e8940707 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 27 Feb 2007 02:24:13 +0000 Subject: no proxy support if libcurl is built with HTTP disabled --- lib/multi.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c04cad142..96d013d3e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -61,7 +61,9 @@ typedef enum { CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */ CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */ +#ifndef CURL_DISABLE_HTTP CURLM_STATE_WAITPROXYCONNECT, /* awaiting proxy CONNECT to finalize */ +#endif CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect phase */ CURLM_STATE_WAITDO, /* wait for our turn to send the request */ @@ -875,9 +877,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(protocol_connect) multistate(easy, CURLM_STATE_WAITDO); else { +#ifndef CURL_DISABLE_HTTP if (easy->easy_conn->bits.tunnel_connecting) multistate(easy, CURLM_STATE_WAITPROXYCONNECT); else +#endif multistate(easy, CURLM_STATE_WAITCONNECT); } } @@ -908,9 +912,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(protocol_connect) multistate(easy, CURLM_STATE_DO); else { +#ifndef CURL_DISABLE_HTTP if (easy->easy_conn->bits.tunnel_connecting) multistate(easy, CURLM_STATE_WAITPROXYCONNECT); else +#endif multistate(easy, CURLM_STATE_WAITCONNECT); } } @@ -925,6 +931,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } break; +#ifndef CURL_DISABLE_HTTP case CURLM_STATE_WAITPROXYCONNECT: /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect); @@ -934,6 +941,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_WAITCONNECT); } break; +#endif case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch connect */ -- cgit v1.2.1 From 82d310d0d9220ead1c232cbca560762fee9e09f6 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 1 Mar 2007 12:02:17 +0000 Subject: Do not remove CURLM_STATE_WAITPROXYCONNECT from the CURLMstate enum in builds with HTTP support disabled to keep consistent enum values for CURLMstate in all kind of builds. --- lib/multi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 96d013d3e..59e4b4419 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -61,11 +61,8 @@ typedef enum { CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */ CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */ -#ifndef CURL_DISABLE_HTTP CURLM_STATE_WAITPROXYCONNECT, /* awaiting proxy CONNECT to finalize */ -#endif - CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect - phase */ + CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect phase */ CURLM_STATE_WAITDO, /* wait for our turn to send the request */ CURLM_STATE_DO, /* start send off the request (part 1) */ CURLM_STATE_DOING, /* sending off the request (part 1) */ -- cgit v1.2.1 From 3d528e1b152808fb4b027927bb83b0b42b4e63bd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 23 Mar 2007 22:25:04 +0000 Subject: add missing state name for the debug state switch output --- lib/multi.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 59e4b4419..3cf3dd3a7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -56,6 +56,9 @@ struct Curl_message { struct Curl_message *next; }; +/* NOTE: if you add a state here, add the name to the statename[] array as + well! +*/ typedef enum { CURLM_STATE_INIT, /* start in this state */ CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ @@ -182,6 +185,7 @@ static const char *statename[]={ "CONNECT", "WAITRESOLVE", "WAITCONNECT", + "WAITPROXYCONNECT", "PROTOCONNECT", "WAITDO", "DO", -- cgit v1.2.1 From 6c56b5301f86d81bd7e77ff2adeafb8ca36fcddc Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 25 Mar 2007 08:16:16 +0000 Subject: - Daniel Johnson fixed multi code to traverse the easy handle list properly. A left-over bug from the February 21 fix. --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3cf3dd3a7..2a4f1a16c 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1488,7 +1488,7 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) return NULL; /* no messages left to return */ easy=multi->easy.next; - while(easy) { + while(easy != &multi->easy) { if(easy->msg_num) { easy->msg_num--; break; @@ -1999,7 +1999,7 @@ void curl_multi_dump(CURLM *multi_handle) int i; fprintf(stderr, "* Multi status: %d handles, %d alive\n", multi->num_easy, multi->num_alive); - for(easy=multi->easy.next; easy; easy = easy->next) { + for(easy=multi->easy.next; easy != &multi->easy; easy = easy->next) { if(easy->state != CURLM_STATE_COMPLETED) { /* only display handles that are not completed */ fprintf(stderr, "handle %p, state %s, %d sockets\n", -- cgit v1.2.1 From b9e5fecf5f381f5a54edc73882f5fabaac1ac40d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 31 Mar 2007 10:56:07 +0000 Subject: Check for a NULL easy->easy_conn in multi_getsock() since it can in fact happen when curl_multi_remove_handle() is called. CID 13. coverity.com scan --- lib/multi.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2a4f1a16c..ec9fd3309 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -680,7 +680,14 @@ static int multi_getsock(struct Curl_one_easy *easy, of sockets */ int numsocks) { - if (easy->easy_handle->state.pipe_broke) { + /* If the pipe broke, or if there's no connection left for this easy handle, + then we MUST bail out now with no bitmask set. The no connection case can + happen when this is called from curl_multi_remove_handle() => + singlesocket() => multi_getsock(). + */ + + if (easy->easy_handle->state.pipe_broke || + !easy->easy_conn) { return 0; } -- cgit v1.2.1 From 6c6e4710b570eccdbae87fa9bd8df2f4940ac76d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 1 Apr 2007 08:24:23 +0000 Subject: Robert Iakobashvili made curl_multi_remove_handle() a lot faster when many easy handles are added to a multi handle, by avoiding the looping over all the handles to find which one to remove. --- lib/multi.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ec9fd3309..54037b358 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -413,6 +413,9 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, easy->easy_handle = easy_handle; multistate(easy, CURLM_STATE_INIT); + /* set the back pointer to one_easy to assist in removal */ + easy->easy_handle->multi_pos = easy; + /* for multi interface connections, we share DNS cache automatically if the easy handle's one is currently private. */ if (easy->easy_handle->dns.hostcache && @@ -516,13 +519,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, if(!GOOD_EASY_HANDLE(curl_handle)) return CURLM_BAD_EASY_HANDLE; - /* scan through the list and remove the 'curl_handle' */ - easy = multi->easy.next; - while(easy != &multi->easy) { - if(easy->easy_handle == (struct SessionHandle *)curl_handle) - break; - easy=easy->next; - } + /* pick-up from the 'curl_handle' the kept position in the list */ + easy = ((struct SessionHandle *)curl_handle)->multi_pos; if(easy) { bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED); @@ -626,6 +624,9 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, easy->easy_handle->set.one_easy = NULL; /* detached */ + /* Null the position in the controlling structure */ + easy->easy_handle->multi_pos = NULL; + /* NOTE NOTE NOTE We do not touch the easy handle here! */ if (easy->msg) -- cgit v1.2.1 From a11374d99458de06ade789eb4f4308112f100be4 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 7 Apr 2007 17:25:19 +0000 Subject: fix out of memory handling issue --- lib/multi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 54037b358..17003f47b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -352,6 +352,7 @@ CURLM *curl_multi_init(void) multi->connc = Curl_mk_connc(CONNCACHE_MULTI, -1); if(!multi->connc) { + Curl_hash_destroy(multi->sockhash); Curl_hash_destroy(multi->hostcache); free(multi); return NULL; -- cgit v1.2.1 From 5daa6b93679b1e9a630975fef66191ae3bc71ab1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 10 Apr 2007 20:46:40 +0000 Subject: Ravi Pratap provided fixes for HTTP pipelining --- lib/multi.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 17 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 17003f47b..3245b520d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -207,7 +207,7 @@ void curl_multi_dump(CURLM *multi_handle); static void multistate(struct Curl_one_easy *easy, CURLMstate state) { #ifdef CURLDEBUG - long index = -1; + long index = -5000; #endif CURLMstate oldstate = easy->state; @@ -534,10 +534,12 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, multi->num_alive--; if (easy->easy_handle->state.is_in_pipeline && - easy->state > CURLM_STATE_DO) { + easy->state > CURLM_STATE_DO && + easy->state < CURLM_STATE_COMPLETED) { /* If the handle is in a pipeline and has finished sending off its - request, we need to remember the fact that we want to remove this - handle but do the actual removal at a later time */ + request but not received its reponse yet, we need to remember the + fact that we want to remove this handle but do the actual removal at + a later time */ easy->easy_handle->state.cancelled = TRUE; return CURLM_OK; } @@ -603,7 +605,9 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* and modify the connectindex since this handle can't point to the connection cache anymore */ - if(easy->easy_conn) + if(easy->easy_conn && + (easy->easy_conn->send_pipe->size + + easy->easy_conn->recv_pipe->size == 0)) easy->easy_conn->connectindex = -1; } @@ -648,6 +652,14 @@ bool Curl_multi_canPipeline(struct Curl_multi* multi) return multi->pipelining_enabled; } +void Curl_multi_handlePipeBreak(struct SessionHandle *data) +{ + struct Curl_one_easy *one_easy = data->set.one_easy; + + if (one_easy) + one_easy->easy_conn = NULL; +} + static int waitconnect_getsock(struct connectdata *conn, curl_socket_t *sock, int numsocks) @@ -791,13 +803,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, struct Curl_transfer_keeper *k; do { + bool disconnect_conn = FALSE; if(!GOOD_EASY_HANDLE(easy->easy_handle)) return CURLM_BAD_EASY_HANDLE; + /* Handle the case when the pipe breaks, i.e., the connection + we're using gets cleaned up and we're left with nothing. */ if (easy->easy_handle->state.pipe_broke) { infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n", easy, easy->easy_handle->reqdata.path); + if(easy->easy_handle->state.is_in_pipeline) { /* Head back to the CONNECT state */ multistate(easy, CURLM_STATE_CONNECT); @@ -920,7 +936,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* call again please so that we get the next socket setup */ result = CURLM_CALL_MULTI_PERFORM; if(protocol_connect) - multistate(easy, CURLM_STATE_DO); + multistate(easy, CURLM_STATE_WAITDO); else { #ifndef CURL_DISABLE_HTTP if (easy->easy_conn->bits.tunnel_connecting) @@ -934,8 +950,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK != easy->result) { /* failure detected */ - Curl_disconnect(easy->easy_conn); /* disconnect properly */ - easy->easy_conn = NULL; /* no more connection */ + disconnect_conn = TRUE; break; } } @@ -964,8 +979,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK != easy->result) { /* failure detected */ - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ + /* Just break, the cleaning up is handled all in one place */ + disconnect_conn = TRUE; break; } @@ -998,8 +1013,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* failure detected */ Curl_posttransfer(easy->easy_handle); Curl_done(&easy->easy_conn, easy->result, FALSE); - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ + disconnect_conn = TRUE; } break; @@ -1065,8 +1079,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* failure detected */ Curl_posttransfer(easy->easy_handle); Curl_done(&easy->easy_conn, easy->result, FALSE); - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ + disconnect_conn = TRUE; } } break; @@ -1098,8 +1111,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* failure detected */ Curl_posttransfer(easy->easy_handle); Curl_done(&easy->easy_conn, easy->result, FALSE); - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ + disconnect_conn = TRUE; } break; @@ -1208,9 +1220,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(easy->result) { /* The transfer phase returned error, we mark the connection to get - * closed to prevent being re-used. This is becasue we can't + * closed to prevent being re-used. This is because we can't * possibly know if the connection is in a good shape or not now. */ easy->easy_conn->bits.close = TRUE; + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) { /* if we failed anywhere, we must clean up the secondary socket if @@ -1292,10 +1306,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* This node should be delinked from the list now and we should post an information message that we are complete. */ + + /* Important: reset the conn pointer so that we don't point to memory + that could be freed anytime */ + easy->easy_conn = NULL; break; case CURLM_STATE_CANCELLED: /* Cancelled transfer, wait to be cleaned up */ + + /* Reset the conn pointer so we don't leave it dangling */ + easy->easy_conn = NULL; break; default: @@ -1308,6 +1329,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * If an error was returned, and we aren't in completed state now, * then we go to completed and consider this transfer aborted. */ + + /* NOTE: no attempt to disconnect connections must be made + in the case blocks above - cleanup happens only here */ + easy->easy_handle->state.is_in_pipeline = FALSE; easy->easy_handle->state.pipe_broke = FALSE; @@ -1315,7 +1340,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* if this has a connection, unsubscribe from the pipelines */ easy->easy_conn->writechannel_inuse = FALSE; easy->easy_conn->readchannel_inuse = FALSE; + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->send_pipe); + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); } + + if (disconnect_conn) { + Curl_disconnect(easy->easy_conn); /* disconnect properly */ + + /* This is where we make sure that the easy_conn pointer is reset. + We don't have to do this in every case block above where a + failure is detected */ + easy->easy_conn = NULL; + } + multistate(easy, CURLM_STATE_COMPLETED); } } -- cgit v1.2.1 From 038fe54e2133119e7836d79ea4aaa5ca705159fc Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 13 Apr 2007 07:57:31 +0000 Subject: fix compiler warning --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3245b520d..530357392 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -796,7 +796,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, struct Curl_message *msg = NULL; bool connected; bool async; - bool protocol_connect; + bool protocol_connect = FALSE; bool dophase_done; bool done; CURLMcode result = CURLM_OK; -- cgit v1.2.1 From 76627b322e369c209c60863b9e1f05e3ce02953d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 16 Apr 2007 16:34:08 +0000 Subject: - Robert Iakobashvil added curl_multi_socket_action() to libcurl, which is a function that deprecates the curl_multi_socket() function. Using the new function the application tell libcurl what action that was found in the socket that it passes in. This gives a significant performance boost as it allows libcurl to avoid a call to poll()/select() for every call to curl_multi_socket*(). --- lib/multi.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 530357392..76614c760 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1661,6 +1661,7 @@ static void singlesocket(struct Curl_multi *multi, static CURLMcode multi_socket(struct Curl_multi *multi, bool checkall, curl_socket_t s, + int ev_bitmask, int *running_handles) { CURLMcode result = CURLM_OK; @@ -1698,8 +1699,14 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* bad bad bad bad bad bad bad */ return CURLM_INTERNAL_ERROR; + if (data->set.one_easy->easy_conn) /* set socket event bitmask */ + data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; + result = multi_runsingle(multi, data->set.one_easy); + if (data->set.one_easy->easy_conn) + data->set.one_easy->easy_conn->cselect_bits = 0; + if(result == CURLM_OK) /* get the socket(s) and check if the state has been changed since last */ @@ -1791,12 +1798,24 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, return res; } +/* we define curl_multi_socket() in the public multi.h header */ +#undef curl_multi_socket CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles) { CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, - running_handles); + 0, running_handles); + if (CURLM_OK == result) + update_timer((struct Curl_multi *)multi_handle); + return result; +} + +CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t s, + int ev_bitmask, int *running_handles) +{ + CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, + ev_bitmask, running_handles); if (CURLM_OK == result) update_timer((struct Curl_multi *)multi_handle); return result; @@ -1806,7 +1825,7 @@ CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) { CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, - TRUE, CURL_SOCKET_BAD, running_handles); + TRUE, CURL_SOCKET_BAD, 0, running_handles); if (CURLM_OK == result) update_timer((struct Curl_multi *)multi_handle); return result; -- cgit v1.2.1 From 2f0539d8803e4f270c8584994f7fc8b90cd5e15c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 2 May 2007 13:47:56 +0000 Subject: - Set the timeout for easy handles to expire really soon after addition or when CURLM_CALL_MULTI_PERFORM is returned from curl_multi_socket*/perform, to make applications using only curl_multi_socket() to properly function when adding easy handles "on the fly". Bug report and test app provided by Michael Wallner. --- lib/multi.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 76614c760..4b42981e8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -469,6 +469,14 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* make the SessionHandle struct refer back to this struct */ easy->easy_handle->set.one_easy = easy; + /* Set the timeout for this handle to expire really soon so that it will + be taken care of even when this handle is added in the midst of operation + when only the curl_multi_socket() API is used. During that flow, only + sockets that time-out or have actions will be dealt with. Since this + handle has no action yet, we make sure it times out to get things to + happen. */ + Curl_expire(easy->easy_handle, 10); + /* increase the node-counter */ multi->num_easy++; @@ -1385,6 +1393,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multi->num_msgs++; /* increase message counter */ } + if(CURLM_CALL_MULTI_PERFORM == result) + /* Set the timeout for this handle to expire really soon so that it will + be taken care of even when this handle is added in the midst of + operation when only the curl_multi_socket() API is used. During that + flow, only sockets that time-out or have actions will be dealt + with. Since this handle has no action yet, we make sure it times out to + get things to happen. Also, this makes it less important for callers of + the curl_multi_* functions to bother about the CURLM_CALL_MULTI_PERFORM + return code, as long as they deal with the timeouts properly. */ + Curl_expire(easy->easy_handle, 10); + return result; } -- cgit v1.2.1 From 9f72db13c4608d57e8232f355e5b96335d2035f6 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Wed, 2 May 2007 19:13:56 +0000 Subject: Fixed an out of memory handling issue with HTTP pipelines. --- lib/multi.c | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 4b42981e8..c61958ea4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -121,9 +121,9 @@ struct Curl_one_easy { #define CURL_MULTI_HANDLE 0x000bab1e #define GOOD_MULTI_HANDLE(x) \ - ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE)) + ((x)&&(((struct Curl_multi *)(x))->type == CURL_MULTI_HANDLE)) #define GOOD_EASY_HANDLE(x) \ - (((struct SessionHandle *)x)->magic == CURLEASY_MAGIC_NUMBER) + (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER) /* This is the struct known as CURLM on the outside */ struct Curl_multi { @@ -180,7 +180,7 @@ static void add_closure(struct Curl_multi *multi, static int update_timer(struct Curl_multi *multi); #ifdef CURLDEBUG -static const char *statename[]={ +static const char * const statename[]={ "INIT", "CONNECT", "WAITRESOLVE", @@ -896,29 +896,30 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK == easy->result) { /* Add this handle to the send pipeline */ - Curl_addHandleToPipeline(easy->easy_handle, - easy->easy_conn->send_pipe); - - if(async) - /* We're now waiting for an asynchronous name lookup */ - multistate(easy, CURLM_STATE_WAITRESOLVE); - else { - /* after the connect has been sent off, go WAITCONNECT unless the - protocol connect is already done and we can go directly to - WAITDO! */ - result = CURLM_CALL_MULTI_PERFORM; - - if(protocol_connect) - multistate(easy, CURLM_STATE_WAITDO); - else { + easy->result = Curl_addHandleToPipeline(easy->easy_handle, + easy->easy_conn->send_pipe); + if(CURLE_OK == easy->result) { + if(async) + /* We're now waiting for an asynchronous name lookup */ + multistate(easy, CURLM_STATE_WAITRESOLVE); + else { + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to + WAITDO! */ + result = CURLM_CALL_MULTI_PERFORM; + + if(protocol_connect) + multistate(easy, CURLM_STATE_WAITDO); + else { #ifndef CURL_DISABLE_HTTP - if (easy->easy_conn->bits.tunnel_connecting) - multistate(easy, CURLM_STATE_WAITPROXYCONNECT); - else + if (easy->easy_conn->bits.tunnel_connecting) + multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + else #endif - multistate(easy, CURLM_STATE_WAITCONNECT); - } - } + multistate(easy, CURLM_STATE_WAITCONNECT); + } + } + } } break; @@ -1153,8 +1154,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_removeHandleFromPipeline(easy->easy_handle, easy->easy_conn->send_pipe); /* Add ourselves to the recv pipeline */ - Curl_addHandleToPipeline(easy->easy_handle, - easy->easy_conn->recv_pipe); + easy->result = Curl_addHandleToPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); multistate(easy, CURLM_STATE_WAITPERFORM); result = CURLM_CALL_MULTI_PERFORM; -- cgit v1.2.1 From ad19f95f15bac425c1b37a7a260025d8fc9fe63c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 7 May 2007 07:07:55 +0000 Subject: James Bursa fixed a bug in the multi handle code that made the connection cache grow a bit too much, beyond the normal 4 * easy_handles. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c61958ea4..d9d7eb290 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -485,7 +485,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, the shared cache every single easy handle had 5 entries in their cache by default. */ CURLcode res = Curl_ch_connc(easy_handle, multi->connc, - multi->connc->num*4); + multi->num_easy * 4); if(res != CURLE_OK) /* TODO: we need to do some cleaning up here! */ return CURLM_OUT_OF_MEMORY; -- cgit v1.2.1 From a49e78d9b758cad12d886df2d5c8459a34477bbb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 30 May 2007 20:04:44 +0000 Subject: Added CURLMOPT_MAXCONNECTS which is a curl_multi_setopt() option for setting the maximum size of the connection cache maximum size of the multi handle. --- lib/multi.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d9d7eb290..35c74dc16 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -160,6 +160,8 @@ struct Curl_multi { /* shared connection cache */ struct conncache *connc; + long maxconnects; /* if >0, a fixed limit of the maximum number of entries + we're allowed to grow the connection cache to */ /* list of easy handles kept around for doing nice connection closures */ struct closure *closure; @@ -484,11 +486,19 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* We want the connection cache to have plenty room. Before we supported the shared cache every single easy handle had 5 entries in their cache by default. */ - CURLcode res = Curl_ch_connc(easy_handle, multi->connc, - multi->num_easy * 4); - if(res != CURLE_OK) - /* TODO: we need to do some cleaning up here! */ - return CURLM_OUT_OF_MEMORY; + int newmax = multi->num_easy * 4; + + if(multi->maxconnects && (multi->maxconnects < newmax)) + /* don't grow beyond the allowed size */ + newmax = multi->maxconnects; + + if(newmax > multi->connc->num) { + /* we only do this is we can in fact grow the cache */ + CURLcode res = Curl_ch_connc(easy_handle, multi->connc, newmax); + if(res != CURLE_OK) + /* TODO: we need to do some cleaning up here! */ + return CURLM_OUT_OF_MEMORY; + } } /* increase the alive-counter */ @@ -1810,6 +1820,9 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, case CURLMOPT_TIMERDATA: multi->timer_userp = va_arg(param, void *); break; + case CURLMOPT_MAXCONNECTS: + multi->maxconnects = va_arg(param, long); + break; default: res = CURLM_UNKNOWN_OPTION; break; -- cgit v1.2.1 From 86a25239ec85cc1d4f6b34aad3e029574ba77a3b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 1 Jun 2007 21:01:57 +0000 Subject: do the update timer stuff even when CURLM_CALL_MULTI_PERFORM is returned --- lib/multi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 35c74dc16..d4fd82525 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1473,7 +1473,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) *running_handles = multi->num_alive; - if ( CURLM_OK == returncode ) + if ( CURLM_OK >= returncode ) update_timer(multi); return returncode; } @@ -1737,7 +1737,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, if (data->set.one_easy->easy_conn) data->set.one_easy->easy_conn->cselect_bits = 0; - if(result == CURLM_OK) + if(result >= CURLM_OK) /* get the socket(s) and check if the state has been changed since last */ singlesocket(multi, data->set.one_easy); @@ -1763,7 +1763,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, if(data) { result = multi_runsingle(multi, data->set.one_easy); - if(result == CURLM_OK) + if(result >= CURLM_OK) /* get the socket(s) and check if the state has been changed since last */ singlesocket(multi, data->set.one_easy); @@ -1839,7 +1839,7 @@ CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, { CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, 0, running_handles); - if (CURLM_OK == result) + if (CURLM_OK >= result) update_timer((struct Curl_multi *)multi_handle); return result; } @@ -1849,7 +1849,7 @@ CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t s, { CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, ev_bitmask, running_handles); - if (CURLM_OK == result) + if (CURLM_OK >= result) update_timer((struct Curl_multi *)multi_handle); return result; } @@ -1859,7 +1859,7 @@ CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) { CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, TRUE, CURL_SOCKET_BAD, 0, running_handles); - if (CURLM_OK == result) + if (CURLM_OK >= result) update_timer((struct Curl_multi *)multi_handle); return result; } -- cgit v1.2.1 From b10ff9791bb0decdab7ae4f1a4165b7042b27ae6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 1 Jun 2007 21:24:34 +0000 Subject: ouch, two conditionals were turned backwards! --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d4fd82525..bbcf6319e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1737,7 +1737,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, if (data->set.one_easy->easy_conn) data->set.one_easy->easy_conn->cselect_bits = 0; - if(result >= CURLM_OK) + if(CURLM_OK >= result) /* get the socket(s) and check if the state has been changed since last */ singlesocket(multi, data->set.one_easy); @@ -1763,7 +1763,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, if(data) { result = multi_runsingle(multi, data->set.one_easy); - if(result >= CURLM_OK) + if(CURLM_OK >= result) /* get the socket(s) and check if the state has been changed since last */ singlesocket(multi, data->set.one_easy); -- cgit v1.2.1 From b691102ec7a0409d831dff01d2d7075d56dd7516 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 14 Jun 2007 14:42:21 +0000 Subject: Shmulik Regev fixed a flaw in the multi interface that occurred when doing HTTP CONNECT over a proxy --- lib/multi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index bbcf6319e..5e91a5e7c 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1007,8 +1007,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!protocol_connect) { /* We have a TCP connection, but 'protocol_connect' may be false and then we continue to 'STATE_PROTOCONNECT'. If protocol - connect is TRUE, we move on to STATE_DO. */ - multistate(easy, CURLM_STATE_PROTOCONNECT); + connect is TRUE, we move on to STATE_DO. + BUT if we are using a proxy we must change to WAITPROXYCONNECT + */ +#ifndef CURL_DISABLE_HTTP + if (easy->easy_conn->bits.tunnel_connecting) + multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + else +#endif + multistate(easy, CURLM_STATE_PROTOCONNECT); } else { /* after the connect has completed, go WAITDO */ -- cgit v1.2.1 From 62f0f5571da55de683688c8fca8f8acdcbd98bec Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 26 Jun 2007 21:09:28 +0000 Subject: Robert Iakobashvili re-arranged the internal hash code to work with a custom hash function for different hashes, and also expanded the default size for the socket hash table used in multi handles to greatly enhance speed when very many connections are added and the socket API is used. --- lib/multi.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 5e91a5e7c..0bdfd6170 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -50,6 +50,15 @@ /* The last #include file should be: */ #include "memdebug.h" +/* + CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 + to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every + CURL handle takes 45-50 K memory, therefore this 3K are not significant. +*/ +#ifndef CURL_SOCKET_HASH_TABLE_SIZE +#define CURL_SOCKET_HASH_TABLE_SIZE 911 +#endif + struct Curl_message { /* the 'CURLMsg' is the part that is visible to the external user */ struct CURLMsg extmsg; @@ -305,6 +314,21 @@ static void sh_freeentry(void *freethis) free(p); } +static size_t fd_key_compare(void*k1, size_t k1_len, void*k2, size_t k2_len) +{ + (void) k1_len; (void) k2_len; + + return ((*((int* ) k1)) == (*((int* ) k2))) ? 1 : 0; +} + +static size_t hash_fd(void* key, size_t key_length, size_t slots_num) +{ + int fd = * ((int* ) key); + (void) key_length; + + return (fd % (int)slots_num); +} + /* * sh_init() creates a new socket hash and returns the handle for it. * @@ -325,7 +349,8 @@ static void sh_freeentry(void *freethis) */ static struct curl_hash *sh_init(void) { - return Curl_hash_alloc(97, sh_freeentry); + return Curl_hash_alloc(CURL_SOCKET_HASH_TABLE_SIZE, hash_fd, fd_key_compare, + sh_freeentry); } CURLM *curl_multi_init(void) -- cgit v1.2.1 From 523767660c05cf359091694fcaccb763ebb7b2d7 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Sun, 26 Aug 2007 05:53:26 +0000 Subject: Fixed some minor mismatched types found by splint. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0bdfd6170..42294eeb6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -690,7 +690,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, return CURLM_BAD_EASY_HANDLE; /* twasn't found */ } -bool Curl_multi_canPipeline(struct Curl_multi* multi) +bool Curl_multi_canPipeline(const struct Curl_multi* multi) { return multi->pipelining_enabled; } -- cgit v1.2.1 From 5d4c981e130adac09fc13bc04494f5f56294cc79 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Thu, 30 Aug 2007 18:26:19 +0000 Subject: Fixed a few compiler warnings. Try to do a slightly better job of cleaning up after an OOM condition in curl_multi_add_handle --- lib/multi.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 42294eeb6..b2e513043 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -511,7 +511,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* We want the connection cache to have plenty room. Before we supported the shared cache every single easy handle had 5 entries in their cache by default. */ - int newmax = multi->num_easy * 4; + long newmax = multi->num_easy * 4; if(multi->maxconnects && (multi->maxconnects < newmax)) /* don't grow beyond the allowed size */ @@ -520,9 +520,11 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(newmax > multi->connc->num) { /* we only do this is we can in fact grow the cache */ CURLcode res = Curl_ch_connc(easy_handle, multi->connc, newmax); - if(res != CURLE_OK) - /* TODO: we need to do some cleaning up here! */ - return CURLM_OUT_OF_MEMORY; + if(res != CURLE_OK) { + /* FIXME: may need to do more cleanup here */ + curl_multi_remove_handle(multi_handle, easy_handle); + return res; + } } } -- cgit v1.2.1 From 4f17c583153cd42f40e89e25fa33259886fcdd8e Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 31 Aug 2007 17:54:01 +0000 Subject: Fixed an invalid returned error code added in my last submission. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b2e513043..95544e662 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -523,7 +523,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(res != CURLE_OK) { /* FIXME: may need to do more cleanup here */ curl_multi_remove_handle(multi_handle, easy_handle); - return res; + return CURLM_OUT_OF_MEMORY; } } } -- cgit v1.2.1 From 16b95fc77316fdd3866f7de4ebb5d14bd136ac11 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Thu, 27 Sep 2007 01:45:22 +0000 Subject: Enabled a few more gcc warnings with --enable-debug. Renamed a few variables to avoid shadowing global declarations. --- lib/multi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 95544e662..f9fcac12e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -218,7 +218,7 @@ void curl_multi_dump(CURLM *multi_handle); static void multistate(struct Curl_one_easy *easy, CURLMstate state) { #ifdef CURLDEBUG - long index = -5000; + long connectindex = -5000; #endif CURLMstate oldstate = easy->state; @@ -231,12 +231,12 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) #ifdef CURLDEBUG if(easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) - index = easy->easy_conn->connectindex; + connectindex = easy->easy_conn->connectindex; infof(easy->easy_handle, "STATE: %s => %s handle %p; (connection #%ld) \n", statename[oldstate], statename[easy->state], - (char *)easy, index); + (char *)easy, connectindex); #endif if(state == CURLM_STATE_COMPLETED) /* changing to COMPLETED means there's one less easy handle 'alive' */ -- cgit v1.2.1 From 51009a40b403230cc4d70f51daf74ffe96e7758f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 3 Nov 2007 14:44:38 +0000 Subject: make sure the code deals with failures on the DO_MORE state properly --- lib/multi.c | 55 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 25 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f9fcac12e..b4ca522dd 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -935,28 +935,28 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Add this handle to the send pipeline */ easy->result = Curl_addHandleToPipeline(easy->easy_handle, easy->easy_conn->send_pipe); - if(CURLE_OK == easy->result) { - if(async) - /* We're now waiting for an asynchronous name lookup */ - multistate(easy, CURLM_STATE_WAITRESOLVE); - else { - /* after the connect has been sent off, go WAITCONNECT unless the - protocol connect is already done and we can go directly to - WAITDO! */ - result = CURLM_CALL_MULTI_PERFORM; - - if(protocol_connect) - multistate(easy, CURLM_STATE_WAITDO); - else { + if(CURLE_OK == easy->result) { + if(async) + /* We're now waiting for an asynchronous name lookup */ + multistate(easy, CURLM_STATE_WAITRESOLVE); + else { + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to + WAITDO! */ + result = CURLM_CALL_MULTI_PERFORM; + + if(protocol_connect) + multistate(easy, CURLM_STATE_WAITDO); + else { #ifndef CURL_DISABLE_HTTP - if (easy->easy_conn->bits.tunnel_connecting) - multistate(easy, CURLM_STATE_WAITPROXYCONNECT); - else + if (easy->easy_conn->bits.tunnel_connecting) + multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + else #endif - multistate(easy, CURLM_STATE_WAITCONNECT); - } - } - } + multistate(easy, CURLM_STATE_WAITCONNECT); + } + } + } } break; @@ -1057,7 +1057,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* protocol-specific connect phase */ easy->result = Curl_protocol_connecting(easy->easy_conn, &protocol_connect); - if(protocol_connect) { + if((easy->result == CURLE_OK) && protocol_connect) { /* after the connect has completed, go WAITDO */ multistate(easy, CURLM_STATE_WAITDO); result = CURLM_CALL_MULTI_PERFORM; @@ -1181,15 +1181,20 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK == easy->result) easy->result = Curl_readwrite_init(easy->easy_conn); - else - /* Remove ourselves from the send pipeline */ - Curl_removeHandleFromPipeline(easy->easy_handle, - easy->easy_conn->send_pipe); + + /* No need to remove ourselves from the send pipeline here since that + is done for us in Curl_done() */ if(CURLE_OK == easy->result) { multistate(easy, CURLM_STATE_DO_DONE); result = CURLM_CALL_MULTI_PERFORM; } + else { + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result, FALSE); + disconnect_conn = TRUE; + } } break; -- cgit v1.2.1 From ad6e28073c985a42e8b15d2234baa7ef67ffcb35 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 5 Nov 2007 09:45:09 +0000 Subject: removed space after if and while before the parenthesis for better source code consistency --- lib/multi.c | 78 ++++++++++++++++++++++++++++++------------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b4ca522dd..1b5dd1869 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -446,14 +446,14 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* for multi interface connections, we share DNS cache automatically if the easy handle's one is currently private. */ - if (easy->easy_handle->dns.hostcache && + if(easy->easy_handle->dns.hostcache && (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) { Curl_hash_destroy(easy->easy_handle->dns.hostcache); easy->easy_handle->dns.hostcache = NULL; easy->easy_handle->dns.hostcachetype = HCACHE_NONE; } - if (!easy->easy_handle->dns.hostcache || + if(!easy->easy_handle->dns.hostcache || (easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) { easy->easy_handle->dns.hostcache = multi->hostcache; easy->easy_handle->dns.hostcachetype = HCACHE_MULTI; @@ -578,7 +578,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, alive connections when this is removed */ multi->num_alive--; - if (easy->easy_handle->state.is_in_pipeline && + if(easy->easy_handle->state.is_in_pipeline && easy->state > CURLM_STATE_DO && easy->state < CURLM_STATE_COMPLETED) { /* If the handle is in a pipeline and has finished sending off its @@ -679,7 +679,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* NOTE NOTE NOTE We do not touch the easy handle here! */ - if (easy->msg) + if(easy->msg) free(easy->msg); free(easy); @@ -701,7 +701,7 @@ void Curl_multi_handlePipeBreak(struct SessionHandle *data) { struct Curl_one_easy *one_easy = data->set.one_easy; - if (one_easy) + if(one_easy) one_easy->easy_conn = NULL; } @@ -745,12 +745,12 @@ static int multi_getsock(struct Curl_one_easy *easy, singlesocket() => multi_getsock(). */ - if (easy->easy_handle->state.pipe_broke || + if(easy->easy_handle->state.pipe_broke || !easy->easy_conn) { return 0; } - if (easy->state > CURLM_STATE_CONNECT && + if(easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) { /* Set up ownership correctly */ easy->easy_conn->data = easy->easy_handle; @@ -855,7 +855,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Handle the case when the pipe breaks, i.e., the connection we're using gets cleaned up and we're left with nothing. */ - if (easy->easy_handle->state.pipe_broke) { + if(easy->easy_handle->state.pipe_broke) { infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n", easy, easy->easy_handle->reqdata.path); @@ -875,13 +875,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; } - if (easy->state > CURLM_STATE_CONNECT && + if(easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) { /* Make sure we set the connection's current owner */ easy->easy_conn->data = easy->easy_handle; } - if (CURLM_STATE_WAITCONNECT <= easy->state && + if(CURLM_STATE_WAITCONNECT <= easy->state && easy->state <= CURLM_STATE_DO && easy->easy_handle->change.url_changed) { char *gotourl; @@ -949,7 +949,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_WAITDO); else { #ifndef CURL_DISABLE_HTTP - if (easy->easy_conn->bits.tunnel_connecting) + if(easy->easy_conn->bits.tunnel_connecting) multistate(easy, CURLM_STATE_WAITPROXYCONNECT); else #endif @@ -985,7 +985,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_WAITDO); else { #ifndef CURL_DISABLE_HTTP - if (easy->easy_conn->bits.tunnel_connecting) + if(easy->easy_conn->bits.tunnel_connecting) multistate(easy, CURLM_STATE_WAITPROXYCONNECT); else #endif @@ -1008,7 +1008,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect); if(CURLE_OK == easy->result) { - if (!easy->easy_conn->bits.tunnel_connecting) + if(!easy->easy_conn->bits.tunnel_connecting) multistate(easy, CURLM_STATE_WAITCONNECT); } break; @@ -1038,7 +1038,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, BUT if we are using a proxy we must change to WAITPROXYCONNECT */ #ifndef CURL_DISABLE_HTTP - if (easy->easy_conn->bits.tunnel_connecting) + if(easy->easy_conn->bits.tunnel_connecting) multistate(easy, CURLM_STATE_WAITPROXYCONNECT); else #endif @@ -1080,7 +1080,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_isHandleAtHead(easy->easy_handle, easy->easy_conn->send_pipe)); #endif - if (!easy->easy_conn->writechannel_inuse && + if(!easy->easy_conn->writechannel_inuse && Curl_isHandleAtHead(easy->easy_handle, easy->easy_conn->send_pipe)) { /* Grab the channel */ @@ -1222,7 +1222,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->recv_pipe)); #endif /* Wait for our turn to PERFORM */ - if (!easy->easy_conn->readchannel_inuse && + if(!easy->easy_conn->readchannel_inuse && Curl_isHandleAtHead(easy->easy_handle, easy->easy_conn->recv_pipe)) { /* Grab the channel */ @@ -1235,7 +1235,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ Curl_pgrsUpdate(easy->easy_conn); - if ( ( ( easy->easy_handle->set.max_send_speed == 0 ) || + if( ( ( easy->easy_handle->set.max_send_speed == 0 ) || ( easy->easy_handle->progress.ulspeed < easy->easy_handle->set.max_send_speed ) ) && ( ( easy->easy_handle->set.max_recv_speed == 0 ) || @@ -1247,7 +1247,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_PERFORM: /* check if over speed */ - if ( ( ( easy->easy_handle->set.max_send_speed > 0 ) && + if( ( ( easy->easy_handle->set.max_send_speed > 0 ) && ( easy->easy_handle->progress.ulspeed > easy->easy_handle->set.max_send_speed ) ) || ( ( easy->easy_handle->set.max_recv_speed > 0 ) && @@ -1266,12 +1266,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, k = &easy->easy_handle->reqdata.keep; - if (!(k->keepon & KEEP_READ)) { + if(!(k->keepon & KEEP_READ)) { /* We're done reading */ easy->easy_conn->readchannel_inuse = FALSE; } - if (!(k->keepon & KEEP_WRITE)) { + if(!(k->keepon & KEEP_WRITE)) { /* We're done writing */ easy->easy_conn->writechannel_inuse = FALSE; } @@ -1337,14 +1337,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->recv_pipe); easy->easy_handle->state.is_in_pipeline = FALSE; - if (easy->easy_conn->bits.stream_was_rewound) { + if(easy->easy_conn->bits.stream_was_rewound) { /* This request read past its response boundary so we quickly let the other requests consume those bytes since there is no guarantee that the socket will become active again */ result = CURLM_CALL_MULTI_PERFORM; } - if (!easy->easy_handle->state.cancelled) { + if(!easy->easy_handle->state.cancelled) { /* post-transfer command */ easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); @@ -1356,7 +1356,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_COMPLETED: - if (easy->easy_handle->state.cancelled) + if(easy->easy_handle->state.cancelled) /* Go into the CANCELLED state if we were cancelled */ multistate(easy, CURLM_STATE_CANCELLED); @@ -1404,7 +1404,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->recv_pipe); } - if (disconnect_conn) { + if(disconnect_conn) { Curl_disconnect(easy->easy_conn); /* disconnect properly */ /* This is where we make sure that the easy_conn pointer is reset. @@ -1417,9 +1417,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } - } while (easy->easy_handle->change.url_changed); + } while(easy->easy_handle->change.url_changed); - if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { + if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ easy->easy_handle->dns.hostcache = NULL; @@ -1472,7 +1472,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) while(easy != &multi->easy) { CURLMcode result; - if (easy->easy_handle->state.cancelled && + if(easy->easy_handle->state.cancelled && easy->state == CURLM_STATE_CANCELLED) { /* Remove cancelled handles once it's safe to do so */ Curl_multi_rmeasy(multi_handle, easy->easy_handle); @@ -1498,7 +1498,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) int key = now.tv_sec; /* drop the usec part */ multi->timetree = Curl_splaygetbest(key, multi->timetree, &t); - if (t) { + if(t) { struct SessionHandle *d = t->payload; struct timeval* tv = &d->state.expiretime; @@ -1512,7 +1512,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) *running_handles = multi->num_alive; - if ( CURLM_OK >= returncode ) + if( CURLM_OK >= returncode ) update_timer(multi); return returncode; } @@ -1578,7 +1578,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */ - if (easy->msg) + if(easy->msg) free(easy->msg); free(easy); easy = nexteasy; @@ -1753,7 +1753,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* or should we fall-through and do the timer-based stuff? */ return result; } - else if (s != CURL_SOCKET_TIMEOUT) { + else if(s != CURL_SOCKET_TIMEOUT) { struct Curl_sh_entry *entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); @@ -1768,12 +1768,12 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* bad bad bad bad bad bad bad */ return CURLM_INTERNAL_ERROR; - if (data->set.one_easy->easy_conn) /* set socket event bitmask */ + if(data->set.one_easy->easy_conn) /* set socket event bitmask */ data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; result = multi_runsingle(multi, data->set.one_easy); - if (data->set.one_easy->easy_conn) + if(data->set.one_easy->easy_conn) data->set.one_easy->easy_conn->cselect_bits = 0; if(CURLM_OK >= result) @@ -1878,7 +1878,7 @@ CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, { CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, 0, running_handles); - if (CURLM_OK >= result) + if(CURLM_OK >= result) update_timer((struct Curl_multi *)multi_handle); return result; } @@ -1888,7 +1888,7 @@ CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t s, { CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, ev_bitmask, running_handles); - if (CURLM_OK >= result) + if(CURLM_OK >= result) update_timer((struct Curl_multi *)multi_handle); return result; } @@ -1898,7 +1898,7 @@ CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) { CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, TRUE, CURL_SOCKET_BAD, 0, running_handles); - if (CURLM_OK >= result) + if(CURLM_OK >= result) update_timer((struct Curl_multi *)multi_handle); return result; } @@ -1945,11 +1945,11 @@ CURLMcode curl_multi_timeout(CURLM *multi_handle, static int update_timer(struct Curl_multi *multi) { long timeout_ms; - if (!multi->timer_cb) + if(!multi->timer_cb) return 0; - if ( multi_timeout(multi, &timeout_ms) != CURLM_OK ) + if( multi_timeout(multi, &timeout_ms) != CURLM_OK ) return -1; - if ( timeout_ms < 0 ) + if( timeout_ms < 0 ) return 0; /* When multi_timeout() is done, multi->timetree points to the node with the -- cgit v1.2.1 From 50feea3eef87f1c07b954ad3020fdb836c7f279f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 15 Nov 2007 21:45:45 +0000 Subject: Rearranged code and changed Curl_readwrite_init() and Curl_pre_readwrite() into do_init() and do_complete() which now are called first and last in the DO function. It simplified the flow in multi.c and the functions got more sensible names! --- lib/multi.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 1b5dd1869..061e7b5e4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1121,11 +1121,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* we're done with the DO, now DO_DONE */ - easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_DO_DONE); - result = CURLM_CALL_MULTI_PERFORM; - } + multistate(easy, CURLM_STATE_DO_DONE); + result = CURLM_CALL_MULTI_PERFORM; } } else { @@ -1152,11 +1149,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* we're done with the DO, now DO_DONE */ - easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_DO_DONE); - result = CURLM_CALL_MULTI_PERFORM; - } + multistate(easy, CURLM_STATE_DO_DONE); + result = CURLM_CALL_MULTI_PERFORM; } } /* dophase_done */ } @@ -1179,9 +1173,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, */ easy->result = Curl_do_more(easy->easy_conn); - if(CURLE_OK == easy->result) - easy->result = Curl_readwrite_init(easy->easy_conn); - /* No need to remove ourselves from the send pipeline here since that is done for us in Curl_done() */ @@ -1207,9 +1198,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->recv_pipe); multistate(easy, CURLM_STATE_WAITPERFORM); result = CURLM_CALL_MULTI_PERFORM; - - Curl_pre_readwrite(easy->easy_conn); - break; case CURLM_STATE_WAITPERFORM: -- cgit v1.2.1 From 13648f8ccda6f99674ac407640474634e856804c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 24 Nov 2007 23:16:55 +0000 Subject: struct HandleData is now called struct SingleRequest, and is only for data that is inited at the start of the DO action. I removed the Curl_transfer_keeper struct completely, and I had to move out a few struct members (that had to be set before DO or used after DONE) to the UrlState struct. The SingleRequest struct is accessed with SessionHandle->req. One of the biggest reasons for doing this was the bunch of duplicate struct members in HandleData and Curl_transfer_keeper since it was really messy to keep track of two variables with the same name and basically the same purpose! --- lib/multi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 061e7b5e4..042694ca5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -845,7 +845,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, bool dophase_done; bool done; CURLMcode result = CURLM_OK; - struct Curl_transfer_keeper *k; + struct SingleRequest *k; do { bool disconnect_conn = FALSE; @@ -857,7 +857,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, we're using gets cleaned up and we're left with nothing. */ if(easy->easy_handle->state.pipe_broke) { infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n", - easy, easy->easy_handle->reqdata.path); + easy, easy->easy_handle->state.path); if(easy->easy_handle->state.is_in_pipeline) { /* Head back to the CONNECT state */ @@ -1252,7 +1252,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); - k = &easy->easy_handle->reqdata.keep; + k = &easy->easy_handle->req; if(!(k->keepon & KEEP_READ)) { /* We're done reading */ @@ -1289,14 +1289,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_posttransfer(easy->easy_handle); /* When we follow redirects, must to go back to the CONNECT state */ - if(easy->easy_handle->reqdata.newurl || retry) { + if(easy->easy_handle->req.newurl || retry) { Curl_removeHandleFromPipeline(easy->easy_handle, easy->easy_conn->recv_pipe); if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ - newurl = easy->easy_handle->reqdata.newurl; - easy->easy_handle->reqdata.newurl = NULL; + newurl = easy->easy_handle->req.newurl; + easy->easy_handle->req.newurl = NULL; } easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); if(easy->result == CURLE_OK) -- cgit v1.2.1 From de23b98522991dbc1f2c184216d9f73bead83895 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 8 Jan 2008 14:52:05 +0000 Subject: Introducing curl_easy_pause() and new magic return codes for both the read and the write callbacks that now can make a connection's reading and/or writing get paused. --- lib/multi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 042694ca5..a88a0b74a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1255,13 +1255,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, k = &easy->easy_handle->req; if(!(k->keepon & KEEP_READ)) { - /* We're done reading */ - easy->easy_conn->readchannel_inuse = FALSE; + /* We're done reading */ + easy->easy_conn->readchannel_inuse = FALSE; } if(!(k->keepon & KEEP_WRITE)) { - /* We're done writing */ - easy->easy_conn->writechannel_inuse = FALSE; + /* We're done writing */ + easy->easy_conn->writechannel_inuse = FALSE; } if(easy->result) { -- cgit v1.2.1 From b3de497d8316647d988e97703f975e0acd40cacd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 16 Jan 2008 12:24:00 +0000 Subject: Dmitry Kurochkin worked a lot on improving the HTTP Pipelining support that previously had a number of flaws, perhaps most notably when an application fired up N transfers at once as then they wouldn't pipeline at all that nicely as anyone would think... Test case 530 was also updated to take the improved functionality into account. --- lib/multi.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 119 insertions(+), 32 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a88a0b74a..98f35bff5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -190,6 +190,14 @@ static void add_closure(struct Curl_multi *multi, struct SessionHandle *data); static int update_timer(struct Curl_multi *multi); +static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, + struct connectdata *conn); +static int checkPendPipeline(struct connectdata *conn); +static int moveHandleFromSendToRecvPipeline(struct SessionHandle *habdle, + struct connectdata *conn); +static bool isHandleAtHead(struct SessionHandle *handle, + struct curl_llist *pipeline); + #ifdef CURLDEBUG static const char * const statename[]={ "INIT", @@ -932,28 +940,32 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, &async, &protocol_connect); if(CURLE_OK == easy->result) { - /* Add this handle to the send pipeline */ - easy->result = Curl_addHandleToPipeline(easy->easy_handle, - easy->easy_conn->send_pipe); + /* Add this handle to the send or pend pipeline */ + easy->result = addHandleToSendOrPendPipeline(easy->easy_handle, + easy->easy_conn); if(CURLE_OK == easy->result) { - if(async) - /* We're now waiting for an asynchronous name lookup */ - multistate(easy, CURLM_STATE_WAITRESOLVE); + if (easy->easy_handle->state.is_in_pipeline) + multistate(easy, CURLM_STATE_WAITDO); else { - /* after the connect has been sent off, go WAITCONNECT unless the - protocol connect is already done and we can go directly to - WAITDO! */ - result = CURLM_CALL_MULTI_PERFORM; - - if(protocol_connect) - multistate(easy, CURLM_STATE_WAITDO); + if(async) + /* We're now waiting for an asynchronous name lookup */ + multistate(easy, CURLM_STATE_WAITRESOLVE); else { + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to + WAITDO! */ + result = CURLM_CALL_MULTI_PERFORM; + + if(protocol_connect) + multistate(easy, CURLM_STATE_WAITDO); + else { #ifndef CURL_DISABLE_HTTP - if(easy->easy_conn->bits.tunnel_connecting) - multistate(easy, CURLM_STATE_WAITPROXYCONNECT); - else + if(easy->easy_conn->bits.tunnel_connecting) + multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + else #endif - multistate(easy, CURLM_STATE_WAITCONNECT); + multistate(easy, CURLM_STATE_WAITCONNECT); + } } } } @@ -1077,12 +1089,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->connectindex, easy->easy_conn->send_pipe->size, easy->easy_conn->writechannel_inuse, - Curl_isHandleAtHead(easy->easy_handle, - easy->easy_conn->send_pipe)); + isHandleAtHead(easy->easy_handle, + easy->easy_conn->send_pipe)); #endif if(!easy->easy_conn->writechannel_inuse && - Curl_isHandleAtHead(easy->easy_handle, - easy->easy_conn->send_pipe)) { + isHandleAtHead(easy->easy_handle, + easy->easy_conn->send_pipe)) { /* Grab the channel */ easy->easy_conn->writechannel_inuse = TRUE; multistate(easy, CURLM_STATE_DO); @@ -1190,12 +1202,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_DO_DONE: - /* Remove ourselves from the send pipeline */ - Curl_removeHandleFromPipeline(easy->easy_handle, - easy->easy_conn->send_pipe); - /* Add ourselves to the recv pipeline */ - easy->result = Curl_addHandleToPipeline(easy->easy_handle, - easy->easy_conn->recv_pipe); + /* Move ourselves from the send to recv pipeline */ + moveHandleFromSendToRecvPipeline(easy->easy_handle, easy->easy_conn); + /* Check if we can move pending requests to send pipe */ + checkPendPipeline(easy->easy_conn); multistate(easy, CURLM_STATE_WAITPERFORM); result = CURLM_CALL_MULTI_PERFORM; break; @@ -1206,13 +1216,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->connectindex, easy->easy_conn->recv_pipe->size, easy->easy_conn->readchannel_inuse, - Curl_isHandleAtHead(easy->easy_handle, - easy->easy_conn->recv_pipe)); + isHandleAtHead(easy->easy_handle, + easy->easy_conn->recv_pipe)); #endif /* Wait for our turn to PERFORM */ if(!easy->easy_conn->readchannel_inuse && - Curl_isHandleAtHead(easy->easy_handle, - easy->easy_conn->recv_pipe)) { + isHandleAtHead(easy->easy_handle, + easy->easy_conn->recv_pipe)) { /* Grab the channel */ easy->easy_conn->readchannel_inuse = TRUE; multistate(easy, CURLM_STATE_PERFORM); @@ -1292,6 +1302,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(easy->easy_handle->req.newurl || retry) { Curl_removeHandleFromPipeline(easy->easy_handle, easy->easy_conn->recv_pipe); + /* Check if we can move pending requests to send pipe */ + checkPendPipeline(easy->easy_conn); if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ @@ -1323,6 +1335,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Remove ourselves from the receive pipeline */ Curl_removeHandleFromPipeline(easy->easy_handle, easy->easy_conn->recv_pipe); + /* Check if we can move pending requests to send pipe */ + checkPendPipeline(easy->easy_conn); easy->easy_handle->state.is_in_pipeline = FALSE; if(easy->easy_conn->bits.stream_was_rewound) { @@ -1390,6 +1404,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->send_pipe); Curl_removeHandleFromPipeline(easy->easy_handle, easy->easy_conn->recv_pipe); + /* Check if we can move pending requests to send pipe */ + checkPendPipeline(easy->easy_conn); } if(disconnect_conn) { @@ -1952,6 +1968,77 @@ static int update_timer(struct Curl_multi *multi) return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp); } +static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, + struct connectdata *conn) +{ + size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; + struct curl_llist *pipeline; + + if(!Curl_isPipeliningEnabled(handle) || + pipeLen == 0) + pipeline = conn->send_pipe; + else { + if(conn->server_supports_pipelining && + pipeLen < MAX_PIPELINE_LENGTH) + pipeline = conn->send_pipe; + else + pipeline = conn->pend_pipe; + } + + return Curl_addHandleToPipeline(handle, pipeline); +} + +static int checkPendPipeline(struct connectdata *conn) +{ + int result = 0; + + if (conn->server_supports_pipelining) { + size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; + struct curl_llist_element *curr = conn->pend_pipe->head; + + while(pipeLen < MAX_PIPELINE_LENGTH && curr) { + Curl_llist_move(conn->pend_pipe, curr, + conn->send_pipe, conn->send_pipe->tail); + Curl_pgrsTime(curr->ptr, TIMER_CONNECT); + ++result; /* count how many handles we moved */ + curr = conn->pend_pipe->head; + ++pipeLen; + } + if (result > 0) + conn->now = Curl_tvnow(); + } + + return result; +} + +static int moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, + struct connectdata *conn) +{ + struct curl_llist_element *curr; + + curr = conn->send_pipe->head; + while(curr) { + if(curr->ptr == handle) { + Curl_llist_move(conn->send_pipe, curr, + conn->recv_pipe, conn->recv_pipe->tail); + return 1; /* we moved a handle */ + } + curr = curr->next; + } + + return 0; +} + +static bool isHandleAtHead(struct SessionHandle *handle, + struct curl_llist *pipeline) +{ + struct curl_llist_element *curr = pipeline->head; + if(curr) + return (bool)(curr->ptr == handle); + + return FALSE; +} + /* 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) -- cgit v1.2.1 From ddaa78f08b394f1c7cd1488ce24aee1412679c29 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 16 Jan 2008 21:33:52 +0000 Subject: Dmitry Kurochkin's additional pipelining bugfix --- lib/multi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 98f35bff5..fef632adf 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -944,8 +944,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = addHandleToSendOrPendPipeline(easy->easy_handle, easy->easy_conn); if(CURLE_OK == easy->result) { - if (easy->easy_handle->state.is_in_pipeline) + if (easy->easy_handle->state.is_in_pipeline) { multistate(easy, CURLM_STATE_WAITDO); + if(isHandleAtHead(easy->easy_handle, + easy->easy_conn->send_pipe)) + result = CURLM_CALL_MULTI_PERFORM; + } else { if(async) /* We're now waiting for an asynchronous name lookup */ -- cgit v1.2.1 From 62df0ff025e247a5d1d2850e22a8cdfb9edcbfd7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 18 Jan 2008 21:51:10 +0000 Subject: Lau Hang Kin found and fixed a problem with the multi interface when doing CONNECT over a proxy. curl_multi_fdset() didn't report back the socket properly during that state, due to a missing case in the switch in the multi_getsock() function. --- lib/multi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index fef632adf..e0a10cfce 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -780,6 +780,7 @@ static int multi_getsock(struct Curl_one_easy *easy, case CURLM_STATE_DOING: return Curl_doing_getsock(easy->easy_conn, socks, numsocks); + case CURLM_STATE_WAITPROXYCONNECT: case CURLM_STATE_WAITCONNECT: return waitconnect_getsock(easy->easy_conn, socks, numsocks); -- cgit v1.2.1 From ef0ed9b7206e186c7d3d08b91885efdd0d0db9df Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 21 Jan 2008 23:48:58 +0000 Subject: Dmitry Kurochkin removed the cancelled state for pipelining, as we agreed that it is bad anyway. Starting now, removing a handle that is in used in a pipeline will break the pipeline - it'll be set back up again but still... --- lib/multi.c | 49 +++++++++++-------------------------------------- 1 file changed, 11 insertions(+), 38 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e0a10cfce..e531dd60b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -85,7 +85,6 @@ typedef enum { CURLM_STATE_TOOFAST, /* wait because limit-rate exceeded */ CURLM_STATE_DONE, /* post data transfer operation */ CURLM_STATE_COMPLETED, /* operation complete */ - CURLM_STATE_CANCELLED, /* cancelled */ CURLM_STATE_LAST /* not a true state, never use this */ } CURLMstate; @@ -216,7 +215,6 @@ static const char * const statename[]={ "TOOFAST", "DONE", "COMPLETED", - "CANCELLED" }; void curl_multi_dump(CURLM *multi_handle); @@ -587,15 +585,12 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, multi->num_alive--; if(easy->easy_handle->state.is_in_pipeline && - easy->state > CURLM_STATE_DO && - easy->state < CURLM_STATE_COMPLETED) { - /* If the handle is in a pipeline and has finished sending off its - request but not received its reponse yet, we need to remember the - fact that we want to remove this handle but do the actual removal at - a later time */ - easy->easy_handle->state.cancelled = TRUE; - return CURLM_OK; - } + easy->state > CURLM_STATE_WAITDO && + easy->state < CURLM_STATE_COMPLETED) + /* If the handle is in a pipeline and has started sending off its + request but not received its reponse yet, we need to close + connection. */ + easy->easy_conn->bits.close = TRUE; /* The timer must be shut down before easy->multi is set to NULL, else the timenode will remain in the splay tree after @@ -1351,22 +1346,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_CALL_MULTI_PERFORM; } - if(!easy->easy_handle->state.cancelled) { - /* post-transfer command */ - easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); + /* post-transfer command */ + easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); - /* after we have DONE what we're supposed to do, go COMPLETED, and - it doesn't matter what the Curl_done() returned! */ - multistate(easy, CURLM_STATE_COMPLETED); - } + /* after we have DONE what we're supposed to do, go COMPLETED, and + it doesn't matter what the Curl_done() returned! */ + multistate(easy, CURLM_STATE_COMPLETED); break; case CURLM_STATE_COMPLETED: - if(easy->easy_handle->state.cancelled) - /* Go into the CANCELLED state if we were cancelled */ - multistate(easy, CURLM_STATE_CANCELLED); - /* this is a completed transfer, it is likely to still be connected */ /* This node should be delinked from the list now and we should post @@ -1377,13 +1366,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn = NULL; break; - case CURLM_STATE_CANCELLED: - /* Cancelled transfer, wait to be cleaned up */ - - /* Reset the conn pointer so we don't leave it dangling */ - easy->easy_conn = NULL; - break; - default: return CURLM_INTERNAL_ERROR; } @@ -1481,15 +1463,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) while(easy != &multi->easy) { CURLMcode result; - if(easy->easy_handle->state.cancelled && - easy->state == CURLM_STATE_CANCELLED) { - /* Remove cancelled handles once it's safe to do so */ - Curl_multi_rmeasy(multi_handle, easy->easy_handle); - easy->easy_handle = NULL; - easy = easy->next; - continue; - } - result = multi_runsingle(multi, easy); if(result) returncode = result; -- cgit v1.2.1 From 79cb74f03a4fa5512812cc8c8e35d735a099b35c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 23 Jan 2008 12:22:04 +0000 Subject: Dmitry Kurochkin's pipelining close-down segfault fix --- lib/multi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e531dd60b..f52cce5df 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -584,13 +584,18 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, alive connections when this is removed */ multi->num_alive--; - if(easy->easy_handle->state.is_in_pipeline && + if(easy->easy_conn && + easy->easy_handle->state.is_in_pipeline && easy->state > CURLM_STATE_WAITDO && - easy->state < CURLM_STATE_COMPLETED) + easy->state < CURLM_STATE_COMPLETED) { /* If the handle is in a pipeline and has started sending off its request but not received its reponse yet, we need to close connection. */ easy->easy_conn->bits.close = TRUE; + /* Set connection owner so that Curl_done() closes it. + We can sefely do this here since connection is killed. */ + easy->easy_conn->data = easy->easy_handle; + } /* The timer must be shut down before easy->multi is set to NULL, else the timenode will remain in the splay tree after -- cgit v1.2.1 From 87fdfe770d054d06cd4548cd300866915caad4af Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 27 Jan 2008 22:53:09 +0000 Subject: Dmitry Kurochkin: In "real world" testing I found more bugs in pipelining. Broken connection is not restored and we get into infinite loop. It happens because of wrong is_in_pipeline values. --- lib/multi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f52cce5df..5aac09e2d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -871,6 +871,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(easy->easy_handle->state.is_in_pipeline) { /* Head back to the CONNECT state */ multistate(easy, CURLM_STATE_CONNECT); + easy->easy_handle->state.is_in_pipeline = FALSE; result = CURLM_CALL_MULTI_PERFORM; easy->result = CURLE_OK; } @@ -1286,6 +1287,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->bits.close = TRUE; Curl_removeHandleFromPipeline(easy->easy_handle, easy->easy_conn->recv_pipe); + easy->easy_handle->state.is_in_pipeline = FALSE; if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) { /* if we failed anywhere, we must clean up the secondary socket if @@ -1309,6 +1311,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->recv_pipe); /* Check if we can move pending requests to send pipe */ checkPendPipeline(easy->easy_conn); + easy->easy_handle->state.is_in_pipeline = FALSE; if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ -- cgit v1.2.1 From ffae4f6b48cc8d227a41701f8f043bd8549b4ba3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 3 Feb 2008 12:31:35 +0000 Subject: - Dmitry Kurochkin cleaned up the pipelining code and removed the need for and use of the "is_in_pipeline" struct field. --- lib/multi.c | 51 ++++++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 5aac09e2d..504e43b0e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -585,7 +585,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, multi->num_alive--; if(easy->easy_conn && - easy->easy_handle->state.is_in_pipeline && + (easy->easy_conn->send_pipe->size + + easy->easy_conn->recv_pipe->size > 1) && easy->state > CURLM_STATE_WAITDO && easy->state < CURLM_STATE_COMPLETED) { /* If the handle is in a pipeline and has started sending off its @@ -868,17 +869,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n", easy, easy->easy_handle->state.path); - if(easy->easy_handle->state.is_in_pipeline) { + if(easy->state != CURLM_STATE_COMPLETED) { /* Head back to the CONNECT state */ multistate(easy, CURLM_STATE_CONNECT); - easy->easy_handle->state.is_in_pipeline = FALSE; result = CURLM_CALL_MULTI_PERFORM; easy->result = CURLE_OK; } - else { - easy->result = CURLE_COULDNT_CONNECT; - multistate(easy, CURLM_STATE_COMPLETED); - } easy->easy_handle->state.pipe_broke = FALSE; easy->easy_conn = NULL; @@ -946,32 +942,24 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = addHandleToSendOrPendPipeline(easy->easy_handle, easy->easy_conn); if(CURLE_OK == easy->result) { - if (easy->easy_handle->state.is_in_pipeline) { - multistate(easy, CURLM_STATE_WAITDO); - if(isHandleAtHead(easy->easy_handle, - easy->easy_conn->send_pipe)) - result = CURLM_CALL_MULTI_PERFORM; - } + if(async) + /* We're now waiting for an asynchronous name lookup */ + multistate(easy, CURLM_STATE_WAITRESOLVE); else { - if(async) - /* We're now waiting for an asynchronous name lookup */ - multistate(easy, CURLM_STATE_WAITRESOLVE); + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to + WAITDO! */ + result = CURLM_CALL_MULTI_PERFORM; + + if(protocol_connect) + multistate(easy, CURLM_STATE_WAITDO); else { - /* after the connect has been sent off, go WAITCONNECT unless the - protocol connect is already done and we can go directly to - WAITDO! */ - result = CURLM_CALL_MULTI_PERFORM; - - if(protocol_connect) - multistate(easy, CURLM_STATE_WAITDO); - else { #ifndef CURL_DISABLE_HTTP - if(easy->easy_conn->bits.tunnel_connecting) - multistate(easy, CURLM_STATE_WAITPROXYCONNECT); - else + if(easy->easy_conn->bits.tunnel_connecting) + multistate(easy, CURLM_STATE_WAITPROXYCONNECT); + else #endif - multistate(easy, CURLM_STATE_WAITCONNECT); - } + multistate(easy, CURLM_STATE_WAITCONNECT); } } } @@ -1287,7 +1275,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->bits.close = TRUE; Curl_removeHandleFromPipeline(easy->easy_handle, easy->easy_conn->recv_pipe); - easy->easy_handle->state.is_in_pipeline = FALSE; if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) { /* if we failed anywhere, we must clean up the secondary socket if @@ -1311,7 +1298,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->recv_pipe); /* Check if we can move pending requests to send pipe */ checkPendPipeline(easy->easy_conn); - easy->easy_handle->state.is_in_pipeline = FALSE; + if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ @@ -1345,7 +1332,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->recv_pipe); /* Check if we can move pending requests to send pipe */ checkPendPipeline(easy->easy_conn); - easy->easy_handle->state.is_in_pipeline = FALSE; if(easy->easy_conn->bits.stream_was_rewound) { /* This request read past its response boundary so we quickly @@ -1388,7 +1374,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* NOTE: no attempt to disconnect connections must be made in the case blocks above - cleanup happens only here */ - easy->easy_handle->state.is_in_pipeline = FALSE; easy->easy_handle->state.pipe_broke = FALSE; if(easy->easy_conn) { -- cgit v1.2.1 From 0e73361a06fd0b458816d1121fe39e0415f9f150 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 17 Feb 2008 13:38:19 +0000 Subject: added a comment about the ignoring of the Curl_done() return code --- lib/multi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 504e43b0e..ab0db3236 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -615,8 +615,11 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, (easy->easy_conn->data == easy->easy_handle)) { /* Curl_done() clears the conn->data field to lose the association - between the easy handle and the connection */ - Curl_done(&easy->easy_conn, easy->result, premature); + between the easy handle and the connection + + Note that this ignores the return code simply because there's nothing + really useful to do with it anyway! */ + (void)Curl_done(&easy->easy_conn, easy->result, premature); if(easy->easy_conn) /* the connection is still alive, set back the association to enable -- cgit v1.2.1 From 55700cb01f4a01b8187f387e1655371e6fe0703a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 20 Feb 2008 08:28:02 +0000 Subject: - We no longer support setting the CURLOPT_URL option from inside a callback such as the CURLOPT_SSL_CTX_FUNCTION one treat that as if it was a Location: following. The patch that introduced this feature was done for 7.11.0, but this code and functionality has been broken since about 7.15.4 (March 2006) with the introduction of non-blocking OpenSSL "connects". It was a hack to begin with and since it doesn't work and hasn't worked correctly for a long time and nobody has even noticed, I consider it a very suitable subject for plain removal. And so it was done. --- lib/multi.c | 45 +++++++-------------------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ab0db3236..a29b6a0ae 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -860,12 +860,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLMcode result = CURLM_OK; struct SingleRequest *k; + if(!GOOD_EASY_HANDLE(easy->easy_handle)) + return CURLM_BAD_EASY_HANDLE; + do { + /* this is a do-while loop just to allow a break to skip to the end + of it */ bool disconnect_conn = FALSE; - if(!GOOD_EASY_HANDLE(easy->easy_handle)) - return CURLM_BAD_EASY_HANDLE; - /* Handle the case when the pipe breaks, i.e., the connection we're using gets cleaned up and we're left with nothing. */ if(easy->easy_handle->state.pipe_broke) { @@ -885,40 +887,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } if(easy->state > CURLM_STATE_CONNECT && - easy->state < CURLM_STATE_COMPLETED) { + easy->state < CURLM_STATE_COMPLETED) /* Make sure we set the connection's current owner */ easy->easy_conn->data = easy->easy_handle; - } - - if(CURLM_STATE_WAITCONNECT <= easy->state && - easy->state <= CURLM_STATE_DO && - easy->easy_handle->change.url_changed) { - char *gotourl; - Curl_posttransfer(easy->easy_handle); - - easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); - /* We make sure that the pipe broken flag is reset - because in this case, it isn't an actual break */ - easy->easy_handle->state.pipe_broke = FALSE; - if(CURLE_OK == easy->result) { - gotourl = strdup(easy->easy_handle->change.url); - if(gotourl) { - easy->easy_handle->change.url_changed = FALSE; - easy->result = Curl_follow(easy->easy_handle, gotourl, FALSE); - if(CURLE_OK == easy->result) - multistate(easy, CURLM_STATE_CONNECT); - else - free(gotourl); - } - else { - easy->result = CURLE_OUT_OF_MEMORY; - multistate(easy, CURLM_STATE_COMPLETED); - break; - } - } - } - - easy->easy_handle->change.url_changed = FALSE; switch(easy->state) { case CURLM_STATE_INIT: @@ -1403,9 +1374,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_COMPLETED); } } - - } while(easy->easy_handle->change.url_changed); - + } while(0); if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ -- cgit v1.2.1 From 6f3166c15b7fc951b10ba7cb2607527065264f0e Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Tue, 18 Mar 2008 08:14:37 +0000 Subject: - Added curl_easy_getinfo typechecker. - Added macros for curl_share_setopt and curl_multi_setopt to check at least the correct number of arguments. --- lib/multi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a29b6a0ae..73e8e7e3d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1778,6 +1778,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, return result; } +#undef curl_multi_setopt CURLMcode curl_multi_setopt(CURLM *multi_handle, CURLMoption option, ...) { -- cgit v1.2.1 From 852989856d3802a9e7bd2f1e368302d92ddf66e2 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 30 Apr 2008 21:20:08 +0000 Subject: - To make it easier for applications that want lots of magic stuff done on redirections and thus cannot use CURLOPT_FOLLOWLOCATION easily, we now introduce the new CURLINFO_REDIRECT_URL option that lets applications extract the URL libcurl would've redirected to if it had been told to. This then enables the application to continue to that URL as it thinks is suitable, without having to re-implement the magic of creating the new URL from the Location: header etc. Test 1029 verifies it. --- lib/multi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 73e8e7e3d..df287129b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1262,6 +1262,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else if(TRUE == done) { char *newurl; bool retry = Curl_retry_request(easy->easy_conn, &newurl); + followtype follow=FOLLOW_NONE; /* call this even if the readwrite function returned error */ Curl_posttransfer(easy->easy_handle); @@ -1278,10 +1279,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, then figure out the URL here */ newurl = easy->easy_handle->req.newurl; easy->easy_handle->req.newurl = NULL; + follow = FOLLOW_REDIR; } + else + follow = FOLLOW_RETRY; easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); if(easy->result == CURLE_OK) - easy->result = Curl_follow(easy->easy_handle, newurl, retry); + easy->result = Curl_follow(easy->easy_handle, newurl, follow); if(CURLE_OK == easy->result) { multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; -- cgit v1.2.1 From eb68aa38e3a79ee76967261aeb8c4364223f87d9 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Wed, 7 May 2008 15:41:41 +0000 Subject: Christopher Palow provided the patch (edited by me) that introduces the use of microsecond resolution keys for internal splay trees. http://curl.haxx.se/mail/lib-2008-04/0513.html --- lib/multi.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index df287129b..48e7c410a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -23,9 +23,6 @@ #include "setup.h" -#include -#include - #ifdef HAVE_SYS_SOCKET_H #include #endif @@ -177,8 +174,8 @@ struct Curl_multi { /* timer callback and user data pointer for the *socket() API */ curl_multi_timer_callback timer_cb; void *timer_userp; - time_t timer_lastcall; /* the fixed time for the timeout for the previous - callback */ + struct timeval timer_lastcall; /* the fixed time for the timeout for the + previous callback */ }; static bool multi_conn_using(struct Curl_multi *multi, @@ -1446,9 +1443,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) */ do { struct timeval now = Curl_tvnow(); - int key = now.tv_sec; /* drop the usec part */ - multi->timetree = Curl_splaygetbest(key, multi->timetree, &t); + multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); if(t) { struct SessionHandle *d = t->payload; struct timeval* tv = &d->state.expiretime; @@ -1746,7 +1742,6 @@ static CURLMcode multi_socket(struct Curl_multi *multi, * handle we deal with. */ do { - int key; struct timeval now; /* the first loop lap 'data' can be NULL */ @@ -1763,9 +1758,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, 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); + multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); if(t) { /* assign 'data' to be the easy handle we just removed from the splay tree */ @@ -1858,17 +1852,19 @@ CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) static CURLMcode multi_timeout(struct Curl_multi *multi, long *timeout_ms) { + static struct timeval tv_zero = {0,0}; + 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); + multi->timetree = Curl_splay(tv_zero, 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) + if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) + /* some time left before expiration */ + *timeout_ms = curlx_tvdiff(multi->timetree->key, now); + else /* 0 means immediately */ *timeout_ms = 0; } @@ -1908,7 +1904,7 @@ static int update_timer(struct Curl_multi *multi) * timeout we got the (relative) time-out time for. We can thus easily check * if this is the same (fixed) time as we got in a previous call and then * avoid calling the callback again. */ - if(multi->timetree->key == multi->timer_lastcall) + if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0) return 0; multi->timer_lastcall = multi->timetree->key; @@ -2002,7 +1998,7 @@ void Curl_expire(struct SessionHandle *data, long milli) if(!milli) { /* No timeout, clear the time data. */ - if(nowp->tv_sec) { + if(nowp->tv_sec || nowp->tv_usec) { /* Since this is an cleared time, we must remove the previous entry from the splay tree */ rc = Curl_splayremovebyaddr(multi->timetree, @@ -2030,7 +2026,7 @@ void Curl_expire(struct SessionHandle *data, long milli) set.tv_usec -= 1000000; } - if(nowp->tv_sec) { + if(nowp->tv_sec || nowp->tv_usec) { /* This means that the struct is added as a node in the splay tree. Compare if the new time is earlier, and only remove-old/add-new if it is. */ @@ -2054,7 +2050,7 @@ void Curl_expire(struct SessionHandle *data, long milli) (long)nowp->tv_sec, (long)nowp->tv_usec, milli); #endif data->state.timenode.payload = data; - multi->timetree = Curl_splayinsert((int)nowp->tv_sec, + multi->timetree = Curl_splayinsert(*nowp, multi->timetree, &data->state.timenode); } -- cgit v1.2.1 From 0510759bc4902f6b15b476f9ae94403beb9a02cb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 9 May 2008 12:59:24 +0000 Subject: - Stefan Krause reported a busy-looping case when using the multi interface and doing CONNECT to a proxy. The app would then busy-loop until the proxy completed its response. --- lib/multi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 48e7c410a..10341808f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -722,6 +722,12 @@ static int waitconnect_getsock(struct connectdata *conn, return GETSOCK_BLANK; sock[0] = conn->sock[FIRSTSOCKET]; + + /* when we've sent a CONNECT to a proxy, we should rather wait for the + socket to become readable to be able to get the response headers */ + if(conn->bits.tunnel_connecting) + return GETSOCK_READSOCK(0); + return GETSOCK_WRITESOCK(0); } -- cgit v1.2.1 From ae45a462e033ae67a41b6024984484a13087f67d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 19 May 2008 20:58:01 +0000 Subject: with pipelining disabled, the state should never be set to WAITDO but rather go straight to DO we had multiple states for which the internal function returned no socket at all to wait for, with the effect that libcurl calls the socket callback (when curl_multi_socket() is used) with REMOVE prematurely (as it would be added again within very shortly) --- lib/multi.c | 79 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 29 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 10341808f..3b6e4aeb2 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -772,10 +772,19 @@ static int multi_getsock(struct Curl_one_easy *easy, } switch(easy->state) { - case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */ default: +#if 0 /* switch back on these cases to get the compiler to check for all enums + to be present */ + case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */ + case CURLM_STATE_COMPLETED: + case CURLM_STATE_INIT: + case CURLM_STATE_CONNECT: + case CURLM_STATE_WAITDO: + case CURLM_STATE_DONE: + case CURLM_STATE_LAST: /* this will get called with CURLM_STATE_COMPLETED when a handle is removed */ +#endif return 0; case CURLM_STATE_WAITRESOLVE: @@ -784,6 +793,7 @@ static int multi_getsock(struct Curl_one_easy *easy, case CURLM_STATE_PROTOCONNECT: return Curl_protocol_getsock(easy->easy_conn, socks, numsocks); + case CURLM_STATE_DO: case CURLM_STATE_DOING: return Curl_doing_getsock(easy->easy_conn, socks, numsocks); @@ -794,6 +804,8 @@ static int multi_getsock(struct Curl_one_easy *easy, case CURLM_STATE_DO_MORE: return domore_getsock(easy->easy_conn, socks, numsocks); + case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch + to waiting for the same as the *PERFORM states */ case CURLM_STATE_PERFORM: case CURLM_STATE_WAITPERFORM: return Curl_single_getsock(easy->easy_conn, socks, numsocks); @@ -925,11 +937,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* after the connect has been sent off, go WAITCONNECT unless the protocol connect is already done and we can go directly to - WAITDO! */ + WAITDO or DO! */ result = CURLM_CALL_MULTI_PERFORM; if(protocol_connect) - multistate(easy, CURLM_STATE_WAITDO); + multistate(easy, multi->pipelining_enabled? + CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP if(easy->easy_conn->bits.tunnel_connecting) @@ -965,7 +978,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* call again please so that we get the next socket setup */ result = CURLM_CALL_MULTI_PERFORM; if(protocol_connect) - multistate(easy, CURLM_STATE_WAITDO); + multistate(easy, multi->pipelining_enabled? + CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP if(easy->easy_conn->bits.tunnel_connecting) @@ -1028,8 +1042,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_PROTOCONNECT); } else { - /* after the connect has completed, go WAITDO */ - multistate(easy, CURLM_STATE_WAITDO); + /* after the connect has completed, go WAITDO or DO */ + multistate(easy, multi->pipelining_enabled? + CURLM_STATE_WAITDO:CURLM_STATE_DO); result = CURLM_CALL_MULTI_PERFORM; } @@ -1041,8 +1056,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = Curl_protocol_connecting(easy->easy_conn, &protocol_connect); if((easy->result == CURLE_OK) && protocol_connect) { - /* after the connect has completed, go WAITDO */ - multistate(easy, CURLM_STATE_WAITDO); + /* after the connect has completed, go WAITDO or DO */ + multistate(easy, multi->pipelining_enabled? + CURLM_STATE_WAITDO:CURLM_STATE_DO); result = CURLM_CALL_MULTI_PERFORM; } else if(easy->result) { @@ -1712,34 +1728,39 @@ static CURLMcode multi_socket(struct Curl_multi *multi, Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); if(!entry) - /* unmatched socket, major problemo! */ - return CURLM_BAD_SOCKET; /* better return code? */ - - data = entry->easy; + /* Unmatched socket, we can't act on it but we ignore this fact. In + real-world tests it has been proved that libevent can in fact give + the application actions even though the socket was just previously + asked to get removed, so thus we better survive stray socket actions + and just move on. */ + ; + else { + data = entry->easy; - if(data->magic != CURLEASY_MAGIC_NUMBER) - /* bad bad bad bad bad bad bad */ - return CURLM_INTERNAL_ERROR; + if(data->magic != CURLEASY_MAGIC_NUMBER) + /* bad bad bad bad bad bad bad */ + return CURLM_INTERNAL_ERROR; - if(data->set.one_easy->easy_conn) /* set socket event bitmask */ - data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; + if(data->set.one_easy->easy_conn) /* set socket event bitmask */ + data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; - result = multi_runsingle(multi, data->set.one_easy); + result = multi_runsingle(multi, data->set.one_easy); - if(data->set.one_easy->easy_conn) - data->set.one_easy->easy_conn->cselect_bits = 0; + if(data->set.one_easy->easy_conn) + data->set.one_easy->easy_conn->cselect_bits = 0; - if(CURLM_OK >= result) - /* get the socket(s) and check if the state has been changed since - last */ - singlesocket(multi, data->set.one_easy); + if(CURLM_OK >= result) + /* get the socket(s) and check if the state has been changed since + last */ + singlesocket(multi, data->set.one_easy); - /* Now we fall-through and do the timer-based stuff, since we don't want - to force the user to have to deal with timeouts as long as at least one - connection in fact has traffic. */ + /* Now we fall-through and do the timer-based stuff, since we don't want + to force the user to have to deal with timeouts as long as at least + one connection in fact has traffic. */ - data = NULL; /* set data to NULL again to avoid calling multi_runsingle() - in case there's no need to */ + data = NULL; /* set data to NULL again to avoid calling + multi_runsingle() in case there's no need to */ + } } /* -- cgit v1.2.1 From ec4f6e93c27f899c219863fab9b48863fc2d3600 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 28 May 2008 20:56:19 +0000 Subject: - Emil Romanus found a problem and helped me repeat it. It occured when using the curl_multi_socket() API with HTTP pipelining enabled and could lead to the pipeline basically stalling for a very long period of time until it took off again. --- lib/multi.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 19 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3b6e4aeb2..9349ee6c6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -189,8 +189,8 @@ static int update_timer(struct Curl_multi *multi); static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, struct connectdata *conn); static int checkPendPipeline(struct connectdata *conn); -static int moveHandleFromSendToRecvPipeline(struct SessionHandle *habdle, - struct connectdata *conn); +static void moveHandleFromSendToRecvPipeline(struct SessionHandle *habdle, + struct connectdata *conn); static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline); @@ -505,7 +505,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, sockets that time-out or have actions will be dealt with. Since this handle has no action yet, we make sure it times out to get things to happen. */ - Curl_expire(easy->easy_handle, 10); + Curl_expire(easy->easy_handle, 1); /* increase the node-counter */ multi->num_easy++; @@ -1286,13 +1286,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* call this even if the readwrite function returned error */ Curl_posttransfer(easy->easy_handle); + /* we're no longer receving */ + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); + + /* expire the new receiving pipeline head */ + if(easy->easy_conn->recv_pipe->head) + Curl_expire(easy->easy_conn->recv_pipe->head->ptr, 1); + + /* Check if we can move pending requests to send pipe */ + checkPendPipeline(easy->easy_conn); + /* When we follow redirects, must to go back to the CONNECT state */ if(easy->easy_handle->req.newurl || retry) { - Curl_removeHandleFromPipeline(easy->easy_handle, - easy->easy_conn->recv_pipe); - /* Check if we can move pending requests to send pipe */ - checkPendPipeline(easy->easy_conn); - if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ @@ -1331,10 +1337,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, checkPendPipeline(easy->easy_conn); if(easy->easy_conn->bits.stream_was_rewound) { - /* This request read past its response boundary so we quickly - let the other requests consume those bytes since there is no - guarantee that the socket will become active again */ - result = CURLM_CALL_MULTI_PERFORM; + /* This request read past its response boundary so we quickly let the + other requests consume those bytes since there is no guarantee that + the socket will become active again */ + result = CURLM_CALL_MULTI_PERFORM; } /* post-transfer command */ @@ -1431,7 +1437,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, get things to happen. Also, this makes it less important for callers of the curl_multi_* functions to bother about the CURLM_CALL_MULTI_PERFORM return code, as long as they deal with the timeouts properly. */ - Curl_expire(easy->easy_handle, 10); + Curl_expire(easy->easy_handle, 1); return result; } @@ -1785,6 +1791,9 @@ static CURLMcode multi_socket(struct Curl_multi *multi, extracts a matching node if there is one */ now = Curl_tvnow(); + now.tv_usec += 1000; /* to compensate for the truncating of 999us to 0ms, + we always add time here to make the comparison + below better */ multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); if(t) { @@ -1962,6 +1971,7 @@ static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, static int checkPendPipeline(struct connectdata *conn) { int result = 0; + struct curl_llist_element *sendhead = conn->send_pipe->head; if (conn->server_supports_pipelining) { size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; @@ -1979,10 +1989,27 @@ static int checkPendPipeline(struct connectdata *conn) conn->now = Curl_tvnow(); } + if(result) { + /* something moved, check for a new send pipeline leader */ + if(sendhead != conn->send_pipe->head) { + /* this is a new one as head, expire it */ + conn->writechannel_inuse = FALSE; /* not in use yet */ + infof(conn->data, "%p is at send pipe head!\n", + conn->send_pipe->head->ptr); + Curl_expire(conn->send_pipe->head->ptr, 1); + } + } + return result; } -static int moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, +/* Move this transfer from the sending list to the receiving list. + + Pay special attention to the new sending list "leader" as it needs to get + checked to update what sockets it acts on. + + */ +static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, struct connectdata *conn) { struct curl_llist_element *curr; @@ -1992,12 +2019,24 @@ static int moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, if(curr->ptr == handle) { Curl_llist_move(conn->send_pipe, curr, conn->recv_pipe, conn->recv_pipe->tail); - return 1; /* we moved a handle */ + + if(conn->send_pipe->head) { + /* Since there's a new easy handle at the start of the send pipeline, + set its timeout value to 1ms to make it trigger instantly */ + conn->writechannel_inuse = FALSE; /* not used now */ + infof(conn->data, "%p is at send pipe head B!\n", + conn->send_pipe->head->ptr); + Curl_expire(conn->send_pipe->head->ptr, 1); + } + + /* The receiver's list is not really interesting here since either this + handle is now first in the list and we'll deal with it soon, or + another handle is already first and thus is already taken care of */ + + break; /* we're done! */ } curr = curr->next; } - - return 0; } static bool isHandleAtHead(struct SessionHandle *handle, @@ -2073,8 +2112,8 @@ void Curl_expire(struct SessionHandle *data, long milli) *nowp = set; #if 0 - infof(data, "Expire at %ld / %ld (%ldms)\n", - (long)nowp->tv_sec, (long)nowp->tv_usec, milli); + infof(data, "Expire at %ld / %ld (%ldms) %p\n", + (long)nowp->tv_sec, (long)nowp->tv_usec, milli, data); #endif data->state.timenode.payload = data; multi->timetree = Curl_splayinsert(*nowp, -- cgit v1.2.1 From 2597020d22ed6bcac9a9527f0fe9b235309d5a62 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 13 Jun 2008 21:16:10 +0000 Subject: In checkPendPipeline() we can't be setting the TIMER_CONNECT correctly as that is for the TCP connect. I changed it to TIMER_PRETRANSFER which seems to be what was intended here. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9349ee6c6..3b7831680 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1980,7 +1980,7 @@ static int checkPendPipeline(struct connectdata *conn) while(pipeLen < MAX_PIPELINE_LENGTH && curr) { Curl_llist_move(conn->pend_pipe, curr, conn->send_pipe, conn->send_pipe->tail); - Curl_pgrsTime(curr->ptr, TIMER_CONNECT); + Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER); ++result; /* count how many handles we moved */ curr = conn->pend_pipe->head; ++pipeLen; -- cgit v1.2.1 From d09b6ecaa5cd3e7639b4354d048459eaa6c4c2b3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 19 Jun 2008 21:32:51 +0000 Subject: - Christopher Palow fixed a curl_multi_socket() issue which previous caused libcurl to not tell the app properly when a socket was closed (when the name resolve done by c-ares is done) and then immediately re-created and put to use again (for the actual connection). Since the closure will make the "watch status" get lost in several event-based systems libcurl will need to tell the app about this close/re-create case. --- lib/multi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3b7831680..f0a7bfb31 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -965,6 +965,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = Curl_is_resolved(easy->easy_conn, &dns); if(dns) { + /* Update sockets here. Mainly because the socket(s) may have been + closed and the application thus needs to be told, even if it is + likely that the same socket(s) will again be used further down. */ + singlesocket(multi, easy); + /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ easy->result = Curl_async_resolved(easy->easy_conn, -- cgit v1.2.1 From e54209d643deeeee47a23d9247947b98ba6f0639 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 8 Aug 2008 01:52:08 +0000 Subject: Fixed an uninitialized variable in multi_runsingle() that could cause a request to prematurely end. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f0a7bfb31..a2a4eb50b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -871,7 +871,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, bool async; bool protocol_connect = FALSE; bool dophase_done; - bool done; + bool done = FALSE; CURLMcode result = CURLM_OK; struct SingleRequest *k; -- cgit v1.2.1 From 13dc82b9d4e3280e9fbb038027169a544fcbd171 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 23 Aug 2008 12:11:38 +0000 Subject: - Constantine Sapuntzakis fixed a bug when doing proxy CONNECT with the multi interface, and the proxy would send Connection: close during the authentication phase. http://curl.haxx.se/bug/view.cgi?id=2069047 --- lib/multi.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a2a4eb50b..f49d42699 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1009,7 +1009,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect); - if(CURLE_OK == easy->result) { + if(easy->easy_conn->bits.proxy_connect_closed) { + /* reset the error buffer */ + if(easy->easy_handle->set.errorbuffer) + easy->easy_handle->set.errorbuffer[0] = '\0'; + easy->easy_handle->state.errorbuf = FALSE; + + easy->result = CURLE_OK; + result = CURLM_CALL_MULTI_PERFORM; + multistate(easy, CURLM_STATE_CONNECT); + } + else if (CURLE_OK == easy->result) { if(!easy->easy_conn->bits.tunnel_connecting) multistate(easy, CURLM_STATE_WAITCONNECT); } -- cgit v1.2.1 From a622fd90b4c563a4fced20c5b88cb57537e809b0 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 6 Sep 2008 04:47:14 +0000 Subject: remove unnecessary typecasting of calloc() --- lib/multi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f49d42699..acf43a204 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -358,7 +358,7 @@ static struct curl_hash *sh_init(void) CURLM *curl_multi_init(void) { - struct Curl_multi *multi = (void *)calloc(sizeof(struct Curl_multi), 1); + struct Curl_multi *multi = calloc(sizeof(struct Curl_multi), 1); if(!multi) return NULL; @@ -419,7 +419,7 @@ 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 *)calloc(sizeof(struct Curl_one_easy), 1); + easy = calloc(sizeof(struct Curl_one_easy), 1); if(!easy) return CURLM_OUT_OF_MEMORY; @@ -2181,7 +2181,7 @@ static void add_closure(struct Curl_multi *multi, struct SessionHandle *data) { int i; - struct closure *cl = (struct closure *)calloc(sizeof(struct closure), 1); + struct closure *cl = calloc(sizeof(struct closure), 1); struct closure *p=NULL; struct closure *n; if(cl) { -- cgit v1.2.1 From 59e378f48fed849e8e41f0bc6a10bf7a1732ae8a Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 6 Sep 2008 05:29:05 +0000 Subject: remove unnecessary typecasting of malloc() --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index acf43a204..f8602e1d6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1427,7 +1427,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } /* now add a node to the Curl_message linked list with this info */ - msg = (struct Curl_message *)malloc(sizeof(struct Curl_message)); + msg = malloc(sizeof(struct Curl_message)); if(!msg) return CURLM_OUT_OF_MEMORY; -- cgit v1.2.1 From 2816902f0e91073887a0f746eae70edb75d9ea43 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 8 Sep 2008 12:15:09 +0000 Subject: Dmitry Kurochkin fixed pipelining over proxy using the multi interface --- lib/multi.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f8602e1d6..70ea38120 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1988,11 +1988,13 @@ static int checkPendPipeline(struct connectdata *conn) int result = 0; struct curl_llist_element *sendhead = conn->send_pipe->head; - if (conn->server_supports_pipelining) { - size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; + size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; + if (conn->server_supports_pipelining || pipeLen == 0) { struct curl_llist_element *curr = conn->pend_pipe->head; + const size_t maxPipeLen = + conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1; - while(pipeLen < MAX_PIPELINE_LENGTH && curr) { + while(pipeLen < maxPipeLen && curr) { Curl_llist_move(conn->pend_pipe, curr, conn->send_pipe, conn->send_pipe->tail); Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER); @@ -2000,11 +2002,10 @@ static int checkPendPipeline(struct connectdata *conn) curr = conn->pend_pipe->head; ++pipeLen; } - if (result > 0) - conn->now = Curl_tvnow(); } - if(result) { + if (result) { + conn->now = Curl_tvnow(); /* something moved, check for a new send pipeline leader */ if(sendhead != conn->send_pipe->head) { /* this is a new one as head, expire it */ -- cgit v1.2.1 From 6cea51585fc82f3abc540abfc2068517fb804128 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Wed, 10 Sep 2008 20:05:45 +0000 Subject: Checked in some code improvements and minor fixes that I discovered in the FreeBSD ports system. --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 70ea38120..af355b6c7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -321,7 +321,7 @@ static size_t fd_key_compare(void*k1, size_t k1_len, void*k2, size_t k2_len) { (void) k1_len; (void) k2_len; - return ((*((int* ) k1)) == (*((int* ) k2))) ? 1 : 0; + return (*((int* ) k1)) == (*((int* ) k2)); } static size_t hash_fd(void* key, size_t key_length, size_t slots_num) -- cgit v1.2.1 From 61c0bdb09c8e844117d816d0b16185b58f360ebb Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 13 Sep 2008 03:49:33 +0000 Subject: fix compiler warning: external declaration in primary source file --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index af355b6c7..9d8a8f990 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -214,7 +214,7 @@ static const char * const statename[]={ "COMPLETED", }; -void curl_multi_dump(CURLM *multi_handle); +static void curl_multi_dump(CURLM *multi_handle); #endif /* always use this function to change state, to make debugging easier */ @@ -2232,7 +2232,7 @@ static void add_closure(struct Curl_multi *multi, } #ifdef CURLDEBUG -void curl_multi_dump(CURLM *multi_handle) +static void curl_multi_dump(CURLM *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; -- cgit v1.2.1 From f591ab3ba0fafef16f3dab90f213e883c13a8c03 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 13 Sep 2008 15:59:14 +0000 Subject: fix compiler warning: defined but not used --- lib/multi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9d8a8f990..0fd1aa15b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -213,8 +213,6 @@ static const char * const statename[]={ "DONE", "COMPLETED", }; - -static void curl_multi_dump(CURLM *multi_handle); #endif /* always use this function to change state, to make debugging easier */ @@ -2232,7 +2230,7 @@ static void add_closure(struct Curl_multi *multi, } #ifdef CURLDEBUG -static void curl_multi_dump(CURLM *multi_handle) +void Curl_multi_dump(const struct Curl_multi *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; -- cgit v1.2.1 From 0a305eb79f444db81b4f6ae8c6f201af46f1888e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 29 Sep 2008 12:22:10 +0000 Subject: - Bug #2107803 (http://curl.haxx.se/bug/view.cgi?id=2107803) "no CURLINFO_REDIRECT_URL in multi mode" also contained a patch that fixed the problem. --- lib/multi.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0fd1aa15b..6eda8c1c8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1310,7 +1310,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Check if we can move pending requests to send pipe */ checkPendPipeline(easy->easy_conn); - /* When we follow redirects, must to go back to the CONNECT state */ + /* When we follow redirects or is set to retry the connection, we must + to go back to the CONNECT state */ if(easy->easy_handle->req.newurl || retry) { if(!retry) { /* if the URL is a follow-location and not just a retried request @@ -1335,6 +1336,18 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* after the transfer is done, go DONE */ + + /* but first check to see if we got a location info even though we're + not following redirects */ + if (easy->easy_handle->req.location) { + newurl = easy->easy_handle->req.location; + easy->easy_handle->req.location = NULL; + easy->result = Curl_follow(easy->easy_handle, newurl, FOLLOW_FAKE); + if (easy->result) + free(newurl); + break; + } + multistate(easy, CURLM_STATE_DONE); result = CURLM_CALL_MULTI_PERFORM; } -- cgit v1.2.1 From 544f2f74dfa7ef9bdb08b098995e61c1ca0e1aca Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 8 Oct 2008 21:42:29 +0000 Subject: - Igor filed bug #2111613 (http://curl.haxx.se/bug/view.cgi?id=2111613) that eventually identified a flaw in how the multi_socket interface in some cases missed to call the timeout callback when easy interfaces are removed and added within the same millisecond. --- lib/multi.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 6eda8c1c8..63db12a21 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -532,6 +532,18 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* increase the alive-counter */ multi->num_alive++; + /* A somewhat crude work-around for a little glitch in update_timer() that + happens if the lastcall time is set to the same time when the handle is + removed as when the next handle is added, as then the check in + update_timer() that prevents calling the application multiple times with + the same timer infor will not trigger and then the new handle's timeout + will not be notified to the app. + + The work-around is thus simply to clear the 'lastcall' variable to force + update_timer() to always trigger a callback to the app when a new easy + handle is added */ + memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); + update_timer(multi); return CURLM_OK; } @@ -1013,9 +1025,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_handle->set.errorbuffer[0] = '\0'; easy->easy_handle->state.errorbuf = FALSE; - easy->result = CURLE_OK; - result = CURLM_CALL_MULTI_PERFORM; - multistate(easy, CURLM_STATE_CONNECT); + easy->result = CURLE_OK; + result = CURLM_CALL_MULTI_PERFORM; + multistate(easy, CURLM_STATE_CONNECT); } else if (CURLE_OK == easy->result) { if(!easy->easy_conn->bits.tunnel_connecting) -- cgit v1.2.1 From 5779283a52a1369cccbe1a1d314e2ec8ac949e0f Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sun, 19 Oct 2008 20:17:16 +0000 Subject: attempt to fix or allow further detection of an elusive icc SIGSEGV --- lib/multi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 63db12a21..e503a11a2 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1551,6 +1551,8 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) multi->type = 0; /* not good anymore */ Curl_hash_destroy(multi->hostcache); Curl_hash_destroy(multi->sockhash); + multi->hostcache = NULL; + multi->sockhash = NULL; /* go over all connections that have close actions */ for(i=0; i< multi->connc->num; i++) { -- cgit v1.2.1 From 417bac4055ce215d3edb34f33f46e1dedaab48bb Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 25 Oct 2008 16:15:21 +0000 Subject: add missing header inclusions --- lib/multi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e503a11a2..a91c8d766 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -37,13 +37,16 @@ #include "url.h" #include "connect.h" #include "progress.h" -#include "memory.h" #include "easyif.h" #include "multiif.h" #include "sendf.h" #include "timeval.h" #include "http.h" +#define _MPRINTF_REPLACE /* use our functions only */ +#include + +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" -- cgit v1.2.1 From 0ce97f77e02acd2de15970270834a7011ce6cb38 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 30 Oct 2008 13:45:25 +0000 Subject: Use our Curl_addrinfo definition even when an addrinfo struct is available. Use a wrapper function to call system's getaddrinfo(). --- lib/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index a91c8d766..b82be6245 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -315,7 +315,8 @@ static void sh_freeentry(void *freethis) { struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis; - free(p); + if(p) + free(p); } static size_t fd_key_compare(void*k1, size_t k1_len, void*k2, size_t k2_len) -- cgit v1.2.1 From 479ddb1fee2fa5f7204a64742e9dcc2842d668dd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 3 Dec 2008 15:20:27 +0000 Subject: - Igor Novoseltsev filed bug #2351645 (http://curl.haxx.se/bug/view.cgi?id=2351645) that identified a problem with the multi interface that occured if you removed an easy handle while in progress and the handle was used in a HTTP pipeline. --- lib/multi.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b82be6245..ccccf7e8f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -620,22 +620,27 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, easy->easy_handle->dns.hostcachetype = HCACHE_NONE; } - /* we must call Curl_done() here (if we still "own it") so that we don't - leave a half-baked one around */ - if(easy->easy_conn && - (easy->easy_conn->data == easy->easy_handle)) { + if(easy->easy_conn) { + + /* we must call Curl_done() here (if we still "own it") so that we don't + leave a half-baked one around */ + if (easy->easy_conn->data == easy->easy_handle) { - /* Curl_done() clears the conn->data field to lose the association - between the easy handle and the connection + /* Curl_done() clears the conn->data field to lose the association + between the easy handle and the connection - Note that this ignores the return code simply because there's nothing - really useful to do with it anyway! */ - (void)Curl_done(&easy->easy_conn, easy->result, premature); + Note that this ignores the return code simply because there's + nothing really useful to do with it anyway! */ + (void)Curl_done(&easy->easy_conn, easy->result, premature); - if(easy->easy_conn) - /* the connection is still alive, set back the association to enable - the check below to trigger TRUE */ - easy->easy_conn->data = easy->easy_handle; + if(easy->easy_conn) + /* the connection is still alive, set back the association to enable + the check below to trigger TRUE */ + easy->easy_conn->data = easy->easy_handle; + } + else + /* Clear connection pipelines, if Curl_done above was not called */ + Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn); } /* If this easy_handle was the last one in charge for one or more -- cgit v1.2.1 From 792279581b0449ce5fe2e4580efc1b3990772ee7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 12 Dec 2008 12:21:11 +0000 Subject: - More work with Igor Novoseltsev to first fix the remaining stuff for removing easy handles from multi handles when the easy handle is/was within a HTTP pipeline. His bug report #2351653 (http://curl.haxx.se/bug/view.cgi?id=2351653) was also related and was eventually fixed by a patch by Igor himself. --- lib/multi.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ccccf7e8f..21f6ec250 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -587,6 +587,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, if(easy) { bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED); + bool easy_owns_conn = (bool)(easy->easy_conn && + (easy->easy_conn->data == easy->easy_handle)); /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ @@ -624,7 +626,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* we must call Curl_done() here (if we still "own it") so that we don't leave a half-baked one around */ - if (easy->easy_conn->data == easy->easy_handle) { + if (easy_owns_conn) { /* Curl_done() clears the conn->data field to lose the association between the easy handle and the connection @@ -676,9 +678,15 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, to that since we're not part of that handle anymore */ easy->easy_handle->state.connc = NULL; - /* and modify the connectindex since this handle can't point to the - connection cache anymore */ - if(easy->easy_conn && + /* Modify the connectindex since this handle can't point to the + connection cache anymore. + + TODO: consider if this is really what we want. The connection cache + is within the multi handle and that owns the connections so we should + not need to touch connections like this when we just remove an easy + handle... + */ + if(easy->easy_conn && easy_owns_conn && (easy->easy_conn->send_pipe->size + easy->easy_conn->recv_pipe->size == 0)) easy->easy_conn->connectindex = -1; -- cgit v1.2.1 From 07416b61e3c403ea56370858a618f877dcaee57d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 19 Dec 2008 21:14:52 +0000 Subject: - Using the libssh2 0.19 function libssh2_session_block_directions(), libcurl now has an improved ability to do right when the multi interface (both "regular" and multi_socket) is used for SCP and SFTP transfers. This should result in (much) less busy-loop situations and thus less CPU usage with no speed loss. --- lib/multi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 21f6ec250..f17b33377 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -786,11 +786,8 @@ static int multi_getsock(struct Curl_one_easy *easy, happen when this is called from curl_multi_remove_handle() => singlesocket() => multi_getsock(). */ - - if(easy->easy_handle->state.pipe_broke || - !easy->easy_conn) { + if(easy->easy_handle->state.pipe_broke || !easy->easy_conn) return 0; - } if(easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) { @@ -2108,7 +2105,10 @@ static bool isHandleAtHead(struct SessionHandle *handle, } /* 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() */ + this'-time for the transfer, to be extracted by curl_multi_timeout() + + Pass zero to clear the timeout value for this handle. +*/ void Curl_expire(struct SessionHandle *data, long milli) { struct Curl_multi *multi = data->multi; -- cgit v1.2.1 From ffd08df863b13ac8ca276e5d6f0a74ed28b55423 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 19 Dec 2008 22:58:22 +0000 Subject: - curl_multi_timeout() could return a timeout value of 0 even though nothing was actually ready to get done, as the internal time resolution is higher than the returned millisecond timer. Therefore it could cause applications running on fast processors to do short bursts of busy-loops. curl_multi_timeout() will now only return 0 if the timeout is actually alreay triggered. --- lib/multi.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f17b33377..97efc7066 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1541,6 +1541,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) if( CURLM_OK >= returncode ) update_timer(multi); + return returncode; } @@ -1951,9 +1952,19 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, /* splay the lowest to the bottom */ multi->timetree = Curl_splay(tv_zero, multi->timetree); - if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) + if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) { /* some time left before expiration */ *timeout_ms = curlx_tvdiff(multi->timetree->key, now); + if(!*timeout_ms) + /* + * Since we only provide millisecond resolution on the returned value + * and the diff might be less than one millisecond here, we don't + * return zero as that may cause short bursts of busyloops on fast + * processors while the diff is still present but less than one + * millisecond! instead we return 1 until the time is ripe. + */ + *timeout_ms=1; + } else /* 0 means immediately */ *timeout_ms = 0; -- cgit v1.2.1 From 216ad2680bdaade5e6029b86858b3b633359d43c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 20 Dec 2008 22:03:22 +0000 Subject: - Igor Novoseltsev fixed a bad situation for the multi_socket() API when doing pipelining, as libcurl could then easily get confused and A) work on the handle that was not "first in queue" on a pipeline, or even B) tell the app to REMOVE a socket while it was in use by a second handle in a pipeline. Both errors caused hanging or stalling applications. --- lib/multi.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 97efc7066..7c5852ab3 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1668,6 +1668,8 @@ static void singlesocket(struct Curl_multi *multi, curl_socket_t s; int num; unsigned int curraction; + struct Curl_one_easy *easy_by_hash; + bool remove_sock_from_hash; memset(&socks, 0, sizeof(socks)); for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) @@ -1735,21 +1737,69 @@ static void singlesocket(struct Curl_multi *multi, } } if(s != CURL_SOCKET_BAD) { - /* this socket has been removed. Remove it */ + + /* this socket has been removed. Tell the app to remove it */ + remove_sock_from_hash = TRUE; entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); if(entry) { + /* check if the socket to be removed serves a connection which has + other easy-s in a pipeline. In this case the socket should not be + removed. */ + struct connectdata *easy_conn; + + easy_by_hash = entry->easy->multi_pos; + easy_conn = easy_by_hash->easy_conn; + if(easy_conn) { + if (easy_conn->recv_pipe && easy_conn->recv_pipe->size > 1) { + /* the handle should not be removed from the pipe yet */ + remove_sock_from_hash = FALSE; + + /* Update the sockhash entry to instead point to the next in line + for the recv_pipe, or the first (in case this particular easy + isn't already) */ + if (entry->easy == easy->easy_handle) { + if (isHandleAtHead(easy->easy_handle, easy_conn->recv_pipe)) + entry->easy = easy_conn->recv_pipe->head->next->ptr; + else + entry->easy = easy_conn->recv_pipe->head->ptr; + } + } + if (easy_conn->send_pipe && easy_conn->send_pipe->size > 1) { + /* the handle should not be removed from the pipe yet */ + remove_sock_from_hash = FALSE; + + /* Update the sockhash entry to instead point to the next in line + for the send_pipe, or the first (in case this particular easy + isn't already) */ + if (entry->easy == easy->easy_handle) { + if (isHandleAtHead(easy->easy_handle, easy_conn->send_pipe)) + entry->easy = easy_conn->send_pipe->head->next->ptr; + else + entry->easy = easy_conn->send_pipe->head->ptr; + } + } + /* Don't worry about overwriting recv_pipe head with send_pipe_head, + when action will be asked on the socket (see multi_socket()), the + head of the correct pipe will be taken according to the + action. */ + } + } + else /* just a precaution, this socket really SHOULD be in the hash already but in case it isn't, we don't have to tell the app to remove it either since it never got to know about it */ + remove_sock_from_hash = FALSE; + + if (remove_sock_from_hash) { multi->socket_cb(easy->easy_handle, s, CURL_POLL_REMOVE, multi->socket_userp, entry ? entry->socketp : NULL); - sh_delentry(multi->sockhash, s); } + } } @@ -1802,6 +1852,21 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* bad bad bad bad bad bad bad */ return CURLM_INTERNAL_ERROR; + /* If the pipeline is enabled, take the handle which is in the head of + the pipeline. If we should write into the socket, take the send_pipe + head. If we should read from the socket, take the recv_pipe head. */ + if(data->set.one_easy->easy_conn) { + if ((ev_bitmask & CURL_POLL_OUT) && + data->set.one_easy->easy_conn->send_pipe && + data->set.one_easy->easy_conn->send_pipe->head) + data = data->set.one_easy->easy_conn->send_pipe->head->ptr; + else + if ((ev_bitmask & CURL_POLL_IN) && + data->set.one_easy->easy_conn->recv_pipe && + data->set.one_easy->easy_conn->recv_pipe->head) + data = data->set.one_easy->easy_conn->recv_pipe->head->ptr; + } + if(data->set.one_easy->easy_conn) /* set socket event bitmask */ data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; -- cgit v1.2.1 From 452e52f95824a7d2a761c336ba5a006209ce1e9f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 10 Jan 2009 22:10:57 +0000 Subject: - Emil Romanus fixed: When using the multi interface over HTTP and the server returns a Location header, the running easy handle will get stuck in the CURLM_STATE_PERFORM state, leaving the external event loop stuck waiting for data from the ingoing socket (when using the curl_multi_socket_action stuff). While this bug was pretty hard to find, it seems to require only a one-line fix. The break statement on line 1374 in multi.c caused the function to skip the call to multistate(). How to reproduce this bug? Well, that's another question. evhiperfifo.c in the examples directory chokes on this bug only _sometimes_, probably depending on how fast the URLs are added. One way of testing the bug out is writing to hiper.fifo from more than one source at the same time. --- lib/multi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 7c5852ab3..d5ddc0873 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1371,7 +1371,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = Curl_follow(easy->easy_handle, newurl, FOLLOW_FAKE); if (easy->result) free(newurl); - break; } multistate(easy, CURLM_STATE_DONE); -- cgit v1.2.1 From f0332c0b581f4a7af27d54ac1f837beb59a87fdf Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 2 Feb 2009 21:20:59 +0000 Subject: minor comment fix --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d5ddc0873..15339a3d6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -646,7 +646,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, } /* If this easy_handle was the last one in charge for one or more - connections a the shared connection cache, we might need to keep this + connections in the shared connection cache, we might need to keep this handle around until either A) the connection is closed and killed properly, or B) another easy_handle uses the connection. -- cgit v1.2.1 From af91ff0e068cac8fe710539213b754ce60a28edc Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 20 Feb 2009 08:16:03 +0000 Subject: - Linus Nielsen Feltzing reported and helped me repeat and fix a problem with FTP with the multi interface: when a transfer fails, like when aborted by a write callback, the control connection was wrongly closed and thus not re-used properly. This change is also an attempt to cleanup the code somewhat in this area, as now the FTP code attempts to keep (better) track on pending responses necessary to get read in ftp_done(). --- lib/multi.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 15339a3d6..2ce023331 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1237,14 +1237,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_WAITPERFORM: -#ifdef CURLDEBUG - infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n", - easy->easy_conn->connectindex, - easy->easy_conn->recv_pipe->size, - easy->easy_conn->readchannel_inuse, - isHandleAtHead(easy->easy_handle, - easy->easy_conn->recv_pipe)); -#endif /* Wait for our turn to PERFORM */ if(!easy->easy_conn->readchannel_inuse && isHandleAtHead(easy->easy_handle, @@ -1254,6 +1246,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_PERFORM); result = CURLM_CALL_MULTI_PERFORM; } +#ifdef CURLDEBUG + else { + infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n", + easy->easy_conn->connectindex, + easy->easy_conn->recv_pipe->size, + easy->easy_conn->readchannel_inuse, + isHandleAtHead(easy->easy_handle, + easy->easy_conn->recv_pipe)); + } +#endif break; case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ @@ -1302,18 +1304,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(easy->result) { /* The transfer phase returned error, we mark the connection to get - * closed to prevent being re-used. This is because we can't - * possibly know if the connection is in a good shape or not now. */ - easy->easy_conn->bits.close = TRUE; - Curl_removeHandleFromPipeline(easy->easy_handle, - easy->easy_conn->recv_pipe); + * closed to prevent being re-used. This is because we can't possibly + * know if the connection is in a good shape or not now. Unless it is + * a protocol which uses two "channels" like FTP, as then the error + * happened in the data connection. + */ + if(!(easy->easy_conn->protocol & PROT_DUALCHANNEL)) + easy->easy_conn->bits.close = TRUE; - if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) { - /* if we failed anywhere, we must clean up the secondary socket if - it was used */ - sclose(easy->easy_conn->sock[SECONDARYSOCKET]); - easy->easy_conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; - } Curl_posttransfer(easy->easy_handle); Curl_done(&easy->easy_conn, easy->result, FALSE); } -- cgit v1.2.1 From 33a3753c3f41d546ebf3350685eb7201d25783f4 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 21 Apr 2009 11:46:16 +0000 Subject: libcurl's memory.h renamed to curl_memory.h --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2ce023331..95d386f85 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -46,7 +46,7 @@ #define _MPRINTF_REPLACE /* use our functions only */ #include -#include "memory.h" +#include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" -- cgit v1.2.1 From e84c7db0497478b41f435f047d7b0e75b9289bb6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 8 May 2009 10:59:40 +0000 Subject: - Constantine Sapuntzakis fixed bug report #2784055 (http://curl.haxx.se/bug/view.cgi?id=2784055) identifying a problem to connect to SOCKS proxies when using the multi interface. It turned out to almost not work at all previously. We need to wait for the TCP connect to be properly verified before doing the SOCKS magic. There's still a flaw in the FTP code for this. --- lib/multi.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 95d386f85..c5712f619 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1055,9 +1055,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET, &connected); - if(connected) - easy->result = Curl_protocol_connect(easy->easy_conn, - &protocol_connect); + if(connected) { + /* see if we need to do any proxy magic first once we connected */ + easy->result = Curl_connected_proxy(easy->easy_conn); + + if(!easy->result) + /* if everything is still fine we do the protocol-specific connect + setup */ + easy->result = Curl_protocol_connect(easy->easy_conn, + &protocol_connect); + } if(CURLE_OK != easy->result) { /* failure detected */ -- cgit v1.2.1 From 3aa3d7e6293ae18ed2dd25cbfeddaecfbe6e6c78 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 11 May 2009 07:53:38 +0000 Subject: Internal cleanup: KEEP_WRITE and KEEP_READ are now called KEEP_SEND and KEEP_RECV to better match the general terminology: receive and send is what we do from the (remote) servers. We read and write from and to the local fs. --- lib/multi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c5712f619..75dd0277d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1299,13 +1299,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, k = &easy->easy_handle->req; - if(!(k->keepon & KEEP_READ)) { - /* We're done reading */ + if(!(k->keepon & KEEP_RECV)) { + /* We're done receiving */ easy->easy_conn->readchannel_inuse = FALSE; } - if(!(k->keepon & KEEP_WRITE)) { - /* We're done writing */ + if(!(k->keepon & KEEP_SEND)) { + /* We're done sending */ easy->easy_conn->writechannel_inuse = FALSE; } -- cgit v1.2.1 From 2c166812253c28cdfbffdd4de069f5c11db3c7a8 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Wed, 10 Jun 2009 02:49:42 +0000 Subject: Adjusted to take in account that... With the curl memory tracking feature decoupled from the debug build feature, CURLDEBUG and DEBUGBUILD preprocessor symbol definitions are used as follows: CURLDEBUG used for curl debug memory tracking specific code (--enable-curldebug) DEBUGBUILD used for debug enabled specific code (--enable-debug) --- lib/multi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 75dd0277d..25c8b9a9c 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -197,7 +197,7 @@ static void moveHandleFromSendToRecvPipeline(struct SessionHandle *habdle, static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline); -#ifdef CURLDEBUG +#ifdef DEBUGBUILD static const char * const statename[]={ "INIT", "CONNECT", @@ -221,7 +221,7 @@ static const char * const statename[]={ /* always use this function to change state, to make debugging easier */ static void multistate(struct Curl_one_easy *easy, CURLMstate state) { -#ifdef CURLDEBUG +#ifdef DEBUGBUILD long connectindex = -5000; #endif CURLMstate oldstate = easy->state; @@ -232,7 +232,7 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) easy->state = state; -#ifdef CURLDEBUG +#ifdef DEBUGBUILD if(easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) connectindex = easy->easy_conn->connectindex; @@ -1117,7 +1117,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITDO: /* Wait for our turn to DO when we're pipelining requests */ -#ifdef CURLDEBUG +#ifdef DEBUGBUILD infof(easy->easy_handle, "Conn %d send pipe %d inuse %d athead %d\n", easy->easy_conn->connectindex, easy->easy_conn->send_pipe->size, @@ -1253,7 +1253,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_PERFORM); result = CURLM_CALL_MULTI_PERFORM; } -#ifdef CURLDEBUG +#ifdef DEBUGBUILD else { infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n", easy->easy_conn->connectindex, @@ -2353,7 +2353,7 @@ static void add_closure(struct Curl_multi *multi, } -#ifdef CURLDEBUG +#ifdef DEBUGBUILD void Curl_multi_dump(const struct Curl_multi *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; -- cgit v1.2.1 From 4ea513cc387c0a67c861a87aea01e6d1cac4854c Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 12 Jun 2009 02:41:16 +0000 Subject: fix compiler warning --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 25c8b9a9c..2c5b6d94d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -382,7 +382,7 @@ CURLM *curl_multi_init(void) return NULL; } - multi->connc = Curl_mk_connc(CONNCACHE_MULTI, -1); + multi->connc = Curl_mk_connc(CONNCACHE_MULTI, -1L); if(!multi->connc) { Curl_hash_destroy(multi->sockhash); Curl_hash_destroy(multi->hostcache); -- cgit v1.2.1 From 3a9c03bef3c671c8fdfe42025ace811b74cbee59 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 3 Aug 2009 11:32:55 +0000 Subject: indentation fixes only --- lib/multi.c | 55 +++++++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 28 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2c5b6d94d..0f75c2d72 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -452,14 +452,14 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* for multi interface connections, we share DNS cache automatically if the easy handle's one is currently private. */ if(easy->easy_handle->dns.hostcache && - (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) { + (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) { Curl_hash_destroy(easy->easy_handle->dns.hostcache); easy->easy_handle->dns.hostcache = NULL; easy->easy_handle->dns.hostcachetype = HCACHE_NONE; } if(!easy->easy_handle->dns.hostcache || - (easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) { + (easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) { easy->easy_handle->dns.hostcache = multi->hostcache; easy->easy_handle->dns.hostcachetype = HCACHE_MULTI; } @@ -598,10 +598,10 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, multi->num_alive--; if(easy->easy_conn && - (easy->easy_conn->send_pipe->size + - easy->easy_conn->recv_pipe->size > 1) && - easy->state > CURLM_STATE_WAITDO && - easy->state < CURLM_STATE_COMPLETED) { + (easy->easy_conn->send_pipe->size + + easy->easy_conn->recv_pipe->size > 1) && + easy->state > CURLM_STATE_WAITDO && + easy->state < CURLM_STATE_COMPLETED) { /* If the handle is in a pipeline and has started sending off its request but not received its reponse yet, we need to close connection. */ @@ -660,7 +660,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, points to this handle and are using PROT_CLOSEACTION. If there's any, we need to add this handle to the list of "easy handles kept around for nice connection closures". - */ + */ if(multi_conn_using(multi, easy->easy_handle)) { /* There's at least one connection using this handle so we must keep this handle around. We also keep the connection cache pointer @@ -778,7 +778,7 @@ static int domore_getsock(struct connectdata *conn, /* 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 */ + of sockets */ int numsocks) { /* If the pipe broke, or if there's no connection left for this easy handle, @@ -790,7 +790,7 @@ static int multi_getsock(struct Curl_one_easy *easy, return 0; if(easy->state > CURLM_STATE_CONNECT && - easy->state < CURLM_STATE_COMPLETED) { + easy->state < CURLM_STATE_COMPLETED) { /* Set up ownership correctly */ easy->easy_conn->data = easy->easy_handle; } @@ -1079,7 +1079,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, and then we continue to 'STATE_PROTOCONNECT'. If protocol connect is TRUE, we move on to STATE_DO. BUT if we are using a proxy we must change to WAITPROXYCONNECT - */ + */ #ifndef CURL_DISABLE_HTTP if(easy->easy_conn->bits.tunnel_connecting) multistate(easy, CURLM_STATE_WAITPROXYCONNECT); @@ -1269,23 +1269,23 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* if both rates are within spec, resume transfer */ Curl_pgrsUpdate(easy->easy_conn); if( ( ( easy->easy_handle->set.max_send_speed == 0 ) || - ( easy->easy_handle->progress.ulspeed < - easy->easy_handle->set.max_send_speed ) ) && - ( ( easy->easy_handle->set.max_recv_speed == 0 ) || - ( easy->easy_handle->progress.dlspeed < - easy->easy_handle->set.max_recv_speed ) ) + ( easy->easy_handle->progress.ulspeed < + easy->easy_handle->set.max_send_speed ) ) && + ( ( easy->easy_handle->set.max_recv_speed == 0 ) || + ( easy->easy_handle->progress.dlspeed < + easy->easy_handle->set.max_recv_speed ) ) ) - multistate(easy, CURLM_STATE_PERFORM); + multistate(easy, CURLM_STATE_PERFORM); break; case CURLM_STATE_PERFORM: /* check if over speed */ if( ( ( easy->easy_handle->set.max_send_speed > 0 ) && - ( easy->easy_handle->progress.ulspeed > - easy->easy_handle->set.max_send_speed ) ) || - ( ( easy->easy_handle->set.max_recv_speed > 0 ) && - ( easy->easy_handle->progress.dlspeed > - easy->easy_handle->set.max_recv_speed ) ) + ( easy->easy_handle->progress.ulspeed > + easy->easy_handle->set.max_send_speed ) ) || + ( ( easy->easy_handle->set.max_recv_speed > 0 ) && + ( easy->easy_handle->progress.dlspeed > + easy->easy_handle->set.max_recv_speed ) ) ) { /* Transfer is over the speed limit. Change state. TODO: Call * Curl_expire() with the time left until we're targeted to be below @@ -1864,10 +1864,9 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data->set.one_easy->easy_conn->send_pipe && data->set.one_easy->easy_conn->send_pipe->head) data = data->set.one_easy->easy_conn->send_pipe->head->ptr; - else - if ((ev_bitmask & CURL_POLL_IN) && - data->set.one_easy->easy_conn->recv_pipe && - data->set.one_easy->easy_conn->recv_pipe->head) + else if ((ev_bitmask & CURL_POLL_IN) && + data->set.one_easy->easy_conn->recv_pipe && + data->set.one_easy->easy_conn->recv_pipe->head) data = data->set.one_easy->easy_conn->recv_pipe->head->ptr; } @@ -1990,7 +1989,7 @@ CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, } CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t s, - int ev_bitmask, int *running_handles) + int ev_bitmask, int *running_handles) { CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s, ev_bitmask, running_handles); @@ -2143,9 +2142,9 @@ static int checkPendPipeline(struct connectdata *conn) Pay special attention to the new sending list "leader" as it needs to get checked to update what sockets it acts on. - */ +*/ static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, - struct connectdata *conn) + struct connectdata *conn) { struct curl_llist_element *curr; -- cgit v1.2.1 From 1048043963d5487ed4d70f426a85aff07c2ee5f1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 21 Aug 2009 07:11:20 +0000 Subject: - Lots of good work by Krister Johansen, mostly related to pipelining: Fix SIGSEGV on free'd easy_conn when pipe unexpectedly breaks Fix data corruption issue with re-connected transfers Fix use after free if we're completed but easy_conn not NULL --- lib/multi.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 25 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0f75c2d72..1099b525d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -192,7 +192,9 @@ static int update_timer(struct Curl_multi *multi); static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, struct connectdata *conn); static int checkPendPipeline(struct connectdata *conn); -static void moveHandleFromSendToRecvPipeline(struct SessionHandle *habdle, +static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, + struct connectdata *conn); +static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle, struct connectdata *conn); static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline); @@ -233,14 +235,16 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) easy->state = state; #ifdef DEBUGBUILD - if(easy->state > CURLM_STATE_CONNECT && - easy->state < CURLM_STATE_COMPLETED) - connectindex = easy->easy_conn->connectindex; + if(easy->easy_conn) { + if(easy->state > CURLM_STATE_CONNECT && + easy->state < CURLM_STATE_COMPLETED) + connectindex = easy->easy_conn->connectindex; - infof(easy->easy_handle, - "STATE: %s => %s handle %p; (connection #%ld) \n", - statename[oldstate], statename[easy->state], - (char *)easy, connectindex); + infof(easy->easy_handle, + "STATE: %s => %s handle %p; (connection #%ld) \n", + statename[oldstate], statename[easy->state], + (char *)easy, connectindex); + } #endif if(state == CURLM_STATE_COMPLETED) /* changing to COMPLETED means there's one less easy handle 'alive' */ @@ -925,7 +929,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; } - if(easy->state > CURLM_STATE_CONNECT && + if(easy->easy_conn && easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) /* Make sure we set the connection's current owner */ easy->easy_conn->data = easy->easy_handle; @@ -1149,7 +1153,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, &dophase_done); if(CURLE_OK == easy->result) { - if(!dophase_done) { /* DO was not completed in one function call, we must continue DOING... */ @@ -1170,6 +1173,49 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_CALL_MULTI_PERFORM; } } + else if ((CURLE_SEND_ERROR == easy->result) && + easy->easy_conn->bits.reuse) { + /* + * In this situation, a connection that we were trying to use + * may have unexpectedly died. If possible, send the connection + * back to the CONNECT phase so we can try again. + */ + char *newurl; + followtype follow=FOLLOW_NONE; + CURLcode drc; + bool retry = Curl_retry_request(easy->easy_conn, &newurl); + + Curl_posttransfer(easy->easy_handle); + drc = Curl_done(&easy->easy_conn, easy->result, FALSE); + + /* When set to retry the connection, we must to go back to + * the CONNECT state */ + if(retry) { + if ((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) { + follow = FOLLOW_RETRY; + drc = Curl_follow(easy->easy_handle, newurl, follow); + if(drc == CURLE_OK) { + multistate(easy, CURLM_STATE_CONNECT); + result = CURLM_CALL_MULTI_PERFORM; + easy->result = CURLE_OK; + } + else { + /* Follow failed */ + easy->result = drc; + free(newurl); + } + } + else { + /* done didn't return OK or SEND_ERROR */ + easy->result = drc; + free(newurl); + } + } + else { + /* Have error handler disconnect conn if we can't retry */ + disconnect_conn = TRUE; + } + } else { /* failure detected */ Curl_posttransfer(easy->easy_handle); @@ -1331,8 +1377,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_posttransfer(easy->easy_handle); /* we're no longer receving */ - Curl_removeHandleFromPipeline(easy->easy_handle, - easy->easy_conn->recv_pipe); + moveHandleFromRecvToDonePipeline(easy->easy_handle, + easy->easy_conn); /* expire the new receiving pipeline head */ if(easy->easy_conn->recv_pipe->head) @@ -1386,21 +1432,35 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_DONE: - /* Remove ourselves from the receive pipeline */ - Curl_removeHandleFromPipeline(easy->easy_handle, - easy->easy_conn->recv_pipe); - /* Check if we can move pending requests to send pipe */ - checkPendPipeline(easy->easy_conn); - if(easy->easy_conn->bits.stream_was_rewound) { - /* This request read past its response boundary so we quickly let the - other requests consume those bytes since there is no guarantee that - the socket will become active again */ - result = CURLM_CALL_MULTI_PERFORM; - } + if(easy->easy_conn) { + /* Remove ourselves from the receive and done pipelines. Handle + should be on one of these lists, depending upon how we got here. */ + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->recv_pipe); + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->done_pipe); + /* Check if we can move pending requests to send pipe */ + checkPendPipeline(easy->easy_conn); + + if(easy->easy_conn->bits.stream_was_rewound) { + /* This request read past its response boundary so we quickly let + the other requests consume those bytes since there is no + guarantee that the socket will become active again */ + result = CURLM_CALL_MULTI_PERFORM; + } - /* post-transfer command */ - easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); + /* post-transfer command */ + easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); + /* + * If there are other handles on the pipeline, Curl_done won't set + * easy_conn to NULL. In such a case, curl_multi_remove_handle() can + * access free'd data, if the connection is free'd and the handle + * removed before we perform the processing in CURLM_STATE_COMPLETED + */ + if (easy->easy_conn) + easy->easy_conn = NULL; + } /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the Curl_done() returned! */ @@ -1443,6 +1503,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->send_pipe); Curl_removeHandleFromPipeline(easy->easy_handle, easy->easy_conn->recv_pipe); + Curl_removeHandleFromPipeline(easy->easy_handle, + easy->easy_conn->done_pipe); /* Check if we can move pending requests to send pipe */ checkPendPipeline(easy->easy_conn); } @@ -2173,6 +2235,21 @@ static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, } } +static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle, + struct connectdata *conn) +{ + struct curl_llist_element *curr; + + curr = conn->recv_pipe->head; + while(curr) { + if(curr->ptr == handle) { + Curl_llist_move(conn->recv_pipe, curr, + conn->done_pipe, conn->done_pipe->tail); + break; + } + curr = curr->next; + } +} static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline) { -- cgit v1.2.1 From 8b5102ca835d73d5cc633f1e7b8d05b3a8082f61 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 21 Aug 2009 12:01:36 +0000 Subject: - Andre Guibert de Bruet pointed out a missing return code check for a strdup() that could lead to segfault if it returned NULL. I extended his suggest patch to now have Curl_retry_request() return a regular return code and better check that. --- lib/multi.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 1099b525d..686372ad1 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1183,7 +1183,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, char *newurl; followtype follow=FOLLOW_NONE; CURLcode drc; - bool retry = Curl_retry_request(easy->easy_conn, &newurl); + bool retry = FALSE; + + drc = Curl_retry_request(easy->easy_conn, &newurl); + if(drc) { + /* a failure here pretty much implies an out of memory */ + easy->result = drc; + disconnect_conn = TRUE; + } + else + retry = newurl?TRUE:FALSE; Curl_posttransfer(easy->easy_handle); drc = Curl_done(&easy->easy_conn, easy->result, FALSE); @@ -1370,9 +1379,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else if(TRUE == done) { char *newurl; - bool retry = Curl_retry_request(easy->easy_conn, &newurl); + bool retry = FALSE; followtype follow=FOLLOW_NONE; + easy->result = Curl_retry_request(easy->easy_conn, &newurl); + if(!easy->result) + retry = newurl?TRUE:FALSE; + /* call this even if the readwrite function returned error */ Curl_posttransfer(easy->easy_handle); @@ -1406,7 +1419,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; } - else + else if(newurl) /* Since we "took it", we are in charge of freeing this on failure */ free(newurl); -- cgit v1.2.1 From 6ede4ce79d740e9ea9430bba7654aa8d92c6926a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 24 Aug 2009 08:41:17 +0000 Subject: clarify the code by initing newurl to NULL --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 686372ad1..3525af65b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1180,7 +1180,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * may have unexpectedly died. If possible, send the connection * back to the CONNECT phase so we can try again. */ - char *newurl; + char *newurl = NULL; followtype follow=FOLLOW_NONE; CURLcode drc; bool retry = FALSE; @@ -1378,7 +1378,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_done(&easy->easy_conn, easy->result, FALSE); } else if(TRUE == done) { - char *newurl; + char *newurl = NULL; bool retry = FALSE; followtype follow=FOLLOW_NONE; -- cgit v1.2.1 From 59939313f8452a9d817c178425c2ba3b91798ea9 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Wed, 18 Nov 2009 10:33:54 +0000 Subject: Make usage of calloc()'s arguments consistent with rest of code base --- lib/multi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3525af65b..2210c2fa9 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -282,7 +282,7 @@ static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh, return there; /* not present, add it */ - check = calloc(sizeof(struct Curl_sh_entry), 1); + check = calloc(1, sizeof(struct Curl_sh_entry)); if(!check) return NULL; /* major failure */ check->easy = data; @@ -364,7 +364,7 @@ static struct curl_hash *sh_init(void) CURLM *curl_multi_init(void) { - struct Curl_multi *multi = calloc(sizeof(struct Curl_multi), 1); + struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); if(!multi) return NULL; @@ -425,7 +425,7 @@ 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 = calloc(sizeof(struct Curl_one_easy), 1); + easy = calloc(1, sizeof(struct Curl_one_easy)); if(!easy) return CURLM_OUT_OF_MEMORY; @@ -2393,7 +2393,7 @@ static void add_closure(struct Curl_multi *multi, struct SessionHandle *data) { int i; - struct closure *cl = calloc(sizeof(struct closure), 1); + struct closure *cl = calloc(1, sizeof(struct closure)); struct closure *p=NULL; struct closure *n; if(cl) { -- cgit v1.2.1 From 315253b367a8d3c439a4acccfe68723109f3d1b1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 10 Dec 2009 20:20:15 +0000 Subject: - Constantine Sapuntzakis figured out a case which would lead to libcurl accessing alredy freed memory and thus crash when using HTTPS (with OpenSSL), multi interface and the CURLOPT_DEBUGFUNCTION and a certain order of cleaning things up. I fixed it. (http://curl.haxx.se/bug/view.cgi?id=2891591) --- lib/multi.c | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2210c2fa9..c746612e2 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -181,8 +181,8 @@ struct Curl_multi { previous callback */ }; -static bool multi_conn_using(struct Curl_multi *multi, - struct SessionHandle *data); +static struct connectdata *conn_using(struct Curl_multi *multi, + struct SessionHandle *data); static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy); static void add_closure(struct Curl_multi *multi, @@ -577,6 +577,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; + struct connectdata *conn; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -649,6 +650,9 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn); } + /* figure out if the easy handle is used by a connection in the cache */ + conn = conn_using(multi, easy->easy_handle); + /* If this easy_handle was the last one in charge for one or more connections in the shared connection cache, we might need to keep this handle around until either A) the connection is closed and killed @@ -665,16 +669,22 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, we need to add this handle to the list of "easy handles kept around for nice connection closures". */ - if(multi_conn_using(multi, easy->easy_handle)) { - /* There's at least one connection using this handle so we must keep - this handle around. We also keep the connection cache pointer - pointing to the shared one since that will be used on close as - well. */ - easy->easy_handle->state.shared_conn = multi; - - /* this handle is still being used by a shared connection cache and - thus we leave it around for now */ - add_closure(multi, easy->easy_handle); + if(conn) { + if(conn->protocol & PROT_CLOSEACTION) { + /* There's at least one CLOSEACTION connection using this handle so we + must keep this handle around. We also keep the connection cache + pointer pointing to the shared one since that will be used on close + as well. */ + easy->easy_handle->state.shared_conn = multi; + + /* this handle is still being used by a shared connection cache and + thus we leave it around for now */ + add_closure(multi, easy->easy_handle); + } + else + /* disconect the easy handle from the connection since the connection + will now remain but this easy handle is going */ + conn->data = NULL; } if(easy->easy_handle->state.connc->type == CONNCACHE_MULTI) { @@ -2369,20 +2379,19 @@ CURLMcode curl_multi_assign(CURLM *multi_handle, return CURLM_OK; } -static bool multi_conn_using(struct Curl_multi *multi, - struct SessionHandle *data) +static struct connectdata *conn_using(struct Curl_multi *multi, + struct SessionHandle *data) { - /* any live CLOSEACTION-connections pointing to the give 'data' ? */ + /* a connection in the connection cache pointing to the given 'data' ? */ int i; for(i=0; i< multi->connc->num; i++) { if(multi->connc->connects[i] && - (multi->connc->connects[i]->data == data) && - multi->connc->connects[i]->protocol & PROT_CLOSEACTION) - return TRUE; + (multi->connc->connects[i]->data == data)) + return multi->connc->connects[i]; } - return FALSE; + return NULL; } /* Add the given data pointer to the list of 'closure handles' that are kept -- cgit v1.2.1 From fd903eb6bee230dd6751e144baa1eb4470cdf8a4 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Sun, 3 Jan 2010 13:46:37 +0000 Subject: - Julien Chaffraix eliminated a duplicated initialization in singlesocket(). --- lib/multi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c746612e2..6c59ed09f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1760,7 +1760,6 @@ static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy_by_hash; bool remove_sock_from_hash; - memset(&socks, 0, sizeof(socks)); for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) socks[i] = CURL_SOCKET_BAD; -- cgit v1.2.1 From b90703f5943e27153b824dc0e2070a49c611c251 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 9 Jan 2010 00:03:33 +0000 Subject: struct Curl_sh_entry's 'inuse' member was no longer used and is now removed --- lib/multi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 6c59ed09f..d7381e7fa 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -258,7 +258,6 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) struct Curl_sh_entry { struct SessionHandle *easy; time_t timestamp; - long inuse; int action; /* what action READ/WRITE this socket waits for */ curl_socket_t socket; /* mainly to ease debugging */ void *socketp; /* settable by users with curl_multi_assign() */ -- cgit v1.2.1 From 2158e234aa155bc60bc6f68fe4578f41e72d998a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 11 Jan 2010 23:05:35 +0000 Subject: After the TCP connect is confirmed in CURLM_STATE_WAITCONNECT and it changes state, we return CURLM_CALL_MULTI_PERFORM unconditionally then so that we can act faster like in the case the protocol-specific connect doesn't block on anything and we can just persue on the next action immediately. It also then avoids a case where curl_multi_fdset() would return -1. --- lib/multi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d7381e7fa..5ccadc2a8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1099,14 +1099,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else #endif multistate(easy, CURLM_STATE_PROTOCONNECT); + } - else { + else /* after the connect has completed, go WAITDO or DO */ multistate(easy, multi->pipelining_enabled? CURLM_STATE_WAITDO:CURLM_STATE_DO); - result = CURLM_CALL_MULTI_PERFORM; - } + result = CURLM_CALL_MULTI_PERFORM; } break; -- cgit v1.2.1 From d65cf7889b4ce669876f9e05442fd09f6fe40e37 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 28 Jan 2010 15:34:18 +0000 Subject: fix printf-style format strings --- lib/multi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 5ccadc2a8..d9414b9eb 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -923,7 +923,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Handle the case when the pipe breaks, i.e., the connection we're using gets cleaned up and we're left with nothing. */ if(easy->easy_handle->state.pipe_broke) { - infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n", + infof(easy->easy_handle, "Pipe broke: handle 0x%p, url = %s\n", easy, easy->easy_handle->state.path); if(easy->state != CURLM_STATE_COMPLETED) { @@ -1131,7 +1131,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITDO: /* Wait for our turn to DO when we're pipelining requests */ #ifdef DEBUGBUILD - infof(easy->easy_handle, "Conn %d send pipe %d inuse %d athead %d\n", + infof(easy->easy_handle, "Conn %ld send pipe %d inuse %d athead %d\n", easy->easy_conn->connectindex, easy->easy_conn->send_pipe->size, easy->easy_conn->writechannel_inuse, @@ -1319,7 +1319,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } #ifdef DEBUGBUILD else { - infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n", + infof(easy->easy_handle, "Conn %ld recv pipe %d inuse %d athead %d\n", easy->easy_conn->connectindex, easy->easy_conn->recv_pipe->size, easy->easy_conn->readchannel_inuse, -- cgit v1.2.1 From 55f1e787f34cd3d86aa3d6bf981f077de86be265 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 1 Feb 2010 21:42:44 +0000 Subject: We introduce a loop in lib/multi.c around all calls to multi_runsingle() and simply check for CURLM_CALL_MULTI_PERFORM internally. This has the added benefit that this goes in line with my long-term wishes to get rid of the CURLM_CALL_MULTI_PERFORM all together from the public API. --- lib/multi.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d9414b9eb..5d19b089b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1568,17 +1568,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multi->num_msgs++; /* increase message counter */ } - if(CURLM_CALL_MULTI_PERFORM == result) - /* Set the timeout for this handle to expire really soon so that it will - be taken care of even when this handle is added in the midst of - operation when only the curl_multi_socket() API is used. During that - flow, only sockets that time-out or have actions will be dealt - with. Since this handle has no action yet, we make sure it times out to - get things to happen. Also, this makes it less important for callers of - the curl_multi_* functions to bother about the CURLM_CALL_MULTI_PERFORM - return code, as long as they deal with the timeouts properly. */ - Curl_expire(easy->easy_handle, 1); - return result; } @@ -1597,7 +1586,10 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) while(easy != &multi->easy) { CURLMcode result; - result = multi_runsingle(multi, easy); + do + result = multi_runsingle(multi, easy); + while (CURLM_CALL_MULTI_PERFORM == result); + if(result) returncode = result; @@ -1956,7 +1948,9 @@ static CURLMcode multi_socket(struct Curl_multi *multi, if(data->set.one_easy->easy_conn) /* set socket event bitmask */ data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; - result = multi_runsingle(multi, data->set.one_easy); + do + result = multi_runsingle(multi, data->set.one_easy); + while (CURLM_CALL_MULTI_PERFORM == result); if(data->set.one_easy->easy_conn) data->set.one_easy->easy_conn->cselect_bits = 0; @@ -1985,7 +1979,9 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* the first loop lap 'data' can be NULL */ if(data) { - result = multi_runsingle(multi, data->set.one_easy); + do + result = multi_runsingle(multi, data->set.one_easy); + while (CURLM_CALL_MULTI_PERFORM == result); if(CURLM_OK >= result) /* get the socket(s) and check if the state has been changed since -- cgit v1.2.1 From 839b61c32a23a6204e4283c7c9eda7dc920d89d0 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 2 Feb 2010 16:23:01 +0000 Subject: Fix compiler warnings: conversion from 'const int ' to 'unsigned char ', possible loss of data --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 5d19b089b..02f84aa7d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1201,7 +1201,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, disconnect_conn = TRUE; } else - retry = newurl?TRUE:FALSE; + retry = (bool)(newurl?TRUE:FALSE); Curl_posttransfer(easy->easy_handle); drc = Curl_done(&easy->easy_conn, easy->result, FALSE); @@ -1393,7 +1393,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = Curl_retry_request(easy->easy_conn, &newurl); if(!easy->result) - retry = newurl?TRUE:FALSE; + retry = (bool)(newurl?TRUE:FALSE); /* call this even if the readwrite function returned error */ Curl_posttransfer(easy->easy_handle); -- cgit v1.2.1 From 7aef172a347a0422a0968fde9c487639ff673383 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 4 Feb 2010 19:44:31 +0000 Subject: fix printf-style format strings --- lib/multi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 02f84aa7d..e4e1ce8c9 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -567,7 +567,7 @@ static void debug_print_sock_hash(void *p) struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; fprintf(stderr, " [easy %p/magic %x/socket %d]", - (void *)sh->easy, sh->easy->magic, sh->socket); + (void *)sh->easy, sh->easy->magic, (int)sh->socket); } #endif @@ -1131,12 +1131,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITDO: /* Wait for our turn to DO when we're pipelining requests */ #ifdef DEBUGBUILD - infof(easy->easy_handle, "Conn %ld send pipe %d inuse %d athead %d\n", + infof(easy->easy_handle, "Conn %ld send pipe %zu inuse %d athead %d\n", easy->easy_conn->connectindex, easy->easy_conn->send_pipe->size, - easy->easy_conn->writechannel_inuse, + easy->easy_conn->writechannel_inuse?1:0, isHandleAtHead(easy->easy_handle, - easy->easy_conn->send_pipe)); + easy->easy_conn->send_pipe)?1:0); #endif if(!easy->easy_conn->writechannel_inuse && isHandleAtHead(easy->easy_handle, @@ -1319,12 +1319,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } #ifdef DEBUGBUILD else { - infof(easy->easy_handle, "Conn %ld recv pipe %d inuse %d athead %d\n", + infof(easy->easy_handle, "Conn %ld recv pipe %zu inuse %d athead %d\n", easy->easy_conn->connectindex, easy->easy_conn->recv_pipe->size, - easy->easy_conn->readchannel_inuse, + easy->easy_conn->readchannel_inuse?1:0, isHandleAtHead(easy->easy_handle, - easy->easy_conn->recv_pipe)); + easy->easy_conn->recv_pipe)?1:0); } #endif break; -- cgit v1.2.1 From 733f794cb8f93e413b5f17d106b265bce0379772 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 15 Mar 2010 22:40:42 +0000 Subject: - Constantine Sapuntzakis brought a patch: The problem mentioned on Dec 10 2009 (http://curl.haxx.se/bug/view.cgi?id=2905220) was only partially fixed. Partially because an easy handle can be associated with many connections in the cache (e.g. if there is a redirect during the lifetime of the easy handle). The previous patch only cleaned up the first one. The new fix now removes the easy handle from all connections, not just the first one. --- lib/multi.c | 137 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 70 insertions(+), 67 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e4e1ce8c9..b24021280 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -181,12 +181,12 @@ struct Curl_multi { previous callback */ }; -static struct connectdata *conn_using(struct Curl_multi *multi, +static void multi_connc_remove_handle(struct Curl_multi *multi, struct SessionHandle *data); static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy); -static void add_closure(struct Curl_multi *multi, - struct SessionHandle *data); +static CURLMcode add_closure(struct Curl_multi *multi, + struct SessionHandle *data); static int update_timer(struct Curl_multi *multi); static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, @@ -576,7 +576,6 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; - struct connectdata *conn; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -649,42 +648,9 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn); } - /* figure out if the easy handle is used by a connection in the cache */ - conn = conn_using(multi, easy->easy_handle); - - /* If this easy_handle was the last one in charge for one or more - connections in the shared connection cache, we might need to keep this - handle around until either A) the connection is closed and killed - properly, or B) another easy_handle uses the connection. - - The reason why we need to have a easy_handle associated with a live - connection is simply that some connections will need a handle to get - closed down properly. Currently, the only connections that need to keep - a easy_handle handle around are using FTP(S). Such connections have - the PROT_CLOSEACTION bit set. - - Thus, we need to check for all connections in the shared cache that - points to this handle and are using PROT_CLOSEACTION. If there's any, - we need to add this handle to the list of "easy handles kept around for - nice connection closures". - */ - if(conn) { - if(conn->protocol & PROT_CLOSEACTION) { - /* There's at least one CLOSEACTION connection using this handle so we - must keep this handle around. We also keep the connection cache - pointer pointing to the shared one since that will be used on close - as well. */ - easy->easy_handle->state.shared_conn = multi; - - /* this handle is still being used by a shared connection cache and - thus we leave it around for now */ - add_closure(multi, easy->easy_handle); - } - else - /* disconect the easy handle from the connection since the connection - will now remain but this easy handle is going */ - conn->data = NULL; - } + /* figure out if the easy handle is used by one or more connections in the + cache */ + multi_connc_remove_handle(multi, easy->easy_handle); if(easy->easy_handle->state.connc->type == CONNCACHE_MULTI) { /* if this was using the shared connection cache we clear the pointer @@ -2373,48 +2339,71 @@ CURLMcode curl_multi_assign(CURLM *multi_handle, return CURLM_OK; } -static struct connectdata *conn_using(struct Curl_multi *multi, +static void multi_connc_remove_handle(struct Curl_multi *multi, struct SessionHandle *data) { /* a connection in the connection cache pointing to the given 'data' ? */ int i; for(i=0; i< multi->connc->num; i++) { - if(multi->connc->connects[i] && - (multi->connc->connects[i]->data == data)) - return multi->connc->connects[i]; - } + struct connectdata * conn = multi->connc->connects[i]; + + if(conn && conn->data == data) { + /* If this easy_handle was the last one in charge for one or more + connections in the shared connection cache, we might need to keep + this handle around until either A) the connection is closed and + killed properly, or B) another easy_handle uses the connection. + + The reason why we need to have a easy_handle associated with a live + connection is simply that some connections will need a handle to get + closed down properly. Currently, the only connections that need to + keep a easy_handle handle around are using FTP(S). Such connections + have the PROT_CLOSEACTION bit set. + + Thus, we need to check for all connections in the shared cache that + points to this handle and are using PROT_CLOSEACTION. If there's any, + we need to add this handle to the list of "easy handles kept around + for nice connection closures". + */ - return NULL; + if(conn->protocol & PROT_CLOSEACTION) { + /* this handle is still being used by a shared connection and + thus we leave it around for now */ + if(add_closure(multi, data) == CURLM_OK) + data->state.shared_conn = multi; + else { + /* out of memory - so much for graceful shutdown */ + Curl_disconnect(conn); + multi->connc->connects[i] = NULL; + } + } + else + /* disconect the easy handle from the connection since the connection + will now remain but this easy handle is going */ + conn->data = NULL; + } + } } /* Add the given data pointer to the list of 'closure handles' that are kept around only to be able to close some connections nicely - just make sure that this handle isn't already added, like for the cases when an easy handle is removed, added and removed again... */ -static void add_closure(struct Curl_multi *multi, - struct SessionHandle *data) +static CURLMcode add_closure(struct Curl_multi *multi, + struct SessionHandle *data) { - int i; - struct closure *cl = calloc(1, sizeof(struct closure)); - struct closure *p=NULL; - struct closure *n; - if(cl) { - cl->easy_handle = data; - cl->next = multi->closure; - multi->closure = cl; - } - - p = multi->closure; - cl = p->next; /* start immediately on the second since the first is the one - we just added and it is _very_ likely to actually exist - used in the cache since that's the whole purpose of adding - it to this list! */ + struct closure *cl = multi->closure; + struct closure *p = NULL; + bool add = TRUE; - /* When adding, scan through all the other currently kept handles and see if - there are any connections still referring to them and kill them if not. */ + /* Before adding, scan through all the other currently kept handles and see + if there are any connections still referring to them and kill them if + not. */ while(cl) { + struct closure *n; bool inuse = FALSE; + int i; + for(i=0; i< multi->connc->num; i++) { if(multi->connc->connects[i] && (multi->connc->connects[i]->data == cl->easy_handle)) { @@ -2436,13 +2425,27 @@ static void add_closure(struct Curl_multi *multi, else multi->closure = n; free(cl); - } - else + } else { + if(cl->easy_handle == data) + add = FALSE; + p = cl; + } cl = n; } + if (add) { + cl = calloc(1, sizeof(struct closure)); + if(!cl) + return CURLM_OUT_OF_MEMORY; + + cl->easy_handle = data; + cl->next = multi->closure; + multi->closure = cl; + } + + return CURLM_OK; } #ifdef DEBUGBUILD -- cgit v1.2.1 From 2a94293efd3896eca067c4b53ccfdf362a042db2 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 23 Mar 2010 13:18:30 +0100 Subject: delayed easy handle kill caused double Curl_close() call Hauke Duden provided an example program that made the multi interface crash. His example simply used the multi interface and did first one FTP transfer and after completion it used a second easy handle and did another FTP transfer on the same FTP server. This triggered a bug in the "delayed easy handle kill" system that curl uses: when an FTP connection is left alive it must keep an easy handle around internally - only for the purpose of having an easy handle when it later disconnects it. The code assumed that when the easy handle was removed and an internal reference was made, that version could be killed later on when a new easy handle came using the same connection. This was wrong as Hauke's example showed that the removed handle wasn't killed for real until later. This caused a double close attempt => segfault. --- lib/multi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b24021280..d04fcf660 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2416,10 +2416,16 @@ static CURLMcode add_closure(struct Curl_multi *multi, if(!inuse) { /* cl->easy_handle is now killable */ - infof(data, "Delayed kill of easy handle %p\n", cl->easy_handle); + /* unmark it as not having a connection around that uses it anymore */ cl->easy_handle->state.shared_conn= NULL; - Curl_close(cl->easy_handle); + + if(cl->easy_handle->state.closed) { + infof(data, "Delayed kill of easy handle %p\n", cl->easy_handle); + /* close handle only if curl_easy_cleanup() already has been called + for this easy handle */ + Curl_close(cl->easy_handle); + } if(p) p->next = n; else -- cgit v1.2.1 From 2309b4e330b96bc2e1f8e36b6184015e59544037 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 24 Mar 2010 11:02:54 +0100 Subject: remove the CVSish $Id$ lines --- lib/multi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d04fcf660..476cb8138 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -18,7 +18,6 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * - * $Id$ ***************************************************************************/ #include "setup.h" -- cgit v1.2.1 From 0825cd80a62c21725fb3615f1fdd3aa6cc5f0f34 Mon Sep 17 00:00:00 2001 From: Pavel Raiskup Date: Wed, 12 May 2010 15:33:22 +0200 Subject: FTP: WILDCARDMATCH/CHUNKING/FNMATCH added --- lib/multi.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 476cb8138..34d7eccbb 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1128,6 +1128,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK == easy->result) { if(!dophase_done) { + /* some steps needed for wildcard matching */ + if(easy->easy_handle->set.wildcardmatch) { + struct WildcardData *wc = &easy->easy_handle->wildcard; + if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { + /* skip some states if it is important */ + Curl_done(&easy->easy_conn, CURLE_OK, FALSE); + multistate(easy, CURLM_STATE_DONE); + result = CURLM_CALL_MULTI_PERFORM; + break; + } + } /* DO was not completed in one function call, we must continue DOING... */ multistate(easy, CURLM_STATE_DOING); @@ -1338,7 +1349,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->writechannel_inuse = FALSE; } - if(easy->result) { + if(easy->result) { /* The transfer phase returned error, we mark the connection to get * closed to prevent being re-used. This is because we can't possibly * know if the connection is in a good shape or not now. Unless it is @@ -1449,6 +1460,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn = NULL; } + if(easy->easy_handle->set.wildcardmatch) { + if(easy->easy_handle->wildcard.state != CURLWC_DONE) { + /* if a wildcard is set and we are not ending -> lets start again + with CURLM_STATE_INIT */ + result = CURLM_CALL_MULTI_PERFORM; + multistate(easy, CURLM_STATE_INIT); + break; + } + } + /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the Curl_done() returned! */ multistate(easy, CURLM_STATE_COMPLETED); @@ -1550,11 +1571,26 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy != &multi->easy) { CURLMcode result; + struct WildcardData *wc = &easy->easy_handle->wildcard; + + if(easy->easy_handle->set.wildcardmatch) { + if(!wc->filelist) { + CURLcode ret = Curl_wildcard_init(wc); /* init wildcard structures */ + if(ret) + return CURLM_OUT_OF_MEMORY; + } + } do result = multi_runsingle(multi, easy); while (CURLM_CALL_MULTI_PERFORM == result); + if(easy->easy_handle->set.wildcardmatch) { + /* destruct wildcard structures if it is needed */ + if(wc->state == CURLWC_DONE || result) + Curl_wildcard_dtor(wc); + } + if(result) returncode = result; -- cgit v1.2.1 From dcc061543af355d0b8f30d9bfbbd67ebcb0e5752 Mon Sep 17 00:00:00 2001 From: Tor Arntsen Date: Sun, 16 May 2010 22:01:17 +0200 Subject: lib: Change some CRLF line endings to LF An update had added a couple of lines with DOS line endings, and some compilers will choke on that (e.g. the Tru64 compiler). --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 34d7eccbb..44771a309 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1462,7 +1462,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(easy->easy_handle->set.wildcardmatch) { if(easy->easy_handle->wildcard.state != CURLWC_DONE) { - /* if a wildcard is set and we are not ending -> lets start again + /* if a wildcard is set and we are not ending -> lets start again with CURLM_STATE_INIT */ result = CURLM_CALL_MULTI_PERFORM; multistate(easy, CURLM_STATE_INIT); -- cgit v1.2.1 From 2c72732ebf3da5eba414f08507a28a90a1ec1f2c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 1 Jun 2010 23:18:34 +0200 Subject: multi_socket: handles timer inaccuracy better for timeouts Igor Novoseltsev reported a problem with the multi socket API and using timeouts and timers. It boiled down to a problem with libcurl's use of GetTickCount() interally to figure out the current time, while Igor's own application code used another function call. It made his app call the socket API timeout function a bit _before_ libcurl would consider the timeout to trigger, and that could easily lead to timeouts or stalls in the app. It seems GetTickCount() in general often has no better resolution than 16ms and switching to the alternative function QueryPerformanceCounter has its share of problems: http://www.virtualdub.org/blog/pivot/entry.php?id=106 We address this problem by simply having libcurl treat timers that already has occured or will occur within 40ms subject for treatment. I'm confident that there are other implementations and operating systems with similarly in accurate timer functions so it makes sense to have applied generically and I don't believe we sacrifice much by adding a 40ms inaccuracy on these timeouts. --- lib/multi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 44771a309..06ffbd7b1 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1994,9 +1994,11 @@ static CURLMcode multi_socket(struct Curl_multi *multi, extracts a matching node if there is one */ now = Curl_tvnow(); - now.tv_usec += 1000; /* to compensate for the truncating of 999us to 0ms, - we always add time here to make the comparison - below better */ + now.tv_usec += 40000; /* compensate for bad precision timers */ + if(now.tv_usec > 1000000) { + now.tv_sec++; + now.tv_usec -= 1000000; + } multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); if(t) { -- cgit v1.2.1 From c072bd460992355217c9077e2d33152ca0007e8f Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Wed, 2 Jun 2010 23:11:59 +0200 Subject: lib: eliminate some dead code --- lib/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 06ffbd7b1..9abf339b1 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1793,11 +1793,12 @@ static void singlesocket(struct Curl_multi *multi, return; } + /* we know (entry != NULL) at this point, see the logic above */ multi->socket_cb(easy->easy_handle, s, action, multi->socket_userp, - entry ? entry->socketp : NULL); + entry->socketp); entry->action = action; /* store the current action state */ } -- cgit v1.2.1 From 51a757c11bce9ccf9069af606bc2d8b7df584eb3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 17 Jun 2010 15:08:55 +0200 Subject: multi: call the progress callback in all states As long as no error is reported, the progress function can get called. This may be a little TOO often so we should keep an eye on this and possibly make this conditional somehow. --- lib/multi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9abf339b1..f210dcf04 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1528,6 +1528,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_COMPLETED); } + else + Curl_pgrsUpdate(easy->easy_conn); } } while(0); if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { -- cgit v1.2.1 From 01c2b397aa58a8727f3b12ca0808dc602fc4901e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 17 Jun 2010 15:19:30 +0200 Subject: multi: call the progress function only once and allow abort 1) no need to call the progress function twice when in the CURLM_STATE_TOOFAST state. 2) Make sure that the progress callback's return code is acknowledged when used --- lib/multi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f210dcf04..af1db606c 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1307,7 +1307,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ - Curl_pgrsUpdate(easy->easy_conn); if( ( ( easy->easy_handle->set.max_send_speed == 0 ) || ( easy->easy_handle->progress.ulspeed < easy->easy_handle->set.max_send_speed ) ) && @@ -1528,8 +1527,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_COMPLETED); } - else - Curl_pgrsUpdate(easy->easy_conn); + else if(Curl_pgrsUpdate(easy->easy_conn)) + easy->result = CURLE_ABORTED_BY_CALLBACK; } } while(0); if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { -- cgit v1.2.1 From 43edcc4a2eb0aa1dec888cad7b2a66b3a4948026 Mon Sep 17 00:00:00 2001 From: Krister Johansen Date: Thu, 17 Jun 2010 15:46:27 +0200 Subject: multi: unmark handle as used when no longer head of pipeline --- lib/multi.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index af1db606c..b42f68d0f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2169,7 +2169,9 @@ static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, struct connectdata *conn) { size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; + struct curl_llist_element *sendhead = conn->send_pipe->head; struct curl_llist *pipeline; + CURLcode rc; if(!Curl_isPipeliningEnabled(handle) || pipeLen == 0) @@ -2182,7 +2184,17 @@ static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, pipeline = conn->pend_pipe; } - return Curl_addHandleToPipeline(handle, pipeline); + rc = Curl_addHandleToPipeline(handle, pipeline); + + if (pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) { + /* this is a new one as head, expire it */ + conn->writechannel_inuse = FALSE; /* not in use yet */ + infof(conn->data, "%p is at send pipe head!\n", + conn->send_pipe->head->ptr); + Curl_expire(conn->send_pipe->head->ptr, 1); + } + + return rc; } static int checkPendPipeline(struct connectdata *conn) -- cgit v1.2.1 From e6d85923c1e9d4a9b76b67a4b31f0815e051b071 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 18 Jun 2010 23:46:09 +0200 Subject: multi: prevent NULL pointer dereference My additional call to Curl_pgrsUpdate() would sometimes get called even though there's no connection (left) so a NULL pointer would get passed, causing a segfault. --- lib/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b42f68d0f..ea284b3f3 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1527,7 +1527,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_COMPLETED); } - else if(Curl_pgrsUpdate(easy->easy_conn)) + /* if there's still a connection to use, call the progress function */ + else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) easy->result = CURLE_ABORTED_BY_CALLBACK; } } while(0); -- cgit v1.2.1 From 9be951a41503cd212d035496a003924f00b7aa06 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 1 Jul 2010 23:32:20 +0200 Subject: multi: CURLINFO_LASTSOCKET doesn't work after remove_handle When curl_multi_remove_handle() is called and an easy handle is returned to the connection cache held in the multi handle, then we cannot allow CURLINFO_LASTSOCKET to extract it since that will more or less encourage that the user uses the socket while it can get used by libcurl again. Without this fix, we'd get a segfault in Curl_getconnectinfo() trying to dereference the NULL pointer in 'data->state.connc'. Bug: http://curl.haxx.se/bug/view.cgi?id=3023840 --- lib/multi.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ea284b3f3..9273f8dcf 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -656,6 +656,10 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, to that since we're not part of that handle anymore */ easy->easy_handle->state.connc = NULL; + /* Since we return the connection back to the communal connection pool + we mark the last connection as inaccessible */ + easy->easy_handle->state.lastconnect = -1; + /* Modify the connectindex since this handle can't point to the connection cache anymore. -- cgit v1.2.1 From d4e64041352a4454dc0a961d125794692a913136 Mon Sep 17 00:00:00 2001 From: Constantine Sapuntzakis Date: Mon, 12 Jul 2010 19:19:31 +0200 Subject: multi: fix condition that remove timers before trigger curl_multi perform has two phases: run through every easy handle calling multi_runsingle and remove expired timers (timer removal). If a small timer (e.g. 1-10ms) is set during multi_runsingle, then it's possible that the timer has passed by when the timer removal runs. The timer which was just added is then removed. This will potentially cause the timer list to be empty and cause the next call to curl_multi_timeout to return -1. Ideally, curl_multi_timeout should return 0 in this case. One way to fix this is to move the struct timeval now = Curl_tvnow(); to the top of curl_multi_perform. The change does that. --- lib/multi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9273f8dcf..f64577f56 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1570,6 +1570,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) struct Curl_one_easy *easy; CURLMcode returncode=CURLM_OK; struct Curl_tree *t; + struct timeval now = Curl_tvnow(); if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -1607,10 +1608,13 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) * 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. + * + * It is important that the 'now' value is set at the entry of this function + * and not for the current time as it may have ticked a little while since + * then and then we risk this loop to remove timers that actually have not + * been handled! */ do { - struct timeval now = Curl_tvnow(); - multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); if(t) { struct SessionHandle *d = t->payload; -- cgit v1.2.1 From 1267719735480ead35373bde4ebed0de2b77dedb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 8 Aug 2010 01:06:29 +0200 Subject: multi: avoid a malloc() when a transfer is complete The struct used for storing the message for a completed transfer is now no longer allocated separatly but is kept within the main struct kept for each easy handle so that we avoid one malloc (and the subsequent free). --- lib/multi.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f64577f56..e6e3b0794 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -61,7 +61,6 @@ struct Curl_message { /* the 'CURLMsg' is the part that is visible to the external user */ struct CURLMsg extmsg; - struct Curl_message *next; }; /* NOTE: if you add a state here, add the name to the statename[] array as @@ -110,12 +109,8 @@ struct Curl_one_easy { CURLMstate state; /* the handle's state */ CURLcode result; /* previous result */ - struct Curl_message *msg; /* A pointer to one single posted message. - Cleanup should be done on this pointer NOT on - the linked list in Curl_multi. This message - will be deleted when this handle is removed - from the multi-handle */ - int msg_num; /* number of messages left in 'msg' to return */ + struct Curl_message msg; /* A single posted message. */ + int msg_stored; /* a message is stored in 'msg' to return */ /* Array with the plain socket numbers this handle takes care of, in no particular order. Note that all sockets are added to the sockhash, where @@ -697,8 +692,6 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* NOTE NOTE NOTE We do not touch the easy handle here! */ - if(easy->msg) - free(easy->msg); free(easy); multi->num_easy--; /* one less to care about now */ @@ -1536,26 +1529,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = CURLE_ABORTED_BY_CALLBACK; } } while(0); - if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { + if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg_stored) { if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ easy->easy_handle->dns.hostcache = NULL; easy->easy_handle->dns.hostcachetype = HCACHE_NONE; } - /* now add a node to the Curl_message linked list with this info */ - msg = malloc(sizeof(struct Curl_message)); - - if(!msg) - return CURLM_OUT_OF_MEMORY; + /* now fill in the Curl_message with this info */ + msg = &easy->msg; msg->extmsg.msg = CURLMSG_DONE; msg->extmsg.easy_handle = easy->easy_handle; msg->extmsg.data.result = easy->result; - msg->next = NULL; - easy->msg = msg; - easy->msg_num = 1; /* there is one unread message here */ + easy->msg_stored = 1; /* there is an unread message here */ multi->num_msgs++; /* increase message counter */ } @@ -1699,8 +1687,6 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */ - if(easy->msg) - free(easy->msg); free(easy); easy = nexteasy; } @@ -1727,8 +1713,8 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) easy=multi->easy.next; while(easy != &multi->easy) { - if(easy->msg_num) { - easy->msg_num--; + if(easy->msg_stored) { + easy->msg_stored = 0;; break; } easy = easy->next; @@ -1739,7 +1725,7 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) multi->num_msgs--; *msgs_in_queue = multi->num_msgs; - return &easy->msg->extmsg; + return &easy->msg.extmsg; } else return NULL; -- cgit v1.2.1 From 6ccbd1bee4470da4fc73e46dd27c809dbcdf8e2e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 8 Aug 2010 22:51:37 +0200 Subject: typo: remove duplicate semicolon --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e6e3b0794..03fddb458 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1714,7 +1714,7 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) easy=multi->easy.next; while(easy != &multi->easy) { if(easy->msg_stored) { - easy->msg_stored = 0;; + easy->msg_stored = 0; break; } easy = easy->next; -- cgit v1.2.1 From 4d53dc5d8036c199a5eed6f1df4ae89d9f0a1857 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 9 Aug 2010 00:01:36 +0200 Subject: multi: make curl_multi_info_read perform O(1) Instead of looping over all attached easy handles, this now keeps a list of messages in the multi handle. It allows curl_multi_info_read() to perform O(1) no matter how many easy handles that are handled. This is of importance since this function may be polled very frequently by apps using the multi interface. --- lib/multi.c | 134 ++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 40 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 03fddb458..c449542d6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -110,7 +110,6 @@ struct Curl_one_easy { CURLcode result; /* previous result */ struct Curl_message msg; /* A single posted message. */ - int msg_stored; /* a message is stored in 'msg' to return */ /* Array with the plain socket numbers this handle takes care of, in no particular order. Note that all sockets are added to the sockhash, where @@ -137,10 +136,11 @@ struct Curl_multi { struct Curl_one_easy easy; int num_easy; /* amount of entries in the linked list above. */ - int num_msgs; /* amount of messages in the easy handles */ int num_alive; /* amount of easy handles that are added but have not yet reached COMPLETE state */ + struct curl_llist *msglist; /* a list of messages from completed transfers */ + /* callback function and user data pointer for the *socket() API */ curl_socket_callback socket_cb; void *socket_userp; @@ -355,6 +355,33 @@ static struct curl_hash *sh_init(void) sh_freeentry); } +/* + * multi_addmsg() + * + * Called when a transfer is completed. Adds the given msg pointer to + * the list kept in the multi handle. + */ +static CURLMcode multi_addmsg(struct Curl_multi *multi, + struct Curl_message *msg) +{ + if(!Curl_llist_insert_next(multi->msglist, multi->msglist->tail, msg)) + return CURLM_OUT_OF_MEMORY; + + return CURLM_OK; +} + +/* + * multi_freeamsg() + * + * Callback used by the llist system when a single list entry is destroyed. + */ +static void multi_freeamsg(void *a, void *b) +{ + (void)a; + (void)b; +} + + CURLM *curl_multi_init(void) { struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); @@ -365,27 +392,20 @@ CURLM *curl_multi_init(void) multi->type = CURL_MULTI_HANDLE; multi->hostcache = Curl_mk_dnscache(); - if(!multi->hostcache) { - /* failure, free mem and bail out */ - free(multi); - return NULL; - } + if(!multi->hostcache) + goto error; multi->sockhash = sh_init(); - if(!multi->sockhash) { - /* failure, free mem and bail out */ - Curl_hash_destroy(multi->hostcache); - free(multi); - return NULL; - } + if(!multi->sockhash) + goto error; multi->connc = Curl_mk_connc(CONNCACHE_MULTI, -1L); - if(!multi->connc) { - Curl_hash_destroy(multi->sockhash); - Curl_hash_destroy(multi->hostcache); - free(multi); - return NULL; - } + if(!multi->connc) + goto error; + + multi->msglist = Curl_llist_alloc(multi_freeamsg); + if(!multi->msglist) + goto error; /* Let's make the doubly-linked list a circular list. This makes the linked list code simpler and allows inserting at the end @@ -394,6 +414,17 @@ CURLM *curl_multi_init(void) multi->easy.prev = &multi->easy; return (CURLM *) multi; + + error: + if(multi->sockhash) + Curl_hash_destroy(multi->sockhash); + if(multi->hostcache) + Curl_hash_destroy(multi->hostcache); + if(multi->connc) + Curl_rm_connc(multi->connc); + + free(multi); + return NULL; } CURLMcode curl_multi_add_handle(CURLM *multi_handle, @@ -678,6 +709,22 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association to this multi handle */ + { + /* make sure there's no pending message in the queue sent from this easy + handle */ + struct curl_llist_element *e; + + for(e = multi->msglist->head; e; e = e->next) { + struct Curl_message *msg = e->ptr; + + if(msg->extmsg.easy_handle == easy->easy_handle) { + Curl_llist_remove(multi->msglist, e, NULL); + /* there can only be one from this specific handle */ + break; + } + } + } + /* make the previous node point to our next */ if(easy->prev) easy->prev->next = easy->next; @@ -1529,7 +1576,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = CURLE_ABORTED_BY_CALLBACK; } } while(0); - if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg_stored) { + + if(CURLM_STATE_COMPLETED == easy->state) { if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ easy->easy_handle->dns.hostcache = NULL; @@ -1543,9 +1591,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, msg->extmsg.easy_handle = easy->easy_handle; msg->extmsg.data.result = easy->result; - easy->msg_stored = 1; /* there is an unread message here */ - - multi->num_msgs++; /* increase message counter */ + result = multi_addmsg(multi, msg); } return result; @@ -1672,6 +1718,9 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) Curl_rm_connc(multi->connc); + /* remove the pending list of messages */ + Curl_llist_destroy(multi->msglist, NULL); + /* remove all easy handles */ easy = multi->easy.next; while(easy != &multi->easy) { @@ -1699,33 +1748,38 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) return CURLM_BAD_HANDLE; } +/* + * curl_multi_info_read() + * + * This function is the primary way for a multi/multi_socket application to + * figure out if a transfer has ended. We MUST make this function as fast as + * possible as it will be polled frequently and we MUST NOT scan any lists in + * here to figure out things. We must scale fine to thousands of handles and + * beyond. The current design is fully O(1). + */ + CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_message *msg; *msgs_in_queue = 0; /* default to none */ - if(GOOD_MULTI_HANDLE(multi)) { - struct Curl_one_easy *easy; + if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(multi->msglist)) { + /* there is one or more messages in the list */ + struct curl_llist_element *e; - if(!multi->num_msgs) - return NULL; /* no messages left to return */ + /* extract the head of the list to return */ + e = multi->msglist->head; - easy=multi->easy.next; - while(easy != &multi->easy) { - if(easy->msg_stored) { - easy->msg_stored = 0; - break; - } - easy = easy->next; - } - if(!easy) - return NULL; /* this means internal count confusion really */ + msg = e->ptr; + + /* remove the extracted entry */ + Curl_llist_remove(multi->msglist, e, NULL); - multi->num_msgs--; - *msgs_in_queue = multi->num_msgs; + *msgs_in_queue = Curl_llist_count(multi->msglist); - return &easy->msg.extmsg; + return &msg->extmsg; } else return NULL; -- cgit v1.2.1 From 232ad6549a684505efcbb6ed9d7a78943cc5f817 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 10 Aug 2010 11:02:07 +0200 Subject: multi: support timeouts Curl_expire() is now expanded to hold a list of timeouts for each easy handle. Only the closest in time will be the one used as the primary timeout for the handle and will be used for the splay tree (which sorts and lists all handles within the multi handle). When the main timeout has triggered/expired, the next timeout in time that is kept in the list will be moved to the main timeout position and used as the key to splay with. This way, all timeouts that are set with Curl_expire() internally will end up as a proper timeout. Previously any Curl_expire() that set a _later_ timeout than what was already set was just silently ignored and thus missed. Setting Curl_expire() with timeout 0 (zero) will cancel all previously added timeouts. Corrects known bug #62. --- lib/multi.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 123 insertions(+), 19 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index c449542d6..69b80f0ee 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -214,6 +214,8 @@ static const char * const statename[]={ }; #endif +static void multi_freetimeout(void *a, void *b); + /* always use this function to change state, to make debugging easier */ static void multistate(struct Curl_one_easy *easy, CURLMstate state) { @@ -434,6 +436,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, struct Curl_one_easy *easy; struct closure *cl; struct closure *prev=NULL; + struct SessionHandle *data = easy_handle; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -448,6 +451,10 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* possibly we should create a new unique error code for this condition */ return CURLM_BAD_EASY_HANDLE; + data->state.timeoutlist = Curl_llist_alloc(multi_freetimeout); + if(!data->state.timeoutlist) + return CURLM_OUT_OF_MEMORY; + /* Now, time to add an easy handle to the multi stack */ easy = calloc(1, sizeof(struct Curl_one_easy)); if(!easy) @@ -601,6 +608,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; + struct SessionHandle *data = curl_handle; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -611,7 +619,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, return CURLM_BAD_EASY_HANDLE; /* pick-up from the 'curl_handle' the kept position in the list */ - easy = ((struct SessionHandle *)curl_handle)->multi_pos; + easy = data->multi_pos; if(easy) { bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED); @@ -644,6 +652,12 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, curl_easy_cleanup is called. */ Curl_expire(easy->easy_handle, 0); + /* destroy the timeout list that is held in the easy handle */ + if(data->state.timeoutlist) { + Curl_llist_destroy(data->state.timeoutlist, NULL); + data->state.timeoutlist = NULL; + } + if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ easy->easy_handle->dns.hostcache = NULL; @@ -1652,12 +1666,34 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); if(t) { struct SessionHandle *d = t->payload; - struct timeval* tv = &d->state.expiretime; + struct timeval *tv = &d->state.expiretime; + struct curl_llist *list = d->state.timeoutlist; + struct curl_llist_element *e; - /* clear the expire times within the handles that we remove from the - splay tree */ - tv->tv_sec = 0; - tv->tv_usec = 0; + /* move over the timeout list for this specific handle and remove all + timeouts that are now passed tense and store the next pending + timeout in *tv */ + for(e = list->head; e; ) { + struct curl_llist_element *n = e->next; + if(curlx_tvdiff(*(struct timeval *)e->ptr, now) < 0) + /* remove outdated entry */ + Curl_llist_remove(list, e, NULL); + e = n; + } + if(!list->size) { + /* clear the expire times within the handles that we remove from the + splay tree */ + tv->tv_sec = 0; + tv->tv_usec = 0; + } + else { + e = list->head; + /* copy the first entry to 'tv' */ + memcpy(tv, e->ptr, sizeof(*tv)); + + /* remove first entry from list */ + Curl_llist_remove(list, e, NULL); + } } } while(t); @@ -1670,14 +1706,6 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) return returncode; } -/* This is called when an easy handle is cleanup'ed that is part of a multi - handle */ -void Curl_multi_rmeasy(void *multi_handle, CURL *easy_handle) -{ - curl_multi_remove_handle(multi_handle, easy_handle); -} - - CURLMcode curl_multi_cleanup(CURLM *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; @@ -2343,10 +2371,72 @@ static bool isHandleAtHead(struct SessionHandle *handle, return FALSE; } -/* 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() +/* + * multi_freetimeout() + * + * Callback used by the llist system when a single timeout list entry is + * destroyed. + */ +static void multi_freetimeout(void *user, void *entryptr) +{ + (void)user; + + /* the entry was plain malloc()'ed */ + free(entryptr); +} + +/* + * multi_addtimeout() + * + * Add a timestamp to the list of timeouts. Keep the list sorted so that head + * of list is always the timeout nearest in time. + * + */ +static CURLMcode +multi_addtimeout(struct curl_llist *timeoutlist, + struct timeval *stamp) +{ + struct curl_llist_element *e; + struct timeval *timedup; + struct curl_llist_element *prev = NULL; + + timedup = malloc(sizeof(*timedup)); + if(!timedup) + return CURLM_OUT_OF_MEMORY; + + /* copy the timestamp */ + memcpy(timedup, stamp, sizeof(*timedup)); + + if(Curl_llist_count(timeoutlist)) { + /* find the correct spot in the list */ + for(e = timeoutlist->head; e; e = e->next) { + struct timeval *checktime = e->ptr; + long diff = curlx_tvdiff(*checktime, *timedup); + if(diff > 0) + break; + prev = e; + } + + } + /* else + this is the first timeout on the list */ + + if(!Curl_llist_insert_next(timeoutlist, prev, timedup)) + return CURLM_OUT_OF_MEMORY; + + return CURLM_OK; +} - Pass zero to clear the timeout value for this handle. +/* + * Curl_expire() + * + * 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() + * + * Note that the timeout will be added to a queue of timeouts if it defines a + * moment in time that is later than the current head of queue. + * + * Pass zero to clear all timeout values for this handle. */ void Curl_expire(struct SessionHandle *data, long milli) { @@ -2364,11 +2454,18 @@ void Curl_expire(struct SessionHandle *data, long milli) if(nowp->tv_sec || nowp->tv_usec) { /* Since this is an cleared time, we must remove the previous entry from the splay tree */ + struct curl_llist *list = data->state.timeoutlist; + rc = Curl_splayremovebyaddr(multi->timetree, &data->state.timenode, &multi->timetree); if(rc) infof(data, "Internal error clearing splay node = %d\n", rc); + + /* flush the timeout list too */ + while(list->size > 0) + Curl_llist_remove(list, list->tail, NULL); + infof(data, "Expire cleared\n"); nowp->tv_sec = 0; nowp->tv_usec = 0; @@ -2394,9 +2491,16 @@ void Curl_expire(struct SessionHandle *data, long milli) Compare if the new time is earlier, and only remove-old/add-new if it is. */ long diff = curlx_tvdiff(set, *nowp); - if(diff > 0) - /* the new expire time was later so we don't change this */ + if(diff > 0) { + /* the new expire time was later so just add it to the queue + and get out */ + multi_addtimeout(data->state.timeoutlist, &set); return; + } + + /* the new time is newer than the presently set one, so add the current + to the queue and update the head */ + multi_addtimeout(data->state.timeoutlist, nowp); /* Since this is an updated time, we must remove the previous entry from the splay tree first and then re-add the new value */ -- cgit v1.2.1 From 9124bfba450d4e5909cd55aa1f45362fd54a6299 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 10 Aug 2010 14:12:05 +0200 Subject: multi: use timeouts properly for MAX_RECV/SEND_SPEED When detecting that the send or recv speed, the multi interface changes state to TOOFAST and previously there was no timeout set that would force a recheck but it would rely on the application to somehow call libcurl anyway. This now sets a timeout for a suitable future time to check again if the average transfer speed is then below the threshold again. --- lib/multi.c | 163 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 72 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 69b80f0ee..778bc4128 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -934,10 +934,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, bool done = FALSE; CURLMcode result = CURLM_OK; struct SingleRequest *k; + struct SessionHandle *data; if(!GOOD_EASY_HANDLE(easy->easy_handle)) return CURLM_BAD_EASY_HANDLE; + data = easy->easy_handle; + do { /* this is a do-while loop just to allow a break to skip to the end of it */ @@ -945,9 +948,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Handle the case when the pipe breaks, i.e., the connection we're using gets cleaned up and we're left with nothing. */ - if(easy->easy_handle->state.pipe_broke) { - infof(easy->easy_handle, "Pipe broke: handle 0x%p, url = %s\n", - easy, easy->easy_handle->state.path); + if(data->state.pipe_broke) { + infof(data, "Pipe broke: handle 0x%p, url = %s\n", + easy, data->state.path); if(easy->state != CURLM_STATE_COMPLETED) { /* Head back to the CONNECT state */ @@ -956,7 +959,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->result = CURLE_OK; } - easy->easy_handle->state.pipe_broke = FALSE; + data->state.pipe_broke = FALSE; easy->easy_conn = NULL; break; } @@ -964,31 +967,31 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(easy->easy_conn && easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) /* Make sure we set the connection's current owner */ - easy->easy_conn->data = easy->easy_handle; + easy->easy_conn->data = data; switch(easy->state) { case CURLM_STATE_INIT: /* init this transfer. */ - easy->result=Curl_pretransfer(easy->easy_handle); + easy->result=Curl_pretransfer(data); if(CURLE_OK == easy->result) { /* after init, go CONNECT */ multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; - easy->easy_handle->state.used_interface = Curl_if_multi; + data->state.used_interface = Curl_if_multi; } break; case CURLM_STATE_CONNECT: /* Connect. We get a connection identifier filled in. */ - Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); - easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, + Curl_pgrsTime(data, TIMER_STARTSINGLE); + easy->result = Curl_connect(data, &easy->easy_conn, &async, &protocol_connect); if(CURLE_OK == easy->result) { /* Add this handle to the send or pend pipeline */ - easy->result = addHandleToSendOrPendPipeline(easy->easy_handle, + easy->result = addHandleToSendOrPendPipeline(data, easy->easy_conn); if(CURLE_OK == easy->result) { if(async) @@ -1071,9 +1074,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(easy->easy_conn->bits.proxy_connect_closed) { /* reset the error buffer */ - if(easy->easy_handle->set.errorbuffer) - easy->easy_handle->set.errorbuffer[0] = '\0'; - easy->easy_handle->state.errorbuf = FALSE; + if(data->set.errorbuffer) + data->set.errorbuffer[0] = '\0'; + data->state.errorbuf = FALSE; easy->result = CURLE_OK; result = CURLM_CALL_MULTI_PERFORM; @@ -1145,7 +1148,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else if(easy->result) { /* failure detected */ - Curl_posttransfer(easy->easy_handle); + Curl_posttransfer(data); Curl_done(&easy->easy_conn, easy->result, FALSE); disconnect_conn = TRUE; } @@ -1154,15 +1157,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITDO: /* Wait for our turn to DO when we're pipelining requests */ #ifdef DEBUGBUILD - infof(easy->easy_handle, "Conn %ld send pipe %zu inuse %d athead %d\n", + infof(data, "Conn %ld send pipe %zu inuse %d athead %d\n", easy->easy_conn->connectindex, easy->easy_conn->send_pipe->size, easy->easy_conn->writechannel_inuse?1:0, - isHandleAtHead(easy->easy_handle, + isHandleAtHead(data, easy->easy_conn->send_pipe)?1:0); #endif if(!easy->easy_conn->writechannel_inuse && - isHandleAtHead(easy->easy_handle, + isHandleAtHead(data, easy->easy_conn->send_pipe)) { /* Grab the channel */ easy->easy_conn->writechannel_inuse = TRUE; @@ -1172,7 +1175,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_DO: - if(easy->easy_handle->set.connect_only) { + if(data->set.connect_only) { /* keep connection open for application to use the socket */ easy->easy_conn->bits.close = FALSE; multistate(easy, CURLM_STATE_DONE); @@ -1187,8 +1190,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK == easy->result) { if(!dophase_done) { /* some steps needed for wildcard matching */ - if(easy->easy_handle->set.wildcardmatch) { - struct WildcardData *wc = &easy->easy_handle->wildcard; + if(data->set.wildcardmatch) { + struct WildcardData *wc = &data->wildcard; if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { /* skip some states if it is important */ Curl_done(&easy->easy_conn, CURLE_OK, FALSE); @@ -1237,7 +1240,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else retry = (bool)(newurl?TRUE:FALSE); - Curl_posttransfer(easy->easy_handle); + Curl_posttransfer(data); drc = Curl_done(&easy->easy_conn, easy->result, FALSE); /* When set to retry the connection, we must to go back to @@ -1245,7 +1248,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(retry) { if ((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) { follow = FOLLOW_RETRY; - drc = Curl_follow(easy->easy_handle, newurl, follow); + drc = Curl_follow(data, newurl, follow); if(drc == CURLE_OK) { multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; @@ -1270,7 +1273,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* failure detected */ - Curl_posttransfer(easy->easy_handle); + Curl_posttransfer(data); Curl_done(&easy->easy_conn, easy->result, FALSE); disconnect_conn = TRUE; } @@ -1299,7 +1302,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* failure detected */ - Curl_posttransfer(easy->easy_handle); + Curl_posttransfer(data); Curl_done(&easy->easy_conn, easy->result, FALSE); disconnect_conn = TRUE; } @@ -1325,7 +1328,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* failure detected */ - Curl_posttransfer(easy->easy_handle); + Curl_posttransfer(data); Curl_done(&easy->easy_conn, easy->result, FALSE); disconnect_conn = TRUE; } @@ -1334,7 +1337,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DO_DONE: /* Move ourselves from the send to recv pipeline */ - moveHandleFromSendToRecvPipeline(easy->easy_handle, easy->easy_conn); + moveHandleFromSendToRecvPipeline(data, easy->easy_conn); /* Check if we can move pending requests to send pipe */ checkPendPipeline(easy->easy_conn); multistate(easy, CURLM_STATE_WAITPERFORM); @@ -1344,7 +1347,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITPERFORM: /* Wait for our turn to PERFORM */ if(!easy->easy_conn->readchannel_inuse && - isHandleAtHead(easy->easy_handle, + isHandleAtHead(data, easy->easy_conn->recv_pipe)) { /* Grab the channel */ easy->easy_conn->readchannel_inuse = TRUE; @@ -1353,11 +1356,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } #ifdef DEBUGBUILD else { - infof(easy->easy_handle, "Conn %ld recv pipe %zu inuse %d athead %d\n", + infof(data, "Conn %ld recv pipe %zu inuse %d athead %d\n", easy->easy_conn->connectindex, easy->easy_conn->recv_pipe->size, easy->easy_conn->readchannel_inuse?1:0, - isHandleAtHead(easy->easy_handle, + isHandleAtHead(data, easy->easy_conn->recv_pipe)?1:0); } #endif @@ -1365,36 +1368,52 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ - if( ( ( easy->easy_handle->set.max_send_speed == 0 ) || - ( easy->easy_handle->progress.ulspeed < - easy->easy_handle->set.max_send_speed ) ) && - ( ( easy->easy_handle->set.max_recv_speed == 0 ) || - ( easy->easy_handle->progress.dlspeed < - easy->easy_handle->set.max_recv_speed ) ) - ) + if( ( (data->set.max_send_speed == 0) || + (data->progress.ulspeed < data->set.max_send_speed )) && + ( (data->set.max_recv_speed == 0) || + (data->progress.dlspeed < data->set.max_recv_speed) ) ) multistate(easy, CURLM_STATE_PERFORM); break; case CURLM_STATE_PERFORM: - /* check if over speed */ - if( ( ( easy->easy_handle->set.max_send_speed > 0 ) && - ( easy->easy_handle->progress.ulspeed > - easy->easy_handle->set.max_send_speed ) ) || - ( ( easy->easy_handle->set.max_recv_speed > 0 ) && - ( easy->easy_handle->progress.dlspeed > - easy->easy_handle->set.max_recv_speed ) ) - ) { - /* Transfer is over the speed limit. Change state. TODO: Call - * Curl_expire() with the time left until we're targeted to be below - * the speed limit again. */ - multistate(easy, CURLM_STATE_TOOFAST ); + /* check if over send speed */ + if( (data->set.max_send_speed > 0) && + (data->progress.ulspeed > data->set.max_send_speed) ) { + int buffersize; + long timeout_ms; + + multistate(easy, CURLM_STATE_TOOFAST); + + /* calculate upload rate-limitation timeout. */ + buffersize = (int)(data->set.buffer_size ? + data->set.buffer_size : BUFSIZE); + timeout_ms = Curl_sleep_time(data->set.max_send_speed, + data->progress.ulspeed, buffersize); + Curl_expire(data, timeout_ms); + break; + } + + /* check if over recv speed */ + if( (data->set.max_recv_speed > 0) && + (data->progress.dlspeed > data->set.max_recv_speed) ) { + int buffersize; + long timeout_ms; + + multistate(easy, CURLM_STATE_TOOFAST); + + /* Calculate download rate-limitation timeout. */ + buffersize = (int)(data->set.buffer_size ? + data->set.buffer_size : BUFSIZE); + timeout_ms = Curl_sleep_time(data->set.max_recv_speed, + data->progress.dlspeed, buffersize); + Curl_expire(data, timeout_ms); break; } /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); - k = &easy->easy_handle->req; + k = &data->req; if(!(k->keepon & KEEP_RECV)) { /* We're done receiving */ @@ -1416,7 +1435,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!(easy->easy_conn->protocol & PROT_DUALCHANNEL)) easy->easy_conn->bits.close = TRUE; - Curl_posttransfer(easy->easy_handle); + Curl_posttransfer(data); Curl_done(&easy->easy_conn, easy->result, FALSE); } else if(TRUE == done) { @@ -1429,10 +1448,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, retry = (bool)(newurl?TRUE:FALSE); /* call this even if the readwrite function returned error */ - Curl_posttransfer(easy->easy_handle); + Curl_posttransfer(data); /* we're no longer receving */ - moveHandleFromRecvToDonePipeline(easy->easy_handle, + moveHandleFromRecvToDonePipeline(data, easy->easy_conn); /* expire the new receiving pipeline head */ @@ -1444,19 +1463,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* When we follow redirects or is set to retry the connection, we must to go back to the CONNECT state */ - if(easy->easy_handle->req.newurl || retry) { + if(data->req.newurl || retry) { if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ - newurl = easy->easy_handle->req.newurl; - easy->easy_handle->req.newurl = NULL; + newurl = data->req.newurl; + data->req.newurl = NULL; follow = FOLLOW_REDIR; } else follow = FOLLOW_RETRY; easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE); if(easy->result == CURLE_OK) - easy->result = Curl_follow(easy->easy_handle, newurl, follow); + easy->result = Curl_follow(data, newurl, follow); if(CURLE_OK == easy->result) { multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; @@ -1471,10 +1490,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* but first check to see if we got a location info even though we're not following redirects */ - if (easy->easy_handle->req.location) { - newurl = easy->easy_handle->req.location; - easy->easy_handle->req.location = NULL; - easy->result = Curl_follow(easy->easy_handle, newurl, FOLLOW_FAKE); + if (data->req.location) { + newurl = data->req.location; + data->req.location = NULL; + easy->result = Curl_follow(data, newurl, FOLLOW_FAKE); if (easy->result) free(newurl); } @@ -1491,9 +1510,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(easy->easy_conn) { /* Remove ourselves from the receive and done pipelines. Handle should be on one of these lists, depending upon how we got here. */ - Curl_removeHandleFromPipeline(easy->easy_handle, + Curl_removeHandleFromPipeline(data, easy->easy_conn->recv_pipe); - Curl_removeHandleFromPipeline(easy->easy_handle, + Curl_removeHandleFromPipeline(data, easy->easy_conn->done_pipe); /* Check if we can move pending requests to send pipe */ checkPendPipeline(easy->easy_conn); @@ -1517,8 +1536,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn = NULL; } - if(easy->easy_handle->set.wildcardmatch) { - if(easy->easy_handle->wildcard.state != CURLWC_DONE) { + if(data->set.wildcardmatch) { + if(data->wildcard.state != CURLWC_DONE) { /* if a wildcard is set and we are not ending -> lets start again with CURLM_STATE_INIT */ result = CURLM_CALL_MULTI_PERFORM; @@ -1558,17 +1577,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* NOTE: no attempt to disconnect connections must be made in the case blocks above - cleanup happens only here */ - easy->easy_handle->state.pipe_broke = FALSE; + data->state.pipe_broke = FALSE; if(easy->easy_conn) { /* if this has a connection, unsubscribe from the pipelines */ easy->easy_conn->writechannel_inuse = FALSE; easy->easy_conn->readchannel_inuse = FALSE; - Curl_removeHandleFromPipeline(easy->easy_handle, + Curl_removeHandleFromPipeline(data, easy->easy_conn->send_pipe); - Curl_removeHandleFromPipeline(easy->easy_handle, + Curl_removeHandleFromPipeline(data, easy->easy_conn->recv_pipe); - Curl_removeHandleFromPipeline(easy->easy_handle, + Curl_removeHandleFromPipeline(data, easy->easy_conn->done_pipe); /* Check if we can move pending requests to send pipe */ checkPendPipeline(easy->easy_conn); @@ -1592,17 +1611,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } while(0); if(CURLM_STATE_COMPLETED == easy->state) { - if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { + if(data->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ - easy->easy_handle->dns.hostcache = NULL; - easy->easy_handle->dns.hostcachetype = HCACHE_NONE; + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; } /* now fill in the Curl_message with this info */ msg = &easy->msg; msg->extmsg.msg = CURLMSG_DONE; - msg->extmsg.easy_handle = easy->easy_handle; + msg->extmsg.easy_handle = data; msg->extmsg.data.result = easy->result; result = multi_addmsg(multi, msg); -- cgit v1.2.1 From 280d2cff2eed563cb19b46056fcb17e06dc4e158 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 18 Aug 2010 19:59:46 +0200 Subject: multi: avoid sending multiple complete messages I fell over this bug report that mentioned that libcurl could wrongly send more than one complete messages at the end of a transfer. Reading the code confirmed this, so I've added a new multi state to make it not happen. The mentioned bug report was made by Brad Jorsch but is (oddly enough) filed in Debian's bug tracker for the "wmweather+" tool. Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593390 --- lib/multi.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 778bc4128..80fe6b580 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -83,7 +83,7 @@ typedef enum { CURLM_STATE_TOOFAST, /* wait because limit-rate exceeded */ CURLM_STATE_DONE, /* post data transfer operation */ CURLM_STATE_COMPLETED, /* operation complete */ - + CURLM_STATE_MSGSENT, /* the operation complete message is sent */ CURLM_STATE_LAST /* not a true state, never use this */ } CURLMstate; @@ -211,6 +211,7 @@ static const char * const statename[]={ "TOOFAST", "DONE", "COMPLETED", + "MSGSENT", }; #endif @@ -622,7 +623,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, easy = data->multi_pos; if(easy) { - bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED); + bool premature = (bool)(easy->state < CURLM_STATE_COMPLETED); bool easy_owns_conn = (bool)(easy->easy_conn && (easy->easy_conn->data == easy->easy_handle)); @@ -837,6 +838,7 @@ static int multi_getsock(struct Curl_one_easy *easy, to be present */ case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */ case CURLM_STATE_COMPLETED: + case CURLM_STATE_MSGSENT: case CURLM_STATE_INIT: case CURLM_STATE_CONNECT: case CURLM_STATE_WAITDO: @@ -952,7 +954,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, infof(data, "Pipe broke: handle 0x%p, url = %s\n", easy, data->state.path); - if(easy->state != CURLM_STATE_COMPLETED) { + if(easy->state < CURLM_STATE_COMPLETED) { /* Head back to the CONNECT state */ multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; @@ -1563,11 +1565,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn = NULL; break; + case CURLM_STATE_MSGSENT: + return CURLM_OK; /* do nothing */ + default: return CURLM_INTERNAL_ERROR; } - if(CURLM_STATE_COMPLETED != easy->state) { + if(CURLM_STATE_COMPLETED > easy->state) { if(CURLE_OK != easy->result) { /* * If an error was returned, and we aren't in completed state now, @@ -1625,6 +1630,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, msg->extmsg.data.result = easy->result; result = multi_addmsg(multi, msg); + + multistate(easy, CURLM_STATE_MSGSENT); } return result; @@ -2686,7 +2693,7 @@ void Curl_multi_dump(const struct Curl_multi *multi_handle) fprintf(stderr, "* Multi status: %d handles, %d alive\n", multi->num_easy, multi->num_alive); for(easy=multi->easy.next; easy != &multi->easy; easy = easy->next) { - if(easy->state != CURLM_STATE_COMPLETED) { + if(easy->state < CURLM_STATE_COMPLETED) { /* only display handles that are not completed */ fprintf(stderr, "handle %p, state %s, %d sockets\n", (void *)easy->easy_handle, -- cgit v1.2.1 From ab81f6c7c48e796baa4ba92d3fd121eeeba287a7 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Wed, 18 Aug 2010 23:08:18 -0700 Subject: Fixed a memory leak during OOM in the multi timeout code --- lib/multi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 80fe6b580..dd196b7eb 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2447,8 +2447,10 @@ multi_addtimeout(struct curl_llist *timeoutlist, /* else this is the first timeout on the list */ - if(!Curl_llist_insert_next(timeoutlist, prev, timedup)) + if(!Curl_llist_insert_next(timeoutlist, prev, timedup)) { + free(timedup); return CURLM_OUT_OF_MEMORY; + } return CURLM_OK; } -- cgit v1.2.1 From bed311eda27a3c7c87f0988310462414c54e5384 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 24 Aug 2010 18:30:26 +0200 Subject: multi: Fix compile warning on 64-bit systems --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index dd196b7eb..4bf74a746 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1831,7 +1831,7 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) /* remove the extracted entry */ Curl_llist_remove(multi->msglist, e, NULL); - *msgs_in_queue = Curl_llist_count(multi->msglist); + *msgs_in_queue = (int)Curl_llist_count(multi->msglist); return &msg->extmsg; } -- cgit v1.2.1 From 0db9140747e65ab402768670b130b0ddaa88f883 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 31 Aug 2010 00:08:45 +0200 Subject: multi: make sure the next timeout is used when one expires Each easy handle has a list of timeouts, so as soon as the main timeout for a handle expires, we must make sure to get the next entry from the list and re-add the handle to the splay tree. This was attempted previously but was done poorly in my commit 232ad6549a68450. --- lib/multi.c | 104 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 40 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 4bf74a746..5a5470246 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -192,6 +192,9 @@ static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle, struct connectdata *conn); static bool isHandleAtHead(struct SessionHandle *handle, struct curl_llist *pipeline); +static CURLMcode add_next_timeout(struct timeval now, + struct Curl_multi *multi, + struct SessionHandle *d); #ifdef DEBUGBUILD static const char * const statename[]={ @@ -1690,37 +1693,9 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) */ do { multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) { - struct SessionHandle *d = t->payload; - struct timeval *tv = &d->state.expiretime; - struct curl_llist *list = d->state.timeoutlist; - struct curl_llist_element *e; - - /* move over the timeout list for this specific handle and remove all - timeouts that are now passed tense and store the next pending - timeout in *tv */ - for(e = list->head; e; ) { - struct curl_llist_element *n = e->next; - if(curlx_tvdiff(*(struct timeval *)e->ptr, now) < 0) - /* remove outdated entry */ - Curl_llist_remove(list, e, NULL); - e = n; - } - if(!list->size) { - /* clear the expire times within the handles that we remove from the - splay tree */ - tv->tv_sec = 0; - tv->tv_usec = 0; - } - else { - e = list->head; - /* copy the first entry to 'tv' */ - memcpy(tv, e->ptr, sizeof(*tv)); - - /* remove first entry from list */ - Curl_llist_remove(list, e, NULL); - } - } + if(t) + /* the removed may have another timeout in queue */ + (void)add_next_timeout(now, multi, t->payload); } while(t); @@ -1992,6 +1967,62 @@ static void singlesocket(struct Curl_multi *multi, easy->numsocks = num; } +/* + * add_next_timeout() + * + * Each SessionHandle has a list of timeouts. The add_next_timeout() is called + * when it has just been removed from the splay tree because the timeout has + * expired. This function is then to advance in the list to pick the next + * timeout to use (skip the already expired ones) and add this node back to + * the splay tree again. + * + * The splay tree only has each sessionhandle as a single node and the nearest + * timeout is used to sort it on. + */ +static CURLMcode add_next_timeout(struct timeval now, + struct Curl_multi *multi, + struct SessionHandle *d) +{ + struct timeval *tv = &d->state.expiretime; + struct curl_llist *list = d->state.timeoutlist; + struct curl_llist_element *e; + + /* move over the timeout list for this specific handle and remove all + timeouts that are now passed tense and store the next pending + timeout in *tv */ + for(e = list->head; e; ) { + struct curl_llist_element *n = e->next; + long diff = curlx_tvdiff(*(struct timeval *)e->ptr, now); + if(diff <= 0) + /* remove outdated entry */ + Curl_llist_remove(list, e, NULL); + else + /* the list is sorted so get out on the first mismatch */ + break; + e = n; + } + if(!list->size) { + /* clear the expire times within the handles that we remove from the + splay tree */ + tv->tv_sec = 0; + tv->tv_usec = 0; + } + else { + e = list->head; + /* copy the first entry to 'tv' */ + memcpy(tv, e->ptr, sizeof(*tv)); + + /* remove first entry from list */ + Curl_llist_remove(list, e, NULL); + + /* insert this node again into the splay */ + multi->timetree = Curl_splayinsert(*tv, multi->timetree, + &d->state.timenode); + } + return CURLM_OK; +} + + static CURLMcode multi_socket(struct Curl_multi *multi, bool checkall, curl_socket_t s, @@ -2106,15 +2137,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, } multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) { - /* assign 'data' to be the easy handle we just removed from the splay - tree */ - data = t->payload; - /* clear the expire time within the handle we removed from the - splay tree */ - data->state.expiretime.tv_sec = 0; - data->state.expiretime.tv_usec = 0; - } + if(t) + (void)add_next_timeout(now, multi, t->payload); } while(t); -- cgit v1.2.1 From ca10e28f06f168ec433d376ee8fd64a25b317f0f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 1 Sep 2010 16:52:23 +0200 Subject: multi: fixes for timing out handles Add a timeout check for handles in the state machine so that they will timeout in all states disregarding what actions that may or may not happen. Fixed a bug in socket_action introduced recently when looping over timed out handles: it wouldn't assign the 'data' variable and thus it wouldn't properly take care of handles. In the update_timer function, the code now checks if the timeout has been removed and then it tells the application. Previously it would always let the remaining timeout(s) just linger to expire later on. --- lib/multi.c | 64 +++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 21 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 5a5470246..13915c120 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -929,6 +929,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, } static CURLMcode multi_runsingle(struct Curl_multi *multi, + struct timeval now, struct Curl_one_easy *easy) { struct Curl_message *msg = NULL; @@ -940,6 +941,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLMcode result = CURLM_OK; struct SingleRequest *k; struct SessionHandle *data; + long timeout_ms; if(!GOOD_EASY_HANDLE(easy->easy_handle)) return CURLM_BAD_EASY_HANDLE; @@ -974,6 +976,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Make sure we set the connection's current owner */ easy->easy_conn->data = data; + if(easy->easy_conn && (easy->state >= CURLM_STATE_CONNECT)) { + /* we need to wait for the connect state as only then is the + start time stored */ + + timeout_ms = Curl_timeleft(easy->easy_conn, &now, + easy->state <= CURLM_STATE_WAITDO); + + if(timeout_ms < 0) { + /* Handle timed out */ + easy->result = CURLE_OPERATION_TIMEDOUT; + multistate(easy, CURLM_STATE_COMPLETED); + break; + } + } + switch(easy->state) { case CURLM_STATE_INIT: /* init this transfer. */ @@ -1385,7 +1402,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if( (data->set.max_send_speed > 0) && (data->progress.ulspeed > data->set.max_send_speed) ) { int buffersize; - long timeout_ms; multistate(easy, CURLM_STATE_TOOFAST); @@ -1402,7 +1418,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if( (data->set.max_recv_speed > 0) && (data->progress.dlspeed > data->set.max_recv_speed) ) { int buffersize; - long timeout_ms; multistate(easy, CURLM_STATE_TOOFAST); @@ -1666,7 +1681,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } do - result = multi_runsingle(multi, easy); + result = multi_runsingle(multi, now, easy); while (CURLM_CALL_MULTI_PERFORM == result); if(easy->easy_handle->set.wildcardmatch) { @@ -2032,6 +2047,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, CURLMcode result = CURLM_OK; struct SessionHandle *data = NULL; struct Curl_tree *t; + struct timeval now = Curl_tvnow(); if(checkall) { struct Curl_one_easy *easyp; @@ -2086,7 +2102,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; do - result = multi_runsingle(multi, data->set.one_easy); + result = multi_runsingle(multi, now, data->set.one_easy); while (CURLM_CALL_MULTI_PERFORM == result); if(data->set.one_easy->easy_conn) @@ -2106,18 +2122,23 @@ static CURLMcode multi_socket(struct Curl_multi *multi, } } + now.tv_usec += 40000; /* compensate for bad precision timers that might've + triggered too early */ + if(now.tv_usec > 1000000) { + now.tv_sec++; + now.tv_usec -= 1000000; + } + /* * 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 { - struct timeval now; - /* the first loop lap 'data' can be NULL */ if(data) { do - result = multi_runsingle(multi, data->set.one_easy); + result = multi_runsingle(multi, now, data->set.one_easy); while (CURLM_CALL_MULTI_PERFORM == result); if(CURLM_OK >= result) @@ -2129,16 +2150,11 @@ static CURLMcode multi_socket(struct Curl_multi *multi, /* Check if there's one (more) expired timer to deal with! This function extracts a matching node if there is one */ - now = Curl_tvnow(); - now.tv_usec += 40000; /* compensate for bad precision timers */ - if(now.tv_usec > 1000000) { - now.tv_sec++; - now.tv_usec -= 1000000; - } - multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) + if(t) { + data = t->payload; /* assign this for next loop */ (void)add_next_timeout(now, multi, t->payload); + } } while(t); @@ -2273,12 +2289,22 @@ CURLMcode curl_multi_timeout(CURLM *multi_handle, static int update_timer(struct Curl_multi *multi) { long timeout_ms; + if(!multi->timer_cb) return 0; - if( multi_timeout(multi, &timeout_ms) != CURLM_OK ) + if(multi_timeout(multi, &timeout_ms)) { return -1; - if( timeout_ms < 0 ) + } + if( timeout_ms < 0 ) { + static const struct timeval none={0,0}; + if(Curl_splaycomparekeys(none, multi->timer_lastcall)) { + multi->timer_lastcall = none; + /* there's no timeout now but there was one previously, tell the app to + disable it */ + return multi->timer_cb((CURLM*)multi, -1, multi->timer_userp); + } return 0; + } /* When multi_timeout() is done, multi->timetree points to the node with the * timeout we got the (relative) time-out time for. We can thus easily check @@ -2564,10 +2590,6 @@ void Curl_expire(struct SessionHandle *data, long milli) } *nowp = set; -#if 0 - infof(data, "Expire at %ld / %ld (%ldms) %p\n", - (long)nowp->tv_sec, (long)nowp->tv_usec, milli, data); -#endif data->state.timenode.payload = data; multi->timetree = Curl_splayinsert(*nowp, multi->timetree, -- cgit v1.2.1 From 397e61128f8e384bdb24b71a29a7f7fe93b07262 Mon Sep 17 00:00:00 2001 From: Dirk Manske Date: Mon, 20 Sep 2010 13:58:51 +0200 Subject: multi_runsingle: set timeout error messages With the latest changes to fix the timeout handling with multi interface we lost the timeout error messages. This patch brings them back. --- lib/multi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 13915c120..e857392e0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -985,6 +985,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(timeout_ms < 0) { /* Handle timed out */ + if(easy->state == CURLM_STATE_WAITRESOLVE) + failf(data, "Resolving timed out after %ld milliseconds", + Curl_tvdiff(now, data->progress.t_startsingle)); + else if(easy->state == CURLM_STATE_WAITCONNECT) + failf(data, "Connection timed out after %ld milliseconds", + Curl_tvdiff(now, data->progress.t_startsingle)); + else { + k = &data->req; + failf(data, "Operation timed out after %ld milliseconds with %" + FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received", + Curl_tvdiff(now, data->progress.t_startsingle), k->bytecount, + k->size); + } easy->result = CURLE_OPERATION_TIMEDOUT; multistate(easy, CURLM_STATE_COMPLETED); break; -- cgit v1.2.1 From ed4eecc05e0cce36d8ce4ac9466376ca5fcfcba2 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 28 Sep 2010 23:17:34 +0200 Subject: multi: don't expire timeouts at disonnect or done The functions Curl_disconnect() and Curl_done() are both used within the scope of a single request so they cannot be allowed to use Curl_expire(... 0) to kill all timeouts as there are some timeouts that are set before a request that are supposed to remain until the request is done. The timeouts are now instead cleared at curl_easy_cleanup() and when the multi state machine changes a handle to the complete state. --- lib/multi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e857392e0..875e136ee 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1594,6 +1594,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Important: reset the conn pointer so that we don't point to memory that could be freed anytime */ easy->easy_conn = NULL; + + Curl_expire(data, 0); /* stop all timers */ break; case CURLM_STATE_MSGSENT: -- cgit v1.2.1 From 5087f89ac854a25c56d975a49827c3b607bdbaf9 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 25 Nov 2010 02:58:59 +0100 Subject: curl_multi_info_read: fix compiler warning: conversion may lose significant bits --- lib/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 875e136ee..9f51b7adb 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -41,6 +41,7 @@ #include "sendf.h" #include "timeval.h" #include "http.h" +#include "warnless.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -1836,7 +1837,7 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue) /* remove the extracted entry */ Curl_llist_remove(multi->msglist, e, NULL); - *msgs_in_queue = (int)Curl_llist_count(multi->msglist); + *msgs_in_queue = curlx_uztosi(Curl_llist_count(multi->msglist)); return &msg->extmsg; } -- cgit v1.2.1 From e39ab6f2039210c40a72f5219e7f211a95c9d007 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 26 Nov 2010 19:34:10 +0100 Subject: multi: fix compiler warning: enumerated type mixed with another type --- lib/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9f51b7adb..ee5e76ee9 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -982,7 +982,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, start time stored */ timeout_ms = Curl_timeleft(easy->easy_conn, &now, - easy->state <= CURLM_STATE_WAITDO); + (easy->state <= CURLM_STATE_WAITDO)? + TRUE:FALSE); if(timeout_ms < 0) { /* Handle timed out */ -- cgit v1.2.1 From 3590874999230e856fe6ab9114b3f4455d984721 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 26 Nov 2010 19:57:27 +0100 Subject: multi: fix compiler warning: conversion may lose significant bits --- lib/multi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ee5e76ee9..2be553bdb 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2568,15 +2568,12 @@ void Curl_expire(struct SessionHandle *data, long milli) } 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 */ + if(set.tv_usec > 1000000) { set.tv_sec++; set.tv_usec -= 1000000; } -- cgit v1.2.1 From cbe67a1b71f7098779b8c0cecccb60382cec2d20 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sun, 28 Nov 2010 20:49:40 +0100 Subject: multi: fix compiler warning: conversion may lose significant bits follow-up --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2be553bdb..b57724e62 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2141,7 +2141,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, now.tv_usec += 40000; /* compensate for bad precision timers that might've triggered too early */ - if(now.tv_usec > 1000000) { + if(now.tv_usec >= 1000000) { now.tv_sec++; now.tv_usec -= 1000000; } @@ -2573,7 +2573,7 @@ void Curl_expire(struct SessionHandle *data, long milli) set.tv_sec += milli/1000; set.tv_usec += (milli%1000)*1000; - if(set.tv_usec > 1000000) { + if(set.tv_usec >= 1000000) { set.tv_sec++; set.tv_usec -= 1000000; } -- cgit v1.2.1 From 5c7c9a768d009319520142fcaee1dea33625060f Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Fri, 19 Nov 2010 13:43:20 +0100 Subject: url: provide dead_connection flag in Curl_handler::disconnect It helps to prevent a hangup with some FTP servers in case idle session timeout has exceeded. But it may be useful also for other protocols that send any quit message on disconnect. Currently used by FTP, POP3, IMAP and SMTP. --- lib/multi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b57724e62..7be479bd4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1634,7 +1634,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } if(disconnect_conn) { - Curl_disconnect(easy->easy_conn); /* disconnect properly */ + /* disconnect properly */ + Curl_disconnect(easy->easy_conn, /* dead_connection */ FALSE); /* This is where we make sure that the easy_conn pointer is reset. We don't have to do this in every case block above where a @@ -1759,7 +1760,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) for(i=0; i< multi->connc->num; i++) { if(multi->connc->connects[i] && multi->connc->connects[i]->protocol & PROT_CLOSEACTION) { - Curl_disconnect(multi->connc->connects[i]); + Curl_disconnect(multi->connc->connects[i], /* dead_connection */ FALSE); multi->connc->connects[i] = NULL; } } @@ -2665,7 +2666,7 @@ static void multi_connc_remove_handle(struct Curl_multi *multi, data->state.shared_conn = multi; else { /* out of memory - so much for graceful shutdown */ - Curl_disconnect(conn); + Curl_disconnect(conn, /* dead_connection */ FALSE); multi->connc->connects[i] = NULL; } } -- cgit v1.2.1 From 0fd439ebaceed1ff049a4173e98da1ec2b8d0ed0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 13 Dec 2010 12:51:48 +0100 Subject: multi_runsingle: don't timeout completed handles The generic timeout code must not check easy handles that are already completed. Going to completed (again) within there risked decreasing the number of alive handles again and thus it could go negative. This regression bug was added in 7.21.2 in commit ca10e28f06f1 --- lib/multi.c | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 7be479bd4..ff4bf861b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -68,24 +68,25 @@ struct Curl_message { well! */ typedef enum { - CURLM_STATE_INIT, /* start in this state */ - CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ - CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */ - CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */ - CURLM_STATE_WAITPROXYCONNECT, /* awaiting proxy CONNECT to finalize */ - CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect phase */ - CURLM_STATE_WAITDO, /* wait for our turn to send the request */ - CURLM_STATE_DO, /* start send off the request (part 1) */ - CURLM_STATE_DOING, /* sending off the request (part 1) */ - CURLM_STATE_DO_MORE, /* send off the request (part 2) */ - CURLM_STATE_DO_DONE, /* done sending off request */ - CURLM_STATE_WAITPERFORM, /* wait for our turn to read the response */ - CURLM_STATE_PERFORM, /* transfer data */ - CURLM_STATE_TOOFAST, /* wait because limit-rate exceeded */ - CURLM_STATE_DONE, /* post data transfer operation */ - CURLM_STATE_COMPLETED, /* operation complete */ - CURLM_STATE_MSGSENT, /* the operation complete message is sent */ - CURLM_STATE_LAST /* not a true state, never use this */ + CURLM_STATE_INIT, /* 0 - start in this state */ + CURLM_STATE_CONNECT, /* 1 - resolve/connect has been sent off */ + CURLM_STATE_WAITRESOLVE, /* 2 - awaiting the resolve to finalize */ + CURLM_STATE_WAITCONNECT, /* 3 - awaiting the connect to finalize */ + CURLM_STATE_WAITPROXYCONNECT, /* 4 - awaiting proxy CONNECT to finalize */ + CURLM_STATE_PROTOCONNECT, /* 5 - completing the protocol-specific connect + phase */ + CURLM_STATE_WAITDO, /* 6 - wait for our turn to send the request */ + CURLM_STATE_DO, /* 7 - start send off the request (part 1) */ + CURLM_STATE_DOING, /* 8 - sending off the request (part 1) */ + CURLM_STATE_DO_MORE, /* 9 - send off the request (part 2) */ + CURLM_STATE_DO_DONE, /* 10 - done sending off request */ + CURLM_STATE_WAITPERFORM, /* 11 - wait for our turn to read the response */ + CURLM_STATE_PERFORM, /* 12 - transfer data */ + CURLM_STATE_TOOFAST, /* 13 - wait because limit-rate exceeded */ + CURLM_STATE_DONE, /* 14 - post data transfer operation */ + CURLM_STATE_COMPLETED, /* 15 - operation complete */ + CURLM_STATE_MSGSENT, /* 16 - the operation complete message is sent */ + CURLM_STATE_LAST /* 17 - not a true state, never use this */ } CURLMstate; /* we support N sockets per easy handle. Set the corresponding bit to what @@ -977,9 +978,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Make sure we set the connection's current owner */ easy->easy_conn->data = data; - if(easy->easy_conn && (easy->state >= CURLM_STATE_CONNECT)) { - /* we need to wait for the connect state as only then is the - start time stored */ + if(easy->easy_conn && + (easy->state >= CURLM_STATE_CONNECT) && + (easy->state < CURLM_STATE_COMPLETED)) { + /* we need to wait for the connect state as only then is the start time + stored, but we must not check already completed handles */ timeout_ms = Curl_timeleft(easy->easy_conn, &now, (easy->state <= CURLM_STATE_WAITDO)? -- cgit v1.2.1 From be16b227b7e2d24b49983f50379f9e93827fd48e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 22 Dec 2010 15:29:21 +0100 Subject: multi: inhibit some verbose outputs The info about pipe status and expire cleared are clearly debug-related and not anything mere mortals will or should care about so they are now ifdef'ed DEBUGBUILD --- lib/multi.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ff4bf861b..2edd12e62 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2363,8 +2363,10 @@ static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, if (pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) { /* this is a new one as head, expire it */ conn->writechannel_inuse = FALSE; /* not in use yet */ +#ifdef DEBUGBUILD infof(conn->data, "%p is at send pipe head!\n", conn->send_pipe->head->ptr); +#endif Curl_expire(conn->send_pipe->head->ptr, 1); } @@ -2398,8 +2400,10 @@ static int checkPendPipeline(struct connectdata *conn) if(sendhead != conn->send_pipe->head) { /* this is a new one as head, expire it */ conn->writechannel_inuse = FALSE; /* not in use yet */ +#ifdef DEBUGBUILD infof(conn->data, "%p is at send pipe head!\n", conn->send_pipe->head->ptr); +#endif Curl_expire(conn->send_pipe->head->ptr, 1); } } @@ -2428,8 +2432,10 @@ static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle, /* Since there's a new easy handle at the start of the send pipeline, set its timeout value to 1ms to make it trigger instantly */ conn->writechannel_inuse = FALSE; /* not used now */ +#ifdef DEBUGBUILD infof(conn->data, "%p is at send pipe head B!\n", conn->send_pipe->head->ptr); +#endif Curl_expire(conn->send_pipe->head->ptr, 1); } @@ -2565,7 +2571,9 @@ void Curl_expire(struct SessionHandle *data, long milli) while(list->size > 0) Curl_llist_remove(list, list->tail, NULL); +#ifdef DEBUGBUILD infof(data, "Expire cleared\n"); +#endif nowp->tv_sec = 0; nowp->tv_usec = 0; } -- cgit v1.2.1 From adb49ad8bb280b586b387ba930c0681afee03923 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 4 Jan 2011 23:07:58 +0100 Subject: Curl_timeleft: s/conn/data in first argument As the function doesn't really use the connectdata struct but only the SessionHanadle struct I modified what argument it wants. --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 2edd12e62..ecd72bb32 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -984,7 +984,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* we need to wait for the connect state as only then is the start time stored, but we must not check already completed handles */ - timeout_ms = Curl_timeleft(easy->easy_conn, &now, + timeout_ms = Curl_timeleft(data, &now, (easy->state <= CURLM_STATE_WAITDO)? TRUE:FALSE); -- cgit v1.2.1 From 73eb9965cf3c553e354def0d8b55b066eb1fc074 Mon Sep 17 00:00:00 2001 From: Nicholas Maniscalco Date: Wed, 2 Feb 2011 13:41:22 +0100 Subject: multi: fix CURLM_STATE_TOOFAST for multi_socket The code in the toofast state needs to first recalculate the values before it uses them again since it may have been a while since it last did it when it reaches this point. --- lib/multi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ecd72bb32..122f66b40 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1408,6 +1408,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ + Curl_pgrsUpdate(easy->easy_conn); if( ( (data->set.max_send_speed == 0) || (data->progress.ulspeed < data->set.max_send_speed )) && ( (data->set.max_recv_speed == 0) || -- cgit v1.2.1 From d85cae922575d8966529fee364e6d611e3062a99 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 17 Feb 2011 23:51:43 +0100 Subject: multi: better failed connect treatment When failing to connect the protocol during the CURLM_STATE_PROTOCONNECT state, Curl_done() has to be called with the premature flag set TRUE as for the pingpong protocols this can be important. When Curl_done() is called with premature == TRUE, it needs to call Curl_disconnect() with its 'dead_connection' argument set to TRUE as well so that any protocol handler's disconnect function won't attempt to use the (control) connection for anything. This problem caused the pingpong protocols to fail to disconnect when STARTTLS failed. Reported by: Alona Rossen Bug: http://curl.haxx.se/mail/lib-2011-02/0195.html --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 122f66b40..31127028d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1189,7 +1189,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else if(easy->result) { /* failure detected */ Curl_posttransfer(data); - Curl_done(&easy->easy_conn, easy->result, FALSE); + Curl_done(&easy->easy_conn, easy->result, TRUE); disconnect_conn = TRUE; } break; -- cgit v1.2.1 From c4369f34b9b493cbed4e7bcafa77614e9d55055d Mon Sep 17 00:00:00 2001 From: Mike Crowe Date: Fri, 18 Feb 2011 23:19:14 +0100 Subject: multi: close connection on timeout After a request times out, the connection wasn't properly closed and prevented to get re-used, so subsequent transfers could still mistakenly get to use the previously aborted connection. --- lib/multi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 31127028d..91d92df90 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1003,6 +1003,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_tvdiff(now, data->progress.t_startsingle), k->bytecount, k->size); } + + /* Force the connection closed because the server could continue to + send us stuff at any time. (The disconnect_conn logic used below + doesn't work at this point). */ + easy->easy_conn->bits.close = TRUE; easy->result = CURLE_OPERATION_TIMEDOUT; multistate(easy, CURLM_STATE_COMPLETED); break; -- cgit v1.2.1 From 3eac14b43c62717cc14733aba6827c0c3d38dc9a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 13 Mar 2011 23:21:03 +0100 Subject: SSH: add protocol lock direction Some protocols have to call the underlying functions without regard to what exact state the socket signals. For example even if the socket says "readable", the send function might need to be called while uploading, or vice versa. This is the case for libssh2 based protocols: SCP and SFTP and we now introduce a define to set those protocols and we make the multi interface code aware of this concept. This is another fix to make test 582 run properly. --- lib/multi.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 91d92df90..d68c368f3 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2105,35 +2105,41 @@ static CURLMcode multi_socket(struct Curl_multi *multi, and just move on. */ ; else { + struct connectdata *conn; data = entry->easy; if(data->magic != CURLEASY_MAGIC_NUMBER) /* bad bad bad bad bad bad bad */ return CURLM_INTERNAL_ERROR; + /* note that this can possibly be NULL at this point */ + conn = data->set.one_easy->easy_conn; + /* If the pipeline is enabled, take the handle which is in the head of the pipeline. If we should write into the socket, take the send_pipe head. If we should read from the socket, take the recv_pipe head. */ - if(data->set.one_easy->easy_conn) { + if(conn) { if ((ev_bitmask & CURL_POLL_OUT) && - data->set.one_easy->easy_conn->send_pipe && - data->set.one_easy->easy_conn->send_pipe->head) - data = data->set.one_easy->easy_conn->send_pipe->head->ptr; + conn->send_pipe && + conn->send_pipe->head) + data = conn->send_pipe->head->ptr; else if ((ev_bitmask & CURL_POLL_IN) && - data->set.one_easy->easy_conn->recv_pipe && - data->set.one_easy->easy_conn->recv_pipe->head) - data = data->set.one_easy->easy_conn->recv_pipe->head->ptr; + conn->recv_pipe && + conn->recv_pipe->head) + data = conn->recv_pipe->head->ptr; } - if(data->set.one_easy->easy_conn) /* set socket event bitmask */ - data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; + if(conn && !(conn->handler->protocol & PROT_LOCKEDBITS)) + /* set socket event bitmask if they're not locked */ + conn->cselect_bits = ev_bitmask; do result = multi_runsingle(multi, now, data->set.one_easy); while (CURLM_CALL_MULTI_PERFORM == result); - if(data->set.one_easy->easy_conn) - data->set.one_easy->easy_conn->cselect_bits = 0; + if(conn && !(conn->handler->protocol & PROT_LOCKEDBITS)) + /* clear the bitmask only if not locked */ + conn->cselect_bits = 0; if(CURLM_OK >= result) /* get the socket(s) and check if the state has been changed since -- cgit v1.2.1 From 8831000bc07de463d277975a3ddfb6a31dcf14b4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 14 Mar 2011 22:22:22 +0100 Subject: protocol handler: added flags field The protocol handler struct got a 'flags' field for special information and characteristics of the given protocol. This now enables us to move away central protocol information such as CLOSEACTION and DUALCHANNEL from single defines in a central place, out to each protocol's definition. It also made us stop abusing the protocol field for other info than the protocol, and we could start cleaning up other protocol-specific things by adding flags bits to set in the handler struct. The "protocol" field connectdata struct was removed as well and the code now refers directly to the conn->handler->protocol field instead. To make things work properly, the code now always store a conn->given pointer that points out the original handler struct so that the code can learn details from the original protocol even if conn->handler is modified along the way - for example when switching to go over a HTTP proxy. --- lib/multi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d68c368f3..8172d061d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1476,7 +1476,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * a protocol which uses two "channels" like FTP, as then the error * happened in the data connection. */ - if(!(easy->easy_conn->protocol & PROT_DUALCHANNEL)) + if(!(easy->easy_conn->handler->flags & PROTOPT_DUAL)) easy->easy_conn->bits.close = TRUE; Curl_posttransfer(data); @@ -1768,7 +1768,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) /* go over all connections that have close actions */ for(i=0; i< multi->connc->num; i++) { if(multi->connc->connects[i] && - multi->connc->connects[i]->protocol & PROT_CLOSEACTION) { + multi->connc->connects[i]->handler->flags & PROTOPT_CLOSEACTION) { Curl_disconnect(multi->connc->connects[i], /* dead_connection */ FALSE); multi->connc->connects[i] = NULL; } @@ -2129,7 +2129,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data = conn->recv_pipe->head->ptr; } - if(conn && !(conn->handler->protocol & PROT_LOCKEDBITS)) + if(conn && !(conn->handler->flags & PROTOPT_DIRLOCK)) /* set socket event bitmask if they're not locked */ conn->cselect_bits = ev_bitmask; @@ -2137,7 +2137,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, result = multi_runsingle(multi, now, data->set.one_easy); while (CURLM_CALL_MULTI_PERFORM == result); - if(conn && !(conn->handler->protocol & PROT_LOCKEDBITS)) + if(conn && !(conn->handler->flags & PROTOPT_DIRLOCK)) /* clear the bitmask only if not locked */ conn->cselect_bits = 0; @@ -2682,7 +2682,7 @@ static void multi_connc_remove_handle(struct Curl_multi *multi, for nice connection closures". */ - if(conn->protocol & PROT_CLOSEACTION) { + if(conn->handler->flags & PROTOPT_CLOSEACTION) { /* this handle is still being used by a shared connection and thus we leave it around for now */ if(add_closure(multi, data) == CURLM_OK) -- cgit v1.2.1 From c2459c4328e14b40b4274f74f8ee60bf7a231f84 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 2 Apr 2011 23:44:11 +0200 Subject: multi: conn goes bad when data change Within multi_socket when conn is used as a shorthand, data could be changed and multi_runsingle could modify the connectdata struct to deal with. This bug has not been included in a public release. Using 'conn' like that turned out to be ugly. This change is a partial revert of commit f1c6cd42f474df59. Reported by: Miroslav Spousta Bug: http://curl.haxx.se/bug/view.cgi?id=3265485 --- lib/multi.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 8172d061d..82453f848 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2105,41 +2105,37 @@ static CURLMcode multi_socket(struct Curl_multi *multi, and just move on. */ ; else { - struct connectdata *conn; data = entry->easy; if(data->magic != CURLEASY_MAGIC_NUMBER) /* bad bad bad bad bad bad bad */ return CURLM_INTERNAL_ERROR; - /* note that this can possibly be NULL at this point */ - conn = data->set.one_easy->easy_conn; - /* If the pipeline is enabled, take the handle which is in the head of the pipeline. If we should write into the socket, take the send_pipe head. If we should read from the socket, take the recv_pipe head. */ - if(conn) { + if(data->set.one_easy->easy_conn) { if ((ev_bitmask & CURL_POLL_OUT) && - conn->send_pipe && - conn->send_pipe->head) - data = conn->send_pipe->head->ptr; + data->set.one_easy->easy_conn->send_pipe && + data->set.one_easy->easy_conn->send_pipe->head) + data = data->set.one_easy->easy_conn->send_pipe->head->ptr; else if ((ev_bitmask & CURL_POLL_IN) && - conn->recv_pipe && - conn->recv_pipe->head) - data = conn->recv_pipe->head->ptr; + data->set.one_easy->easy_conn->recv_pipe && + data->set.one_easy->easy_conn->recv_pipe->head) + data = data->set.one_easy->easy_conn->recv_pipe->head->ptr; } - if(conn && !(conn->handler->flags & PROTOPT_DIRLOCK)) + if(data->set.one_easy->easy_conn && !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK)) /* set socket event bitmask if they're not locked */ - conn->cselect_bits = ev_bitmask; + data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; do result = multi_runsingle(multi, now, data->set.one_easy); while (CURLM_CALL_MULTI_PERFORM == result); - if(conn && !(conn->handler->flags & PROTOPT_DIRLOCK)) + if(data->set.one_easy->easy_conn && !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK)) /* clear the bitmask only if not locked */ - conn->cselect_bits = 0; + data->set.one_easy->easy_conn->cselect_bits = 0; if(CURLM_OK >= result) /* get the socket(s) and check if the state has been changed since -- cgit v1.2.1 From 318c5c802b5481e184def6f711991657be277cc3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 3 Apr 2011 00:07:32 +0200 Subject: multi: shorten lines We keep them less than 80 columns --- lib/multi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 82453f848..f1c045b44 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -872,7 +872,8 @@ static int multi_getsock(struct Curl_one_easy *easy, return domore_getsock(easy->easy_conn, socks, numsocks); case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch - to waiting for the same as the *PERFORM states */ + to waiting for the same as the *PERFORM + states */ case CURLM_STATE_PERFORM: case CURLM_STATE_WAITPERFORM: return Curl_single_getsock(easy->easy_conn, socks, numsocks); @@ -1769,7 +1770,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) for(i=0; i< multi->connc->num; i++) { if(multi->connc->connects[i] && multi->connc->connects[i]->handler->flags & PROTOPT_CLOSEACTION) { - Curl_disconnect(multi->connc->connects[i], /* dead_connection */ FALSE); + Curl_disconnect(multi->connc->connects[i], FALSE); multi->connc->connects[i] = NULL; } } @@ -2125,7 +2126,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data = data->set.one_easy->easy_conn->recv_pipe->head->ptr; } - if(data->set.one_easy->easy_conn && !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK)) + if(data->set.one_easy->easy_conn && + !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK)) /* set socket event bitmask if they're not locked */ data->set.one_easy->easy_conn->cselect_bits = ev_bitmask; @@ -2133,7 +2135,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, result = multi_runsingle(multi, now, data->set.one_easy); while (CURLM_CALL_MULTI_PERFORM == result); - if(data->set.one_easy->easy_conn && !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK)) + if(data->set.one_easy->easy_conn && + !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK)) /* clear the bitmask only if not locked */ data->set.one_easy->easy_conn->cselect_bits = 0; -- cgit v1.2.1 From 1702a2c08d3a0ed5945f34e6cd38436611f65164 Mon Sep 17 00:00:00 2001 From: Fabian Keil Date: Tue, 19 Apr 2011 15:54:13 +0200 Subject: Fix a couple of spelling errors in lib/ Found with codespell. --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f1c045b44..40a341d29 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -645,7 +645,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, easy->state > CURLM_STATE_WAITDO && easy->state < CURLM_STATE_COMPLETED) { /* If the handle is in a pipeline and has started sending off its - request but not received its reponse yet, we need to close + request but not received its response yet, we need to close connection. */ easy->easy_conn->bits.close = TRUE; /* Set connection owner so that Curl_done() closes it. @@ -1495,7 +1495,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* call this even if the readwrite function returned error */ Curl_posttransfer(data); - /* we're no longer receving */ + /* we're no longer receiving */ moveHandleFromRecvToDonePipeline(data, easy->easy_conn); -- cgit v1.2.1 From 7de2f9271c68c10ee7057c10741b0406bca6c156 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 31 Jan 2011 00:10:35 +0100 Subject: async resolvers: further cleanups asyn-ares.c and asyn-thread.c are two separate backends that implement the same (internal) async resolver API for libcurl to use. Backend is specified at build time. The internal resolver API is defined in asyn.h for asynch resolvers. --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 40a341d29..650be6836 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -855,7 +855,7 @@ static int multi_getsock(struct Curl_one_easy *easy, return 0; case CURLM_STATE_WAITRESOLVE: - return Curl_resolv_getsock(easy->easy_conn, socks, numsocks); + return Curl_resolver_getsock(easy->easy_conn, socks, numsocks); case CURLM_STATE_PROTOCONNECT: return Curl_protocol_getsock(easy->easy_conn, socks, numsocks); @@ -1071,7 +1071,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, struct Curl_dns_entry *dns = NULL; /* check if we have the name resolved by now */ - easy->result = Curl_is_resolved(easy->easy_conn, &dns); + easy->result = Curl_resolver_is_resolved(easy->easy_conn, &dns); if(dns) { /* Update sockets here. Mainly because the socket(s) may have been -- cgit v1.2.1 From b903186fa0189ff241d756d25d07fdfe9885ae49 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 20 Apr 2011 15:17:42 +0200 Subject: source cleanup: unify look, style and indent levels By the use of a the new lib/checksrc.pl script that checks that our basic source style rules are followed. --- lib/multi.c | 69 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 34 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 650be6836..89e6ed74c 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -674,7 +674,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* we must call Curl_done() here (if we still "own it") so that we don't leave a half-baked one around */ - if (easy_owns_conn) { + if(easy_owns_conn) { /* Curl_done() clears the conn->data field to lose the association between the easy handle and the connection @@ -1128,7 +1128,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_CALL_MULTI_PERFORM; multistate(easy, CURLM_STATE_CONNECT); } - else if (CURLE_OK == easy->result) { + else if(CURLE_OK == easy->result) { if(!easy->easy_conn->bits.tunnel_connecting) multistate(easy, CURLM_STATE_WAITCONNECT); } @@ -1265,8 +1265,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = CURLM_CALL_MULTI_PERFORM; } } - else if ((CURLE_SEND_ERROR == easy->result) && - easy->easy_conn->bits.reuse) { + else if((CURLE_SEND_ERROR == easy->result) && + easy->easy_conn->bits.reuse) { /* * In this situation, a connection that we were trying to use * may have unexpectedly died. If possible, send the connection @@ -1292,7 +1292,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* When set to retry the connection, we must to go back to * the CONNECT state */ if(retry) { - if ((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) { + if((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) { follow = FOLLOW_RETRY; drc = Curl_follow(data, newurl, follow); if(drc == CURLE_OK) { @@ -1535,11 +1535,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* but first check to see if we got a location info even though we're not following redirects */ - if (data->req.location) { + if(data->req.location) { newurl = data->req.location; data->req.location = NULL; easy->result = Curl_follow(data, newurl, FOLLOW_FAKE); - if (easy->result) + if(easy->result) free(newurl); } @@ -1577,7 +1577,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * access free'd data, if the connection is free'd and the handle * removed before we perform the processing in CURLM_STATE_COMPLETED */ - if (easy->easy_conn) + if(easy->easy_conn) easy->easy_conn = NULL; } @@ -1710,7 +1710,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) do result = multi_runsingle(multi, now, easy); - while (CURLM_CALL_MULTI_PERFORM == result); + while(CURLM_CALL_MULTI_PERFORM == result); if(easy->easy_handle->set.wildcardmatch) { /* destruct wildcard structures if it is needed */ @@ -1954,29 +1954,29 @@ static void singlesocket(struct Curl_multi *multi, easy_by_hash = entry->easy->multi_pos; easy_conn = easy_by_hash->easy_conn; if(easy_conn) { - if (easy_conn->recv_pipe && easy_conn->recv_pipe->size > 1) { + if(easy_conn->recv_pipe && easy_conn->recv_pipe->size > 1) { /* the handle should not be removed from the pipe yet */ remove_sock_from_hash = FALSE; /* Update the sockhash entry to instead point to the next in line for the recv_pipe, or the first (in case this particular easy isn't already) */ - if (entry->easy == easy->easy_handle) { - if (isHandleAtHead(easy->easy_handle, easy_conn->recv_pipe)) + if(entry->easy == easy->easy_handle) { + if(isHandleAtHead(easy->easy_handle, easy_conn->recv_pipe)) entry->easy = easy_conn->recv_pipe->head->next->ptr; else entry->easy = easy_conn->recv_pipe->head->ptr; } } - if (easy_conn->send_pipe && easy_conn->send_pipe->size > 1) { + if(easy_conn->send_pipe && easy_conn->send_pipe->size > 1) { /* the handle should not be removed from the pipe yet */ remove_sock_from_hash = FALSE; /* Update the sockhash entry to instead point to the next in line for the send_pipe, or the first (in case this particular easy isn't already) */ - if (entry->easy == easy->easy_handle) { - if (isHandleAtHead(easy->easy_handle, easy_conn->send_pipe)) + if(entry->easy == easy->easy_handle) { + if(isHandleAtHead(easy->easy_handle, easy_conn->send_pipe)) entry->easy = easy_conn->send_pipe->head->next->ptr; else entry->easy = easy_conn->send_pipe->head->ptr; @@ -1994,7 +1994,7 @@ static void singlesocket(struct Curl_multi *multi, either since it never got to know about it */ remove_sock_from_hash = FALSE; - if (remove_sock_from_hash) { + if(remove_sock_from_hash) { multi->socket_cb(easy->easy_handle, s, CURL_POLL_REMOVE, @@ -2116,13 +2116,13 @@ static CURLMcode multi_socket(struct Curl_multi *multi, the pipeline. If we should write into the socket, take the send_pipe head. If we should read from the socket, take the recv_pipe head. */ if(data->set.one_easy->easy_conn) { - if ((ev_bitmask & CURL_POLL_OUT) && - data->set.one_easy->easy_conn->send_pipe && - data->set.one_easy->easy_conn->send_pipe->head) + if((ev_bitmask & CURL_POLL_OUT) && + data->set.one_easy->easy_conn->send_pipe && + data->set.one_easy->easy_conn->send_pipe->head) data = data->set.one_easy->easy_conn->send_pipe->head->ptr; - else if ((ev_bitmask & CURL_POLL_IN) && - data->set.one_easy->easy_conn->recv_pipe && - data->set.one_easy->easy_conn->recv_pipe->head) + else if((ev_bitmask & CURL_POLL_IN) && + data->set.one_easy->easy_conn->recv_pipe && + data->set.one_easy->easy_conn->recv_pipe->head) data = data->set.one_easy->easy_conn->recv_pipe->head->ptr; } @@ -2133,7 +2133,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, do result = multi_runsingle(multi, now, data->set.one_easy); - while (CURLM_CALL_MULTI_PERFORM == result); + while(CURLM_CALL_MULTI_PERFORM == result); if(data->set.one_easy->easy_conn && !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK)) @@ -2171,7 +2171,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, if(data) { do result = multi_runsingle(multi, now, data->set.one_easy); - while (CURLM_CALL_MULTI_PERFORM == result); + while(CURLM_CALL_MULTI_PERFORM == result); if(CURLM_OK >= result) /* get the socket(s) and check if the state has been changed since @@ -2371,14 +2371,14 @@ static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, rc = Curl_addHandleToPipeline(handle, pipeline); - if (pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) { - /* this is a new one as head, expire it */ - conn->writechannel_inuse = FALSE; /* not in use yet */ + if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) { + /* this is a new one as head, expire it */ + conn->writechannel_inuse = FALSE; /* not in use yet */ #ifdef DEBUGBUILD - infof(conn->data, "%p is at send pipe head!\n", - conn->send_pipe->head->ptr); + infof(conn->data, "%p is at send pipe head!\n", + conn->send_pipe->head->ptr); #endif - Curl_expire(conn->send_pipe->head->ptr, 1); + Curl_expire(conn->send_pipe->head->ptr, 1); } return rc; @@ -2390,7 +2390,7 @@ static int checkPendPipeline(struct connectdata *conn) struct curl_llist_element *sendhead = conn->send_pipe->head; size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; - if (conn->server_supports_pipelining || pipeLen == 0) { + if(conn->server_supports_pipelining || pipeLen == 0) { struct curl_llist_element *curr = conn->pend_pipe->head; const size_t maxPipeLen = conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1; @@ -2405,7 +2405,7 @@ static int checkPendPipeline(struct connectdata *conn) } } - if (result) { + if(result) { conn->now = Curl_tvnow(); /* something moved, check for a new send pipeline leader */ if(sendhead != conn->send_pipe->head) { @@ -2746,7 +2746,8 @@ static CURLMcode add_closure(struct Curl_multi *multi, else multi->closure = n; free(cl); - } else { + } + else { if(cl->easy_handle == data) add = FALSE; @@ -2756,7 +2757,7 @@ static CURLMcode add_closure(struct Curl_multi *multi, cl = n; } - if (add) { + if(add) { cl = calloc(1, sizeof(struct closure)); if(!cl) return CURLM_OUT_OF_MEMORY; -- cgit v1.2.1 From 889d1e973fb718a77c5000141d724ce03863af23 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 22 Apr 2011 23:01:30 +0200 Subject: whitespace cleanup: no space first in conditionals "if(a)" is our style, not "if( a )" --- lib/multi.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 89e6ed74c..9b707abc4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1415,17 +1415,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ Curl_pgrsUpdate(easy->easy_conn); - if( ( (data->set.max_send_speed == 0) || - (data->progress.ulspeed < data->set.max_send_speed )) && - ( (data->set.max_recv_speed == 0) || - (data->progress.dlspeed < data->set.max_recv_speed) ) ) + if(( (data->set.max_send_speed == 0) || + (data->progress.ulspeed < data->set.max_send_speed )) && + ( (data->set.max_recv_speed == 0) || + (data->progress.dlspeed < data->set.max_recv_speed))) multistate(easy, CURLM_STATE_PERFORM); break; case CURLM_STATE_PERFORM: /* check if over send speed */ - if( (data->set.max_send_speed > 0) && - (data->progress.ulspeed > data->set.max_send_speed) ) { + if((data->set.max_send_speed > 0) && + (data->progress.ulspeed > data->set.max_send_speed)) { int buffersize; multistate(easy, CURLM_STATE_TOOFAST); @@ -1440,8 +1440,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } /* check if over recv speed */ - if( (data->set.max_recv_speed > 0) && - (data->progress.dlspeed > data->set.max_recv_speed) ) { + if((data->set.max_recv_speed > 0) && + (data->progress.dlspeed > data->set.max_recv_speed)) { int buffersize; multistate(easy, CURLM_STATE_TOOFAST); @@ -1744,7 +1744,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) *running_handles = multi->num_alive; - if( CURLM_OK >= returncode ) + if(CURLM_OK >= returncode) update_timer(multi); return returncode; @@ -2327,7 +2327,7 @@ static int update_timer(struct Curl_multi *multi) if(multi_timeout(multi, &timeout_ms)) { return -1; } - if( timeout_ms < 0 ) { + if(timeout_ms < 0) { static const struct timeval none={0,0}; if(Curl_splaycomparekeys(none, multi->timer_lastcall)) { multi->timer_lastcall = none; -- cgit v1.2.1 From 4a42e5cdaa344755c6bf5317908849619f61798b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 28 Apr 2011 09:39:33 +0200 Subject: multi-socks: fix connect to proxy When connecting to a socks or similar proxy we do the proxy handshake at once when we know the TCP connect is completed and we only consider the "connection" complete after the proxy handshake. This fixes test 564 which is now no longer considered disabled. Reported by: Dmitri Shubin Bug: http://curl.haxx.se/mail/lib-2011-04/0127.html --- lib/multi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9b707abc4..aee190cea 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1141,8 +1141,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, FIRSTSOCKET, &connected); if(connected) { - /* see if we need to do any proxy magic first once we connected */ - easy->result = Curl_connected_proxy(easy->easy_conn); if(!easy->result) /* if everything is still fine we do the protocol-specific connect -- cgit v1.2.1 From 9194e1700312f27c6e2abdfb1f604e130497e887 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 2 Sep 2011 19:40:53 +0200 Subject: MemoryTracking: fix logging of free() calls done where Curl_safefree is called Just internal stuff... Curl_safefree is now a macro defined in memdebug.h instead of a function prototyped in url.h and implemented in url.c, so inclusion of url.h is no longer required in order to simply use Curl_safefree. Provide definition of macro WHILE_FALSE in setup_once.h in order to allow other macros such as DEBUGF and DEBUGASSERT, and code using it, to compile without 'conditional expression is constant' warnings. The WHILE_FALSE stuff fixes 150+ MSVC compiler warnings. --- lib/multi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index aee190cea..d100ef0a0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -952,8 +952,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data = easy->easy_handle; do { - /* this is a do-while loop just to allow a break to skip to the end - of it */ + /* this is a single-iteration do-while loop just to allow a + break to skip to the end of it */ bool disconnect_conn = FALSE; /* Handle the case when the pipe breaks, i.e., the connection @@ -1657,7 +1657,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) easy->result = CURLE_ABORTED_BY_CALLBACK; } - } while(0); + } WHILE_FALSE; /* just to break out from! */ if(CURLM_STATE_COMPLETED == easy->state) { if(data->dns.hostcachetype == HCACHE_MULTI) { -- cgit v1.2.1 From a50210710ab6fd772e2762ed36602c15adfb49e1 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Mon, 5 Sep 2011 20:46:09 +0200 Subject: fix bool variables checking and assignment --- lib/multi.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d100ef0a0..5a1cef40d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -628,9 +628,10 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, easy = data->multi_pos; if(easy) { - bool premature = (bool)(easy->state < CURLM_STATE_COMPLETED); - bool easy_owns_conn = (bool)(easy->easy_conn && - (easy->easy_conn->data == easy->easy_handle)); + bool premature = (easy->state < CURLM_STATE_COMPLETED) ? TRUE : FALSE; + bool easy_owns_conn = (easy->easy_conn && + (easy->easy_conn->data == easy->easy_handle)) ? + TRUE : FALSE; /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ @@ -1282,7 +1283,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, disconnect_conn = TRUE; } else - retry = (bool)(newurl?TRUE:FALSE); + retry = (newurl)?TRUE:FALSE; Curl_posttransfer(data); drc = Curl_done(&easy->easy_conn, easy->result, FALSE); @@ -1481,14 +1482,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_posttransfer(data); Curl_done(&easy->easy_conn, easy->result, FALSE); } - else if(TRUE == done) { + else if(done) { char *newurl = NULL; bool retry = FALSE; followtype follow=FOLLOW_NONE; easy->result = Curl_retry_request(easy->easy_conn, &newurl); if(!easy->result) - retry = (bool)(newurl?TRUE:FALSE); + retry = (newurl)?TRUE:FALSE; /* call this even if the readwrite function returned error */ Curl_posttransfer(data); @@ -2213,7 +2214,7 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, multi->socket_userp = va_arg(param, void *); break; case CURLMOPT_PIPELINING: - multi->pipelining_enabled = (bool)(0 != va_arg(param, long)); + multi->pipelining_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE; break; case CURLMOPT_TIMERFUNCTION: multi->timer_cb = va_arg(param, curl_multi_timer_callback); @@ -2478,7 +2479,7 @@ static bool isHandleAtHead(struct SessionHandle *handle, { struct curl_llist_element *curr = pipeline->head; if(curr) - return (bool)(curr->ptr == handle); + return (curr->ptr == handle) ? TRUE : FALSE; return FALSE; } -- cgit v1.2.1 From 2d6796aac51d8a5963f552b8fa23a2396a987913 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 25 Sep 2011 17:34:12 +0200 Subject: curl_multi_fdset: avoid FD_SET out of bounds If a socket is larger than FD_SETSIZE, avoid using FD_SET() on the platforms where this is possible. Bug: http://curl.haxx.se/bug/view.cgi?id=3413274 Reported by: Tim Starling --- lib/multi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 5a1cef40d..13f5619cc 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -42,6 +42,7 @@ #include "timeval.h" #include "http.h" #include "warnless.h" +#include "select.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -907,11 +908,11 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { curl_socket_t s = CURL_SOCKET_BAD; - if(bitmap & GETSOCK_READSOCK(i)) { + if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { FD_SET(sockbunch[i], read_fd_set); s = sockbunch[i]; } - if(bitmap & GETSOCK_WRITESOCK(i)) { + if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { FD_SET(sockbunch[i], write_fd_set); s = sockbunch[i]; } -- cgit v1.2.1 From d2a47021c0a65548c06cf7273b30794f22042ecc Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sun, 25 Sep 2011 19:08:12 +0200 Subject: Q&D fix header inclusion order --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 13f5619cc..9cc6944c9 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -41,8 +41,8 @@ #include "sendf.h" #include "timeval.h" #include "http.h" -#include "warnless.h" #include "select.h" +#include "warnless.h" #define _MPRINTF_REPLACE /* use our functions only */ #include -- cgit v1.2.1 From 3d19e1eedf73f48e03c68b8ff7e8a7ad178345ad Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 30 Sep 2011 22:59:50 +0200 Subject: multi_runsingle: change state on callback abort Reported by: Marcin Adamski Bug: http://curl.haxx.se/mail/lib-2011-09/0329.html --- lib/multi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 9cc6944c9..d2c94590e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1656,8 +1656,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_COMPLETED); } /* if there's still a connection to use, call the progress function */ - else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) + else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) { easy->result = CURLE_ABORTED_BY_CALLBACK; + multistate(easy, CURLM_STATE_COMPLETED); + } } } WHILE_FALSE; /* just to break out from! */ -- cgit v1.2.1 From 9dd85bced56f6951107f69e581c872c1e7e3e58e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 2 Oct 2011 19:28:39 +0200 Subject: multi: progress function abort must close connection When the progress function returns to cancel the request, we must mark the connection to get closed and it must do to the DONE state. do_init() must be called as early as possible so that state variables for new connections are reset early. We could otherwise see that the old values were still there when a connection was to be disconnected very early and it would make it behave wrongly. Bug: http://curl.haxx.se/mail/lib-2011-10/0006.html Reported by: Vladimir Grishchenko --- lib/multi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d2c94590e..bf1f46764 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1657,8 +1657,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } /* if there's still a connection to use, call the progress function */ else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) { - easy->result = CURLE_ABORTED_BY_CALLBACK; - multistate(easy, CURLM_STATE_COMPLETED); + /* aborted due to progress callback return code must close the + connection */ + easy->easy_conn->bits.close = TRUE; + + /* if not yet in DONE state, go there, otherwise COMPLETED */ + multistate(easy, (easy->state < CURLM_STATE_DONE)? + CURLM_STATE_DONE: CURLM_STATE_COMPLETED); + result = CURLM_CALL_MULTI_PERFORM; } } } WHILE_FALSE; /* just to break out from! */ -- cgit v1.2.1 From b82bd05354cfa756a013d2bed4ffdc951ce903db Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 6 Oct 2011 20:30:34 +0200 Subject: multi.c: OOM handling fixes making torture tests 560 580 581 pass --- lib/multi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index bf1f46764..0f7a24993 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1041,7 +1041,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Add this handle to the send or pend pipeline */ easy->result = addHandleToSendOrPendPipeline(data, easy->easy_conn); - if(CURLE_OK == easy->result) { + if(CURLE_OK != easy->result) + disconnect_conn = TRUE; + else { if(async) /* We're now waiting for an asynchronous name lookup */ multistate(easy, CURLM_STATE_WAITRESOLVE); @@ -1539,8 +1541,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, newurl = data->req.location; data->req.location = NULL; easy->result = Curl_follow(data, newurl, FOLLOW_FAKE); - if(easy->result) + if(easy->result) { + disconnect_conn = TRUE; free(newurl); + } } multistate(easy, CURLM_STATE_DONE); -- cgit v1.2.1 From 17f48fe87979f159e2d8769d678641c60f4c0eed Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 7 Oct 2011 20:50:57 +0200 Subject: libcurl: some OOM handling fixes --- lib/multi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 0f7a24993..7786ccced 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1804,6 +1804,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) /* remove the pending list of messages */ Curl_llist_destroy(multi->msglist, NULL); + multi->msglist = NULL; /* remove all easy handles */ easy = multi->easy.next; -- cgit v1.2.1 From 584dc8b8af862f7f47a3a9f02f874ac0bd0076be Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 11 Oct 2011 19:41:30 +0200 Subject: OOM handling/cleanup slight adjustments --- lib/multi.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 7786ccced..8a8779c23 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -425,12 +425,13 @@ CURLM *curl_multi_init(void) return (CURLM *) multi; error: - if(multi->sockhash) - Curl_hash_destroy(multi->sockhash); - if(multi->hostcache) - Curl_hash_destroy(multi->hostcache); - if(multi->connc) - Curl_rm_connc(multi->connc); + + Curl_hash_destroy(multi->sockhash); + multi->sockhash = NULL; + Curl_hash_destroy(multi->hostcache); + multi->hostcache = NULL; + Curl_rm_connc(multi->connc); + multi->connc = NULL; free(multi); return NULL; @@ -1801,6 +1802,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) } Curl_rm_connc(multi->connc); + multi->connc = NULL; /* remove the pending list of messages */ Curl_llist_destroy(multi->msglist, NULL); -- cgit v1.2.1 From 34770b8ab0f2881b7437ef73ca757957335997a0 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 13 Oct 2011 18:04:56 +0200 Subject: multi.c: OOM handling fixes Prevent modification of easy handle being added with curl_multi_add_handle() unless this function actually suceeds. Run Curl_posttransfer() to allow restoring of SIGPIPE handler when Curl_connect() fails early in multi_runsingle(). --- lib/multi.c | 138 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 77 insertions(+), 61 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 8a8779c23..5e4ce5080 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -135,7 +135,7 @@ struct Curl_multi { this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ long type; - /* We have a linked list with easy handles */ + /* We have a doubly-linked circular list with easy handles */ struct Curl_one_easy easy; int num_easy; /* amount of entries in the linked list above. */ @@ -440,11 +440,12 @@ CURLM *curl_multi_init(void) CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *easy_handle) { - struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct curl_llist *timeoutlist; struct Curl_one_easy *easy; struct closure *cl; - struct closure *prev=NULL; - struct SessionHandle *data = easy_handle; + struct closure *prev = NULL; + struct Curl_multi *multi = (struct Curl_multi *)multi_handle; + struct SessionHandle *data = (struct SessionHandle *)easy_handle; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -454,39 +455,74 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, if(!GOOD_EASY_HANDLE(easy_handle)) return CURLM_BAD_EASY_HANDLE; - /* Prevent users to add the same handle more than once! */ - if(((struct SessionHandle *)easy_handle)->multi) + /* Prevent users from adding same easy handle more than + once and prevent adding to more than one multi stack */ + if(data->multi) /* possibly we should create a new unique error code for this condition */ return CURLM_BAD_EASY_HANDLE; - data->state.timeoutlist = Curl_llist_alloc(multi_freetimeout); - if(!data->state.timeoutlist) + /* We want the connection cache to have plenty of room. Before we supported + the shared cache every single easy handle had 5 entries in their cache + by default. */ + if(((multi->num_easy + 1) * 4) > multi->connc->num) { + long newmax = (multi->num_easy + 1) * 4; + + if(multi->maxconnects && (newmax > multi->maxconnects)) + /* don't grow beyond the allowed size */ + newmax = multi->maxconnects; + + if(newmax > multi->connc->num) { + /* we only do this is we can in fact grow the cache */ + CURLcode res = Curl_ch_connc(data, multi->connc, newmax); + if(res) + return CURLM_OUT_OF_MEMORY; + } + } + + /* Allocate and initialize timeout list for easy handle */ + timeoutlist = Curl_llist_alloc(multi_freetimeout); + if(!timeoutlist) return CURLM_OUT_OF_MEMORY; - /* Now, time to add an easy handle to the multi stack */ + /* Allocate new node for the doubly-linked circular list of + Curl_one_easy structs that holds pointers to easy handles */ easy = calloc(1, sizeof(struct Curl_one_easy)); - if(!easy) + if(!easy) { + Curl_llist_destroy(timeoutlist, NULL); return CURLM_OUT_OF_MEMORY; + } + /* + ** No failure allowed in this function beyond this point. And + ** no modification of easy nor multi handle allowed before this + ** except for potential multi's connection cache growing which + ** won't be undone in this function no matter what. + */ + + /* Make easy handle use timeout list initialized above */ + data->state.timeoutlist = timeoutlist; + timeoutlist = NULL; + + /* Remove handle from the list of 'closure handles' in case it is there */ cl = multi->closure; while(cl) { struct closure *next = cl->next; - if(cl->easy_handle == (struct SessionHandle *)easy_handle) { - /* remove this handle from the closure list */ + if(cl->easy_handle == data) { + /* Remove node from list */ free(cl); if(prev) prev->next = next; else multi->closure = next; - break; /* no need to continue since this handle can only be present once - in the list */ + /* No need to continue, handle can only be present once in the list */ + break; } prev = cl; cl = next; } /* set the easy handle */ - easy->easy_handle = easy_handle; + easy->easy_handle = data; multistate(easy, CURLM_STATE_INIT); /* set the back pointer to one_easy to assist in removal */ @@ -507,25 +543,21 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, easy->easy_handle->dns.hostcachetype = HCACHE_MULTI; } - if(easy->easy_handle->state.connc) { - if(easy->easy_handle->state.connc->type == CONNCACHE_PRIVATE) { - /* kill old private version */ - Curl_rm_connc(easy->easy_handle->state.connc); - /* point out our shared one instead */ - easy->easy_handle->state.connc = multi->connc; - } - /* else it is already using multi? */ + /* On a multi stack the connection cache, owned by the multi handle, + is shared between all easy handles within the multi handle. */ + if(easy->easy_handle->state.connc && + (easy->easy_handle->state.connc->type == CONNCACHE_PRIVATE)) { + /* kill old private connection cache */ + Curl_rm_connc(easy->easy_handle->state.connc); + easy->easy_handle->state.connc = NULL; } - else - /* point out our shared one */ - easy->easy_handle->state.connc = multi->connc; - - /* Make sure the type is setup correctly */ + /* Point now to this multi's connection cache */ + easy->easy_handle->state.connc = multi->connc; easy->easy_handle->state.connc->type = CONNCACHE_MULTI; - /* This adds the new entry at the back of the list - to try and maintain a FIFO queue so the pipelined - requests are in order. */ + /* This adds the new entry at the 'end' of the doubly-linked circular + list of Curl_one_easy structs to try and maintain a FIFO queue so + the pipelined requests are in order. */ /* We add this new entry last in the list. We make our 'next' point to the 'first' struct and our 'prev' point to the previous 'prev' */ @@ -539,6 +571,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, the new node */ easy->prev->next = easy; + /* make the SessionHandle refer back to this multi handle */ Curl_easy_addmulti(easy_handle, multi_handle); /* make the SessionHandle struct refer back to this struct */ @@ -555,27 +588,6 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* increase the node-counter */ multi->num_easy++; - if((multi->num_easy * 4) > multi->connc->num) { - /* We want the connection cache to have plenty room. Before we supported - the shared cache every single easy handle had 5 entries in their cache - by default. */ - long newmax = multi->num_easy * 4; - - if(multi->maxconnects && (multi->maxconnects < newmax)) - /* don't grow beyond the allowed size */ - newmax = multi->maxconnects; - - if(newmax > multi->connc->num) { - /* we only do this is we can in fact grow the cache */ - CURLcode res = Curl_ch_connc(easy_handle, multi->connc, newmax); - if(res != CURLE_OK) { - /* FIXME: may need to do more cleanup here */ - curl_multi_remove_handle(multi_handle, easy_handle); - return CURLM_OUT_OF_MEMORY; - } - } - } - /* increase the alive-counter */ multi->num_alive++; @@ -1622,7 +1634,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, return CURLM_INTERNAL_ERROR; } - if(CURLM_STATE_COMPLETED > easy->state) { + if(easy->state < CURLM_STATE_COMPLETED) { if(CURLE_OK != easy->result) { /* * If an error was returned, and we aren't in completed state now, @@ -1646,16 +1658,20 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->done_pipe); /* Check if we can move pending requests to send pipe */ checkPendPipeline(easy->easy_conn); - } - if(disconnect_conn) { - /* disconnect properly */ - Curl_disconnect(easy->easy_conn, /* dead_connection */ FALSE); + if(disconnect_conn) { + /* disconnect properly */ + Curl_disconnect(easy->easy_conn, /* dead_connection */ FALSE); - /* This is where we make sure that the easy_conn pointer is reset. - We don't have to do this in every case block above where a - failure is detected */ - easy->easy_conn = NULL; + /* This is where we make sure that the easy_conn pointer is reset. + We don't have to do this in every case block above where a + failure is detected */ + easy->easy_conn = NULL; + } + } + else if(easy->state == CURLM_STATE_CONNECT) { + /* Curl_connect() failed */ + (void)Curl_posttransfer(data); } multistate(easy, CURLM_STATE_COMPLETED); -- cgit v1.2.1 From a4758c3276e55f160b3a02489b504972fcee0f10 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 21 Oct 2011 16:37:13 +0200 Subject: multi.c: fix segfault --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 5e4ce5080..e53d387f0 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -125,9 +125,9 @@ struct Curl_one_easy { #define CURL_MULTI_HANDLE 0x000bab1e #define GOOD_MULTI_HANDLE(x) \ - ((x)&&(((struct Curl_multi *)(x))->type == CURL_MULTI_HANDLE)) + ((x) && (((struct Curl_multi *)(x))->type == CURL_MULTI_HANDLE)) #define GOOD_EASY_HANDLE(x) \ - (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER) + ((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER)) /* This is the struct known as CURLM on the outside */ struct Curl_multi { -- cgit v1.2.1 From d7934b8bd49114cbb54f7401742f7fb088e2f796 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 21 Oct 2011 23:36:54 +0200 Subject: curl_multi_fdset: correct fdset with FTP PORT use After a PORT has been issued, and the multi handle would switch to the CURLM_STATE_DO_MORE state (which is unique for FTP), libcurl would return the wrong fdset to wait for when curl_multi_fdset() is called. The code would blindly assume that it was waiting for a connect of the second connection, while that isn't true immediately after the PORT command. Also, the function multi.c:domore_getsock() was highly FTP-centric and therefore ugly to keep in protocol-agnostic code. I solved this problem by introducing a new function pointer in the Curl_handler struct called domore_getsock() which is only called during the DOMORE state for protocols that set that pointer. The new ftp.c:ftp_domore_getsock() function now returns fdset info about the control connection's command/response handling while such a state is in use, and goes over to waiting for a writable second connection first once the commands are done. The original problem could be seen by running test 525 and checking the time stamps in the FTP server log. I can verify that this fix at least fixes this problem. Bug: http://curl.haxx.se/mail/lib-2011-10/0250.html Reported by: Gokhan Sengun --- lib/multi.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e53d387f0..6e4ec37a8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -816,20 +816,12 @@ static int waitconnect_getsock(struct connectdata *conn, } static int domore_getsock(struct connectdata *conn, - curl_socket_t *sock, + curl_socket_t *socks, 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); + if(conn && conn->handler->domore_getsock) + return conn->handler->domore_getsock(conn, socks, numsocks); + return GETSOCK_BLANK; } /* returns bitmapped flags for this handle and its sockets */ -- cgit v1.2.1 From b0d42da26bd66baf326781149bdbdd7336d51269 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 27 Oct 2011 12:46:29 +0200 Subject: multi: start ftp state machine when switching to DO_MORE This extends the fix from commit d7934b8bd491 When the multi state is changed within the multi_runsingle from DOING to DO_MORE, we didn't immediately start the FTP state machine again. That then left the FTP state in FTP_STOP. When curl_multi_fdset() was subsequently called, the ftp_domore_getsock() function would return the wrong fd info. Reported by: Gokhan Sengun --- lib/multi.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 6e4ec37a8..5026f8d2d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1339,18 +1339,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, &dophase_done); if(CURLE_OK == easy->result) { if(dophase_done) { - /* after DO, go PERFORM... or DO_MORE */ - if(easy->easy_conn->bits.do_more) { - /* we're supposed to do more, but we need to sit down, relax - and wait a little while first */ - multistate(easy, CURLM_STATE_DO_MORE); - result = CURLM_OK; - } - else { - /* we're done with the DO, now DO_DONE */ - multistate(easy, CURLM_STATE_DO_DONE); - result = CURLM_CALL_MULTI_PERFORM; - } + /* after DO, go DO_DONE or DO_MORE */ + multistate(easy, easy->easy_conn->bits.do_more? + CURLM_STATE_DO_MORE: + CURLM_STATE_DO_DONE); + result = CURLM_CALL_MULTI_PERFORM; } /* dophase_done */ } else { -- cgit v1.2.1 From ddeab4824534227611fe257485a8c22c896f07d7 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 27 Oct 2011 17:08:02 +0200 Subject: multi.c: OOM handling fix Fix curl_multi_cleanup() segfault when using weird cleanup sequence. --- lib/multi.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 5026f8d2d..63aac69fa 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1775,10 +1775,6 @@ 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); - multi->hostcache = NULL; - multi->sockhash = NULL; /* go over all connections that have close actions */ for(i=0; i< multi->connc->num; i++) { @@ -1802,6 +1798,12 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) cl= n; } + Curl_hash_destroy(multi->hostcache); + multi->hostcache = NULL; + + Curl_hash_destroy(multi->sockhash); + multi->sockhash = NULL; + Curl_rm_connc(multi->connc); multi->connc = NULL; -- cgit v1.2.1 From f7dfe2b87a3c79be41ab4e17764675f36dca3670 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 1 Nov 2011 14:38:21 +0100 Subject: multi.c: OOM handling fix --- lib/multi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 63aac69fa..ae708510a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -514,6 +514,9 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, prev->next = next; else multi->closure = next; + /* removed from closure list now, this might reuse an existing + existing connection but we don't know that at this point */ + data->state.shared_conn = NULL; /* No need to continue, handle can only be present once in the list */ break; } @@ -1788,7 +1791,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) able to close connections "properly" */ cl = multi->closure; while(cl) { - cl->easy_handle->state.shared_conn = NULL; /* no more shared */ + cl->easy_handle->state.shared_conn = NULL; /* allow cleanup */ if(cl->easy_handle->state.closed) /* close handle only if curl_easy_cleanup() already has been called for this easy handle */ @@ -2708,12 +2711,15 @@ static void multi_connc_remove_handle(struct Curl_multi *multi, /* out of memory - so much for graceful shutdown */ Curl_disconnect(conn, /* dead_connection */ FALSE); multi->connc->connects[i] = NULL; + data->state.shared_conn = NULL; } } - else + else { /* disconect the easy handle from the connection since the connection will now remain but this easy handle is going */ + data->state.shared_conn = NULL; conn->data = NULL; + } } } } -- cgit v1.2.1 From adc88ca203bbb602cc9a1726669526fa7b3723ef Mon Sep 17 00:00:00 2001 From: Jason Glasgow Date: Wed, 30 Nov 2011 15:23:44 -0500 Subject: multi: handle timeouts on DNS servers by checking for new sockets If the first name server is not available, the multi interface does not invoke the socket_cb when the DNS request to the first name server timesout. Ensure that the list of sockets are always updated after calling Curl_resolver_is_resolved. This bug can be reproduced if Curl is complied with --enable_ares and your code uses the multi socket interfaces and the CURLMOPT_SOCKETFUNCTION option. To test try: iptables -I INPUT \ -s $(sed -n -e '/name/{s/.* //p;q}' /etc/resolv.conf)/32 \ -j REJECT and then run a program which uses the multi-interface. --- lib/multi.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ae708510a..3059e49be 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1085,12 +1085,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* check if we have the name resolved by now */ easy->result = Curl_resolver_is_resolved(easy->easy_conn, &dns); - if(dns) { - /* Update sockets here. Mainly because the socket(s) may have been - closed and the application thus needs to be told, even if it is - likely that the same socket(s) will again be used further down. */ - singlesocket(multi, easy); + /* Update sockets here, because the socket(s) may have been + closed and the application thus needs to be told, even if it + is likely that the same socket(s) will again be used further + down. If the name has not yet been resolved, it is likely + that new sockets have been opened in an attempt to contact + another resolver. */ + singlesocket(multi, easy); + if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ easy->result = Curl_async_resolved(easy->easy_conn, -- cgit v1.2.1 From d81f5ea3e0a5f9a532fcf685898e041fafa93a5b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 2 Dec 2011 21:10:28 +0100 Subject: multi interface: only use non-NULL function pointer! If the socket callback function pointer hasn't been set, we must not attempt to use it. Commit adc88ca20 made it more likely to occur. --- lib/multi.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 3059e49be..f3b892c91 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1940,11 +1940,12 @@ static void singlesocket(struct Curl_multi *multi, } /* we know (entry != NULL) at this point, see the logic above */ - multi->socket_cb(easy->easy_handle, - s, - action, - multi->socket_userp, - entry->socketp); + if(multi->socket_cb) + multi->socket_cb(easy->easy_handle, + s, + action, + multi->socket_userp, + entry->socketp); entry->action = action; /* store the current action state */ } @@ -2019,11 +2020,12 @@ static void singlesocket(struct Curl_multi *multi, remove_sock_from_hash = FALSE; if(remove_sock_from_hash) { - multi->socket_cb(easy->easy_handle, - s, - CURL_POLL_REMOVE, - multi->socket_userp, - entry ? entry->socketp : NULL); + if(multi->socket_cb) + multi->socket_cb(easy->easy_handle, + s, + CURL_POLL_REMOVE, + multi->socket_userp, + entry ? entry->socketp : NULL); sh_delentry(multi->sockhash, s); } -- cgit v1.2.1 From 2b24dd870e81512542f238cb1b124cecb942a34f Mon Sep 17 00:00:00 2001 From: Gokhan Sengun Date: Tue, 6 Dec 2011 23:41:24 +0100 Subject: multi interface: fix block when CONNECT_ONLY option is used --- lib/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f3b892c91..e5073432e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1239,7 +1239,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->bits.close = FALSE; multistate(easy, CURLM_STATE_DONE); easy->result = CURLE_OK; - result = CURLM_OK; + result = CURLM_CALL_MULTI_PERFORM; } else { /* Perform the protocol's DO action */ -- cgit v1.2.1 From c834213ad52c52431e9ca597862dc81839cabe84 Mon Sep 17 00:00:00 2001 From: Gokhan Sengun Date: Mon, 19 Dec 2011 14:35:20 +0100 Subject: FTP: perform active connections non-blocking 1- Two new error codes are introduced. CURLE_FTP_ACCEPT_FAILED to be set whenever ACCEPTing fails because of FTP server connected. CURLE_FTP_ACCEPT_TIMEOUT to be set whenever ACCEPTing timeouts. Neither of these errors are considered fatal and control connection remains OK because it could just be a firewall blocking server to connect to the client. 2- One new setopt option was introduced. CURLOPT_ACCEPTTIMEOUT_MS It sets the maximum amount of time FTP client is going to wait for a server to connect. Internal default accept timeout is 60 seconds. --- lib/multi.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e5073432e..e408ab184 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1388,6 +1388,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_DO_DONE: + + if(easy->easy_conn->bits.wait_data_conn == TRUE) { + multistate(easy, CURLM_STATE_DO_MORE); + result = CURLM_OK; + break; + } + /* Move ourselves from the send to recv pipeline */ moveHandleFromSendToRecvPipeline(data, easy->easy_conn); /* Check if we can move pending requests to send pipe */ -- cgit v1.2.1 From dfdac61522c7d660f884ec7a663dedb2d69d16a8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 20 Dec 2011 12:52:24 +0100 Subject: non-blocking active FTP: cleanup multi state usage Backpedaled out the funny double-change of state in the multi state machine by adding a new argument to the do_more() function to signal completion. This way it can remain in the DO_MORE state properly until done. Long term, the entire DO_MORE logic should be moved into the FTP code and be hidden from the multi code as the logic is only used for FTP. --- lib/multi.c | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e408ab184..6ec20ec80 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1361,40 +1361,31 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_DO_MORE: - /* Ready to do more? */ - easy->result = Curl_is_connected(easy->easy_conn, - SECONDARYSOCKET, - &connected); - if(connected) { - /* - * When we are connected, DO MORE and then go DO_DONE - */ - easy->result = Curl_do_more(easy->easy_conn); - - /* No need to remove ourselves from the send pipeline here since that - is done for us in Curl_done() */ + /* + * When we are connected, DO MORE and then go DO_DONE + */ + easy->result = Curl_do_more(easy->easy_conn, &dophase_done); - if(CURLE_OK == easy->result) { + /* No need to remove this handle from the send pipeline here since that + is done in Curl_done() */ + if(CURLE_OK == easy->result) { + if(dophase_done) { multistate(easy, CURLM_STATE_DO_DONE); result = CURLM_CALL_MULTI_PERFORM; } - else { - /* failure detected */ - Curl_posttransfer(data); - Curl_done(&easy->easy_conn, easy->result, FALSE); - disconnect_conn = TRUE; - } + else + /* stay in DO_MORE */ + result = CURLM_OK; + } + else { + /* failure detected */ + Curl_posttransfer(data); + Curl_done(&easy->easy_conn, easy->result, FALSE); + disconnect_conn = TRUE; } break; case CURLM_STATE_DO_DONE: - - if(easy->easy_conn->bits.wait_data_conn == TRUE) { - multistate(easy, CURLM_STATE_DO_MORE); - result = CURLM_OK; - break; - } - /* Move ourselves from the send to recv pipeline */ moveHandleFromSendToRecvPipeline(data, easy->easy_conn); /* Check if we can move pending requests to send pipe */ -- cgit v1.2.1 From c83de6d07625b813e3bbc31f9a0827c3a0007355 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 21 Mar 2012 23:22:39 +0100 Subject: CONNECT: fix multi interface regression The refactoring of HTTP CONNECT handling in commit 41b0237834232 that made it protocol independent broke it for the multi interface. This fix now introduce a better state handling and moved some logic to the http_proxy.c source file. Reported by: Yang Tse Bug: http://curl.haxx.se/mail/lib-2012-03/0162.html --- lib/multi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 6ec20ec80..b0104310b 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -812,7 +812,7 @@ static int waitconnect_getsock(struct connectdata *conn, /* when we've sent a CONNECT to a proxy, we should rather wait for the socket to become readable to be able to get the response headers */ - if(conn->bits.tunnel_connecting) + if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) return GETSOCK_READSOCK(0); return GETSOCK_WRITESOCK(0); @@ -1066,7 +1066,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP - if(easy->easy_conn->bits.tunnel_connecting) + if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) multistate(easy, CURLM_STATE_WAITPROXYCONNECT); else #endif @@ -1111,7 +1111,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP - if(easy->easy_conn->bits.tunnel_connecting) + if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) multistate(easy, CURLM_STATE_WAITPROXYCONNECT); else #endif @@ -1144,7 +1144,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(easy, CURLM_STATE_CONNECT); } else if(CURLE_OK == easy->result) { - if(!easy->easy_conn->bits.tunnel_connecting) + if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE) multistate(easy, CURLM_STATE_WAITCONNECT); } break; @@ -1179,7 +1179,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, BUT if we are using a proxy we must change to WAITPROXYCONNECT */ #ifndef CURL_DISABLE_HTTP - if(easy->easy_conn->bits.tunnel_connecting) + if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) multistate(easy, CURLM_STATE_WAITPROXYCONNECT); else #endif -- cgit v1.2.1 From c13af843727c06ffec1031129ec5c30dfcd5066e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 12 Jun 2012 23:04:04 +0200 Subject: singlesocket: remove dead code No need to check if 'entry' is non-NULL in a spot where it is already checked and guaranteed to be non-NULL. (Spotted by a Coverity scan) --- lib/multi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b0104310b..f4e15c413 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -2018,12 +2018,13 @@ static void singlesocket(struct Curl_multi *multi, remove_sock_from_hash = FALSE; if(remove_sock_from_hash) { + /* in this case 'entry' is always non-NULL */ if(multi->socket_cb) multi->socket_cb(easy->easy_handle, s, CURL_POLL_REMOVE, multi->socket_userp, - entry ? entry->socketp : NULL); + entry->socketp); sh_delentry(multi->sockhash, s); } -- cgit v1.2.1 From 9d1171693361622762dd078d7a1a7236d84f838e Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 15 Jul 2012 20:31:37 +0200 Subject: multi_runsingle: added precaution against easy_conn NULL pointer In many states the easy_conn pointer is referenced and just assumed to be working. This is an added extra check since analyzing indicates there's a risk we can end up in these states with a NULL pointer there. --- lib/multi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f4e15c413..ff43378f5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -984,6 +984,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; } + if(!easy->easy_conn && + easy->state > CURLM_STATE_CONNECT && + easy->state < CURLM_STATE_DONE) { + /* In all these states, the code will blindly access 'easy->easy_conn' + so this is precaution that it isn't NULL. And it silences static + analyzers. */ + failf(data, "In state %d with no easy_conn, bail out!\n", easy->state); + return CURLM_INTERNAL_ERROR; + } + if(easy->easy_conn && easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) /* Make sure we set the connection's current owner */ -- cgit v1.2.1 From 14afbf361a4fbce8153d98dba4145e5693a92e98 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 8 Aug 2012 14:50:32 +0200 Subject: add_next_timeout: minor restructure of code By reading the ->head pointer and using that instead of the ->size number to figure out if there's a list remaining we avoid the (false positive) clang-analyzer warning that we might dereference of a null pointer. --- lib/multi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index ff43378f5..d252351b7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2079,14 +2079,14 @@ static CURLMcode add_next_timeout(struct timeval now, break; e = n; } - if(!list->size) { + e = list->head; + if(!e) { /* clear the expire times within the handles that we remove from the splay tree */ tv->tv_sec = 0; tv->tv_usec = 0; } else { - e = list->head; /* copy the first entry to 'tv' */ memcpy(tv, e->ptr, sizeof(*tv)); -- cgit v1.2.1 From de24d7bd4c03ea3eeba928edc9ae9e7a826c67c8 Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Mon, 27 Aug 2012 12:48:55 -0700 Subject: multi: add curl_multi_wait() /* * Name: curl_multi_wait() * * Desc: Poll on all fds within a CURLM set as well as any * additional fds passed to the function. * * Returns: CURLMcode type, general multi error code. */ CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms); --- lib/multi.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d252351b7..4a1f601f6 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -941,6 +941,93 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, return CURLM_OK; } +CURLMcode curl_multi_wait(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms) +{ + struct Curl_multi *multi=(struct Curl_multi *)multi_handle; + struct Curl_one_easy *easy; + curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; + int bitmap; + unsigned int i; + unsigned int nfds = extra_nfds; + struct pollfd *ufds; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Count up how many fds we have from the multi handle */ + easy=multi->easy.next; + while(easy != &multi->easy) { + bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE); + + for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { + curl_socket_t s = CURL_SOCKET_BAD; + + if(bitmap & GETSOCK_READSOCK(i)) { + ++nfds; + s = sockbunch[i]; + } + if(bitmap & GETSOCK_WRITESOCK(i)) { + ++nfds; + s = sockbunch[i]; + } + if(s == CURL_SOCKET_BAD) { + break; + } + } + + easy = easy->next; /* check next handle */ + } + + ufds = (struct pollfd *)malloc(nfds * sizeof(struct pollfd)); + nfds = 0; + + /* Add the curl handles to our pollfds first */ + easy=multi->easy.next; + while(easy != &multi->easy) { + bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE); + + for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { + curl_socket_t s = CURL_SOCKET_BAD; + + if(bitmap & GETSOCK_READSOCK(i)) { + ufds[nfds].fd = sockbunch[i]; + ufds[nfds].events = POLLIN; + ++nfds; + s = sockbunch[i]; + } + if(bitmap & GETSOCK_WRITESOCK(i)) { + ufds[nfds].fd = sockbunch[i]; + ufds[nfds].events = POLLOUT; + ++nfds; + s = sockbunch[i]; + } + if(s == CURL_SOCKET_BAD) { + break; + } + } + + easy = easy->next; /* check next handle */ + } + + /* Add external file descriptions from poll-like struct curl_waitfd */ + for(i = 0; i < extra_nfds; i++) { + ufds[nfds].fd = extra_fds[i].fd; + ufds[nfds].events = (short) ( + ((extra_fds[i].events & CURL_WAIT_POLLIN) ? POLLIN : 0) | + ((extra_fds[i].events & CURL_WAIT_POLLPRI) ? POLLPRI : 0) | + ((extra_fds[i].events & CURL_WAIT_POLLOUT) ? POLLOUT : 0) ); + ++nfds; + } + + /* wait... */ + Curl_poll(ufds, nfds, timeout_ms); + free(ufds); + return CURLM_OK; +} + static CURLMcode multi_runsingle(struct Curl_multi *multi, struct timeval now, struct Curl_one_easy *easy) -- cgit v1.2.1 From b78944146a0670b74be00e189f468adfc5fca5b7 Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Sat, 15 Sep 2012 10:38:52 -0700 Subject: curl_multi_wait: Add parameter to return number of active sockets Minor change to recently introduced function. BC breaking, but since curl_multi_wait() doesn't exist in any releases that should be fine. --- lib/multi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 4a1f601f6..b6c327b77 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -944,7 +944,8 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, CURLMcode curl_multi_wait(CURLM *multi_handle, struct curl_waitfd extra_fds[], unsigned int extra_nfds, - int timeout_ms) + int timeout_ms, + int *ret) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; @@ -1023,8 +1024,10 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, } /* wait... */ - Curl_poll(ufds, nfds, timeout_ms); + i = Curl_poll(ufds, nfds, timeout_ms); free(ufds); + if(ret) + *ret = i; return CURLM_OK; } -- cgit v1.2.1 From 971f5bcedd41889dc9652aa0c583477c0b1d49c9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 2 Oct 2012 00:16:20 +0200 Subject: multi_runsingle: CURLOPT_LOW_SPEED_* fix for rate limitation During the periods of rate limitation, the speedcheck function wasn't called and thus the values weren't updated accordingly and it would then easily trigger wrongly once data got transferred again. Also, the progress callback's return code was not acknowledged in this state so it could make an "abort" return code to get ignored and not have the documented effect of aborting an ongoing transfer. Bug: http://curl.haxx.se/mail/lib-2012-09/0081.html Reported by: Jie He --- lib/multi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b6c327b77..6506b5ee4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -43,6 +43,7 @@ #include "http.h" #include "select.h" #include "warnless.h" +#include "speedcheck.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -1518,7 +1519,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ - Curl_pgrsUpdate(easy->easy_conn); + if(Curl_pgrsUpdate(easy->easy_conn)) + easy->result = CURLE_ABORTED_BY_CALLBACK; + else + easy->result = Curl_speedcheck(data, now); + if(( (data->set.max_send_speed == 0) || (data->progress.ulspeed < data->set.max_send_speed )) && ( (data->set.max_recv_speed == 0) || -- cgit v1.2.1 From 8373ca3641ce793a868377600fb5fd7b8e8bf95a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 9 Oct 2012 22:19:49 +0200 Subject: curl_multi_wait: no wait if no descriptors to wait for This is a minor change in behavior after having been pointed out by Mark Tully and discussed on the list. Initially this case would internally call poll() with no sockets and a timeout which would equal a sleep for that specified time. Bug: http://curl.haxx.se/mail/lib-2012-10/0076.html Reported by: Mark Tully --- lib/multi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 6506b5ee4..938846769 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1024,8 +1024,12 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, ++nfds; } - /* wait... */ - i = Curl_poll(ufds, nfds, timeout_ms); + if(nfds) + /* wait... */ + i = Curl_poll(ufds, nfds, timeout_ms); + else + i = 0; + free(ufds); if(ret) *ret = i; -- cgit v1.2.1 From 409f2a041f752ef635354e1dc7b2759fcd7088d6 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 18 Nov 2012 16:17:37 +0100 Subject: fixed memory leak: CURLOPT_RESOLVE with multi interface DNS cache entries populated with CURLOPT_RESOLVE were not properly freed again when done using the multi interface. Test case 1502 added to verify. Bug: http://curl.haxx.se/bug/view.cgi?id=3575448 Reported by: Alex Gruz --- lib/multi.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index 938846769..b4e01bd93 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1789,12 +1789,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } WHILE_FALSE; /* just to break out from! */ if(CURLM_STATE_COMPLETED == easy->state) { - if(data->dns.hostcachetype == HCACHE_MULTI) { - /* clear out the usage of the shared DNS cache */ - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } - /* now fill in the Curl_message with this info */ msg = &easy->msg; @@ -1911,9 +1905,6 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) cl= n; } - Curl_hash_destroy(multi->hostcache); - multi->hostcache = NULL; - Curl_hash_destroy(multi->sockhash); multi->sockhash = NULL; @@ -1930,6 +1921,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) nexteasy=easy->next; if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ + Curl_hostcache_clean(easy->easy_handle); easy->easy_handle->dns.hostcache = NULL; easy->easy_handle->dns.hostcachetype = HCACHE_NONE; } @@ -1943,6 +1935,9 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) easy = nexteasy; } + Curl_hash_destroy(multi->hostcache); + multi->hostcache = NULL; + free(multi); return CURLM_OK; -- cgit v1.2.1 From b33074d8931f2fd19f07ede99228abba393e2a4e Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Mon, 26 Nov 2012 16:20:53 +0100 Subject: multi.c: disambiguate precedence of bitwise and relational operation --- lib/multi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index b4e01bd93..f7ad9cbef 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1017,10 +1017,13 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, /* Add external file descriptions from poll-like struct curl_waitfd */ for(i = 0; i < extra_nfds; i++) { ufds[nfds].fd = extra_fds[i].fd; - ufds[nfds].events = (short) ( - ((extra_fds[i].events & CURL_WAIT_POLLIN) ? POLLIN : 0) | - ((extra_fds[i].events & CURL_WAIT_POLLPRI) ? POLLPRI : 0) | - ((extra_fds[i].events & CURL_WAIT_POLLOUT) ? POLLOUT : 0) ); + ufds[nfds].events = 0; + if(extra_fds[i].events & CURL_WAIT_POLLIN) + ufds[nfds].events |= POLLIN; + if(extra_fds[i].events & CURL_WAIT_POLLPRI) + ufds[nfds].events |= POLLPRI; + if(extra_fds[i].events & CURL_WAIT_POLLOUT) + ufds[nfds].events |= POLLOUT; ++nfds; } -- cgit v1.2.1 From 8b02afd9a938d6db150fa1ba34cca64de873f5d8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 30 Nov 2012 14:52:38 +0100 Subject: multi: fix re-sending request on early connection close This handling already works with the easy-interface code. When a request is sent on a re-used connection that gets closed by the server at the same time as the request is sent, the situation may occur so that we can send the request and we discover the broken connection as a RECV_ERROR in the PERFORM state and then the request needs to be retried on a fresh connection. Test 64 broke with 'multi-always-internally'. --- lib/multi.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f7ad9cbef..d2d154fa7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1539,6 +1539,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_PERFORM: + { + char *newurl = NULL; + bool retry = FALSE; + /* check if over send speed */ if((data->set.max_send_speed > 0) && (data->progress.ulspeed > data->set.max_send_speed)) { @@ -1586,13 +1590,29 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->writechannel_inuse = FALSE; } + if(done || (easy->result == CURLE_RECV_ERROR)) { + /* If CURLE_RECV_ERROR happens early enough, we assume it was a race + * condition and the server closed the re-used connection exactly when + * we wanted to use it, so figure out if that is indeed the case. + */ + CURLcode ret = Curl_retry_request(easy->easy_conn, &newurl); + if(!ret) + retry = (newurl)?TRUE:FALSE; + + if(retry) + /* if we are to retry, set the result to OK */ + easy->result = CURLE_OK; + } + if(easy->result) { - /* The transfer phase returned error, we mark the connection to get + /* + * The transfer phase returned error, we mark the connection to get * closed to prevent being re-used. This is because we can't possibly * know if the connection is in a good shape or not now. Unless it is * a protocol which uses two "channels" like FTP, as then the error * happened in the data connection. */ + if(!(easy->easy_conn->handler->flags & PROTOPT_DUAL)) easy->easy_conn->bits.close = TRUE; @@ -1600,14 +1620,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_done(&easy->easy_conn, easy->result, FALSE); } else if(done) { - char *newurl = NULL; - bool retry = FALSE; followtype follow=FOLLOW_NONE; - easy->result = Curl_retry_request(easy->easy_conn, &newurl); - if(!easy->result) - retry = (newurl)?TRUE:FALSE; - /* call this even if the readwrite function returned error */ Curl_posttransfer(data); @@ -1640,11 +1654,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK == easy->result) { multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; + newurl = NULL; /* handed over the memory ownership to + Curl_follow(), make sure we don't free() it + here */ } - else if(newurl) - /* Since we "took it", we are in charge of freeing this on - failure */ - free(newurl); } else { /* after the transfer is done, go DONE */ @@ -1652,13 +1665,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* but first check to see if we got a location info even though we're not following redirects */ if(data->req.location) { + if(newurl) + free(newurl); newurl = data->req.location; data->req.location = NULL; easy->result = Curl_follow(data, newurl, FOLLOW_FAKE); - if(easy->result) { + newurl = NULL; /* allocation was handed over */ + if(easy->result) disconnect_conn = TRUE; - free(newurl); - } } multistate(easy, CURLM_STATE_DONE); @@ -1666,7 +1680,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } + if(newurl) + free(newurl); break; + } case CURLM_STATE_DONE: -- cgit v1.2.1 From d021f2e8a0067fc769652f27afec9024c0d02b3d Mon Sep 17 00:00:00 2001 From: Linus Nielsen Feltzing Date: Thu, 6 Dec 2012 12:12:04 +0100 Subject: Introducing a new persistent connection caching system using "bundles". A bundle is a list of all persistent connections to the same host. The connection cache consists of a hash of bundles, with the hostname as the key. The benefits may not be obvious, but they are two: 1) Faster search for connections to reuse, since the hash lookup only finds connections to the host in question. 2) It lays out the groundworks for an upcoming patch, which will introduce multiple HTTP pipelines. This patch also removes the awkward list of "closure handles", which were needed to send QUIT commands to the FTP server when closing a connection. Now we allocate a separate closure handle and use that one to close all connections. This has been tested in a live system for a few weeks, and of course passes the test suite. --- lib/multi.c | 313 +++++++++++++----------------------------------------------- 1 file changed, 66 insertions(+), 247 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index d2d154fa7..e309c0d96 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -44,6 +44,8 @@ #include "select.h" #include "warnless.h" #include "speedcheck.h" +#include "conncache.h" +#include "bundles.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -97,11 +99,6 @@ typedef enum { #define GETSOCK_READABLE (0x00ff) #define GETSOCK_WRITABLE (0xff00) -struct closure { - struct closure *next; /* a simple one-way list of structs */ - struct SessionHandle *easy_handle; -}; - struct Curl_one_easy { /* first, two fields for the linked list of these */ struct Curl_one_easy *next; @@ -164,14 +161,16 @@ struct Curl_multi { /* Whether pipelining is enabled for this multi handle */ bool pipelining_enabled; - /* shared connection cache */ - struct conncache *connc; + /* Shared connection cache (bundles)*/ + struct conncache *conn_cache; + + /* This handle will be used for closing the cached connections in + curl_multi_cleanup() */ + struct SessionHandle *closure_handle; + long maxconnects; /* if >0, a fixed limit of the maximum number of entries we're allowed to grow the connection cache to */ - /* list of easy handles kept around for doing nice connection closures */ - struct closure *closure; - /* timer callback and user data pointer for the *socket() API */ curl_multi_timer_callback timer_cb; void *timer_userp; @@ -179,12 +178,8 @@ struct Curl_multi { previous callback */ }; -static void multi_connc_remove_handle(struct Curl_multi *multi, - struct SessionHandle *data); static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy); -static CURLMcode add_closure(struct Curl_multi *multi, - struct SessionHandle *data); static int update_timer(struct Curl_multi *multi); static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle, @@ -228,7 +223,7 @@ static void multi_freetimeout(void *a, void *b); static void multistate(struct Curl_one_easy *easy, CURLMstate state) { #ifdef DEBUGBUILD - long connectindex = -5000; + long connection_id = -5000; #endif CURLMstate oldstate = easy->state; @@ -242,12 +237,12 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state) if(easy->easy_conn) { if(easy->state > CURLM_STATE_CONNECT && easy->state < CURLM_STATE_COMPLETED) - connectindex = easy->easy_conn->connectindex; + connection_id = easy->easy_conn->connection_id; infof(easy->easy_handle, "STATE: %s => %s handle %p; (connection #%ld) \n", statename[oldstate], statename[easy->state], - (char *)easy, connectindex); + (char *)easy, connection_id); } #endif if(state == CURLM_STATE_COMPLETED) @@ -391,7 +386,6 @@ static void multi_freeamsg(void *a, void *b) (void)b; } - CURLM *curl_multi_init(void) { struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); @@ -409,8 +403,8 @@ CURLM *curl_multi_init(void) if(!multi->sockhash) goto error; - multi->connc = Curl_mk_connc(CONNCACHE_MULTI, -1L); - if(!multi->connc) + multi->conn_cache = Curl_conncache_init(CONNCACHE_MULTI); + if(!multi->conn_cache) goto error; multi->msglist = Curl_llist_alloc(multi_freeamsg); @@ -431,8 +425,8 @@ CURLM *curl_multi_init(void) multi->sockhash = NULL; Curl_hash_destroy(multi->hostcache); multi->hostcache = NULL; - Curl_rm_connc(multi->connc); - multi->connc = NULL; + Curl_conncache_destroy(multi->conn_cache); + multi->conn_cache = NULL; free(multi); return NULL; @@ -443,8 +437,6 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, { struct curl_llist *timeoutlist; struct Curl_one_easy *easy; - struct closure *cl; - struct closure *prev = NULL; struct Curl_multi *multi = (struct Curl_multi *)multi_handle; struct SessionHandle *data = (struct SessionHandle *)easy_handle; @@ -462,22 +454,13 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* possibly we should create a new unique error code for this condition */ return CURLM_BAD_EASY_HANDLE; - /* We want the connection cache to have plenty of room. Before we supported - the shared cache every single easy handle had 5 entries in their cache - by default. */ - if(((multi->num_easy + 1) * 4) > multi->connc->num) { - long newmax = (multi->num_easy + 1) * 4; - - if(multi->maxconnects && (newmax > multi->maxconnects)) - /* don't grow beyond the allowed size */ - newmax = multi->maxconnects; - - if(newmax > multi->connc->num) { - /* we only do this is we can in fact grow the cache */ - CURLcode res = Curl_ch_connc(data, multi->connc, newmax); - if(res) - return CURLM_OUT_OF_MEMORY; - } + /* This is a good time to allocate a fresh easy handle to use when closing + cached connections */ + if(!multi->closure_handle) { + multi->closure_handle = + (struct SessionHandle *)curl_easy_init(); + Curl_easy_addmulti(easy_handle, multi_handle); + multi->closure_handle->state.conn_cache = multi->conn_cache; } /* Allocate and initialize timeout list for easy handle */ @@ -504,27 +487,6 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, data->state.timeoutlist = timeoutlist; timeoutlist = NULL; - /* Remove handle from the list of 'closure handles' in case it is there */ - cl = multi->closure; - while(cl) { - struct closure *next = cl->next; - if(cl->easy_handle == data) { - /* Remove node from list */ - free(cl); - if(prev) - prev->next = next; - else - multi->closure = next; - /* removed from closure list now, this might reuse an existing - existing connection but we don't know that at this point */ - data->state.shared_conn = NULL; - /* No need to continue, handle can only be present once in the list */ - break; - } - prev = cl; - cl = next; - } - /* set the easy handle */ easy->easy_handle = data; multistate(easy, CURLM_STATE_INIT); @@ -548,16 +510,15 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, } /* On a multi stack the connection cache, owned by the multi handle, - is shared between all easy handles within the multi handle. */ - if(easy->easy_handle->state.connc && - (easy->easy_handle->state.connc->type == CONNCACHE_PRIVATE)) { - /* kill old private connection cache */ - Curl_rm_connc(easy->easy_handle->state.connc); - easy->easy_handle->state.connc = NULL; + is shared between all easy handles within the multi handle. + Therefore we free the private connection cache if there is one */ + if(easy->easy_handle->state.conn_cache && + easy->easy_handle->state.conn_cache->type == CONNCACHE_PRIVATE) { + Curl_conncache_destroy(easy->easy_handle->state.conn_cache); } + /* Point now to this multi's connection cache */ - easy->easy_handle->state.connc = multi->connc; - easy->easy_handle->state.connc->type = CONNCACHE_MULTI; + easy->easy_handle->state.conn_cache = multi->conn_cache; /* This adds the new entry at the 'end' of the doubly-linked circular list of Curl_one_easy structs to try and maintain a FIFO queue so @@ -701,42 +662,17 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, Note that this ignores the return code simply because there's nothing really useful to do with it anyway! */ (void)Curl_done(&easy->easy_conn, easy->result, premature); - - if(easy->easy_conn) - /* the connection is still alive, set back the association to enable - the check below to trigger TRUE */ - easy->easy_conn->data = easy->easy_handle; } else /* Clear connection pipelines, if Curl_done above was not called */ Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn); } - /* figure out if the easy handle is used by one or more connections in the - cache */ - multi_connc_remove_handle(multi, easy->easy_handle); - - if(easy->easy_handle->state.connc->type == CONNCACHE_MULTI) { + if(easy->easy_handle->state.conn_cache->type == CONNCACHE_MULTI) { /* if this was using the shared connection cache we clear the pointer to that since we're not part of that handle anymore */ - easy->easy_handle->state.connc = NULL; - - /* Since we return the connection back to the communal connection pool - we mark the last connection as inaccessible */ - easy->easy_handle->state.lastconnect = -1; - - /* Modify the connectindex since this handle can't point to the - connection cache anymore. - - TODO: consider if this is really what we want. The connection cache - is within the multi handle and that owns the connections so we should - not need to touch connections like this when we just remove an easy - handle... - */ - if(easy->easy_conn && easy_owns_conn && - (easy->easy_conn->send_pipe->size + - easy->easy_conn->recv_pipe->size == 0)) - easy->easy_conn->connectindex = -1; + easy->easy_handle->state.conn_cache = NULL; + easy->easy_handle->state.lastconnect = NULL; } /* change state without using multistate(), only to make singlesocket() do @@ -745,6 +681,12 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, singlesocket(multi, easy); /* to let the application know what sockets that vanish with this handle */ + /* Remove the association between the connection and the handle */ + if(easy->easy_conn) { + easy->easy_conn->data = NULL; + easy->easy_conn = NULL; + } + Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association to this multi handle */ @@ -1324,8 +1266,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITDO: /* Wait for our turn to DO when we're pipelining requests */ #ifdef DEBUGBUILD - infof(data, "Conn %ld send pipe %zu inuse %d athead %d\n", - easy->easy_conn->connectindex, + infof(data, "WAITDO: Conn %ld send pipe %zu inuse %d athead %d\n", + easy->easy_conn->connection_id, easy->easy_conn->send_pipe->size, easy->easy_conn->writechannel_inuse?1:0, isHandleAtHead(data, @@ -1514,8 +1456,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } #ifdef DEBUGBUILD else { - infof(data, "Conn %ld recv pipe %zu inuse %d athead %d\n", - easy->easy_conn->connectindex, + infof(data, "WAITPERFORM: Conn %ld recv pipe %zu inuse %d athead %d\n", + easy->easy_conn->connection_id, easy->easy_conn->recv_pipe->size, easy->easy_conn->readchannel_inuse?1:0, isHandleAtHead(data, @@ -1891,45 +1833,41 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) return returncode; } +static void close_all_connections(struct Curl_multi *multi) +{ + struct connectdata *conn; + + conn = Curl_conncache_find_first_connection(multi->conn_cache); + while(conn) { + conn->data = multi->closure_handle; + + /* This will remove the connection from the cache */ + (void)Curl_disconnect(conn, FALSE); + + conn = Curl_conncache_find_first_connection(multi->conn_cache); + } +} + CURLMcode curl_multi_cleanup(CURLM *multi_handle) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; struct Curl_one_easy *nexteasy; - int i; - struct closure *cl; - struct closure *n; if(GOOD_MULTI_HANDLE(multi)) { multi->type = 0; /* not good anymore */ - /* go over all connections that have close actions */ - for(i=0; i< multi->connc->num; i++) { - if(multi->connc->connects[i] && - multi->connc->connects[i]->handler->flags & PROTOPT_CLOSEACTION) { - Curl_disconnect(multi->connc->connects[i], FALSE); - multi->connc->connects[i] = NULL; - } - } - /* now walk through the list of handles we kept around only to be - able to close connections "properly" */ - cl = multi->closure; - while(cl) { - cl->easy_handle->state.shared_conn = NULL; /* allow cleanup */ - if(cl->easy_handle->state.closed) - /* close handle only if curl_easy_cleanup() already has been called - for this easy handle */ - Curl_close(cl->easy_handle); - n = cl->next; - free(cl); - cl= n; - } + /* Close all the connections in the connection cache */ + close_all_connections(multi); + + Curl_close(multi->closure_handle); + multi->closure_handle = NULL; Curl_hash_destroy(multi->sockhash); multi->sockhash = NULL; - Curl_rm_connc(multi->connc); - multi->connc = NULL; + Curl_conncache_destroy(multi->conn_cache); + multi->conn_cache = NULL; /* remove the pending list of messages */ Curl_llist_destroy(multi->msglist, NULL); @@ -1947,7 +1885,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) } /* Clear the pointer to the connection cache */ - easy->easy_handle->state.connc = NULL; + easy->easy_handle->state.conn_cache = NULL; Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */ @@ -2803,125 +2741,6 @@ CURLMcode curl_multi_assign(CURLM *multi_handle, return CURLM_OK; } -static void multi_connc_remove_handle(struct Curl_multi *multi, - struct SessionHandle *data) -{ - /* a connection in the connection cache pointing to the given 'data' ? */ - int i; - - for(i=0; i< multi->connc->num; i++) { - struct connectdata * conn = multi->connc->connects[i]; - - if(conn && conn->data == data) { - /* If this easy_handle was the last one in charge for one or more - connections in the shared connection cache, we might need to keep - this handle around until either A) the connection is closed and - killed properly, or B) another easy_handle uses the connection. - - The reason why we need to have a easy_handle associated with a live - connection is simply that some connections will need a handle to get - closed down properly. Currently, the only connections that need to - keep a easy_handle handle around are using FTP(S). Such connections - have the PROT_CLOSEACTION bit set. - - Thus, we need to check for all connections in the shared cache that - points to this handle and are using PROT_CLOSEACTION. If there's any, - we need to add this handle to the list of "easy handles kept around - for nice connection closures". - */ - - if(conn->handler->flags & PROTOPT_CLOSEACTION) { - /* this handle is still being used by a shared connection and - thus we leave it around for now */ - if(add_closure(multi, data) == CURLM_OK) - data->state.shared_conn = multi; - else { - /* out of memory - so much for graceful shutdown */ - Curl_disconnect(conn, /* dead_connection */ FALSE); - multi->connc->connects[i] = NULL; - data->state.shared_conn = NULL; - } - } - else { - /* disconect the easy handle from the connection since the connection - will now remain but this easy handle is going */ - data->state.shared_conn = NULL; - conn->data = NULL; - } - } - } -} - -/* Add the given data pointer to the list of 'closure handles' that are kept - around only to be able to close some connections nicely - just make sure - that this handle isn't already added, like for the cases when an easy - handle is removed, added and removed again... */ -static CURLMcode add_closure(struct Curl_multi *multi, - struct SessionHandle *data) -{ - struct closure *cl = multi->closure; - struct closure *p = NULL; - bool add = TRUE; - - /* Before adding, scan through all the other currently kept handles and see - if there are any connections still referring to them and kill them if - not. */ - while(cl) { - struct closure *n; - bool inuse = FALSE; - int i; - - for(i=0; i< multi->connc->num; i++) { - if(multi->connc->connects[i] && - (multi->connc->connects[i]->data == cl->easy_handle)) { - inuse = TRUE; - break; - } - } - - n = cl->next; - - if(!inuse) { - /* cl->easy_handle is now killable */ - - /* unmark it as not having a connection around that uses it anymore */ - cl->easy_handle->state.shared_conn= NULL; - - if(cl->easy_handle->state.closed) { - infof(data, "Delayed kill of easy handle %p\n", cl->easy_handle); - /* close handle only if curl_easy_cleanup() already has been called - for this easy handle */ - Curl_close(cl->easy_handle); - } - if(p) - p->next = n; - else - multi->closure = n; - free(cl); - } - else { - if(cl->easy_handle == data) - add = FALSE; - - p = cl; - } - - cl = n; - } - - if(add) { - cl = calloc(1, sizeof(struct closure)); - if(!cl) - return CURLM_OUT_OF_MEMORY; - - cl->easy_handle = data; - cl->next = multi->closure; - multi->closure = cl; - } - - return CURLM_OK; -} - #ifdef DEBUGBUILD void Curl_multi_dump(const struct Curl_multi *multi_handle) { -- cgit v1.2.1 From 4675ab7c81e2bea5f4d15fc2ea4bad29f3d4b0a4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 24 Nov 2012 23:06:10 +0100 Subject: internals: always use multi interface internally curl_easy_perform() is now just a wrapper-function that uses multi functions to achieve its functionality. All internals are all non-blocking and multi interface friendly. No more two different behaviors depending on which interface that is used. --- lib/multi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index e309c0d96..f5d1b71a1 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -896,7 +896,7 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, int bitmap; unsigned int i; unsigned int nfds = extra_nfds; - struct pollfd *ufds; + struct pollfd *ufds = NULL; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -925,7 +925,8 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, easy = easy->next; /* check next handle */ } - ufds = (struct pollfd *)malloc(nfds * sizeof(struct pollfd)); + if(nfds) + ufds = (struct pollfd *)malloc(nfds * sizeof(struct pollfd)); nfds = 0; /* Add the curl handles to our pollfds first */ @@ -975,7 +976,7 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, else i = 0; - free(ufds); + Curl_safefree(ufds); if(ret) *ret = i; return CURLM_OK; -- cgit v1.2.1 From 4deb52831a071bc83cefb4871764281a5c32f902 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 11 Dec 2012 22:49:11 +0100 Subject: Curl_getconnectinfo: fixed to work with always-multi To make sure CURLINFO_LASTSOCKET works and curl_easy_recv() etc when the connection cache is now always in the multi handle that the easy handle doesn't have a strong connection to anymore, the code now searches for the last used connection in the last used multi handle to see if it is still there and then gets the socket from there. This makes CURLINFO_LASTSOCKET only work if curl_easy_perform() has been used on an easy handle, but that was always how the feature was documented. As a result of this restructure, I've created multihandle.h which now has the multi handle struct and related stuff defined so that more files than multi.c can use it. --- lib/multi.c | 109 +----------------------------------------------------------- 1 file changed, 1 insertion(+), 108 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index f5d1b71a1..fb2916b40 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -46,6 +46,7 @@ #include "speedcheck.h" #include "conncache.h" #include "bundles.h" +#include "multihandle.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -63,62 +64,6 @@ #define CURL_SOCKET_HASH_TABLE_SIZE 911 #endif -struct Curl_message { - /* the 'CURLMsg' is the part that is visible to the external user */ - struct CURLMsg extmsg; -}; - -/* NOTE: if you add a state here, add the name to the statename[] array as - well! -*/ -typedef enum { - CURLM_STATE_INIT, /* 0 - start in this state */ - CURLM_STATE_CONNECT, /* 1 - resolve/connect has been sent off */ - CURLM_STATE_WAITRESOLVE, /* 2 - awaiting the resolve to finalize */ - CURLM_STATE_WAITCONNECT, /* 3 - awaiting the connect to finalize */ - CURLM_STATE_WAITPROXYCONNECT, /* 4 - awaiting proxy CONNECT to finalize */ - CURLM_STATE_PROTOCONNECT, /* 5 - completing the protocol-specific connect - phase */ - CURLM_STATE_WAITDO, /* 6 - wait for our turn to send the request */ - CURLM_STATE_DO, /* 7 - start send off the request (part 1) */ - CURLM_STATE_DOING, /* 8 - sending off the request (part 1) */ - CURLM_STATE_DO_MORE, /* 9 - send off the request (part 2) */ - CURLM_STATE_DO_DONE, /* 10 - done sending off request */ - CURLM_STATE_WAITPERFORM, /* 11 - wait for our turn to read the response */ - CURLM_STATE_PERFORM, /* 12 - transfer data */ - CURLM_STATE_TOOFAST, /* 13 - wait because limit-rate exceeded */ - CURLM_STATE_DONE, /* 14 - post data transfer operation */ - CURLM_STATE_COMPLETED, /* 15 - operation complete */ - CURLM_STATE_MSGSENT, /* 16 - the operation complete message is sent */ - CURLM_STATE_LAST /* 17 - not a true state, never use this */ -} CURLMstate; - -/* we support N sockets per easy handle. Set the corresponding bit to what - action we should wait for */ -#define MAX_SOCKSPEREASYHANDLE 5 -#define GETSOCK_READABLE (0x00ff) -#define GETSOCK_WRITABLE (0xff00) - -struct Curl_one_easy { - /* first, two fields for the linked list of these */ - struct Curl_one_easy *next; - struct Curl_one_easy *prev; - - struct SessionHandle *easy_handle; /* the easy handle for this unit */ - struct connectdata *easy_conn; /* the "unit's" connection */ - - CURLMstate state; /* the handle's state */ - CURLcode result; /* previous result */ - - struct Curl_message msg; /* A single posted message. */ - - /* Array with the plain socket numbers this handle takes care of, in no - particular order. Note that all sockets are added to the sockhash, where - the state etc are also kept. This array is mostly used to detect when a - socket is to be removed from the hash. See singlesocket(). */ - curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; - int numsocks; -}; #define CURL_MULTI_HANDLE 0x000bab1e @@ -127,57 +72,6 @@ struct Curl_one_easy { #define GOOD_EASY_HANDLE(x) \ ((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER)) -/* This is the struct known as CURLM on the outside */ -struct Curl_multi { - /* First a simple identifier to easier detect if a user mix up - this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ - long type; - - /* We have a doubly-linked circular list with easy handles */ - struct Curl_one_easy easy; - - int num_easy; /* amount of entries in the linked list above. */ - int num_alive; /* amount of easy handles that are added but have not yet - reached COMPLETE state */ - - struct curl_llist *msglist; /* a list of messages from completed transfers */ - - /* 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; - - /* Whether pipelining is enabled for this multi handle */ - bool pipelining_enabled; - - /* Shared connection cache (bundles)*/ - struct conncache *conn_cache; - - /* This handle will be used for closing the cached connections in - curl_multi_cleanup() */ - struct SessionHandle *closure_handle; - - long maxconnects; /* if >0, a fixed limit of the maximum number of entries - we're allowed to grow the connection cache to */ - - /* timer callback and user data pointer for the *socket() API */ - curl_multi_timer_callback timer_cb; - void *timer_userp; - struct timeval timer_lastcall; /* the fixed time for the timeout for the - previous callback */ -}; - static void singlesocket(struct Curl_multi *multi, struct Curl_one_easy *easy); static int update_timer(struct Curl_multi *multi); @@ -672,7 +566,6 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, /* if this was using the shared connection cache we clear the pointer to that since we're not part of that handle anymore */ easy->easy_handle->state.conn_cache = NULL; - easy->easy_handle->state.lastconnect = NULL; } /* change state without using multistate(), only to make singlesocket() do -- cgit v1.2.1