Skip to content

Commit 0f14788

Browse files
committed
Multiple pipelines and limiting the number of connections.
Introducing a number of options to the multi interface that allows for multiple pipelines to the same host, in order to optimize the balance between the penalty for opening new connections and the potential pipelining latency. Two new options for limiting the number of connections: CURLMOPT_MAX_HOST_CONNECTIONS - Limits the number of running connections to the same host. When adding a handle that exceeds this limit, that handle will be put in a pending state until another handle is finished, so we can reuse the connection. CURLMOPT_MAX_TOTAL_CONNECTIONS - Limits the number of connections in total. When adding a handle that exceeds this limit, that handle will be put in a pending state until another handle is finished. The free connection will then be reused, if possible, or closed if the pending handle can't reuse it. Several new options for pipelining: CURLMOPT_MAX_PIPELINE_LENGTH - Limits the pipeling length. If a pipeline is "full" when a connection is to be reused, a new connection will be opened if the CURLMOPT_MAX_xxx_CONNECTIONS limits allow it. If not, the handle will be put in a pending state until a connection is ready (either free or a pipe got shorter). CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE - A pipelined connection will not be reused if it is currently processing a transfer with a content length that is larger than this. CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE - A pipelined connection will not be reused if it is currently processing a chunk larger than this. CURLMOPT_PIPELINING_SITE_BL - A blacklist of hosts that don't allow pipelining. CURLMOPT_PIPELINING_SERVER_BL - A blacklist of server types that don't allow pipelining. See the curl_multi_setopt() man page for details.
1 parent 911b2d3 commit 0f14788

37 files changed

+2210
-279
lines changed

docs/libcurl/curl_multi_setopt.3

+106
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,112 @@ This option is for the multi handle's use only, when using the easy interface
9595
you should instead use the \fICURLOPT_MAXCONNECTS\fP option.
9696

9797
(Added in 7.16.3)
98+
.IP CURLMOPT_MAX_HOST_CONNECTIONS
99+
Pass a long. The set number will be used as the maximum amount of
100+
simultaneously open connections to a single host. For each new session to
101+
a host, libcurl will open a new connection up to the limit set by
102+
CURLMOPT_MAX_HOST_CONNECTIONS. When the limit is reached, the sessions will
103+
be pending until there are available connections. If CURLMOPT_PIPELINING is
104+
1, libcurl will try to pipeline if the host is capable of it.
105+
106+
The default value is 0, which means that there is no limit.
107+
However, for backwards compatibility, setting it to 0 when CURLMOPT_PIPELINING
108+
is 1 will not be treated as unlimited. Instead it will open only 1 connection
109+
and try to pipeline on it.
110+
111+
(Added in 7.30.0)
112+
.IP CURLMOPT_MAX_PIPELINE_LENGTH
113+
Pass a long. The set number will be used as the maximum amount of requests
114+
in a pipelined connection. When this limit is reached, libcurl will use another
115+
connection to the same host (see CURLMOPT_MAX_HOST_CONNECTIONS), or queue the
116+
requests until one of the pipelines to the host is ready to accept a request.
117+
Thus, the total number of requests in-flight is CURLMOPT_MAX_HOST_CONNECTIONS *
118+
CURLMOPT_MAX_PIPELINE_LENGTH.
119+
The default value is 5.
120+
121+
(Added in 7.30.0)
122+
.IP CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE
123+
Pass a long. If a pipelined connection is currently processing a request
124+
with a Content-Length larger than CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, that
125+
connection will not be considered for additional requests, even if it is
126+
shorter than CURLMOPT_MAX_PIPELINE_LENGTH.
127+
The default value is 0, which means that the penalization is inactive.
128+
129+
(Added in 7.30.0)
130+
.IP CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE
131+
Pass a long. If a pipelined connection is currently processing a
132+
chunked (Transfer-encoding: chunked) request with a current chunk length
133+
larger than CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, that connection will not be
134+
considered for additional requests, even if it is shorter than
135+
CURLMOPT_MAX_PIPELINE_LENGTH.
136+
The default value is 0, which means that the penalization is inactive.
137+
138+
(Added in 7.30.0)
139+
.IP CURLMOPT_PIPELINING_SITE_BL
140+
Pass an array of char *, ending with NULL. This is a list of sites that are
141+
blacklisted from pipelining, i.e sites that are known to not support HTTP
142+
pipelining. The array is copied by libcurl.
143+
144+
The default value is NULL, which means that there is no blacklist.
145+
146+
Pass a NULL pointer to clear the blacklist.
147+
148+
Example:
149+
150+
.nf
151+
site_blacklist[] =
152+
{
153+
"www.haxx.se",
154+
"www.example.com:1234",
155+
NULL
156+
};
157+
158+
curl_multi_setopt(m, CURLMOPT_PIPELINE_SITE_BL, site_blacklist);
159+
.fi
160+
161+
(Added in 7.30.0)
162+
.IP CURLMOPT_PIPELINING_SERVER_BL
163+
Pass an array of char *, ending with NULL. This is a list of server types
164+
prefixes (in the Server: HTTP header) that are blacklisted from pipelining,
165+
i.e server types that are known to not support HTTP pipelining. The array is
166+
copied by libcurl.
167+
168+
Note that the comparison matches if the Server: header begins with the string
169+
in the blacklist, i.e "Server: Ninja 1.2.3" and "Server: Ninja 1.4.0" can
170+
both be blacklisted by having "Ninja" in the backlist.
171+
172+
The default value is NULL, which means that there is no blacklist.
173+
174+
Pass a NULL pointer to clear the blacklist.
175+
176+
Example:
177+
178+
.nf
179+
server_blacklist[] =
180+
{
181+
"Microsoft-IIS/6.0",
182+
"nginx/0.8.54",
183+
NULL
184+
};
185+
186+
curl_multi_setopt(m, CURLMOPT_PIPELINE_SERVER_BL, server_blacklist);
187+
.fi
188+
189+
(Added in 7.30.0)
190+
.IP CURLMOPT_MAX_TOTAL_CONNECTIONS
191+
Pass a long. The set number will be used as the maximum amount of
192+
simultaneously open connections in total. For each new session, libcurl
193+
will open a new connection up to the limit set by
194+
CURLMOPT_MAX_TOTAL_CONNECTIONS. When the limit is reached, the sessions will
195+
be pending until there are available connections. If CURLMOPT_PIPELINING is
196+
1, libcurl will try to pipeline if the host is capable of it.
197+
198+
The default value is 0, which means that there is no limit.
199+
However, for backwards compatibility, setting it to 0 when CURLMOPT_PIPELINING
200+
is 1 will not be treated as unlimited. Instead it will open only 1 connection
201+
and try to pipeline on it.
202+
203+
(Added in 7.30.0)
98204
.SH RETURNS
99205
The standard CURLMcode for multi interface error codes. Note that it returns a
100206
CURLM_UNKNOWN_OPTION if you try setting an option that this version of libcurl

