Skip to content

Commit 3907d67

Browse files
committed
casync-http: add max-host-connections option
This commit introduces a new option --max-host-connections=<MAX> that limits the number of connections to a single host. See https://curl.haxx.se/libcurl/c/CURLMOPT_MAX_HOST_CONNECTIONS.html.
1 parent d07cfc0 commit 3907d67

File tree

7 files changed

+113
-0
lines changed

7 files changed

+113
-0
lines changed

doc/casync.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ General options:
165165
--cache-auto, -c Pick encoder cache directory automatically
166166
--rate-limit-bps=<LIMIT> Maximum bandwidth in bytes/s for remote communication
167167
--max-active-chunks=<MAX> Maximum number of simultaneously active chunks for remote communication
168+
--max-host-connections=<MAX> Maximum number of connections to a single host for remote communication
168169
--exclude-nodump=no Don't exclude files with chattr(1)'s +d **nodump** flag when creating archive
169170
--exclude-submounts=yes Exclude submounts when creating archive
170171
--exclude-file=no Don't respect .caexclude files in the file tree

src/caremote.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ struct CaRemote {
6161
int log_level;
6262
uint64_t rate_limit_bps;
6363
unsigned max_active_chunks;
64+
unsigned max_host_connections;
6465

6566
ReallocBuffer input_buffer;
6667
ReallocBuffer output_buffer;
@@ -258,6 +259,15 @@ int ca_remote_set_max_active_chunks(CaRemote *rr, unsigned max_active_chunks) {
258259
return 0;
259260
}
260261

262+
int ca_remote_set_max_host_connections(CaRemote *rr, unsigned max_host_connections) {
263+
if (!rr)
264+
return -EINVAL;
265+
266+
rr->max_host_connections = max_host_connections;
267+
268+
return 0;
269+
}
270+
261271
int ca_remote_set_local_feature_flags(CaRemote *rr, uint64_t flags) {
262272
if (!rr)
263273
return -EINVAL;
@@ -1014,6 +1024,9 @@ static int ca_remote_start(CaRemote *rr) {
10141024
if (rr->max_active_chunks)
10151025
argc++;
10161026

1027+
if (rr->max_host_connections)
1028+
argc++;
1029+
10171030
args = newa(char*, argc + 1);
10181031

10191032
if (rr->callout) {
@@ -1067,6 +1080,14 @@ static int ca_remote_start(CaRemote *rr) {
10671080
i++;
10681081
}
10691082

1083+
if (rr->max_host_connections) {
1084+
r = asprintf(args + i, "--max-host-connections=%u", rr->max_host_connections);
1085+
if (r < 0)
1086+
return log_oom();
1087+
1088+
i++;
1089+
}
1090+
10701091
args[i + CA_REMOTE_ARG_OPERATION] = (char*) ((rr->local_feature_flags & (CA_PROTOCOL_PUSH_CHUNKS|CA_PROTOCOL_PUSH_INDEX|CA_PROTOCOL_PUSH_ARCHIVE)) ? "push" : "pull");
10711092
args[i + CA_REMOTE_ARG_BASE_URL] = /* rr->base_url ? rr->base_url + skip :*/ (char*) "-";
10721093
args[i + CA_REMOTE_ARG_ARCHIVE_URL] = rr->archive_url ? rr->archive_url + skip : (char*) "-";

src/caremote.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ int ca_remote_get_digest_type(CaRemote *rr, CaDigestType *ret);
5353
int ca_remote_set_log_level(CaRemote *rr, int log_level);
5454
int ca_remote_set_rate_limit_bps(CaRemote *rr, uint64_t rate_limit_bps);
5555
int ca_remote_set_max_active_chunks(CaRemote *rr, unsigned max_active_chunks);
56+
int ca_remote_set_max_host_connections(CaRemote *rr, unsigned max_max_connections);
5657

5758
int ca_remote_set_io_fds(CaRemote *rr, int input_fd, int output_fd);
5859
int ca_remote_get_io_fds(CaRemote *rr, int *ret_input_fd, int *ret_output_fd);

src/casync-http.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,23 @@
3939
*/
4040
#define MAX_ACTIVE_CHUNKS 64
4141

42+
/* This is the maximum number of parallel connections per host. This should have
43+
* no effect in case we're doing HTTP/2 with one connection and multiplexing.
44+
* However, if we're doing HTTP/1, curl will open parallel connections, as HTTP/1
45+
* pipelining is no longer supported since libcurl 7.62.
46+
*
47+
* We want to make sure that we don't open too many parallel connections per host.
48+
* It seems that the norm for web browsers ranges from 6 to 8.
49+
*/
50+
#define MAX_HOST_CONNECTIONS 8
51+
4252
static volatile sig_atomic_t quit = false;
4353

4454
static int arg_log_level = -1;
4555
static bool arg_verbose = false;
4656
static curl_off_t arg_rate_limit_bps = 0;
4757
static unsigned arg_max_active_chunks = MAX_ACTIVE_CHUNKS;
58+
static unsigned arg_max_host_connections = MAX_HOST_CONNECTIONS;
4859
static bool arg_ssl_trust_peer = false;
4960

5061
typedef enum Protocol {
@@ -247,6 +258,8 @@ static int make_curl_multi_handle(CURLM **ret) {
247258
if (!h)
248259
return log_oom();
249260

261+
CURL_SETOPT_MULTI(h, CURLMOPT_MAX_HOST_CONNECTIONS, arg_max_host_connections);
262+
250263
/* Default since libcurl 7.62.0 */
251264
CURL_SETOPT_MULTI_CANFAIL(h, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
252265

@@ -1231,6 +1244,7 @@ static int parse_argv(int argc, char *argv[]) {
12311244
enum {
12321245
ARG_RATE_LIMIT_BPS = 0x100,
12331246
ARG_MAX_ACTIVE_CHUNKS,
1247+
ARG_MAX_HOST_CONNECTIONS,
12341248
ARG_SSL_TRUST_PEER,
12351249
};
12361250

@@ -1240,6 +1254,7 @@ static int parse_argv(int argc, char *argv[]) {
12401254
{ "verbose", no_argument, NULL, 'v' },
12411255
{ "rate-limit-bps", required_argument, NULL, ARG_RATE_LIMIT_BPS },
12421256
{ "max-active-chunks", required_argument, NULL, ARG_MAX_ACTIVE_CHUNKS },
1257+
{ "max-host-connections", required_argument, NULL, ARG_MAX_HOST_CONNECTIONS },
12431258
{ "ssl-trust-peer", no_argument, NULL, ARG_SSL_TRUST_PEER },
12441259
{}
12451260
};
@@ -1298,6 +1313,14 @@ static int parse_argv(int argc, char *argv[]) {
12981313
}
12991314
break;
13001315

1316+
case ARG_MAX_HOST_CONNECTIONS:
1317+
r = safe_atou(optarg, &arg_max_host_connections);
1318+
if (r < 0 || arg_max_host_connections == 0) {
1319+
log_error("Invalid value for max-host-connections, refusing");
1320+
return -EINVAL;
1321+
}
1322+
break;
1323+
13011324
case ARG_SSL_TRUST_PEER:
13021325
arg_ssl_trust_peer = true;
13031326
break;

src/casync-tool.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ static size_t arg_chunk_size_avg = 0;
6767
static size_t arg_chunk_size_max = 0;
6868
static uint64_t arg_rate_limit_bps = UINT64_MAX;
6969
static unsigned arg_max_active_chunks = 0;
70+
static unsigned arg_max_host_connections = 0;
7071
static uint64_t arg_with = 0;
7172
static uint64_t arg_without = 0;
7273
static uid_t arg_uid_shift = 0, arg_uid_range = 0x10000U;
@@ -110,6 +111,9 @@ static void help(void) {
110111
" communication\n"
111112
" --max-active-chunks=MAX Maximum number of simultaneously active chunks for\n"
112113
" remote communication\n"
114+
" --max-host-connections=MAX\n"
115+
" Maximum number of connections to a single host for\n"
116+
" remote communication\n"
113117
" --exclude-nodump=no Don't exclude files with chattr(1)'s +d 'nodump'\n"
114118
" flag when creating archive\n"
115119
" --exclude-submounts=yes Exclude submounts when creating archive\n"
@@ -332,6 +336,7 @@ static int parse_argv(int argc, char *argv[]) {
332336
ARG_CACHE,
333337
ARG_RATE_LIMIT_BPS,
334338
ARG_MAX_ACTIVE_CHUNKS,
339+
ARG_MAX_HOST_CONNECTIONS,
335340
ARG_WITH,
336341
ARG_WITHOUT,
337342
ARG_WHAT,
@@ -367,6 +372,7 @@ static int parse_argv(int argc, char *argv[]) {
367372
{ "cache-auto", no_argument, NULL, 'c' },
368373
{ "rate-limit-bps", required_argument, NULL, ARG_RATE_LIMIT_BPS },
369374
{ "max-active-chunks", required_argument, NULL, ARG_MAX_ACTIVE_CHUNKS },
375+
{ "max-host-connections", required_argument, NULL, ARG_MAX_HOST_CONNECTIONS },
370376
{ "with", required_argument, NULL, ARG_WITH },
371377
{ "without", required_argument, NULL, ARG_WITHOUT },
372378
{ "what", required_argument, NULL, ARG_WHAT },
@@ -488,6 +494,14 @@ static int parse_argv(int argc, char *argv[]) {
488494
}
489495
break;
490496

497+
case ARG_MAX_HOST_CONNECTIONS:
498+
r = safe_atou(optarg, &arg_max_host_connections);
499+
if (r < 0) {
500+
log_error("Failed to parse --max-host-connections= value %s", optarg);
501+
return -EINVAL;
502+
}
503+
break;
504+
491505
case ARG_WITH: {
492506
uint64_t u;
493507

@@ -1349,6 +1363,12 @@ static int verb_make(int argc, char *argv[]) {
13491363
return log_error_errno(r, "Failed to set max active chunks: %m");
13501364
}
13511365

1366+
if (arg_max_host_connections) {
1367+
r = ca_sync_set_max_host_connections(s, arg_max_host_connections);
1368+
if (r < 0)
1369+
return log_error_errno(r, "Failed to set max host connections: %m");
1370+
}
1371+
13521372
r = ca_sync_set_base_fd(s, input_fd);
13531373
if (r < 0)
13541374
return log_error_errno(r, "Failed to set sync base: %m");
@@ -1660,6 +1680,12 @@ static int verb_extract(int argc, char *argv[]) {
16601680
return log_error_errno(r, "Failed to set max active chunks: %m");
16611681
}
16621682

1683+
if (arg_max_host_connections) {
1684+
r = ca_sync_set_max_host_connections(s, arg_max_host_connections);
1685+
if (r < 0)
1686+
return log_error_errno(r, "Failed to set max host connections: %m");
1687+
}
1688+
16631689
if (seek_path) {
16641690
if (output_fd >= 0)
16651691
r = ca_sync_set_boundary_fd(s, output_fd);
@@ -2827,6 +2853,12 @@ static int verb_mount(int argc, char *argv[]) {
28272853
return log_error_errno(r, "Failed to set max active chunks: %m");
28282854
}
28292855

2856+
if (arg_max_host_connections) {
2857+
r = ca_sync_set_max_host_connections(s, arg_max_host_connections);
2858+
if (r < 0)
2859+
return log_error_errno(r, "Failed to set max host connections: %m");
2860+
}
2861+
28302862
if (operation == MOUNT_ARCHIVE) {
28312863
if (input_fd >= 0)
28322864
r = ca_sync_set_archive_fd(s, input_fd);
@@ -2959,6 +2991,12 @@ static int verb_mkdev(int argc, char *argv[]) {
29592991
return log_error_errno(r, "Failed to set max active chunks: %m");
29602992
}
29612993

2994+
if (arg_max_host_connections) {
2995+
r = ca_sync_set_max_host_connections(s, arg_max_host_connections);
2996+
if (r < 0)
2997+
return log_error_errno(r, "Failed to set max host connections: %m");
2998+
}
2999+
29623000
if (operation == MKDEV_BLOB) {
29633001
if (input_fd >= 0)
29643002
r = ca_sync_set_archive_fd(s, input_fd);
@@ -3532,6 +3570,12 @@ static int verb_pull(int argc, char *argv[]) {
35323570
return log_error_errno(r, "Failed to set max active chunks: %m");
35333571
}
35343572

3573+
if (arg_max_host_connections) {
3574+
r = ca_remote_set_max_host_connections(rr, arg_max_host_connections);
3575+
if (r < 0)
3576+
return log_error_errno(r, "Failed to set max host connections: %m");
3577+
}
3578+
35353579
r = ca_remote_set_io_fds(rr, STDIN_FILENO, STDOUT_FILENO);
35363580
if (r < 0)
35373581
return log_error_errno(r, "Failed to set I/O file descriptors: %m");
@@ -3697,6 +3741,12 @@ static int verb_push(int argc, char *argv[]) {
36973741
return log_error_errno(r, "Failed to set max active chunks: %m");
36983742
}
36993743

3744+
if (arg_max_host_connections) {
3745+
r = ca_remote_set_max_host_connections(rr, arg_max_host_connections);
3746+
if (r < 0)
3747+
return log_error_errno(r, "Failed to set max host connections: %m");
3748+
}
3749+
37003750
r = ca_remote_set_io_fds(rr, STDIN_FILENO, STDOUT_FILENO);
37013751
if (r < 0)
37023752
log_error_errno(r, "Failed to set I/O file descriptors: %m");

src/casync.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ struct CaSync {
113113
int log_level;
114114
size_t rate_limit_bps;
115115
unsigned max_active_chunks;
116+
unsigned max_host_connections;
116117

117118
uint64_t feature_flags;
118119
uint64_t feature_flags_mask;
@@ -532,6 +533,15 @@ int ca_sync_set_max_active_chunks(CaSync *s, unsigned max_active_chunks) {
532533
return 0;
533534
}
534535

536+
int ca_sync_set_max_host_connections(CaSync *s, unsigned max_host_connections) {
537+
if (!s)
538+
return -EINVAL;
539+
540+
s->max_host_connections = max_host_connections;
541+
542+
return 0;
543+
}
544+
535545
int ca_sync_set_rate_limit_bps(CaSync *s, uint64_t rate_limit_bps) {
536546
if (!s)
537547
return -EINVAL;
@@ -710,6 +720,12 @@ int ca_sync_set_index_remote(CaSync *s, const char *url) {
710720
return r;
711721
}
712722

723+
if (s->max_host_connections > 0) {
724+
r = ca_remote_set_max_host_connections(s->remote_index, s->max_host_connections);
725+
if (r < 0)
726+
return r;
727+
}
728+
713729
r = ca_remote_set_index_url(s->remote_index, url);
714730
if (r < 0)
715731
return r;

src/casync.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(CaSync *, ca_sync_unref);
3434
int ca_sync_set_log_level(CaSync *s, int log_level);
3535
int ca_sync_set_rate_limit_bps(CaSync *s, uint64_t rate_limit_bps);
3636
int ca_sync_set_max_active_chunks(CaSync *s, unsigned max_active_chunks);
37+
int ca_sync_set_max_host_connections(CaSync *s, unsigned max_host_connection);
3738

3839
int ca_sync_set_feature_flags(CaSync *s, uint64_t flags);
3940
int ca_sync_get_feature_flags(CaSync *s, uint64_t *ret);

0 commit comments

Comments
 (0)