curl-library
[PATCH 5/7] pipelining: Add CURLMOPT_PIPELINE_POLICY_FUNCTION
Date: Thu, 6 Nov 2014 03:03:19 +0100
Also adds CURLMOPT_PIPELINE_POLICY_DATA, CURL_SUPPORTS_PIPELINING,
CURL_BLACKLISTED, struct curl_pipeline_policy
and curl_pipeline_policy_callback
This allows the user to tweak the maximum number of connections
per host, the maximum number of requests in a pipeline and
to override blacklisting or implement whitelisting on a per
host basis (since bundles are only per host, not host:port - which
imho is a limitation). Finally they can cause libcurl to start
doing pipelining immediately instead after the first reply
is received.
Usage example:
curl_multi_setopt(multi_handle,
CURLMOPT_PIPELINE_POLICY_FUNCTION,
&pipeline_policy_callback);
void pipeline_policy_callback(
char const* hostname,
int port,
curl_pipeline_policy* policy,
void* userp)
{
if(supports_pipelining(host))
policy->flags |= CURL_SUPPORTS_PIPELINING;
if(port == 80)
policy->flags |= CURL_BLACKLISTED;
if(host == "special.case" && port == 12340) {
policy->flags &= ~CURL_BLACKLISTED;
policy->max_pipeline_length = 100;
}
if((policy->flags & CURL_SUPPORTS_PIPELINING))
policy->max_host_connections = 2;
}
--- include/curl/multi.h | 49 ++++++++++++++++++++++++++++++++++++++++++++++ lib/bundles.c | 3 ++- lib/bundles.h | 6 ++++-- lib/conncache.c | 19 +++++++++++++++++- lib/http.c | 11 ++++++----- lib/multi.c | 8 +++++++- lib/multihandle.h | 10 +++++++--- lib/url.c | 19 +++++++----------- packages/OS400/curl.inc.in | 7 +++++++ 9 files changed, 107 insertions(+), 25 deletions(-) diff --git a/include/curl/multi.h b/include/curl/multi.h index 3c4acb0..e2ac30a 100644 --- a/include/curl/multi.h +++ b/include/curl/multi.h @@ -289,6 +289,49 @@ CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles); +/* + * Name: curl_pipeline_policy + * + * Desc: Contains the per site pipeline policy variables. The values default + * to CURLMOPT_MAX_HOST_CONNECTIONS, CURLMOPT_MAX_PIPELINE_LENGTH, + * and whether or not the site appears in CURLMOPT_PIPELINING_SITE_BL + * respectively. CURL_SUPPORTS_PIPELINING is not set. However, these + * values can be overwritten by the user during the + * curl_pipeline_policy_callback set with + * CURLMOPT_PIPELINE_POLICY_FUNCTION. If CURL_SUPPORTS_PIPELINING and + * CURL_BLACKLISTED are both set then the site is blacklisted from + * pipelining. + */ + +#define CURL_SUPPORTS_PIPELINING 1 +#define CURL_BLACKLISTED 2 + +struct curl_pipeline_policy { + size_t max_host_connections; /* the maximum number of simultaneous + connections to the site */ + long max_pipeline_length; /* the maximum amount of requests in + a pipelined connection */ + int flags; /* bit-mask for CURL_SUPPORTS_PIPELINING + and CURL_BLACKLISTED */ +}; + +/* Return TRUE iff bundle supports pipelining and is not blacklisted */ +#define CURL_CAN_PIPELINE(bundle) ((bundle->policy.flags & \ + (CURL_SUPPORTS_PIPELINING|CURL_BLACKLISTED)) == CURL_SUPPORTS_PIPELINING) + +/* + * Name: curl_pipeline_policy_callback + * + * Desc: Called by libcurl whenever the library creates a new bundle for + * some hostname:port combination. + */ +typedef void (*curl_pipeline_policy_callback)( + char const *hostname, /* connected hostname */ + int port, /* connected port */ + struct curl_pipeline_policy* policy, /* writable policy */ + void *userp); /* private callback + pointer */ + #ifndef CURL_ALLOW_OLD_MULTI_SOCKET /* This macro below was added in 7.16.3 to push users who recompile to use the new curl_multi_socket_action() instead of the old curl_multi_socket() @@ -365,6 +408,12 @@ typedef enum { /* maximum number of open connections in total */ CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13), + /* This is the pipeline policy callback function pointer */ + CINIT(PIPELINE_POLICY_FUNCTION, FUNCTIONPOINT, 14), + + /* This is the argument passed to the pipeline policy callback */ + CINIT(PIPELINE_POLICY_DATA, OBJECTPOINT, 15), + CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; diff --git a/lib/bundles.c b/lib/bundles.c index aadf026..e46b878 100644 --- a/lib/bundles.c +++ b/lib/bundles.c @@ -46,6 +46,7 @@ static void conn_llist_dtor(void *user, void *element) } CURLcode Curl_bundle_create(struct SessionHandle *data, + struct curl_pipeline_policy *policy, struct connectbundle **cb_ptr) { (void)data; @@ -55,7 +56,7 @@ CURLcode Curl_bundle_create(struct SessionHandle *data, return CURLE_OUT_OF_MEMORY; (*cb_ptr)->num_connections = 0; - (*cb_ptr)->server_supports_pipelining = FALSE; + (*cb_ptr)->policy = *policy; (*cb_ptr)->conn_list = Curl_llist_alloc((curl_llist_dtor) conn_llist_dtor); if(!(*cb_ptr)->conn_list) { diff --git a/lib/bundles.h b/lib/bundles.h index 3816c40..e418c2c 100644 --- a/lib/bundles.h +++ b/lib/bundles.h @@ -22,14 +22,16 @@ * ***************************************************************************/ +#include <curl/multi.h> + struct connectbundle { - bool server_supports_pipelining; /* TRUE if server supports pipelining, - set after first response */ + struct curl_pipeline_policy policy; /* connect policy */ size_t num_connections; /* Number of connections in the bundle */ struct curl_llist *conn_list; /* The connectdata members of the bundle */ }; CURLcode Curl_bundle_create(struct SessionHandle *data, + struct curl_pipeline_policy *policy, struct connectbundle **cb_ptr); void Curl_bundle_destroy(struct connectbundle *cb_ptr); diff --git a/lib/conncache.c b/lib/conncache.c index d07718e..9d28116 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -33,6 +33,7 @@ #include "rawstr.h" #include "bundles.h" #include "conncache.h" +#include "pipeline.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -130,7 +131,23 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, bundle = Curl_conncache_find_bundle(data->state.conn_cache, conn->host.name); if(!bundle) { - result = Curl_bundle_create(data, &new_bundle); + struct Curl_multi *multi = data->multi; + struct curl_pipeline_policy policy = { 0, 0, 0 }; + if(multi) { + /* policy default values */ + policy.max_host_connections = Curl_multi_max_host_connections(multi); + policy.max_pipeline_length = Curl_multi_max_pipeline_length(multi); + if(Curl_pipeline_site_blacklisted(data, conn)) { + policy.flags = CURL_BLACKLISTED; + } + /* allow user to tweak the policy */ + if(multi->pipeline_policy_cb) + multi->pipeline_policy_cb(conn->host.name, + conn->remote_port, + &policy, + multi->pipeline_policy_userp); + } + result = Curl_bundle_create(data, &policy, &new_bundle); if(result) return result; diff --git a/lib/http.c b/lib/http.c index 2487bac..3a54cf9 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3302,8 +3302,8 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, /* Activate pipelining if needed */ cb_ptr = conn->bundle; if(cb_ptr) { - if(!Curl_pipeline_site_blacklisted(data, conn)) - cb_ptr->server_supports_pipelining = TRUE; + if(!(cb_ptr->policy.flags & CURL_BLACKLISTED)) + cb_ptr->policy.flags |= CURL_SUPPORTS_PIPELINING; } } @@ -3384,10 +3384,11 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, else if(checkprefix("Server:", k->p)) { char *server_name = Curl_copy_header_value(k->p); - /* Turn off pipelining if the server version is blacklisted */ - if(conn->bundle && conn->bundle->server_supports_pipelining) { + /* Check if the server version is blacklisted when + we're doing pipelining */ + if(conn->bundle && CURL_CAN_PIPELINE(conn->bundle)) { if(Curl_pipeline_server_blacklisted(data, server_name)) - conn->bundle->server_supports_pipelining = FALSE; + conn->bundle->policy.flags |= CURL_BLACKLISTED; } Curl_safefree(server_name); } diff --git a/lib/multi.c b/lib/multi.c index 50c4cf8..db1b582 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1659,7 +1659,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, to remove the bundle: we need to remember that this server is capable of pipelining. */ bundle = data->easy_conn->bundle; - keep_bundle = (bundle->server_supports_pipelining && + keep_bundle = (CURL_CAN_PIPELINE(bundle) && (data->result == CURLE_OPERATION_TIMEDOUT || data->easy_conn->bits.retry)); if(keep_bundle) @@ -2415,6 +2415,12 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, case CURLMOPT_MAX_TOTAL_CONNECTIONS: multi->max_total_connections = va_arg(param, long); break; + case CURLMOPT_PIPELINE_POLICY_FUNCTION: + multi->pipeline_policy_cb = va_arg(param, curl_pipeline_policy_callback); + break; + case CURLMOPT_PIPELINE_POLICY_DATA: + multi->pipeline_policy_userp = va_arg(param, void *); + break; default: res = CURLM_UNKNOWN_OPTION; break; diff --git a/lib/multihandle.h b/lib/multihandle.h index 1a4b1d9..a673f96 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -107,14 +107,18 @@ struct Curl_multi { long maxconnects; /* if >0, a fixed limit of the maximum number of entries we're allowed to grow the connection cache to */ - long max_host_connections; /* if >0, a fixed limit of the maximum number + long max_host_connections; /* if >0, the default limit of the maximum number of connections per host */ long max_total_connections; /* if >0, a fixed limit of the maximum number of connections in total */ - long max_pipeline_length; /* if >0, maximum number of requests in a - pipeline */ + /* callback function and user data pointer for the pipeline policy API */ + curl_pipeline_policy_callback pipeline_policy_cb; + void *pipeline_policy_userp; + + long max_pipeline_length; /* if >0, the default maximum number of requests + in a pipeline */ long content_length_penalty_size; /* a connection with a content-length bigger than diff --git a/lib/url.c b/lib/url.c index 81a27ad..b41f5cb 100644 --- a/lib/url.c +++ b/lib/url.c @@ -3021,25 +3021,20 @@ ConnectionExists(struct SessionHandle *data, *force_reuse = FALSE; - /* We can't pipe if the site is blacklisted */ - if(canPipeline && Curl_pipeline_site_blacklisted(data, needle)) { - canPipeline = FALSE; - } - /* Look up the bundle with all the connections to this particular host */ bundle = Curl_conncache_find_bundle(data->state.conn_cache, needle->host.name); if(bundle) { - size_t max_pipe_len = Curl_multi_max_pipeline_length(data->multi); + size_t max_pipe_len = bundle->policy.max_pipeline_length; size_t best_pipe_len = max_pipe_len; struct curl_llist_element *curr; infof(data, "Found bundle for host %s: %p\n", needle->host.name, (void *)bundle); - /* We can't pipe if we don't know anything about the server */ - if(canPipeline && !bundle->server_supports_pipelining) { + /* Check if we are pipelining */ + if(canPipeline && !CURL_CAN_PIPELINE(bundle)) { infof(data, "Server doesn't support pipelining\n"); canPipeline = FALSE; } @@ -5209,7 +5204,6 @@ static CURLcode create_conn(struct SessionHandle *data, bool prot_missing = FALSE; bool no_connections_available = FALSE; bool force_reuse = FALSE; - size_t max_host_connections = Curl_multi_max_host_connections(data->multi); size_t max_total_connections = Curl_multi_max_total_connections(data->multi); *async = FALSE; @@ -5555,7 +5549,8 @@ static CURLcode create_conn(struct SessionHandle *data, infof(data, "Found connection %ld, with requests in the pipe (%zu)\n", conn_temp->connection_id, pipelen); - if(conn_temp->bundle->num_connections < max_host_connections && + if(conn_temp->bundle->num_connections < + conn_temp->bundle->policy.max_host_connections && data->state.conn_cache->num_connections < max_total_connections) { /* We want a new connection anyway */ reuse = FALSE; @@ -5594,8 +5589,8 @@ static CURLcode create_conn(struct SessionHandle *data, bundle = Curl_conncache_find_bundle(data->state.conn_cache, conn->host.name); - if(max_host_connections > 0 && bundle && - (bundle->num_connections >= max_host_connections)) { + if(bundle && bundle->policy.max_host_connections > 0 && + (bundle->num_connections >= bundle->policy.max_host_connections)) { struct connectdata *conn_candidate; /* The bundle is full. Let's see if we can kill a connection. */ diff --git a/packages/OS400/curl.inc.in b/packages/OS400/curl.inc.in index 39adc6a..1d30a99 100644 --- a/packages/OS400/curl.inc.in +++ b/packages/OS400/curl.inc.in @@ -1551,6 +1551,10 @@ d c 10012 d CURLMOPT_MAX_TOTAL_CONNECTIONS... d c 00013 + d CURLMOPT_PIPELINING_POLICY_FUNCTION... + d c 00014 + d CURLMOPT_PIPELINING_POLICY_DATA... + d c 00015 * * Public API enums for RTSP requests. * @@ -1769,6 +1773,9 @@ d curl_socket_callback... d s * based(######ptr######) procptr * + d curl_pipeline_policy_callback... + d s * based(######ptr######) procptr + * d curl_opensocket_callback... d s * based(######ptr######) procptr * -- 2.1.1 ------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.htmlReceived on 2014-11-06