docs/libcurl/libcurl-errors.3

+3
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ Mismatch of RTSP Session Identifiers.
240240
Unable to parse FTP file list (during FTP wildcard downloading).
241241
.IP "CURLE_CHUNK_FAILED (88)"
242242
Chunk callback reported error.
243+
.IP "CURLE_NO_CONNECTION_AVAILABLE (89)"
244+
(For internal use only, will never be returned by libcurl) No connection
245+
available, the session will be queued. (added in 7.30.0)
243246
.IP "CURLE_OBSOLETE*"
244247
These error codes will never be returned. They were used in an old libcurl
245248
version and are currently unused.

docs/libcurl/symbols-in-versions

+8
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ CURLE_LDAP_SEARCH_FAILED 7.1
8585
CURLE_LIBRARY_NOT_FOUND 7.1 7.17.0
8686
CURLE_LOGIN_DENIED 7.13.1
8787
CURLE_MALFORMAT_USER 7.1 7.17.0
88+
CURLE_NO_CONNECTION_AVAILABLE 7.30.0
8889
CURLE_NOT_BUILT_IN 7.21.5
8990
CURLE_OK 7.1
9091
CURLE_OPERATION_TIMEDOUT 7.10.2
@@ -267,8 +268,15 @@ CURLKHTYPE_DSS 7.19.6
267268
CURLKHTYPE_RSA 7.19.6
268269
CURLKHTYPE_RSA1 7.19.6
269270
CURLKHTYPE_UNKNOWN 7.19.6
271+
CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE 7.30.0
272+
CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE 7.30.0
273+
CURLMOPT_MAX_HOST_CONNECTIONS 7.30.0
274+
CURLMOPT_MAX_PIPELINE_LENGTH 7.30.0
275+
CURLMOPT_MAX_TOTAL_CONNECTIONS 7.30.0
270276
CURLMOPT_MAXCONNECTS 7.16.3
271277
CURLMOPT_PIPELINING 7.16.0
278+
CURLMOPT_PIPELINING_SERVER_BL 7.30.0
279+
CURLMOPT_PIPELINING_SITE_BL 7.30.0
272280
CURLMOPT_SOCKETDATA 7.15.4
273281
CURLMOPT_SOCKETFUNCTION 7.15.4
274282
CURLMOPT_TIMERDATA 7.16.0

include/curl/curl.h

+2
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,8 @@ typedef enum {
507507
CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */
508508
CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */
509509
CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */
510+
CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the
511+
session will be queued */
510512
CURL_LAST /* never use! */
511513
} CURLcode;
512514

