diff --git a/src/Qdrant.Client/Grpc/QdrantChannel.cs b/src/Qdrant.Client/Grpc/QdrantChannel.cs index 842464c..b089840 100644 --- a/src/Qdrant.Client/Grpc/QdrantChannel.cs +++ b/src/Qdrant.Client/Grpc/QdrantChannel.cs @@ -33,20 +33,19 @@ public override CallInvoker CreateCallInvoker() if (Disposed) throw new ObjectDisposedException(nameof(QdrantChannel)); - var hasApiKey = _configuration.ApiKey is not null; - var hasHeaders = _configuration.Headers.Count > 0; - - if (!hasApiKey && !hasHeaders) - return _channel.CreateCallInvoker(); - return _channel.Intercept(metadata => { - if (hasApiKey) - metadata.Add("api-key", _configuration.ApiKey!); + if (_configuration.ApiKey is not null) + metadata.Add("api-key", _configuration.ApiKey); foreach (var header in _configuration.Headers) metadata.Add(header.Key, header.Value); + var requestHeaders = RequestHeaders.Current; + if (requestHeaders is not null) + foreach (var header in requestHeaders) + metadata.Add(header.Key, header.Value); + return metadata; }); } diff --git a/src/Qdrant.Client/RequestHeaders.cs b/src/Qdrant.Client/RequestHeaders.cs new file mode 100644 index 0000000..4fd7687 --- /dev/null +++ b/src/Qdrant.Client/RequestHeaders.cs @@ -0,0 +1,42 @@ +namespace Qdrant.Client; + +/// +/// Utilities for attaching per-request headers to gRPC calls. +/// +public static class RequestHeaders +{ + private static readonly AsyncLocal?> _headers = new(); + + internal static IReadOnlyDictionary? Current => _headers.Value; + + /// + /// Sets key and value as metadata on requests made within the returned scope. + /// + public static IDisposable Use(string key, string value) => + Use(new Dictionary { [key] = value }); + + /// + /// Sets all entries of headers as metadata on requests made within the returned scope. + /// + public static IDisposable Use(IDictionary headers) + { + var previous = _headers.Value; + var merged = new Dictionary(); + if (previous is not null) + foreach (var header in previous) + merged[header.Key] = header.Value; + foreach (var header in headers) + merged[header.Key] = header.Value; + _headers.Value = merged; + return new Scope(() => _headers.Value = previous); + } + + private sealed class Scope : IDisposable + { + private readonly Action _restore; + + internal Scope(Action restore) => _restore = restore; + + public void Dispose() => _restore(); + } +}