@@ -112,6 +112,7 @@ static void config_print(FILE *file, struct benchmark_config *cfg)
112
112
fprintf (file,
113
113
" server = %s\n "
114
114
" port = %u\n "
115
+ " uri = %s\n "
115
116
" unix socket = %s\n "
116
117
" address family = %s\n "
117
118
" protocol = %s\n "
@@ -163,6 +164,7 @@ static void config_print(FILE *file, struct benchmark_config *cfg)
163
164
" print-all-runs = %s\n " ,
164
165
cfg->server ,
165
166
cfg->port ,
167
+ cfg->uri ? cfg->uri : " " ,
166
168
cfg->unix_socket ,
167
169
cfg->resolution == AF_UNSPEC ? " Unspecified" : cfg->resolution == AF_INET ? " AF_INET" : " AF_INET6" ,
168
170
get_protocol_name (cfg->protocol ),
@@ -222,6 +224,7 @@ static void config_print_to_json(json_handler * jsonhandler, struct benchmark_co
222
224
223
225
jsonhandler->write_obj (" server" ," \" %s\" " , cfg->server );
224
226
jsonhandler->write_obj (" port" ," %u" , cfg->port );
227
+ jsonhandler->write_obj (" uri" ," \" %s\" " , cfg->uri ? cfg->uri : " " );
225
228
jsonhandler->write_obj (" unix socket" ," \" %s\" " , cfg->unix_socket );
226
229
jsonhandler->write_obj (" address family" ," \" %s\" " , cfg->resolution == AF_UNSPEC ? " Unspecified" : cfg->resolution == AF_INET ? " AF_INET" : " AF_INET6" );
227
230
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
275
278
jsonhandler->close_nesting ();
276
279
}
277
280
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
+
278
398
static void config_init_defaults (struct benchmark_config *cfg)
279
399
{
280
400
if (!cfg->server && !cfg->unix_socket )
@@ -434,6 +554,7 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf
434
554
o_tls_protocols,
435
555
o_hdr_file_prefix,
436
556
o_rate_limiting,
557
+ o_uri,
437
558
o_help
438
559
};
439
560
@@ -505,14 +626,15 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf
505
626
{ " command-key-pattern" , 1 , 0 , o_command_key_pattern },
506
627
{ " command-ratio" , 1 , 0 , o_command_ratio },
507
628
{ " rate-limiting" , 1 , 0 , o_rate_limiting },
629
+ { " uri" , 1 , 0 , o_uri },
508
630
{ NULL , 0 , 0 , 0 }
509
631
};
510
632
511
633
int option_index;
512
634
int c;
513
635
char *endptr;
514
636
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 )
516
638
{
517
639
switch (c) {
518
640
case o_help:
@@ -813,6 +935,10 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf
813
935
case ' a' :
814
936
cfg->authenticate = optarg;
815
937
break ;
938
+ case ' u' :
939
+ case o_uri:
940
+ cfg->uri = optarg;
941
+ break ;
816
942
case o_select_db:
817
943
cfg->select_db = (int ) strtoul (optarg, &endptr, 10 );
818
944
if (cfg->select_db < 0 || !endptr || *endptr != ' \0 ' ) {
@@ -980,6 +1106,10 @@ void usage() {
980
1106
" and Redis <= 5.x. <USER>:<PASSWORD> can be\n "
981
1107
" specified for memcache_binary or Redis 6.x\n "
982
1108
" 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 "
983
1113
#ifdef USE_TLS
984
1114
" --tls Enable SSL/TLS transport security\n "
985
1115
" --cert=FILE Use specified client certificate for TLS\n "
@@ -1368,6 +1498,38 @@ int main(int argc, char *argv[])
1368
1498
usage ();
1369
1499
}
1370
1500
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
+
1371
1533
config_init_defaults (&cfg);
1372
1534
log_level = cfg.debug ;
1373
1535
if (cfg.show_config ) {
0 commit comments