@@ -388,7 +388,21 @@ const HTTPClient = struct {
388
388
self .allocator .destroy (self );
389
389
}
390
390
391
- fn requestOptions (config : * ConfigOptions ) ! http.Client.RequestOptions {
391
+ fn extraHeaders (allocator : std.mem.Allocator , config : * ConfigOptions ) ! []http.Header {
392
+ var extra_headers = std .ArrayList (http .Header ).init (allocator );
393
+ if (config .headers ) | h | {
394
+ const parsed_headers = try parseHeaders (allocator , h );
395
+ defer allocator .free (parsed_headers );
396
+ try extra_headers .appendSlice (parsed_headers );
397
+ }
398
+ if (config .compression .encodingHeaderValue ()) | comp | {
399
+ const ce : http.Header = .{ .name = "content-encoding" , .value = comp };
400
+ try extra_headers .append (ce );
401
+ }
402
+ return extra_headers .toOwnedSlice ();
403
+ }
404
+
405
+ fn requestOptions (allocator : std.mem.Allocator , config : * ConfigOptions ) ! http.Client.RequestOptions {
392
406
const headers : http.Client.Request.Headers = .{
393
407
.accept_encoding = if (config .compression .encodingHeaderValue ()) | v | .{ .override = v } else .default ,
394
408
.content_type = .{ .override = switch (config .protocol ) {
@@ -398,13 +412,11 @@ const HTTPClient = struct {
398
412
} },
399
413
.user_agent = .{ .override = UserAgent },
400
414
};
401
- var request_options : http.Client.RequestOptions = .{
415
+ const request_options : http.Client.RequestOptions = .{
402
416
.headers = headers ,
403
417
.server_header_buffer = undefined ,
418
+ .extra_headers = try extraHeaders (allocator , config ),
404
419
};
405
- if (config .headers ) | h | {
406
- request_options .extra_headers = try parseHeaders (h );
407
- }
408
420
409
421
return request_options ;
410
422
}
@@ -434,7 +446,10 @@ const HTTPClient = struct {
434
446
};
435
447
defer self .allocator .free (req_body );
436
448
437
- const req_opts = try requestOptions (self .config );
449
+ const req_opts = try requestOptions (self .allocator , self .config );
450
+ defer {
451
+ if (req_opts .extra_headers .len > 0 ) self .allocator .free (req_opts .extra_headers );
452
+ }
438
453
439
454
const fetch_request = http.Client.FetchOptions {
440
455
.location = .{ .url = url },
@@ -537,15 +552,19 @@ fn calculateDelayMillisec(base_delay_ms: u64, max_delay_ms: u64, attempt: u32) u
537
552
return delay + jitter ;
538
553
}
539
554
540
- fn parseHeaders (key_values : []const u8 ) ConfigError ! []std.http.Header {
555
+ // Parses the key-value, comma separated list of headers from the config.
556
+ // Caller owns the memory and must free it.
557
+ fn parseHeaders (allocator : std.mem.Allocator , key_values : []const u8 ) ! []std.http.Header {
541
558
// Maximum 64 items are allowd in the W3C baggage
542
- var headers = [_ ]std.http.Header {.{ .name = "" , .value = "" }} ** 64 ;
543
- var split = std .mem .splitScalar (u8 , key_values , ',' );
559
+ var headers = try allocator .alloc (std .http .Header , 64 );
560
+ defer allocator .free (headers );
561
+
562
+ var comma_split = std .mem .splitScalar (u8 , key_values , ',' );
544
563
545
564
var idx : usize = 0 ;
546
565
// The sum of all characters in the key and value must be less than 8192 bytes (2^13).
547
566
var cum_bytes : u13 = 0 ;
548
- while (split .next ()) | item | {
567
+ while (comma_split .next ()) | item | {
549
568
// Fail if there are more than 64 headers.
550
569
if (idx == headers .len ) {
551
570
return ConfigError .InvalidHeadersTooManyItems ;
@@ -562,40 +581,70 @@ fn parseHeaders(key_values: []const u8) ConfigError![]std.http.Header {
562
581
if (kv .next ()) | _ | {
563
582
return ConfigError .InvalidHeadersSyntax ;
564
583
}
565
- headers [idx ] = std.http.Header { .name = key , .value = value };
566
- idx += 1 ;
567
584
// Fail when the sum of all bytes for the headers overflows.
568
585
// Each header is accompanied by 3 more bytes: a colon, a space and a newline.
569
586
cum_bytes = std .math .add (u13 , cum_bytes , @intCast (key .len + value .len + 3 )) catch return ConfigError .InvalidHeadersTooManyBytes ;
587
+
588
+ headers [idx ] = std.http.Header { .name = key , .value = value };
589
+ idx += 1 ;
570
590
}
571
- return headers [0.. idx ];
591
+ const ret = try allocator .alloc (std .http .Header , idx );
592
+ std .mem .copyForwards (std .http .Header , ret , headers [0.. idx ]);
593
+ return ret ;
572
594
}
573
595
574
596
test "otlp config parse headers" {
597
+ const allocator = std .testing .allocator ;
598
+
599
+ const single_header = "test-header=test-value" ;
600
+ const single_parsed = try parseHeaders (allocator , single_header );
601
+ defer allocator .free (single_parsed );
602
+
603
+ try std .testing .expectEqual (1 , single_parsed .len );
604
+ try std .testing .expectEqualSlices (u8 , "test-header" , single_parsed [0 ].name );
605
+ try std .testing .expectEqualSlices (u8 , "test-value" , single_parsed [0 ].value );
606
+
575
607
const valid_headers = "a=b,123=456,key1=value1 , key2=value2" ;
576
- const parsed = try parseHeaders (valid_headers );
608
+ const parsed = try parseHeaders (allocator , valid_headers );
609
+ defer allocator .free (parsed );
577
610
578
611
try std .testing .expectEqual (parsed .len , 4 );
579
- try std .testing .expectEqualSlices (u8 , parsed [0 ].name , "a" );
580
- try std .testing .expectEqualSlices (u8 , parsed [0 ].value , "b" );
581
- try std .testing .expectEqualSlices (u8 , parsed [1 ].name , "123" );
582
- try std .testing .expectEqualSlices (u8 , parsed [1 ].value , "456" );
583
- try std .testing .expectEqualSlices (u8 , parsed [2 ].name , "key1" );
584
- try std .testing .expectEqualSlices (u8 , parsed [2 ].value , "value1" );
585
- try std .testing .expectEqualSlices (u8 , parsed [3 ].name , "key2" );
586
- try std .testing .expectEqualSlices (u8 , parsed [3 ].value , "value2" );
612
+ try std .testing .expectEqualSlices (u8 , "a" , parsed [0 ].name );
613
+ try std .testing .expectEqualSlices (u8 , "b" , parsed [0 ].value );
614
+ try std .testing .expectEqualSlices (u8 , "123" , parsed [1 ].name );
615
+ try std .testing .expectEqualSlices (u8 , "456" , parsed [1 ].value );
616
+ try std .testing .expectEqualSlices (u8 , "key1" , parsed [2 ].name );
617
+ try std .testing .expectEqualSlices (u8 , "value1" , parsed [2 ].value );
618
+ try std .testing .expectEqualSlices (u8 , "key2" , parsed [3 ].name );
619
+ try std .testing .expectEqualSlices (u8 , "value2" , parsed [3 ].value );
587
620
588
621
const invalid_headers : [4 ][]const u8 = .{ "a=," , "=b" , "a=b=c" , "a=b,=c=d" };
589
622
for (invalid_headers ) | header | {
590
- try std .testing .expectError (ConfigError .InvalidHeadersSyntax , parseHeaders (header ));
623
+ try std .testing .expectError (ConfigError .InvalidHeadersSyntax , parseHeaders (allocator , header ));
591
624
}
592
625
593
626
// 150 bytes * 60 == 9000 bytes
594
627
const invalid_too_many_bytes : []const u8 = "key=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA," ** 60 ;
595
- try std .testing .expectError (ConfigError .InvalidHeadersTooManyBytes , parseHeaders (invalid_too_many_bytes ));
628
+ try std .testing .expectError (ConfigError .InvalidHeadersTooManyBytes , parseHeaders (allocator , invalid_too_many_bytes ));
596
629
597
630
const invalid_too_many_items : []const u8 = "key=val," ** 65 ;
598
- try std .testing .expectError (ConfigError .InvalidHeadersTooManyItems , parseHeaders (invalid_too_many_items ));
631
+ try std .testing .expectError (ConfigError .InvalidHeadersTooManyItems , parseHeaders (allocator , invalid_too_many_items ));
632
+ }
633
+
634
+ test "otlp HTTPClient extra headers" {
635
+ const allocator = std .testing .allocator ;
636
+ var config = try ConfigOptions .init (allocator );
637
+ defer config .deinit ();
638
+
639
+ config .headers = "key1=value1,key2=value2" ;
640
+ const headers = try HTTPClient .extraHeaders (allocator , config );
641
+ defer allocator .free (headers );
642
+
643
+ try std .testing .expectEqual (2 , headers .len );
644
+ try std .testing .expectEqualSlices (u8 , "key1" , headers [0 ].name );
645
+ try std .testing .expectEqualSlices (u8 , "value1" , headers [0 ].value );
646
+ try std .testing .expectEqualSlices (u8 , "key2" , headers [1 ].name );
647
+ try std .testing .expectEqualSlices (u8 , "value2" , headers [1 ].value );
599
648
}
600
649
601
650
test "otlp exp backoff delay calculation" {
0 commit comments