Skip to content

Commit b2041a4

Browse files
Merge remote-tracking branch 'origin/master' into key.placeholder
2 parents 0bede38 + 68fb953 commit b2041a4

File tree

4 files changed

+320
-1
lines changed

4 files changed

+320
-1
lines changed

memtier_benchmark.1

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ and Redis <= 5.x. <USER>:<PASSWORD> can be
3838
specified for memcache_binary or Redis 6.x
3939
or newer with ACL user support.
4040
.TP
41+
\fB\-u\fR, \fB\-\-uri\fR=\fI\,URI\/\fR
42+
Server URI on format redis://user:password@host:port/dbnum
43+
User, password and dbnum are optional. For authentication
44+
without a username, use username 'default'. For TLS, use
45+
the scheme 'rediss'.
46+
.TP
4147
\fB\-\-tls\fR
4248
Enable SSL/TLS transport security
4349
.TP

memtier_benchmark.cpp

Lines changed: 163 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ static void config_print(FILE *file, struct benchmark_config *cfg)
112112
fprintf(file,
113113
"server = %s\n"
114114
"port = %u\n"
115+
"uri = %s\n"
115116
"unix socket = %s\n"
116117
"address family = %s\n"
117118
"protocol = %s\n"
@@ -163,6 +164,7 @@ static void config_print(FILE *file, struct benchmark_config *cfg)
163164
"print-all-runs = %s\n",
164165
cfg->server,
165166
cfg->port,
167+
cfg->uri ? cfg->uri : "",
166168
cfg->unix_socket,
167169
cfg->resolution == AF_UNSPEC ? "Unspecified" : cfg->resolution == AF_INET ? "AF_INET" : "AF_INET6",
168170
get_protocol_name(cfg->protocol),
@@ -222,6 +224,7 @@ static void config_print_to_json(json_handler * jsonhandler, struct benchmark_co
222224

223225
jsonhandler->write_obj("server" ,"\"%s\"", cfg->server);
224226
jsonhandler->write_obj("port" ,"%u", cfg->port);
227+
jsonhandler->write_obj("uri" ,"\"%s\"", cfg->uri ? cfg->uri : "");
225228
jsonhandler->write_obj("unix socket" ,"\"%s\"", cfg->unix_socket);
226229
jsonhandler->write_obj("address family" ,"\"%s\"", cfg->resolution == AF_UNSPEC ? "Unspecified" : cfg->resolution == AF_INET ? "AF_INET" : "AF_INET6");
227230
jsonhandler->write_obj("protocol" ,"\"%s\"", get_protocol_name(cfg->protocol));
@@ -275,6 +278,123 @@ static void config_print_to_json(json_handler * jsonhandler, struct benchmark_co
275278
jsonhandler->close_nesting();
276279
}
277280

281+
// Parse URI and populate config fields
282+
// Returns 0 on success, -1 on error
283+
static int parse_uri(const char *uri, struct benchmark_config *cfg)
284+
{
285+
if (!uri || strlen(uri) == 0) {
286+
fprintf(stderr, "error: empty URI provided.\n");
287+
return -1;
288+
}
289+
290+
// Make a copy to work with
291+
char *uri_copy = strdup(uri);
292+
if (!uri_copy) {
293+
fprintf(stderr, "error: memory allocation failed.\n");
294+
return -1;
295+
}
296+
297+
char *ptr = uri_copy;
298+
299+
// Parse scheme
300+
char *scheme_end = strstr(ptr, "://");
301+
if (!scheme_end) {
302+
fprintf(stderr, "error: invalid URI format, missing scheme.\n");
303+
free(uri_copy);
304+
return -1;
305+
}
306+
307+
*scheme_end = '\0';
308+
if (strcmp(ptr, "redis") == 0) {
309+
// Regular Redis connection
310+
} else if (strcmp(ptr, "rediss") == 0) {
311+
#ifdef USE_TLS
312+
cfg->tls = true;
313+
#else
314+
fprintf(stderr, "error: TLS not supported in this build.\n");
315+
free(uri_copy);
316+
return -1;
317+
#endif
318+
} else {
319+
fprintf(stderr, "error: unsupported URI scheme '%s'. Use 'redis' or 'rediss'.\n", ptr);
320+
free(uri_copy);
321+
return -1;
322+
}
323+
324+
ptr = scheme_end + 3; // Skip "://"
325+
326+
// Parse user:password@host:port/db
327+
char *auth_end = strchr(ptr, '@');
328+
char *host_start = ptr;
329+
330+
if (auth_end) {
331+
// Authentication present
332+
*auth_end = '\0';
333+
char *colon = strchr(ptr, ':');
334+
if (colon) {
335+
// user:password format
336+
*colon = '\0';
337+
char *user = ptr;
338+
char *password = colon + 1;
339+
340+
// Combine as user:password for authenticate field
341+
int auth_len = strlen(user) + strlen(password) + 2;
342+
char *auth_str = (char*)malloc(auth_len);
343+
if (!auth_str) {
344+
fprintf(stderr, "error: memory allocation failed.\n");
345+
free(uri_copy);
346+
return -1;
347+
}
348+
snprintf(auth_str, auth_len, "%s:%s", user, password);
349+
cfg->authenticate = auth_str;
350+
} else {
351+
// Just password (default user)
352+
cfg->authenticate = strdup(ptr);
353+
}
354+
host_start = auth_end + 1;
355+
}
356+
357+
// Parse host:port/db
358+
char *db_start = strchr(host_start, '/');
359+
if (db_start) {
360+
*db_start = '\0';
361+
db_start++;
362+
if (strlen(db_start) > 0) {
363+
char *endptr;
364+
int db = (int)strtol(db_start, &endptr, 10);
365+
if (*endptr != '\0' || db < 0) {
366+
fprintf(stderr, "error: invalid database number '%s'.\n", db_start);
367+
free(uri_copy);
368+
return -1;
369+
}
370+
cfg->select_db = db;
371+
}
372+
}
373+
374+
// Parse host:port
375+
char *port_start = strchr(host_start, ':');
376+
if (port_start) {
377+
*port_start = '\0';
378+
port_start++;
379+
char *endptr;
380+
unsigned long port = strtoul(port_start, &endptr, 10);
381+
if (*endptr != '\0' || port == 0 || port > 65535) {
382+
fprintf(stderr, "error: invalid port number '%s'.\n", port_start);
383+
free(uri_copy);
384+
return -1;
385+
}
386+
cfg->port = (unsigned short)port;
387+
}
388+
389+
// Set host
390+
if (strlen(host_start) > 0) {
391+
cfg->server = strdup(host_start);
392+
}
393+
394+
free(uri_copy);
395+
return 0;
396+
}
397+
278398
static void config_init_defaults(struct benchmark_config *cfg)
279399
{
280400
if (!cfg->server && !cfg->unix_socket)
@@ -434,6 +554,7 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf
434554
o_tls_protocols,
435555
o_hdr_file_prefix,
436556
o_rate_limiting,
557+
o_uri,
437558
o_help
438559
};
439560

@@ -505,14 +626,15 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf
505626
{ "command-key-pattern", 1, 0, o_command_key_pattern },
506627
{ "command-ratio", 1, 0, o_command_ratio },
507628
{ "rate-limiting", 1, 0, o_rate_limiting },
629+
{ "uri", 1, 0, o_uri },
508630
{ NULL, 0, 0, 0 }
509631
};
510632

511633
int option_index;
512634
int c;
513635
char *endptr;
514636
while ((c = getopt_long(argc, argv,
515-
"vs:S:p:P:o:x:DRn:c:t:d:a:h:46", long_options, &option_index)) != -1)
637+
"vs:S:p:P:o:x:DRn:c:t:d:a:h:46u:", long_options, &option_index)) != -1)
516638
{
517639
switch (c) {
518640
case o_help:
@@ -813,6 +935,10 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf
813935
case 'a':
814936
cfg->authenticate = optarg;
815937
break;
938+
case 'u':
939+
case o_uri:
940+
cfg->uri = optarg;
941+
break;
816942
case o_select_db:
817943
cfg->select_db = (int) strtoul(optarg, &endptr, 10);
818944
if (cfg->select_db < 0 || !endptr || *endptr != '\0') {
@@ -980,6 +1106,10 @@ void usage() {
9801106
" and Redis <= 5.x. <USER>:<PASSWORD> can be\n"
9811107
" specified for memcache_binary or Redis 6.x\n"
9821108
" or newer with ACL user support.\n"
1109+
" -u, --uri=URI Server URI on format redis://user:password@host:port/dbnum\n"
1110+
" User, password and dbnum are optional. For authentication\n"
1111+
" without a username, use username 'default'. For TLS, use\n"
1112+
" the scheme 'rediss'.\n"
9831113
#ifdef USE_TLS
9841114
" --tls Enable SSL/TLS transport security\n"
9851115
" --cert=FILE Use specified client certificate for TLS\n"
@@ -1368,6 +1498,38 @@ int main(int argc, char *argv[])
13681498
usage();
13691499
}
13701500

1501+
// Process URI if provided
1502+
if (cfg.uri) {
1503+
// Check for conflicts with individual connection parameters
1504+
if (cfg.server && strcmp(cfg.server, "localhost") != 0) {
1505+
fprintf(stderr, "warning: both URI and --host/--server specified, URI takes precedence.\n");
1506+
}
1507+
if (cfg.port && cfg.port != 6379) {
1508+
fprintf(stderr, "warning: both URI and --port specified, URI takes precedence.\n");
1509+
}
1510+
if (cfg.authenticate) {
1511+
fprintf(stderr, "warning: both URI and --authenticate specified, URI takes precedence.\n");
1512+
}
1513+
if (cfg.select_db) {
1514+
fprintf(stderr, "warning: both URI and --select-db specified, URI takes precedence.\n");
1515+
}
1516+
#ifdef USE_TLS
1517+
if (cfg.tls) {
1518+
fprintf(stderr, "warning: both URI and --tls specified, URI takes precedence.\n");
1519+
}
1520+
#endif
1521+
1522+
if (parse_uri(cfg.uri, &cfg) < 0) {
1523+
exit(1);
1524+
}
1525+
1526+
// Validate cluster mode constraints
1527+
if (cfg.cluster_mode && cfg.select_db > 0) {
1528+
fprintf(stderr, "error: database selection not supported in cluster mode. Redis Cluster only supports database 0.\n");
1529+
exit(1);
1530+
}
1531+
}
1532+
13711533
config_init_defaults(&cfg);
13721534
log_level = cfg.debug;
13731535
if (cfg.show_config) {

memtier_benchmark.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ struct benchmark_config {
9595
int multi_key_get;
9696
const char *authenticate;
9797
int select_db;
98+
const char *uri;
9899
bool no_expiry;
99100
bool resolve_on_connect;
100101
// WAIT related

0 commit comments

Comments
 (0)