include/curl/multi.h

+25
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,31 @@ typedef enum {
338338
/* maximum number of entries in the connection cache */
339339
CINIT(MAXCONNECTS, LONG, 6),
340340

341+
/* maximum number of (pipelining) connections to one host */
342+
CINIT(MAX_HOST_CONNECTIONS, LONG, 7),
343+
344+
/* maximum number of requests in a pipeline */
345+
CINIT(MAX_PIPELINE_LENGTH, LONG, 8),
346+
347+
/* a connection with a content-length longer than this
348+
will not be considered for pipelining */
349+
CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9),
350+
351+
/* a connection with a chunk length longer than this
352+
will not be considered for pipelining */
353+
CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10),
354+
355+
/* a list of site names(+port) that are blacklisted from
356+
pipelining */
357+
CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11),
358+
359+
/* a list of server types that are blacklisted from
360+
pipelining */
361+
CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12),
362+
363+
/* maximum number of open connections in total */
364+
CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13),
365+
341366
CURLMOPT_LASTENTRY /* the last unused */
342367
} CURLMoption;
343368

lib/Makefile.inc

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
2525
http_proxy.c non-ascii.c asyn-ares.c asyn-thread.c curl_gssapi.c \
2626
curl_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_ntlm_msgs.c \
2727
curl_sasl.c curl_schannel.c curl_multibyte.c curl_darwinssl.c \
28-
hostcheck.c bundles.c conncache.c
28+
hostcheck.c bundles.c conncache.c pipeline.c
2929

3030
HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
3131
progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \
@@ -44,4 +44,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
4444
asyn.h curl_ntlm.h curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h \
4545
curl_ntlm_msgs.h curl_sasl.h curl_schannel.h curl_multibyte.h \
4646
curl_darwinssl.h hostcheck.h bundles.h conncache.h curl_setup_once.h \
47-
multihandle.h setup-vms.h
47+
multihandle.h setup-vms.h pipeline.h

lib/README.pipelining

-7
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,3 @@ Details
4242
still resolve the second one properly to make sure that they actually _can_
4343
be considered for pipelining. Also, asking for explicit pipelining on handle
4444
X may be tricky when handle X get a closed connection.
45-
46-
- We need options to control max pipeline length, and probably how to behave
47-
if we reach that limit. As was discussed on the list, it can probably be
48-
made very complicated, so perhaps we can think of a way to pass all
49-
variables involved to a callback and let the application decide how to act
50-
in specific situations. Either way, these fancy options are only interesting
51-
to work on when everything is working and we have working apps to test with.

lib/hash.h

-1
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,3 @@ void Curl_hash_print(struct curl_hash *h,
104104

105105

106106
#endif /* HEADER_CURL_HASH_H */
107-

lib/http.c

+19-1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
#include "http_proxy.h"
7474
#include "warnless.h"
7575
#include "non-ascii.h"
76+
#include "bundles.h"
77+
#include "pipeline.h"
7678

7779
#define _MPRINTF_REPLACE /* use our functions only */
7880
#include <curl/mprintf.h>
@@ -3148,13 +3150,19 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
31483150
}
31493151
else if(conn->httpversion >= 11 &&
31503152
!conn->bits.close) {
3153+
struct connectbundle *cb_ptr;
31513154

31523155
/* If HTTP version is >= 1.1 and connection is persistent
31533156
server supports pipelining. */
31543157
DEBUGF(infof(data,
31553158
"HTTP 1.1 or later with persistent connection, "
31563159
"pipelining supported\n"));
3157-
conn->server_supports_pipelining = TRUE;
3160+
/* Activate pipelining if needed */
3161+
cb_ptr = conn->bundle;
3162+
if(cb_ptr) {
3163+
if(!Curl_pipeline_site_blacklisted(data, conn))
3164+
cb_ptr->server_supports_pipelining = TRUE;
3165+
}
31583166
}
31593167

31603168
switch(k->httpcode) {
@@ -3231,6 +3239,16 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
32313239
data->info.contenttype = contenttype;
32323240
}
32333241
}
3242+
else if(checkprefix("Server:", k->p)) {
3243+
char *server_name = copy_header_value(k->p);
3244+
3245+
/* Turn off pipelining if the server version is blacklisted */
3246+
if(conn->bundle && conn->bundle->server_supports_pipelining) {
3247+
if(Curl_pipeline_server_blacklisted(data, server_name))
3248+
conn->bundle->server_supports_pipelining = FALSE;
3249+
}
3250+
Curl_safefree(server_name);
3251+
}
32343252
else if((conn->httpversion == 10) &&
32353253
conn->bits.httpproxy &&
32363254
Curl_compareheader(k->p,

0 commit comments

Comments
 (0)