aboutsummaryrefslogtreecommitdiff
path: root/lib/multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/multi.c')
-rw-r--r--lib/multi.c261
1 files changed, 110 insertions, 151 deletions
diff --git a/lib/multi.c b/lib/multi.c
index a369d0361..3e2583a21 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -40,6 +40,7 @@
#include "conncache.h"
#include "bundles.h"
#include "multihandle.h"
+#include "pipeline.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@@ -69,13 +70,6 @@ static void singlesocket(struct Curl_multi *multi,
struct Curl_one_easy *easy);
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 *handle,
- struct connectdata *conn);
-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,
@@ -85,6 +79,7 @@ static CURLMcode add_next_timeout(struct timeval now,
#ifdef DEBUGBUILD
static const char * const statename[]={
"INIT",
+ "CONNECT_PEND",
"CONNECT",
"WAITRESOLVE",
"WAITCONNECT",
@@ -125,9 +120,9 @@ static void mstate(struct Curl_one_easy *easy, CURLMstate state
easy->state = state;
#ifdef DEBUGBUILD
- if(easy->easy_conn) {
- if(easy->state > CURLM_STATE_CONNECT &&
- easy->state < CURLM_STATE_COMPLETED)
+ if(easy->state >= CURLM_STATE_CONNECT_PEND &&
+ easy->state < CURLM_STATE_COMPLETED) {
+ if(easy->easy_conn)
connection_id = easy->easy_conn->connection_id;
infof(easy->easy_handle,
@@ -314,6 +309,7 @@ CURLM *curl_multi_init(void)
multi->easy.next = &multi->easy;
multi->easy.prev = &multi->easy;
+ multi->max_pipeline_length = 5;
return (CURLM *) multi;
error:
@@ -580,7 +576,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
/* as this was using a shared connection cache we clear the pointer
to that since we're not part of that multi handle anymore */
- easy->easy_handle->state.conn_cache = NULL;
+ easy->easy_handle->state.conn_cache = NULL;
/* change state without using multistate(), only to make singlesocket() do
what we want */
@@ -638,9 +634,9 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
return CURLM_BAD_EASY_HANDLE; /* twasn't found */
}
-bool Curl_multi_canPipeline(const struct Curl_multi* multi)
+bool Curl_multi_pipeline_enabled(const struct Curl_multi* multi)
{
- return multi->pipelining_enabled;
+ return multi && multi->pipelining_enabled;
}
void Curl_multi_handlePipeBreak(struct SessionHandle *data)
@@ -1007,16 +1003,27 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
break;
+ case CURLM_STATE_CONNECT_PEND:
+ /* We will stay here until there is a connection available. Then
+ we try again in the CURLM_STATE_CONNECT state. */
+ break;
+
case CURLM_STATE_CONNECT:
- /* Connect. We get a connection identifier filled in. */
+ /* Connect. We want to get a connection identifier filled in. */
Curl_pgrsTime(data, TIMER_STARTSINGLE);
easy->result = Curl_connect(data, &easy->easy_conn,
&async, &protocol_connect);
+ if(CURLE_NO_CONNECTION_AVAILABLE == easy->result) {
+ /* There was no connection available. We will go to the pending
+ state and wait for an available connection. */
+ multistate(easy, CURLM_STATE_CONNECT_PEND);
+ easy->result = CURLM_OK;
+ break;
+ }
if(CURLE_OK == easy->result) {
/* Add this handle to the send or pend pipeline */
- easy->result = addHandleToSendOrPendPipeline(data,
- easy->easy_conn);
+ easy->result = Curl_add_handle_to_pipeline(data, easy->easy_conn);
if(CURLE_OK != easy->result)
disconnect_conn = TRUE;
else {
@@ -1357,9 +1364,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_DO_DONE:
/* Move ourselves from the send to recv pipeline */
- moveHandleFromSendToRecvPipeline(data, easy->easy_conn);
+ Curl_move_handle_from_send_to_recv_pipe(data, easy->easy_conn);
/* Check if we can move pending requests to send pipe */
- checkPendPipeline(easy->easy_conn);
+ Curl_multi_process_pending_handles(multi);
multistate(easy, CURLM_STATE_WAITPERFORM);
result = CURLM_CALL_MULTI_PERFORM;
break;
@@ -1491,15 +1498,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
Curl_posttransfer(data);
/* we're no longer receiving */
- moveHandleFromRecvToDonePipeline(data,
- easy->easy_conn);
+ Curl_removeHandleFromPipeline(data, 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);
+ Curl_multi_process_pending_handles(multi);
/* When we follow redirects or is set to retry the connection, we must
to go back to the CONNECT state */
@@ -1554,14 +1560,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_DONE:
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. */
+ /* Remove ourselves from the receive pipeline, if we are there. */
Curl_removeHandleFromPipeline(data,
easy->easy_conn->recv_pipe);
- Curl_removeHandleFromPipeline(data,
- easy->easy_conn->done_pipe);
/* Check if we can move pending requests to send pipe */
- checkPendPipeline(easy->easy_conn);
+ Curl_multi_process_pending_handles(multi);
if(easy->easy_conn->bits.stream_was_rewound) {
/* This request read past its response boundary so we quickly let
@@ -1638,10 +1641,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
easy->easy_conn->send_pipe);
Curl_removeHandleFromPipeline(data,
easy->easy_conn->recv_pipe);
- Curl_removeHandleFromPipeline(data,
- easy->easy_conn->done_pipe);
/* Check if we can move pending requests to send pipe */
- checkPendPipeline(easy->easy_conn);
+ Curl_multi_process_pending_handles(multi);
if(disconnect_conn) {
/* disconnect properly */
@@ -1789,8 +1790,8 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
Curl_hostcache_clean(multi->closure_handle);
Curl_close(multi->closure_handle);
+ multi->closure_handle = NULL;
}
- multi->closure_handle = NULL;
Curl_hash_destroy(multi->sockhash);
multi->sockhash = NULL;
@@ -1825,6 +1826,10 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
Curl_hash_destroy(multi->hostcache);
multi->hostcache = NULL;
+ /* Free the blacklists by setting them to NULL */
+ Curl_pipeline_set_site_blacklist(NULL, &multi->pipelining_site_bl);
+ Curl_pipeline_set_server_blacklist(NULL, &multi->pipelining_server_bl);
+
free(multi);
return CURLM_OK;
@@ -2242,6 +2247,29 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle,
case CURLMOPT_MAXCONNECTS:
multi->maxconnects = va_arg(param, long);
break;
+ case CURLMOPT_MAX_HOST_CONNECTIONS:
+ multi->max_host_connections = va_arg(param, long);
+ break;
+ case CURLMOPT_MAX_PIPELINE_LENGTH:
+ multi->max_pipeline_length = va_arg(param, long);
+ break;
+ case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
+ multi->content_length_penalty_size = va_arg(param, long);
+ break;
+ case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
+ multi->chunk_length_penalty_size = va_arg(param, long);
+ break;
+ case CURLMOPT_PIPELINING_SITE_BL:
+ res = Curl_pipeline_set_site_blacklist(va_arg(param, char **),
+ &multi->pipelining_site_bl);
+ break;
+ case CURLMOPT_PIPELINING_SERVER_BL:
+ res = Curl_pipeline_set_server_blacklist(va_arg(param, char **),
+ &multi->pipelining_server_bl);
+ break;
+ case CURLMOPT_MAX_TOTAL_CONNECTIONS:
+ multi->max_total_connections = va_arg(param, long);
+ break;
default:
res = CURLM_UNKNOWN_OPTION;
break;
@@ -2366,131 +2394,12 @@ 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,
+void Curl_multi_set_easy_connection(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)
- pipeline = conn->send_pipe;
- else {
- if(conn->server_supports_pipelining &&
- pipeLen < MAX_PIPELINE_LENGTH)
- pipeline = conn->send_pipe;
- else
- pipeline = conn->pend_pipe;
- }
-
- 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 */
-#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);
- }
-
- return rc;
-}
-
-static int checkPendPipeline(struct connectdata *conn)
-{
- int result = 0;
- 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) {
- struct curl_llist_element *curr = conn->pend_pipe->head;
- const size_t maxPipeLen =
- conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1;
-
- while(pipeLen < maxPipeLen && curr) {
- Curl_llist_move(conn->pend_pipe, curr,
- conn->send_pipe, conn->send_pipe->tail);
- Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER);
- ++result; /* count how many handles we moved */
- curr = conn->pend_pipe->head;
- ++pipeLen;
- }
- }
-
- 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 */
- 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);
- }
- }
-
- return result;
-}
-
-/* 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;
-
- 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);
-
- 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 */
-#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);
- }
-
- /* 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;
- }
+ handle->set.one_easy->easy_conn = conn;
}
-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)
{
@@ -2670,6 +2579,56 @@ CURLMcode curl_multi_assign(CURLM *multi_handle,
return CURLM_OK;
}
+size_t Curl_multi_max_host_connections(struct Curl_multi *multi)
+{
+ return multi ? multi->max_host_connections : 0;
+}
+
+size_t Curl_multi_max_total_connections(struct Curl_multi *multi)
+{
+ return multi ? multi->max_total_connections : 0;
+}
+
+size_t Curl_multi_max_pipeline_length(struct Curl_multi *multi)
+{
+ return multi ? multi->max_pipeline_length : 0;
+}
+
+curl_off_t Curl_multi_content_length_penalty_size(struct Curl_multi *multi)
+{
+ return multi ? multi->content_length_penalty_size : 0;
+}
+
+curl_off_t Curl_multi_chunk_length_penalty_size(struct Curl_multi *multi)
+{
+ return multi ? multi->chunk_length_penalty_size : 0;
+}
+
+struct curl_llist *Curl_multi_pipelining_site_bl(struct Curl_multi *multi)
+{
+ return multi->pipelining_site_bl;
+}
+
+struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi)
+{
+ return multi->pipelining_server_bl;
+}
+
+void Curl_multi_process_pending_handles(struct Curl_multi *multi)
+{
+ struct Curl_one_easy *easy;
+
+ easy=multi->easy.next;
+ while(easy != &multi->easy) {
+ if(easy->state == CURLM_STATE_CONNECT_PEND) {
+ multistate(easy, CURLM_STATE_CONNECT);
+ /* Make sure that the handle will be processed soonish. */
+ Curl_expire(easy->easy_handle, 1);
+ }
+ easy = easy->next; /* operate on next handle */
+ }
+}
+
#ifdef DEBUGBUILD
void Curl_multi_dump(const struct Curl_multi *multi_handle)
{