src/Elastic.Transport/Components/Pipeline/BoundConfiguration.cs (161 lines of code) (raw):

// Licensed to Elasticsearch B.V under one or more agreements. // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Security.Cryptography.X509Certificates; using Elastic.Transport.Extensions; namespace Elastic.Transport; /// <summary> /// Represents the cumulative configuration from <see cref="ITransportConfiguration" /> /// and <see cref="IRequestConfiguration" />. /// </summary> public sealed record BoundConfiguration : IRequestConfiguration { private const string OpaqueIdHeader = "X-Opaque-Id"; /// The default MIME type used for request and response payloads. public const string DefaultContentType = "application/json"; /// The security header used to run requests as a different user. public const string RunAsSecurityHeader = "es-security-runas-user"; /// <inheritdoc cref="BoundConfiguration"/> public BoundConfiguration(ITransportConfiguration global, IRequestConfiguration? local = null) { ConnectionSettings = global; MemoryStreamFactory = global.MemoryStreamFactory; SkipDeserializationForStatusCodes = global.SkipDeserializationForStatusCodes ?? []; DnsRefreshTimeout = global.DnsRefreshTimeout; MetaHeaderProvider = global.MetaHeaderProvider; ProxyAddress = global.ProxyAddress; ProxyUsername = global.ProxyUsername; ProxyPassword = global.ProxyPassword; DisableAutomaticProxyDetection = global.DisableAutomaticProxyDetection; UserAgent = global.UserAgent ?? local?.UserAgent ?? RequestConfiguration.DefaultUserAgent; KeepAliveInterval = (int)(global.KeepAliveInterval?.TotalMilliseconds ?? 2000); KeepAliveTime = (int)(global.KeepAliveTime?.TotalMilliseconds ?? 2000); RunAs = local?.RunAs ?? global.RunAs; DisableDirectStreaming = local?.DisableDirectStreaming ?? global.DisableDirectStreaming ?? false; ForceNode = global.ForceNode ?? local?.ForceNode; MaxRetries = ForceNode != null ? 0 : Math.Min(global.MaxRetries.GetValueOrDefault(int.MaxValue), global.NodePool.MaxRetries); DisableSniff = global.DisableSniff ?? local?.DisableSniff ?? false; DisablePings = global.DisablePings ?? !global.NodePool.SupportsPinging; HttpPipeliningEnabled = local?.HttpPipeliningEnabled ?? global.HttpPipeliningEnabled ?? true; HttpCompression = global.EnableHttpCompression ?? local?.EnableHttpCompression ?? true; ContentType = local?.ContentType ?? global.Accept ?? DefaultContentType; Accept = local?.Accept ?? global.Accept ?? DefaultContentType; ThrowExceptions = local?.ThrowExceptions ?? global.ThrowExceptions ?? false; RequestTimeout = local?.RequestTimeout ?? global.RequestTimeout ?? RequestConfiguration.DefaultRequestTimeout; RequestMetaData = local?.RequestMetaData; AuthenticationHeader = local?.Authentication ?? global.Authentication; AllowedStatusCodes = local?.AllowedStatusCodes ?? EmptyReadOnly<int>.Collection; ClientCertificates = local?.ClientCertificates ?? global.ClientCertificates; TransferEncodingChunked = local?.TransferEncodingChunked ?? global.TransferEncodingChunked ?? false; EnableTcpStats = local?.EnableTcpStats ?? global.EnableTcpStats ?? false; EnableThreadPoolStats = local?.EnableThreadPoolStats ?? global.EnableThreadPoolStats ?? false; ParseAllHeaders = local?.ParseAllHeaders ?? global.ParseAllHeaders ?? false; ResponseHeadersToParse = local is not null ? new HeadersList(local.ResponseHeadersToParse, global.ResponseHeadersToParse) : global.ResponseHeadersToParse; PingTimeout = local?.PingTimeout ?? global.PingTimeout ?? (global.NodePool.UsingSsl ? RequestConfiguration.DefaultPingTimeoutOnSsl : RequestConfiguration.DefaultPingTimeout); if (global.Headers != null) Headers = new NameValueCollection(global.Headers); if (local?.Headers != null) { Headers ??= []; foreach (var key in local.Headers.AllKeys) Headers[key] = local.Headers[key]; } OpaqueId = local?.OpaqueId; if (!string.IsNullOrEmpty(local?.OpaqueId)) { Headers ??= []; Headers.Add(OpaqueIdHeader, local.OpaqueId); } // If there are builders set at the transport level and on the request config, we combine them, // prioritising the request config response builders as most specific. if (local is not null && local.ResponseBuilders.Count > 0 && global.ResponseBuilders.Count > 0) { var builders = new IResponseBuilder[local.ResponseBuilders.Count + global.ResponseBuilders.Count]; var counter = 0; foreach (var builder in local.ResponseBuilders) { builders[counter++] = builder; } foreach (var builder in global.ResponseBuilders) { builders[counter++] = builder; } ResponseBuilders = builders; } else if (local is not null && local.ResponseBuilders.Count > 0) { ResponseBuilders = local.ResponseBuilders; } else { ResponseBuilders = global.ResponseBuilders; } ProductResponseBuilders = global.ProductRegistration.ResponseBuilders; DisableAuditTrail = local?.DisableAuditTrail ?? global.DisableAuditTrail ?? false; } /// <inheritdoc cref="ITransportConfiguration.MemoryStreamFactory"/> public MemoryStreamFactory MemoryStreamFactory { get; } /// <inheritdoc cref="ITransportConfiguration.MetaHeaderProvider"/> public MetaHeaderProvider? MetaHeaderProvider { get; } /// <inheritdoc cref="ITransportConfiguration.DisableAutomaticProxyDetection"/> public bool DisableAutomaticProxyDetection { get; } /// <inheritdoc cref="ITransportConfiguration.KeepAliveInterval"/> public int KeepAliveInterval { get; } /// <inheritdoc cref="ITransportConfiguration.KeepAliveTime"/> public int KeepAliveTime { get; } /// <inheritdoc cref="ITransportConfiguration.ProxyAddress"/> public string? ProxyAddress { get; } /// <inheritdoc cref="ITransportConfiguration.ProxyPassword"/> public string? ProxyPassword { get; } /// <inheritdoc cref="ITransportConfiguration.ProxyUsername"/> public string? ProxyUsername { get; } /// <inheritdoc cref="ITransportConfiguration.SkipDeserializationForStatusCodes"/> public IReadOnlyCollection<int> SkipDeserializationForStatusCodes { get; } /// <inheritdoc cref="IRequestConfiguration.UserAgent"/> public UserAgent UserAgent { get; } /// <inheritdoc cref="ITransportConfiguration.DnsRefreshTimeout"/> public TimeSpan DnsRefreshTimeout { get; } /// <inheritdoc cref="IRequestConfiguration.RequestMetaData"/> public RequestMetaData? RequestMetaData { get; } /// <inheritdoc cref="IRequestConfiguration.Accept"/> public string Accept { get; } /// <inheritdoc cref="IRequestConfiguration.AllowedStatusCodes"/> public IReadOnlyCollection<int> AllowedStatusCodes { get; } /// <inheritdoc cref="IRequestConfiguration.Authentication"/> public AuthorizationHeader? AuthenticationHeader { get; } /// <inheritdoc cref="IRequestConfiguration.ClientCertificates"/> public X509CertificateCollection? ClientCertificates { get; } /// <inheritdoc cref="ITransportConfiguration"/> public ITransportConfiguration ConnectionSettings { get; } /// <inheritdoc cref="IRequestConfiguration.ResponseHeadersToParse"/> public HeadersList? ResponseHeadersToParse { get; } /// <inheritdoc cref="IRequestConfiguration.Headers"/> public NameValueCollection Headers { get; } /// <inheritdoc cref="IRequestConfiguration.DisableDirectStreaming"/> public bool DisableDirectStreaming { get; } /// <inheritdoc cref="IRequestConfiguration.ParseAllHeaders"/> public bool ParseAllHeaders { get; } /// <inheritdoc cref="IRequestConfiguration.EnableHttpCompression"/> public bool HttpCompression { get; } /// <inheritdoc cref="IRequestConfiguration.ForceNode"/> public Uri? ForceNode { get; } /// <inheritdoc cref="IRequestConfiguration.PingTimeout"/> public TimeSpan PingTimeout { get; } /// <inheritdoc cref="IRequestConfiguration.HttpPipeliningEnabled"/> public bool HttpPipeliningEnabled { get; } /// <inheritdoc cref="IRequestConfiguration.ContentType"/> public string ContentType { get; } /// <inheritdoc cref="IRequestConfiguration.RequestTimeout"/> public TimeSpan RequestTimeout { get; } /// <inheritdoc cref="IRequestConfiguration.RunAs"/> public string? RunAs { get; } /// <inheritdoc cref="IRequestConfiguration.ThrowExceptions"/> public bool ThrowExceptions { get; } /// <inheritdoc cref="IRequestConfiguration.TransferEncodingChunked"/> public bool TransferEncodingChunked { get; } /// <inheritdoc cref="IRequestConfiguration.EnableTcpStats"/> public bool EnableTcpStats { get; } /// <inheritdoc cref="IRequestConfiguration.EnableThreadPoolStats"/> public bool EnableThreadPoolStats { get; } /// <inheritdoc cref="IRequestConfiguration.MaxRetries"/> public int MaxRetries { get; } /// <inheritdoc cref="IRequestConfiguration.DisableSniff"/> public bool DisableSniff { get; } /// <inheritdoc cref="IRequestConfiguration.DisablePings"/> public bool DisablePings { get; } /// <inheritdoc cref="IRequestConfiguration.ResponseBuilders"/> public IReadOnlyCollection<IResponseBuilder> ProductResponseBuilders { get; } /// <inheritdoc cref="IRequestConfiguration.ResponseBuilders"/> public IReadOnlyCollection<IResponseBuilder> ResponseBuilders { get; } /// <inheritdoc cref="IRequestConfiguration.DisableAuditTrail"/> public bool DisableAuditTrail { get; } /// <inheritdoc cref="IRequestConfiguration.OpaqueId"/> public string? OpaqueId { get; } string? IRequestConfiguration.Accept => Accept; IReadOnlyCollection<int>? IRequestConfiguration.AllowedStatusCodes => AllowedStatusCodes; AuthorizationHeader? IRequestConfiguration.Authentication => AuthenticationHeader; X509CertificateCollection? IRequestConfiguration.ClientCertificates => ClientCertificates; string? IRequestConfiguration.ContentType => ContentType; bool? IRequestConfiguration.DisableDirectStreaming => DisableDirectStreaming; bool? IRequestConfiguration.DisableAuditTrail => DisableAuditTrail; bool? IRequestConfiguration.DisablePings => DisablePings; bool? IRequestConfiguration.DisableSniff => DisableSniff; bool? IRequestConfiguration.HttpPipeliningEnabled => HttpPipeliningEnabled; bool? IRequestConfiguration.EnableHttpCompression => HttpCompression; Uri? IRequestConfiguration.ForceNode => ForceNode; int? IRequestConfiguration.MaxRetries => MaxRetries; TimeSpan? IRequestConfiguration.MaxRetryTimeout => RequestTimeout; string? IRequestConfiguration.OpaqueId => OpaqueId; bool? IRequestConfiguration.ParseAllHeaders => ParseAllHeaders; TimeSpan? IRequestConfiguration.PingTimeout => PingTimeout; TimeSpan? IRequestConfiguration.RequestTimeout => RequestTimeout; IReadOnlyCollection<IResponseBuilder> IRequestConfiguration.ResponseBuilders => ResponseBuilders; HeadersList? IRequestConfiguration.ResponseHeadersToParse => ResponseHeadersToParse; string? IRequestConfiguration.RunAs => RunAs; bool? IRequestConfiguration.ThrowExceptions => ThrowExceptions; bool? IRequestConfiguration.TransferEncodingChunked => TransferEncodingChunked; NameValueCollection? IRequestConfiguration.Headers => Headers; bool? IRequestConfiguration.EnableTcpStats => EnableTcpStats; bool? IRequestConfiguration.EnableThreadPoolStats => EnableThreadPoolStats; RequestMetaData? IRequestConfiguration.RequestMetaData => RequestMetaData; /// <summary> /// Create a cachable instance of <see cref="BoundConfiguration"/> for use in high-performance scenarios. /// </summary> /// <param name="transport">An existing <see cref="ITransport{TConfiguration}"/> from which to bind transport configuration.</param> /// <param name="requestConfiguration">A request specific <see cref="IRequestConfiguration"/>.</param> /// <returns></returns> public static BoundConfiguration Create(ITransport<ITransportConfiguration> transport, IRequestConfiguration requestConfiguration) => new(transport.Configuration, requestConfiguration); }