├── CODE_OF_CONDUCT.md
├── nuget-icon.png
├── src
├── Elastic.Transport
│ ├── .editorconfig
│ ├── Extensions
│ │ ├── .editorconfig
│ │ ├── Fluent.cs
│ │ ├── EmptyEnumerator.cs
│ │ ├── StringExtensions.cs
│ │ ├── EmptyReadonly.cs
│ │ ├── TaskExtensions.cs
│ │ ├── RuntimeInformation.cs
│ │ └── NativeMethods.cs
│ ├── Products
│ │ ├── .editorconfig
│ │ └── Elasticsearch
│ │ │ ├── Sniff
│ │ │ ├── NodeInfoHttp.cs
│ │ │ ├── NodeInfo.cs
│ │ │ ├── SniffParser.cs
│ │ │ └── SniffResponse.cs
│ │ │ ├── ErrorSerializationContext.cs
│ │ │ ├── Failures
│ │ │ └── ShardFailure.cs
│ │ │ ├── ElasticsearchNodeFeatures.cs
│ │ │ ├── ElasticsearchConfiguration.cs
│ │ │ └── ElasticsearchErrorExtensions.cs
│ ├── Properties
│ │ └── ClsCompliancy.cs
│ ├── Requests
│ │ ├── IUrlParameter.cs
│ │ ├── MetaData
│ │ │ ├── RequestMetaDataExtensions.cs
│ │ │ ├── MetaHeaderProvider.cs
│ │ │ ├── RequestMetaData.cs
│ │ │ ├── VersionInfo.cs
│ │ │ └── MetaDataHeader.cs
│ │ └── Body
│ │ │ ├── PostData.ByteArray.cs
│ │ │ ├── PostData.ReadOnlyMemory.cs
│ │ │ ├── PostData.Serializable.cs
│ │ │ └── PostData.String.cs
│ ├── IsExternalInit.cs
│ ├── Components
│ │ ├── TransportClient
│ │ │ ├── WebProxy.cs
│ │ │ ├── HttpRequestInvoker-FullFramework.cs
│ │ │ ├── RequestInvokerHelpers.cs
│ │ │ ├── HandlerTracking
│ │ │ │ ├── LifetimeTrackingHttpMessageHandler.cs
│ │ │ │ ├── ExpiredHandlerTrackingEntry.cs
│ │ │ │ └── ValueStopWatch.cs
│ │ │ ├── HttpMethod.cs
│ │ │ └── CertificateHelpers.cs
│ │ ├── Providers
│ │ │ ├── RequestPipelineFactory.cs
│ │ │ ├── DefaultRequestPipelineFactory.cs
│ │ │ ├── DefaultMemoryStreamFactory.cs
│ │ │ ├── MemoryStreamFactory.cs
│ │ │ ├── DateTimeProvider.cs
│ │ │ ├── DefaultDateTimeProvider.cs
│ │ │ └── RecyclableMemoryStreamFactory.cs
│ │ ├── Pipeline
│ │ │ └── RequestPipelineStatics.cs
│ │ ├── Serialization
│ │ │ ├── SerializationFormatting.cs
│ │ │ ├── JsonElementExtensions.cs
│ │ │ ├── SerializerRegistrationInformation.cs
│ │ │ ├── IJsonSerializerOptionsProvider.cs
│ │ │ ├── LowLevelRequestResponseSerializer.cs
│ │ │ └── Converters
│ │ │ │ └── DynamicDictionaryConverter.cs
│ │ └── NodePool
│ │ │ ├── SingleNodePool.cs
│ │ │ ├── StickyNodePool.cs
│ │ │ └── StickySniffingNodePool.cs
│ ├── Responses
│ │ ├── Special
│ │ │ ├── StringResponse.cs
│ │ │ ├── BytesResponse.cs
│ │ │ ├── VoidResponseBuilder.cs
│ │ │ ├── VoidResponse.cs
│ │ │ ├── StreamResponseBuilder.cs
│ │ │ ├── StreamResponse.cs
│ │ │ ├── StreamResponseBase.cs
│ │ │ └── BytesResponseBuilder.cs
│ │ ├── BufferedResponseHelpers.cs
│ │ ├── ErrorResponse.cs
│ │ ├── Dynamic
│ │ │ └── DynamicResponse.cs
│ │ └── TypedResponseBuilder.cs
│ ├── Configuration
│ │ ├── Security
│ │ │ ├── Base64ApiKey.cs
│ │ │ ├── AuthorizationHeader.cs
│ │ │ ├── ApiKey.cs
│ │ │ └── BasicAuthenticationCredentials.cs
│ │ ├── ConnectionInfo.cs
│ │ └── UserAgent.cs
│ ├── Diagnostics
│ │ ├── AuditDiagnosticObserver.cs
│ │ ├── SerializerDiagnosticObserver.cs
│ │ ├── HttpConnectionDiagnosticObserver.cs
│ │ ├── OpenTelemetry
│ │ │ └── SemanticConventions.cs
│ │ ├── RequestPipelineDiagnosticObserver.cs
│ │ ├── Auditing
│ │ │ ├── Auditable.cs
│ │ │ ├── Audit.cs
│ │ │ └── Auditor.cs
│ │ ├── Diagnostic.cs
│ │ └── TcpStats.cs
│ └── Exceptions
│ │ └── UnexpectedTransportException.cs
├── Elastic.Transport.VirtualizedCluster
│ ├── Rules
│ │ ├── TimesHelper.cs
│ │ ├── PingRule.cs
│ │ ├── SniffRule.cs
│ │ └── ClientCallRule.cs
│ ├── Audit
│ │ ├── CallTraceState.cs
│ │ └── ClientCall.cs
│ ├── Setup.cs
│ ├── Extensions
│ │ └── NumericExtensions.cs
│ ├── Elastic.Transport.VirtualizedCluster.csproj
│ ├── Components
│ │ ├── ExposingPipelineFactory.cs
│ │ └── SealedVirtualCluster.cs
│ ├── Providers
│ │ └── TestableDateTimeProvider.cs
│ └── Products
│ │ ├── Elasticsearch
│ │ └── ElasticsearchMockProductRegistration.cs
│ │ └── MockProductRegistration.cs
└── Directory.Build.props
├── request-pipeline.png
├── global.json
├── nuget.config
├── .github
├── license-header.txt
├── workflows
│ ├── license.yml
│ └── test-windows.yml
├── add-license-headers.sh
└── check-license-headers.sh
├── tests
├── Elastic.Transport.Tests
│ ├── Plumbing
│ │ ├── TestResponse.cs
│ │ └── InMemoryConnectionFactory.cs
│ ├── Components
│ │ ├── Serialization
│ │ │ └── SerializationTestBase.cs
│ │ └── NodePool
│ │ │ └── StaticNodePoolTests.cs
│ ├── InstantiationsTests.cs
│ ├── Elastic.Transport.Tests.csproj
│ ├── Configuration
│ │ ├── RequestConfigurationTests.cs
│ │ ├── HeadersListTests.cs
│ │ └── TransportConfigurationTests.cs
│ ├── VolatileUpdates.cs
│ ├── Responses
│ │ ├── Special
│ │ │ └── VoidResponseBuilderTests.cs
│ │ └── Dynamic
│ │ │ └── DynamicResponseBuilderTests.cs
│ └── AddressParsing.cs
├── Elastic.Transport.IntegrationTests
│ ├── Plumbing
│ │ ├── ClassServerTestsBase.cs
│ │ ├── AssemblyServerTestsBase.cs
│ │ ├── Stubs
│ │ │ ├── TestableClientHandler.cs
│ │ │ └── TrackingRequestInvoker.cs
│ │ ├── WebHostExtensions.cs
│ │ ├── DefaultStartup.cs
│ │ └── Examples
│ │ │ ├── ControllerIntegrationTests.cs
│ │ │ └── EndpointIntegrationTests.cs
│ ├── Elastic.Transport.IntegrationTests.csproj
│ └── Http
│ │ └── ApiCompatibilityHeaderTests.cs
├── Elastic.Transport.Tests.Shared
│ ├── Elastic.Transport.Tests.Shared.csproj
│ ├── TrackDisposeStream.cs
│ └── TrackingMemoryStreamFactory.cs
└── Elastic.Elasticsearch.IntegrationTests
│ ├── IntegrationTestBase.cs
│ ├── DefaultClusterTests.cs
│ ├── Elastic.Elasticsearch.IntegrationTests.csproj
│ ├── SecurityClusterTests.cs
│ └── DefaultCluster.cs
├── .gitattributes
├── benchmarks
├── Elastic.Transport.Profiling
│ ├── Elastic.Transport.Profiling.csproj
│ └── Program.cs
└── Elastic.Transport.Benchmarks
│ ├── Elastic.Transport.Benchmarks.csproj
│ ├── Program.cs
│ └── TransportBenchmarks.cs
├── Playground
├── Playground.csproj
└── Program.cs
├── examples
└── transport-aot-example
│ ├── transport-aot-example.csproj
│ └── Program.cs
├── dotnet-tools.json
├── .gitignore
├── NOTICE.txt
├── Directory.Build.props
└── Elastic.Transport.slnx
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | Location: https://www.elastic.co/community/codeofconduct
--------------------------------------------------------------------------------
/nuget-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elastic/elastic-transport-net/HEAD/nuget-icon.png
--------------------------------------------------------------------------------
/src/Elastic.Transport/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | resharper_check_namespace_highlighting=do_not_show
3 |
--------------------------------------------------------------------------------
/request-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elastic/elastic-transport-net/HEAD/request-pipeline.png
--------------------------------------------------------------------------------
/src/Elastic.Transport/Extensions/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | resharper_check_namespace_highlighting=warning
3 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Products/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | resharper_check_namespace_highlighting=warning
3 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "10.0.100",
4 | "rollForward": "latestMinor",
5 | "allowPrerelease": false
6 | }
7 | }
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.github/license-header.txt:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
--------------------------------------------------------------------------------
/.github/workflows/license.yml:
--------------------------------------------------------------------------------
1 | name: License headers
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 |
13 | - name: Check license headers
14 | run: |
15 | ./.github/check-license-headers.sh
--------------------------------------------------------------------------------
/src/Elastic.Transport/Properties/ClsCompliancy.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | [assembly: CLSCompliant(true)]
8 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Plumbing/TestResponse.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport.Tests.Plumbing
6 | {
7 | public class TestResponse : TransportResponse { }
8 | }
9 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto eol=lf
3 |
4 | # Set default behavior for command prompt diff.
5 | # This gives output on command line taking C# language constructs into consideration (e.g showing class name)
6 | *.cs text diff=csharp
7 |
8 | # Set windows specific files explicitly to crlf line ending
9 | *.cmd eol=crlf
10 | *.bat eol=crlf
11 | *.ps1 eol=crlf
12 |
13 | # Mark files specifically as binary to avoid line ending conversion
14 | *.snk binary
15 | *.png binary
--------------------------------------------------------------------------------
/benchmarks/Elastic.Transport.Profiling/Elastic.Transport.Profiling.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net10.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Products/Elasticsearch/Sniff/NodeInfoHttp.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections.Generic;
6 |
7 | namespace Elastic.Transport.Products.Elasticsearch;
8 |
9 | internal sealed class NodeInfoHttp
10 | {
11 | public IList bound_address { get; set; }
12 | public string publish_address { get; set; }
13 | }
14 |
--------------------------------------------------------------------------------
/.github/add-license-headers.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | script_path=$(dirname $(realpath -s $0))/../
3 |
4 | function add_license () {
5 | (find "$script_path" -name $1 | grep -v "/bin/" | grep -v "/obj/" )|while read fname; do
6 | line=$(sed -n '2p;3q' "$fname")
7 | if ! [[ "$line" == " * Licensed to Elasticsearch B.V. under one or more contributor" ]] ; then
8 | cat "${script_path}.github/license-header.txt" "$fname" > "${fname}.new"
9 | mv "${fname}.new" "$fname"
10 | fi
11 | done
12 | }
13 |
14 | add_license "*.cs"
--------------------------------------------------------------------------------
/Playground/Playground.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net10.0
6 | enable
7 | enable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/IUrlParameter.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | /// Implementers define an object that can be serialized as a query string parameter
8 | public interface IUrlParameter
9 | {
10 | /// Get the string representation using
11 | public string GetString(ITransportConfiguration settings);
12 | }
13 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Extensions/Fluent.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport.Extensions;
8 |
9 | internal static class Fluent
10 | {
11 | internal static TDescriptor Assign(TDescriptor self, TValue value, Action assign)
12 | where TDescriptor : class, TInterface
13 | {
14 | assign(self, value);
15 | return self;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Plumbing/ClassServerTestsBase.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Xunit;
6 |
7 | namespace Elastic.Transport.IntegrationTests.Plumbing
8 | {
9 | public class ClassServerTestsBase(TServer instance)
10 | where TServer : class, IHttpTransportTestServer
11 | {
12 | protected TServer Server { get; } = instance;
13 |
14 | protected ITransport RequestHandler => Server.DefaultRequestHandler;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/transport-aot-example/transport-aot-example.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net10.0
6 | transport_aot_example
7 | enable
8 | enable
9 | true
10 | false
11 | true
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Extensions/EmptyEnumerator.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections;
6 | using System.Collections.Generic;
7 |
8 | namespace Elastic.Transport.Extensions;
9 |
10 | internal struct EmptyEnumerator : IEnumerator
11 | {
12 | public T Current => default;
13 | object IEnumerator.Current => Current!;
14 | public bool MoveNext() => false;
15 |
16 | public void Reset()
17 | {
18 | }
19 |
20 | public void Dispose()
21 | {
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/IsExternalInit.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | #if !NET6_0_OR_GREATER
6 |
7 | namespace System.Runtime.CompilerServices;
8 |
9 | using System.ComponentModel;
10 |
11 | ///
12 | /// Reserved to be used by the compiler for tracking metadata.
13 | /// This class should not be used by developers in source code.
14 | ///
15 | [EditorBrowsable(EditorBrowsableState.Never)]
16 | internal static class IsExternalInit
17 | {
18 | }
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/benchmarks/Elastic.Transport.Benchmarks/Elastic.Transport.Benchmarks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | false
6 | net10.0
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Rules/TimesHelper.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport.VirtualizedCluster.Rules;
8 |
9 | public static class TimesHelper
10 | {
11 | public static AllTimes Always = new();
12 | public static readonly int Once = 0;
13 | public static readonly int Twice = 1;
14 |
15 | public static int Times(int n) => Math.Max(0, n - 1);
16 |
17 | public class AllTimes
18 | {
19 | internal AllTimes() { }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Components/Serialization/SerializationTestBase.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 |
7 | namespace Elastic.Transport.Tests.Components.Serialization;
8 |
9 | public abstract class SerializerTestBase
10 | {
11 | protected static Stream SerializeToStream(T data)
12 | {
13 | var stream = new MemoryStream();
14 | LowLevelRequestResponseSerializer.Instance.Serialize(data, stream);
15 | stream.Position = 0;
16 | return stream;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/TransportClient/WebProxy.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | #if !NETFRAMEWORK
6 | using System;
7 | using System.Net;
8 |
9 | namespace Elastic.Transport;
10 |
11 | internal class WebProxy : IWebProxy
12 | {
13 | private readonly Uri _uri;
14 |
15 | public WebProxy(Uri uri) => _uri = uri;
16 |
17 | public ICredentials Credentials { get; set; }
18 |
19 | public Uri GetProxy(Uri destination) => _uri;
20 |
21 | public bool IsBypassed(Uri host) => false;
22 | }
23 | #endif
24 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | internal static class StringExtensions
8 | {
9 | internal static string ToCamelCase(this string s)
10 | {
11 | if (string.IsNullOrEmpty(s))
12 | return s;
13 |
14 | if (!char.IsUpper(s[0]))
15 | return s;
16 |
17 | var camelCase = char.ToLowerInvariant(s[0]).ToString();
18 | if (s.Length > 1)
19 | camelCase += s.Substring(1);
20 |
21 | return camelCase;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Providers/RequestPipelineFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | /// A factory that creates instances of , this factory exists so that transport can be tested.
8 | public abstract class RequestPipelineFactory
9 | {
10 | internal RequestPipelineFactory() { }
11 |
12 | /// Create an instance of
13 | public abstract RequestPipeline Create(BoundConfiguration boundConfiguration);
14 | }
15 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/Special/StringResponse.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | ///
8 | /// A response that exposes the response as .
9 | ///
10 | public sealed class StringResponse : TransportResponse
11 | {
12 | ///
13 | public StringResponse() => Body = string.Empty;
14 |
15 | ///
16 | public StringResponse(string body) => Body = body;
17 | }
18 |
--------------------------------------------------------------------------------
/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "minver-cli": {
6 | "version": "6.0.0",
7 | "commands": [
8 | "minver"
9 | ],
10 | "rollForward": false
11 | },
12 | "assembly-differ": {
13 | "version": "0.17.0",
14 | "commands": [
15 | "assembly-differ"
16 | ],
17 | "rollForward": false
18 | },
19 | "release-notes": {
20 | "version": "0.10.0",
21 | "commands": [
22 | "release-notes"
23 | ],
24 | "rollForward": false
25 | },
26 | "nupkg-validator": {
27 | "version": "0.10.1",
28 | "commands": [
29 | "nupkg-validator"
30 | ],
31 | "rollForward": false
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/Special/BytesResponse.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport;
8 |
9 | ///
10 | /// A response that exposes the response as byte array
11 | ///
12 | public sealed class BytesResponse : TransportResponse
13 | {
14 | ///
15 | public BytesResponse() => Body = Array.Empty();
16 |
17 | ///
18 | public BytesResponse(byte[] body) => Body = body;
19 | }
20 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/BufferedResponseHelpers.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 |
7 | namespace Elastic.Transport;
8 |
9 | internal class BufferedResponseHelpers
10 | {
11 | public const int BufferSize = 81920;
12 |
13 | public static byte[] SwapStreams(ref Stream responseStream, ref MemoryStream ms, bool disposeOriginal = false)
14 | {
15 | var bytes = ms.ToArray();
16 |
17 | if (disposeOriginal)
18 | responseStream.Dispose();
19 |
20 | responseStream = ms;
21 | responseStream.Position = 0;
22 | return bytes;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Configuration/Security/Base64ApiKey.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Text;
7 |
8 | namespace Elastic.Transport;
9 |
10 | ///
11 | /// Credentials for Api Key Authentication
12 | ///
13 | public class Base64ApiKey : ApiKey
14 | {
15 | ///
16 | public Base64ApiKey(string id, string apiKey) :
17 | base(Convert.ToBase64String(Encoding.UTF8.GetBytes($"{id}:{apiKey}"))) {}
18 |
19 | ///
20 | public Base64ApiKey(string base64EncodedApiKey) : base(base64EncodedApiKey) {}
21 | }
22 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Audit/CallTraceState.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using Elastic.Transport.Diagnostics.Auditing;
7 |
8 | namespace Elastic.Transport.VirtualizedCluster.Audit;
9 |
10 | public sealed class CallTraceState
11 | {
12 | public CallTraceState(AuditEvent e) => Event = e;
13 |
14 | public Action AssertWithBecause { get; set; }
15 |
16 | public AuditEvent Event { get; private set; }
17 |
18 | public int? Port { get; set; }
19 |
20 | public Action SimpleAssert { get; set; }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests.Shared/Elastic.Transport.Tests.Shared.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net10.0;net481
5 | false
6 | CS8002
7 | enable
8 | enable
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Plumbing/InMemoryConnectionFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using Elastic.Transport.Products;
7 |
8 | namespace Elastic.Transport.Tests.Plumbing
9 | {
10 | public static class InMemoryConnectionFactory
11 | {
12 | public static TransportConfiguration Create(ProductRegistration productRegistration = null)
13 | {
14 | var invoker = new InMemoryRequestInvoker();
15 | var pool = new SingleNodePool(new Uri("http://localhost:9200"));
16 | var settings = new TransportConfiguration(pool, invoker, productRegistration: productRegistration);
17 | return settings;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Setup.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Elastic.Transport.VirtualizedCluster.Products.Elasticsearch;
6 |
7 | namespace Elastic.Transport.VirtualizedCluster;
8 |
9 | ///
10 | /// Static factory class that can be used to bootstrap virtual product clusters. E.g a cluster of virtual Elasticsearch nodes.
11 | ///
12 | public static class Virtual
13 | {
14 | ///
15 | /// Bootstrap a virtual Elasticsearch cluster using
16 | ///
17 | public static ElasticsearchClusterFactory Elasticsearch { get; } = ElasticsearchClusterFactory.Default;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Providers/DefaultRequestPipelineFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | ///
8 | /// The default implementation for that returns
9 | ///
10 | internal sealed class DefaultRequestPipelineFactory : RequestPipelineFactory
11 | {
12 | public static readonly DefaultRequestPipelineFactory Default = new ();
13 | ///
14 | /// returns instances of
15 | ///
16 | public override RequestPipeline Create(BoundConfiguration boundConfiguration) =>
17 | new RequestPipeline(boundConfiguration);
18 | }
19 |
--------------------------------------------------------------------------------
/benchmarks/Elastic.Transport.Benchmarks/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using BenchmarkDotNet.Configs;
6 | using BenchmarkDotNet.Diagnosers;
7 | using BenchmarkDotNet.Reports;
8 | using BenchmarkDotNet.Running;
9 | using Perfolizer.Metrology;
10 |
11 | namespace Elastic.Transport.Benchmarks
12 | {
13 | internal class Program
14 | {
15 | private static void Main(string[] args)
16 | {
17 | var config = ManualConfig
18 | .Create(DefaultConfig.Instance)
19 | .AddDiagnoser(MemoryDiagnoser.Default)
20 | .WithSummaryStyle(new SummaryStyle(null, false, SizeUnit.B, null));
21 |
22 | BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Extensions/NumericExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport.VirtualizedCluster.Extensions;
6 |
7 | internal static class NumericExtensions
8 | {
9 | public static string ToOrdinal(this int num)
10 | {
11 | if (num <= 0) return num.ToString();
12 |
13 | switch (num % 100)
14 | {
15 | case 11:
16 | case 12:
17 | case 13:
18 | return num + "th";
19 | }
20 |
21 | switch (num % 10)
22 | {
23 | case 1:
24 | return num + "st";
25 | case 2:
26 | return num + "nd";
27 | case 3:
28 | return num + "rd";
29 | default:
30 | return num + "th";
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Pipeline/RequestPipelineStatics.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Diagnostics;
6 | using Elastic.Transport.Diagnostics;
7 |
8 | //#if NETSTANDARD2_0 || NETSTANDARD2_1
9 | //using System.Threading.Tasks.Extensions;
10 | //#endif
11 |
12 | namespace Elastic.Transport;
13 |
14 | internal static class RequestPipelineStatics
15 | {
16 | public static readonly string NoNodesAttemptedMessage =
17 | "No nodes were attempted, this can happen when a node predicate does not match any nodes";
18 |
19 | public static DiagnosticSource DiagnosticSource { get; } = new DiagnosticListener(DiagnosticSources.RequestPipeline.SourceName);
20 | }
21 | #pragma warning restore 1591
22 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/ErrorResponse.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | ///
8 | /// Base class for types representing client specific errors. This may be provided by clients to be used for deserialisation of the HTTP body for non-success status codes.
9 | ///
10 | public abstract class ErrorResponse
11 | {
12 | internal ErrorResponse() { }
13 |
14 | ///
15 | /// May be called by transport to establish whether the instance represents a valid, complete error.
16 | /// This may not always be the case if the error is partially deserialised on the response.
17 | ///
18 | public abstract bool HasError();
19 | }
20 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Configuration/Security/AuthorizationHeader.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | ///
8 | /// The HTTP authorization request header used to provide credentials that authenticate a user agent with a server.
9 | ///
10 | public abstract class AuthorizationHeader
11 | {
12 | ///
13 | /// The authentication scheme that defines how the credentials are encoded.
14 | ///
15 | public abstract string AuthScheme { get; }
16 |
17 | ///
18 | /// If this instance is valid, returns the authorization parameters to include in the header.
19 | ///
20 | public abstract bool TryGetAuthorizationParameters(out string value);
21 | }
22 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/AuditDiagnosticObserver.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using Elastic.Transport.Diagnostics.Auditing;
8 |
9 | namespace Elastic.Transport.Diagnostics;
10 |
11 | /// Provides a typed listener to events that emits
12 | internal sealed class AuditDiagnosticObserver : TypedDiagnosticObserver
13 | {
14 | ///
15 | public AuditDiagnosticObserver(
16 | Action> onNext,
17 | Action? onError = null,
18 | Action? onCompleted = null
19 | ) : base(onNext, onError, onCompleted) { }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/MetaData/RequestMetaDataExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport;
8 |
9 | ///
10 | ///
11 | ///
12 | public static class RequestMetaDataExtensions
13 | {
14 | ///
15 | ///
16 | ///
17 | ///
18 | ///
19 | ///
20 | public static void AddHelper(this RequestMetaData metaData, string helperValue)
21 | {
22 | if (!metaData.TryAddMetaData(RequestMetaData.HelperKey, helperValue))
23 | throw new InvalidOperationException("A helper value has already been added.");
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/SerializerDiagnosticObserver.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 |
8 | namespace Elastic.Transport.Diagnostics;
9 |
10 | /// Provides a typed listener any time an does a write or read
11 | internal sealed class SerializerDiagnosticObserver : TypedDiagnosticObserver
12 | {
13 | ///
14 | public SerializerDiagnosticObserver(
15 | Action> onNext,
16 | Action onError = null,
17 | Action onCompleted = null
18 | ) : base(onNext, onError, onCompleted) { }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Configuration/Security/ApiKey.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | ///
8 | /// Credentials for Api Key Authentication
9 | ///
10 | public class ApiKey : AuthorizationHeader
11 | {
12 | private readonly string _apiKey;
13 |
14 | ///
15 | public ApiKey(string apiKey) => _apiKey = apiKey;
16 |
17 | ///
18 | public override string AuthScheme { get; } = "ApiKey";
19 |
20 | ///
21 | public override bool TryGetAuthorizationParameters(out string value)
22 | {
23 | value = _apiKey;
24 | return true;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Elastic.Transport.VirtualizedCluster.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Elastic.Transport.VirtualizedCluster
4 | Elastic.Transport.VirtualizedCluster - An in memory TransportClient that can simulate large cluster
5 | elasticsearch;elastic;search;lucene;nest
6 | Provides a way to assert transport behaviour through a rule engine backed VirtualClusterConnection
7 | CS1591;$(NoWarn);IDT001
8 |
9 |
10 |
11 | true
12 | true
13 | netstandard2.0
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/Special/VoidResponseBuilder.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Elastic.Transport;
10 |
11 | internal class VoidResponseBuilder : TypedResponseBuilder
12 | {
13 | protected override VoidResponse Build(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType, long contentLength) =>
14 | VoidResponse.Default;
15 |
16 | protected override Task BuildAsync(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType, long contentLength,
17 | CancellationToken cancellationToken = default) =>
18 | Task.FromResult(VoidResponse.Default);
19 | }
20 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/Special/VoidResponse.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | ///
8 | /// A special response that omits reading the response from the server after reading the headers.
9 | ///
10 | public sealed class VoidResponse : TransportResponse
11 | {
12 | ///
13 | public VoidResponse() => Body = new VoidBody();
14 |
15 | ///
16 | /// A static instance that can be reused.
17 | ///
18 | public static VoidResponse Default { get; } = new VoidResponse();
19 |
20 | ///
21 | /// A class that represents the absence of having read the servers response to completion.
22 | ///
23 | public class VoidBody { }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/HttpConnectionDiagnosticObserver.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 |
8 | namespace Elastic.Transport.Diagnostics;
9 |
10 | /// Provides a typed listener to the events that emits
11 | internal sealed class HttpConnectionDiagnosticObserver : TypedDiagnosticObserver
12 | {
13 | /// >
14 | public HttpConnectionDiagnosticObserver(
15 | Action> onNextStart,
16 | Action> onNextEnd,
17 | Action onError = null,
18 | Action onCompleted = null
19 | ) : base(onNextStart, onNextEnd, onError, onCompleted) { }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/InstantiationsTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using FluentAssertions;
6 | using Xunit;
7 |
8 | namespace Elastic.Transport.Tests
9 | {
10 | public class InstantiationsTests
11 | {
12 | public class A {}
13 |
14 | [Fact]
15 | public void SerializableMultiJson()
16 | {
17 | var p = PostData.MultiJson(new [] {new A()});
18 | p.Type.Should().Be(PostType.EnumerableOfObject);
19 | }
20 |
21 | [Fact]
22 | public void StringMultiJson()
23 | {
24 | var p = PostData.MultiJson(new [] {""});
25 | p.Type.Should().Be(PostType.EnumerableOfString);
26 | }
27 |
28 | [Fact]
29 | public void ObjectMultiJson()
30 | {
31 | var p = PostData.MultiJson(new object[] {new A()});
32 | p.Type.Should().Be(PostType.EnumerableOfObject);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests.Shared/TrackDisposeStream.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport.Tests.Shared;
6 |
7 | public class TrackDisposeStream : MemoryStream
8 | {
9 | private readonly bool _canSeek;
10 |
11 | public TrackDisposeStream(bool canSeek = true) : base() => _canSeek = canSeek;
12 |
13 | public TrackDisposeStream(byte[] bytes, bool canSeek = true) : base(bytes) => _canSeek = canSeek;
14 |
15 | public TrackDisposeStream(byte[] bytes, int index, int count, bool canSeek = true) : base(bytes, index, count) => _canSeek = canSeek;
16 |
17 | public override bool CanSeek => _canSeek;
18 |
19 | public bool IsDisposed { get; private set; }
20 |
21 | protected override void Dispose(bool disposing)
22 | {
23 | IsDisposed = true;
24 | base.Dispose(disposing);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/Special/StreamResponseBuilder.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Elastic.Transport;
10 |
11 | internal class StreamResponseBuilder : TypedResponseBuilder
12 | {
13 | protected override StreamResponse Build(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType, long contentLength) =>
14 | new(responseStream, contentType);
15 |
16 | protected override Task BuildAsync(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType,
17 | long contentLength, CancellationToken cancellationToken = default) =>
18 | Task.FromResult(new StreamResponse(responseStream, contentType));
19 | }
20 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/TransportClient/HttpRequestInvoker-FullFramework.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | #if NETFRAMEWORK
6 | using System.Net;
7 |
8 | namespace Elastic.Transport;
9 |
10 | /// The default implementation. Uses on the current .NET desktop framework.
11 | public class HttpRequestInvoker : HttpWebRequestInvoker
12 | {
13 | ///
14 | /// Create a new instance of the .
15 | ///
16 | public HttpRequestInvoker() { }
17 |
18 | /// The default TransportClient implementation. Uses on the current .NET desktop framework.
19 | internal HttpRequestInvoker(ResponseFactory responseFactory) : base(responseFactory) { }
20 | }
21 | #endif
22 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/OpenTelemetry/SemanticConventions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport.Diagnostics;
6 |
7 | ///
8 | /// Constants for OpenTelemetrySemanticConventions
9 | ///
10 | internal static class SemanticConventions
11 | {
12 | // DATABASE
13 | public const string DbSystem = "db.system";
14 | public const string DbUser = "db.user";
15 |
16 | // HTTP
17 | public const string HttpResponseStatusCode = "http.response.status_code";
18 | public const string HttpRequestMethod = "http.request.method";
19 |
20 | // SERVER
21 | public const string ServerAddress = "server.address";
22 | public const string ServerPort = "server.port";
23 |
24 | // URL
25 | public const string UrlFull = "url.full";
26 |
27 | // URL
28 | public const string UserAgentOriginal = "user_agent.original";
29 | }
30 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/RequestPipelineDiagnosticObserver.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 |
8 | namespace Elastic.Transport.Diagnostics;
9 |
10 | /// Provides a typed listener to actions that takes e.g sniff, ping, or making an API call ;
11 | internal sealed class RequestPipelineDiagnosticObserver : TypedDiagnosticObserver
12 | {
13 | ///
14 | public RequestPipelineDiagnosticObserver(
15 | Action> onNextStart,
16 | Action> onNextEnd,
17 | Action onError = null,
18 | Action onCompleted = null
19 | ) : base(onNextStart, onNextEnd, onError, onCompleted) { }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Serialization/SerializationFormatting.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | ///
8 | /// A hint to how to format the json.
9 | /// Implementation of might choose to ignore this hint though.
10 | ///
11 | public enum SerializationFormatting
12 | {
13 | ///
14 | /// Serializer should not render the json with whitespace and line endings.
15 | /// implementation HAVE to be able to adhere this value as for instance nd-json relies on this
16 | ///
17 | None,
18 |
19 | ///
20 | /// A hint that the user prefers readable data being written. implementations
21 | /// should try to adhere to this but won't break anything if they don't.
22 | ///
23 | Indented
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Plumbing/AssemblyServerTestsBase.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Elastic.Transport.IntegrationTests.Plumbing;
6 | using Elastic.Transport.IntegrationTests.Plumbing.Examples;
7 | using Xunit;
8 |
9 | [assembly: CaptureConsole, AssemblyFixture(typeof(TestServerFixture))]
10 | [assembly: AssemblyFixture(typeof(BufferedServerFixture))]
11 |
12 | namespace Elastic.Transport.IntegrationTests.Plumbing
13 | {
14 | public class AssemblyServerTestsBase(TServer instance)
15 | : IClassFixture where TServer : class, IHttpTransportTestServer
16 | {
17 | protected TServer Server { get; } = instance;
18 |
19 | protected ITransport RequestHandler => Server.DefaultRequestHandler;
20 | }
21 |
22 | public class AssemblyServerTestsBase(TestServerFixture instance)
23 | : AssemblyServerTestsBase(instance);
24 | }
25 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/Auditing/Auditable.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport.Diagnostics.Auditing;
8 |
9 | internal class Auditable : IDisposable
10 | {
11 | private readonly Audit _audit;
12 |
13 | private readonly DateTimeProvider _dateTimeProvider;
14 |
15 | public Auditable(AuditEvent type, DateTimeProvider dateTimeProvider, Node? node)
16 | {
17 | _dateTimeProvider = dateTimeProvider;
18 |
19 | var started = _dateTimeProvider.Now();
20 | _audit = new Audit(type, started)
21 | {
22 | Node = node
23 | };
24 | }
25 |
26 | public AuditEvent Event
27 | {
28 | set => Audit.Event = value;
29 | }
30 |
31 | public Exception Exception
32 | {
33 | set => Audit.Exception = value;
34 | }
35 |
36 | public Audit Audit => _audit;
37 |
38 | public void Dispose() => Audit.Ended = _dateTimeProvider.Now();
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests.Shared/TrackingMemoryStreamFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport.Tests.Shared;
6 |
7 | public class TrackingMemoryStreamFactory() : MemoryStreamFactory
8 | {
9 | public IList Created { get; private set; } = [];
10 |
11 | public override MemoryStream Create()
12 | {
13 | var stream = new TrackDisposeStream();
14 | Created.Add(stream);
15 | return stream;
16 | }
17 |
18 | public override MemoryStream Create(byte[] bytes)
19 | {
20 | var stream = new TrackDisposeStream(bytes);
21 | Created.Add(stream);
22 | return stream;
23 | }
24 |
25 | public override MemoryStream Create(byte[] bytes, int index, int count)
26 | {
27 | var stream = new TrackDisposeStream(bytes, index, count);
28 | Created.Add(stream);
29 | return stream;
30 | }
31 |
32 | public void Reset() => Created = [];
33 | }
34 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Providers/DefaultMemoryStreamFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 |
7 | namespace Elastic.Transport;
8 |
9 | ///
10 | /// A factory for creating memory streams using instances of
11 | ///
12 | public sealed class DefaultMemoryStreamFactory : MemoryStreamFactory
13 | {
14 | /// Provide a static instance of this stateless class, so it can be reused
15 | public static DefaultMemoryStreamFactory Default { get; } = new DefaultMemoryStreamFactory();
16 |
17 | ///
18 | public override MemoryStream Create() => new();
19 |
20 | ///
21 | public override MemoryStream Create(byte[] bytes) => new(bytes);
22 |
23 | ///
24 | public override MemoryStream Create(byte[] bytes, int index, int count) => new(bytes, index, count);
25 | }
26 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/TransportClient/RequestInvokerHelpers.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Diagnostics;
6 | using Elastic.Transport.Diagnostics;
7 |
8 | namespace Elastic.Transport;
9 |
10 | internal static class RequestInvokerHelpers
11 | {
12 | public static void SetOtelAttributes(BoundConfiguration boundConfiguration, TResponse response) where TResponse : TransportResponse
13 | {
14 | if (!OpenTelemetry.CurrentSpanIsElasticTransportOwnedAndHasListeners || (!(Activity.Current?.IsAllDataRequested ?? false)))
15 | return;
16 |
17 | var attributes = boundConfiguration.ConnectionSettings.ProductRegistration.ParseOpenTelemetryAttributesFromApiCallDetails(response.ApiCallDetails);
18 |
19 | if (attributes is null) return;
20 |
21 | foreach (var attribute in attributes)
22 | Activity.Current?.SetTag(attribute.Key, attribute.Value);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Elastic.Elasticsearch.IntegrationTests/IntegrationTestBase.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Elastic.Elasticsearch.Xunit.XunitPlumbing;
6 | using Elastic.Transport;
7 | using Xunit.Abstractions;
8 |
9 | namespace Elastic.Elasticsearch.IntegrationTests;
10 |
11 | public abstract class IntegrationTestBase : IntegrationTestBase
12 | {
13 | protected IntegrationTestBase(DefaultCluster cluster, ITestOutputHelper output) : base(cluster, output) { }
14 | }
15 |
16 | public abstract class IntegrationTestBase : IClusterFixture
17 | where TCluster : DefaultCluster, new()
18 | {
19 | protected TCluster Cluster { get; }
20 | protected ITransport RequestHandler { get; }
21 |
22 | protected IntegrationTestBase(TCluster cluster, ITestOutputHelper output)
23 | {
24 | Cluster = cluster;
25 | RequestHandler = cluster.CreateClient(output);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/benchmarks/Elastic.Transport.Benchmarks/TransportBenchmarks.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Threading.Tasks;
7 | using BenchmarkDotNet.Attributes;
8 |
9 | namespace Elastic.Transport.Benchmarks
10 | {
11 | public class TransportBenchmarks
12 | {
13 | private ITransport _transport;
14 |
15 | [GlobalSetup]
16 | public void Setup()
17 | {
18 | var requestInvoker = new InMemoryRequestInvoker();
19 | var pool = new SingleNodePool(new Uri("http://localhost:9200"));
20 | var settings = new TransportConfiguration(pool, requestInvoker);
21 |
22 | _transport = new DistributedTransport(settings);
23 | }
24 |
25 | [Benchmark]
26 | public void TransportSuccessfulRequestBenchmark() => _transport.Get("/");
27 |
28 | [Benchmark]
29 | public async Task TransportSuccessfulAsyncRequestBenchmark() => await _transport.GetAsync("/");
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Plumbing/Stubs/TestableClientHandler.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Net.Http;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace Elastic.Transport.IntegrationTests.Plumbing.Stubs
11 | {
12 | public class TestableClientHandler : DelegatingHandler
13 | {
14 | private readonly Action _responseAction;
15 |
16 | public TestableClientHandler(HttpMessageHandler handler, Action responseAction) : base(handler) =>
17 | _responseAction = responseAction;
18 |
19 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
20 | {
21 | var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
22 | _responseAction?.Invoke(response);
23 | return response;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Providers/MemoryStreamFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 |
7 | namespace Elastic.Transport;
8 |
9 | ///
10 | /// A factory for creating memory streams
11 | ///
12 | public abstract class MemoryStreamFactory
13 | {
14 | ///
15 | /// Constructs a new instance of .
16 | ///
17 | protected MemoryStreamFactory() { }
18 |
19 | ///
20 | /// Creates a memory stream
21 | ///
22 | public abstract MemoryStream Create();
23 |
24 | ///
25 | /// Creates a memory stream with the bytes written to the stream
26 | ///
27 | public abstract MemoryStream Create(byte[] bytes);
28 |
29 | ///
30 | /// Creates a memory stream with the bytes written to the stream
31 | ///
32 | public abstract MemoryStream Create(byte[] bytes, int index, int count);
33 | }
34 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Components/ExposingPipelineFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | #nullable enable
6 | namespace Elastic.Transport.VirtualizedCluster.Components;
7 |
8 | ///
9 | /// An implementation that exposes all the components so that can reference them directly.
10 | ///
11 | public sealed class ExposingPipelineFactory : RequestPipelineFactory
12 | where TConfiguration : class, ITransportConfiguration
13 | {
14 | public ExposingPipelineFactory(TConfiguration configuration)
15 | {
16 | Configuration = configuration;
17 | Transport = new DistributedTransport(Configuration);
18 | }
19 |
20 | private TConfiguration Configuration { get; }
21 | public ITransport Transport { get; }
22 |
23 | public override RequestPipeline Create(BoundConfiguration boundConfiguration) => new(boundConfiguration);
24 | }
25 | #nullable restore
26 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/MetaData/MetaHeaderProvider.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | namespace Elastic.Transport;
6 |
7 | ///
8 | /// Injects metadata headers into all outgoing requests.
9 | ///
10 | public abstract class MetaHeaderProvider
11 | {
12 | ///
13 | /// The list of all s for the provider.
14 | ///
15 | public abstract MetaHeaderProducer[] Producers { get; }
16 | }
17 |
18 | ///
19 | /// Injects a metadata headers into all outgoing requests.
20 | ///
21 | public abstract class MetaHeaderProducer
22 | {
23 | ///
24 | /// The header name.
25 | ///
26 | public abstract string HeaderName { get; }
27 |
28 | ///
29 | /// Produces the header value based on current outgoing .
30 | ///
31 | public abstract string? ProduceHeaderValue(BoundConfiguration boundConfiguration, bool isAsync);
32 | }
33 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Providers/TestableDateTimeProvider.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport.VirtualizedCluster.Providers;
8 |
9 | ///
10 | public sealed class TestableDateTimeProvider : DateTimeProvider
11 | {
12 | private DateTimeOffset MutableNow { get; set; } = DateTimeOffset.UtcNow;
13 |
14 | ///
15 | public override DateTimeOffset Now() => MutableNow;
16 |
17 | ///
18 | /// Advance the time returns
19 | ///
20 | /// A fun that gets passed the current and needs to return the new value
21 | public void ChangeTime(Func change) => MutableNow = change(MutableNow);
22 |
23 | public override DateTimeOffset DeadTime(int attempts, TimeSpan? minDeadTimeout, TimeSpan? maxDeadTimeout) => throw new NotImplementedException();
24 | }
25 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Rules/PingRule.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport.VirtualizedCluster.Rules;
8 |
9 | public sealed class PingRule : RuleBase
10 | {
11 | private IRule Self => this;
12 |
13 | public PingRule Fails(RuleOption times, RuleOption errorState = null)
14 | {
15 | Self.Times = times;
16 | Self.Succeeds = false;
17 | Self.Return = errorState;
18 | return this;
19 | }
20 |
21 | public PingRule Succeeds(RuleOption times, int? validResponseCode = 200)
22 | {
23 | Self.Times = times;
24 | Self.Succeeds = true;
25 | Self.Return = validResponseCode;
26 | return this;
27 | }
28 |
29 | public PingRule SucceedAlways(int? validResponseCode = 200) => Succeeds(TimesHelper.Always, validResponseCode);
30 |
31 | public PingRule FailAlways(RuleOption errorState = null) => Fails(TimesHelper.Always, errorState);
32 | }
33 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Plumbing/WebHostExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Linq;
7 | using System.Text.RegularExpressions;
8 | using Microsoft.AspNetCore.Hosting;
9 | using Microsoft.AspNetCore.Hosting.Server;
10 | using Microsoft.AspNetCore.Hosting.Server.Features;
11 | using Microsoft.Extensions.Hosting;
12 |
13 | namespace Elastic.Transport.IntegrationTests.Plumbing
14 | {
15 | internal static class WebHostExtensions
16 | {
17 | internal static int GetServerPort(this IServer server)
18 | {
19 | var address = server.Features.Get().Addresses.First();
20 | var match = Regex.Match(address, @"^.+:(\d+)$");
21 |
22 | if (!match.Success) throw new Exception($"Unable to parse port from address: {address}");
23 |
24 | var port = int.TryParse(match.Groups[1].Value, out var p);
25 | return port ? p : throw new Exception($"Unable to parse port to integer from address: {address}");
26 | }
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/Elastic.Elasticsearch.IntegrationTests/DefaultClusterTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Elastic.Transport;
6 | using FluentAssertions;
7 | using Xunit;
8 | using Xunit.Abstractions;
9 | using static Elastic.Transport.HttpMethod;
10 |
11 | namespace Elastic.Elasticsearch.IntegrationTests;
12 |
13 | public class DefaultClusterTests : IntegrationTestBase
14 | {
15 | public DefaultClusterTests(DefaultCluster cluster, ITestOutputHelper output) : base(cluster, output) { }
16 |
17 | [Fact]
18 | public async Task AsyncRequestDoesNotThrow()
19 | {
20 | var response = await RequestHandler.RequestAsync(GET, "/");
21 | response.ApiCallDetails.Should().NotBeNull();
22 | response.ApiCallDetails.HasSuccessfulStatusCode.Should().BeTrue();
23 | }
24 |
25 | [Fact]
26 | public void SyncRequestDoesNotThrow()
27 | {
28 | var response = RequestHandler.Request(GET, "/");
29 | response.ApiCallDetails.Should().NotBeNull();
30 | response.ApiCallDetails.HasSuccessfulStatusCode.Should().BeTrue();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.userprefs
2 | *.local.xml
3 | *.sln.docstates
4 | *.obj
5 | *.swp
6 | *.exe
7 | *.pdb
8 | *.user
9 | *.aps
10 | *.pch
11 | *.tss
12 | *.vspscc
13 | *_i.c
14 | *_p.c
15 | *.ncb
16 | *.suo
17 | *.tlb
18 | *.tlh
19 | *.bak
20 | *.cache
21 | *.ilk
22 | *.log
23 | *.nupkg
24 | *.ncrunchsolution
25 | [Bb]in
26 | [Dd]ebug/
27 | test-results
28 | test-results/*
29 | *.lib
30 | *.sbr
31 | *.DotSettings.user
32 | obj/
33 | [Rr]elease*/
34 | _ReSharper*/
35 | _NCrunch*/
36 | [Tt]est[Rr]esult*
37 |
38 | .fake/*
39 | .fake
40 | packages/*
41 | paket.exe
42 | paket-files/*.cached
43 |
44 | build/*
45 | !build/tools
46 | !build/keys
47 | build/tools/*
48 | !build/tools/sn
49 | !build/tools/sn/*
50 | !build/tools/ilmerge
51 | !build/*.fsx
52 | !build/*.fsx
53 | !build/*.ps1
54 | !build/*.nuspec
55 | !build/*.png
56 | !build/*.targets
57 | !build/scripts
58 |
59 | !docs/build
60 | docs/node_modules
61 | doc/Help
62 |
63 | *.ncrunchproject
64 | Cache
65 | YamlCache
66 | tests.yaml
67 |
68 | *.DS_Store
69 | *.sln.ide
70 |
71 | launchSettings.json
72 | project.lock.json
73 | .vs
74 | .vs/*
75 |
76 | .idea/
77 | *.sln.iml
78 | /src/.vs/restore.dg
79 | src/packages/
80 | BenchmarkDotNet.Artifacts
81 | *.received.*
82 |
83 | /.ionide/symbolCache.db
84 | .artifacts
--------------------------------------------------------------------------------
/.github/check-license-headers.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Check that source code files in this repo have the appropriate license
4 | # header.
5 |
6 | if [ "$TRACE" != "" ]; then
7 | export PS4='${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
8 | set -o xtrace
9 | fi
10 | set -o errexit
11 | set -o pipefail
12 |
13 | TOP=$(cd "$(dirname "$0")/.." >/dev/null && pwd)
14 | NLINES=$(wc -l .github/license-header.txt | awk '{print $1}')
15 |
16 | function check_license_header {
17 | local f
18 | f=$1
19 | if ! diff .github/license-header.txt <(head -$NLINES "$f") >/dev/null; then
20 | echo "check-license-headers: error: '$f' does not have required license header, see 'diff -u .github/license-header.txt <(head -$NLINES $f)'"
21 | return 1
22 | else
23 | return 0
24 | fi
25 | }
26 |
27 | cd "$TOP"
28 | nErrors=0
29 | for f in $(git ls-files | grep '\.cs$'); do
30 | if ! check_license_header $f; then
31 | nErrors=$((nErrors+1))
32 | fi
33 | done
34 |
35 | for f in $(git ls-files | grep '\.fs$'); do
36 | if ! check_license_header $f; then
37 | nErrors=$((nErrors+1))
38 | fi
39 | done
40 |
41 | if [[ $nErrors -eq 0 ]]; then
42 | exit 0
43 | else
44 | exit 1
45 | fi
46 |
--------------------------------------------------------------------------------
/examples/transport-aot-example/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Text.Json.Serialization;
6 | using Elastic.Transport;
7 | using Elastic.Transport.Products.Elasticsearch;
8 |
9 | var apiKey = Environment.GetEnvironmentVariable("ELASTIC_API_KEY");
10 | var url = Environment.GetEnvironmentVariable("ELASTIC_URL");
11 |
12 | var configuration = apiKey is not null && url is not null
13 | ? new ElasticsearchConfiguration(new Uri(url), new ApiKey(apiKey))
14 | : new ElasticsearchConfiguration { DebugMode = true };
15 |
16 | var transport = new DistributedTransport(configuration);
17 |
18 | var rootResponse = transport.Get("/");
19 | if (rootResponse.ApiCallDetails.HasSuccessfulStatusCode)
20 | Console.WriteLine(rootResponse.Get("tagline"));
21 | else
22 | Console.WriteLine(rootResponse);
23 |
24 | public class MyDocument
25 | {
26 | [JsonPropertyName("message")]
27 | public string Message { init; get; } = null!;
28 | }
29 |
30 | [JsonSerializable(typeof(MyDocument))]
31 | internal partial class ExampleJsonSerializerContext : JsonSerializerContext;
32 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/MetaData/RequestMetaData.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections.Generic;
6 |
7 | namespace Elastic.Transport;
8 |
9 | ///
10 | /// Holds meta data about a client request.
11 | ///
12 | public sealed class RequestMetaData
13 | {
14 | ///
15 | /// Reserved key for a meta data entry which identifies the helper which produced the request.
16 | ///
17 | internal const string HelperKey = "helper";
18 |
19 | private Dictionary? _metaDataItems;
20 |
21 | internal bool TryAddMetaData(string key, string value)
22 | {
23 | _metaDataItems ??= [];
24 |
25 | #if NETSTANDARD2_1 || NET6_0_OR_GREATER
26 | return _metaDataItems.TryAdd(key, value);
27 | #else
28 | if (_metaDataItems.ContainsKey(key))
29 | return false;
30 |
31 | _metaDataItems.Add(key, value);
32 | return true;
33 | #endif
34 | }
35 |
36 | /// Retrieves a read-only dictionary of metadata items associated with a client request.
37 | public IReadOnlyDictionary Items => _metaDataItems;
38 | }
39 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Providers/DateTimeProvider.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport;
8 |
9 | ///
10 | /// An abstraction to provide access to the current . This abstraction allows time to be tested within
11 | /// the transport.
12 | ///
13 | public abstract class DateTimeProvider
14 | {
15 | internal DateTimeProvider() { }
16 |
17 | /// The current date time
18 | public abstract DateTimeOffset Now();
19 |
20 | ///
21 | /// Calculate the dead time for a node based on the number of attempts.
22 | ///
23 | /// The number of attempts on the node
24 | /// The initial dead time as configured by
25 | /// The configured maximum dead timeout as configured by
26 | public abstract DateTimeOffset DeadTime(int attempts, TimeSpan? minDeadTimeout, TimeSpan? maxDeadTimeout);
27 | }
28 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Products/Elasticsearch/ErrorSerializationContext.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections.Generic;
6 | using System.Text.Json;
7 | using System.Text.Json.Serialization;
8 | using System.Text.Json.Serialization.Metadata;
9 |
10 | namespace Elastic.Transport.Products.Elasticsearch;
11 |
12 | /// Adds support for serializing Elasticsearch errors using
13 | [JsonSerializable(typeof(JsonElement))]
14 | [JsonSerializable(typeof(Error))]
15 | [JsonSerializable(typeof(ErrorCause))]
16 | [JsonSerializable(typeof(IReadOnlyCollection))]
17 | [JsonSerializable(typeof(ElasticsearchServerError))]
18 | [JsonSerializable(typeof(ElasticsearchResponse))]
19 | [JsonSerializable(typeof(object[]))]
20 | [JsonSerializable(typeof(Dictionary))]
21 | [JsonSerializable(typeof(IReadOnlyDictionary))]
22 | [JsonSerializable(typeof(string))]
23 | [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Default, UseStringEnumConverter = true)]
24 | public partial class ElasticsearchTransportSerializerContext : JsonSerializerContext;
25 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/Special/StreamResponse.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.IO;
7 |
8 | namespace Elastic.Transport;
9 |
10 | ///
11 | /// A response that exposes the response as a .
12 | ///
13 | /// MUST be disposed after use to ensure the HTTP connection is freed for reuse.
14 | ///
15 | ///
16 | public sealed class StreamResponse : StreamResponseBase, IDisposable
17 | {
18 | ///
19 | public StreamResponse() : base(Stream.Null) =>
20 | ContentType = string.Empty;
21 |
22 | ///
23 | public StreamResponse(Stream body, string? contentType) : base(body) =>
24 | ContentType = contentType ?? string.Empty;
25 |
26 | ///
27 | /// The MIME type of the response, if present.
28 | ///
29 | public string ContentType { get; }
30 |
31 | ///
32 | /// The raw response stream.
33 | ///
34 | public Stream Body => Stream;
35 |
36 | ///
37 | protected internal override bool LeaveOpen => true;
38 | }
39 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Exceptions/UnexpectedTransportException.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using Elastic.Transport.Extensions;
8 |
9 | namespace Elastic.Transport;
10 |
11 | ///
12 | /// An exception occured that was not the result of one the well defined exit points as modelled by
13 | /// . This exception will always bubble out.
14 | ///
15 | public class UnexpectedTransportException : TransportException
16 | {
17 | ///
18 | public UnexpectedTransportException(Exception killerException, IReadOnlyCollection? seenExceptions)
19 | : base(PipelineFailure.Unexpected, killerException?.Message ?? "An unexpected exception occurred.", killerException) =>
20 | SeenExceptions = seenExceptions ?? EmptyReadOnly.Collection;
21 |
22 | ///
23 | /// Seen Exceptions that we try to failover on before this was thrown.
24 | ///
25 | // ReSharper disable once MemberCanBePrivate.Global
26 | public IReadOnlyCollection SeenExceptions { get; }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Extensions/EmptyReadonly.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections.Generic;
6 | using System.Collections.ObjectModel;
7 | using System.Linq;
8 |
9 | namespace Elastic.Transport.Extensions;
10 |
11 | internal static class EmptyReadOnlyExtensions
12 | {
13 | public static IReadOnlyCollection ToReadOnlyCollection(this IEnumerable enumerable) =>
14 | enumerable == null ? EmptyReadOnly.Collection : new ReadOnlyCollection(enumerable.ToList());
15 |
16 | public static IReadOnlyCollection ToReadOnlyCollection(this IList enumerable) =>
17 | enumerable == null || enumerable.Count == 0 ? EmptyReadOnly.Collection : new ReadOnlyCollection(enumerable);
18 | }
19 |
20 |
21 | internal static class EmptyReadOnly
22 | {
23 | public static readonly IReadOnlyCollection Collection = new ReadOnlyCollection(new TElement[0]);
24 | public static readonly IReadOnlyList List = new List();
25 | }
26 |
27 | internal static class EmptyReadOnly
28 | {
29 | public static readonly IReadOnlyDictionary Dictionary = new ReadOnlyDictionary(new Dictionary(0));
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/TransportClient/HandlerTracking/LifetimeTrackingHttpMessageHandler.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | // Licensed to the .NET Foundation under one or more agreements.
6 | // The .NET Foundation licenses this file to you under the MIT license.
7 | // See the LICENSE file in the project root for more information
8 |
9 | #if !NETFRAMEWORK
10 | using System.Net.Http;
11 |
12 | namespace Elastic.Transport;
13 |
14 | ///
15 | /// This a marker used to check if the underlying handler should be disposed. HttpClients
16 | /// share a reference to an instance of this class, and when it goes out of scope the inner handler
17 | /// is eligible to be disposed.
18 | /// https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Http/src/LifetimeTrackingHttpMessageHandler.cs
19 | ///
20 | internal sealed class LifetimeTrackingHttpMessageHandler : DelegatingHandler
21 | {
22 | public LifetimeTrackingHttpMessageHandler(HttpMessageHandler innerHandler)
23 | : base(innerHandler) { }
24 |
25 | protected override void Dispose(bool disposing)
26 | {
27 | // The lifetime of this is tracked separately by ActiveHandlerTrackingEntry
28 | }
29 | }
30 | #endif
31 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Products/Elasticsearch/Failures/ShardFailure.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Runtime.Serialization;
6 | using System.Text.Json.Serialization;
7 |
8 | namespace Elastic.Transport.Products.Elasticsearch;
9 |
10 | /// Represents a failure that occurred on a shard involved in the request
11 | [DataContract]
12 | public sealed class ShardFailure
13 | {
14 | /// This index this shard belongs to
15 | [JsonPropertyName("index")]
16 | public string Index { get; set; }
17 |
18 | /// The node the shard is currently allocated on
19 | [JsonPropertyName("node")]
20 | public string Node { get; set; }
21 |
22 | ///
23 | /// The java exception that caused the shard to fail
24 | ///
25 | [JsonPropertyName("reason")]
26 | public ErrorCause Reason { get; set; }
27 |
28 | /// The shard number that failed
29 | [JsonPropertyName("shard")]
30 | [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
31 | public int? Shard { get; set; }
32 |
33 | /// The status of the shard when the exception occured
34 | [JsonPropertyName("status")]
35 | public string Status { get; set; }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Providers/DefaultDateTimeProvider.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport;
8 |
9 | ///
10 | public sealed class DefaultDateTimeProvider : DateTimeProvider
11 | {
12 | /// A static instance to reuse as is stateless
13 | public static readonly DefaultDateTimeProvider Default = new();
14 | private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(60);
15 | private static readonly TimeSpan MaximumTimeout = TimeSpan.FromMinutes(30);
16 |
17 | ///
18 | public override DateTimeOffset DeadTime(int attempts, TimeSpan? minDeadTimeout, TimeSpan? maxDeadTimeout)
19 | {
20 | var timeout = minDeadTimeout.GetValueOrDefault(DefaultTimeout);
21 | var maxTimeout = maxDeadTimeout.GetValueOrDefault(MaximumTimeout);
22 | var milliSeconds = Math.Min(timeout.TotalMilliseconds * 2 * Math.Pow(2, attempts * 0.5 - 1), maxTimeout.TotalMilliseconds);
23 | return Now().AddMilliseconds(milliSeconds);
24 | }
25 |
26 | ///
27 | public override DateTimeOffset Now() => DateTimeOffset.UtcNow;
28 | }
29 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Plumbing/DefaultStartup.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.Routing;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.DependencyInjection;
10 | using Microsoft.Extensions.Hosting;
11 |
12 | namespace Elastic.Transport.IntegrationTests.Plumbing
13 | {
14 | public class DefaultStartup
15 | {
16 | public DefaultStartup(IConfiguration configuration) => Configuration = configuration;
17 |
18 | public IConfiguration Configuration { get; }
19 |
20 | public void ConfigureServices(IServiceCollection services) => services.AddControllers();
21 |
22 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
23 | {
24 | if (env.IsDevelopment())
25 | app.UseDeveloperExceptionPage();
26 | else
27 | app.UseHsts();
28 |
29 | app.UseHttpsRedirection();
30 | app.UseRouting();
31 | app.UseEndpoints(endpoints =>
32 | {
33 | MapEndpoints(endpoints);
34 | endpoints.MapControllerRoute("default", "{controller=Default}/{id?}");
35 | });
36 | }
37 |
38 | protected virtual void MapEndpoints(IEndpointRouteBuilder endpoints) { }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Products/Elasticsearch/Sniff/NodeInfo.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Text.Json;
8 |
9 | namespace Elastic.Transport.Products.Elasticsearch;
10 |
11 | internal sealed class NodeInfo
12 | {
13 | public string build_hash { get; set; }
14 | public string host { get; set; }
15 | public NodeInfoHttp http { get; set; }
16 | public string ip { get; set; }
17 | public string name { get; set; }
18 | public IList roles { get; set; }
19 | public IDictionary settings { get; set; }
20 | public string transport_address { get; set; }
21 | public string version { get; set; }
22 | internal bool HoldsData => roles?.Contains("data") ?? false;
23 |
24 | internal bool HttpEnabled
25 | {
26 | get
27 | {
28 | if (settings != null && settings.TryGetValue("http.enabled", out var httpEnabled))
29 | {
30 | if (httpEnabled is JsonElement e)
31 | return e.GetBoolean();
32 | return Convert.ToBoolean(httpEnabled);
33 | }
34 |
35 | return http != null;
36 | }
37 | }
38 |
39 | internal bool IngestEnabled => roles?.Contains("ingest") ?? false;
40 |
41 | internal bool MasterEligible => roles?.Contains("master") ?? false;
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Elastic.Elasticsearch.IntegrationTests/Elastic.Elasticsearch.IntegrationTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net10.0
5 | enable
6 | enable
7 | false
8 | xUnit1041
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 |
16 |
17 |
18 |
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 | all
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/NodePool/SingleNodePool.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using Elastic.Transport.Diagnostics.Auditing;
8 |
9 | namespace Elastic.Transport;
10 |
11 | /// A pool to a single node or endpoint.
12 | public class SingleNodePool : NodePool
13 | {
14 | ///
15 | public SingleNodePool(Uri uri)
16 | {
17 | var node = new Node(uri);
18 | UsingSsl = node.Uri.Scheme == "https";
19 | Nodes = new List { node };
20 | }
21 |
22 | ///
23 | public override DateTimeOffset? LastUpdate { get; protected set; }
24 |
25 | ///
26 | public override int MaxRetries => 0;
27 |
28 | ///
29 | public override IReadOnlyCollection Nodes { get; }
30 |
31 | ///
32 | public override bool SupportsPinging => false;
33 |
34 | ///
35 | public override bool SupportsReseeding => false;
36 |
37 | ///
38 | public override bool UsingSsl { get; protected set; }
39 |
40 | ///
41 | public override IEnumerable CreateView(Auditor? auditor) => Nodes;
42 |
43 | ///
44 | public override void Reseed(IEnumerable nodes) { } //ignored
45 | }
46 |
--------------------------------------------------------------------------------
/benchmarks/Elastic.Transport.Profiling/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Threading.Tasks;
7 | using Elastic.Transport.Products.Elasticsearch;
8 | using JetBrains.Profiler.Api;
9 |
10 | namespace Elastic.Transport.Profiling
11 | {
12 | internal class Program
13 | {
14 | private static async Task Main()
15 | {
16 | MemoryProfiler.CollectAllocations(true);
17 | MemoryProfiler.GetSnapshot("start");
18 |
19 | var config = new TransportConfiguration(new Uri("http://localhost:9200"),
20 | new ElasticsearchProductRegistration(typeof(ElasticsearchProductRegistration)));
21 |
22 | var transport = new DistributedTransport(config);
23 |
24 | // WARMUP
25 | for (var i = 0; i < 50; i++) _ = await transport.GetAsync("/");
26 |
27 | MemoryProfiler.GetSnapshot("before-100-requests");
28 | for (var i = 0; i < 100; i++) _ = await transport.GetAsync("/");
29 | MemoryProfiler.GetSnapshot("after-100-requests");
30 |
31 | await Task.Delay(1000);
32 | MemoryProfiler.ForceGc();
33 |
34 | MemoryProfiler.GetSnapshot("before-final-request");
35 | _ = await transport.GetAsync("/");
36 | MemoryProfiler.GetSnapshot("after-final-request");
37 |
38 | MemoryProfiler.GetSnapshot("end");
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | Elasticsearch .NET Transport
2 | Copyright 2012-2021 Elasticsearch B.V.
3 |
4 | ==========
5 | Notice for: Microsoft.IO.RecyclableMemoryStream
6 | ----------
7 | Based on the Microsoft.IO.RecyclableMemoryStream project by Microsoft,
8 | licensed under the MIT license. https://github.com/Microsoft/Microsoft.IO.RecyclableMemoryStream/
9 |
10 | MIT License
11 |
12 | Copyright (c) 2015-2016 Microsoft
13 |
14 | Permission is hereby granted, free of charge, to any person obtaining a copy
15 | of this software and associated documentation files (the "Software"), to deal
16 | in the Software without restriction, including without limitation the rights
17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 | copies of the Software, and to permit persons to whom the Software is
19 | furnished to do so, subject to the following conditions:
20 |
21 | The above copyright notice and this permission notice shall be included in all
22 | copies or substantial portions of the Software.
23 |
24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 | SOFTWARE.
31 |
32 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/TransportClient/HandlerTracking/ExpiredHandlerTrackingEntry.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | // Licensed to the .NET Foundation under one or more agreements.
6 | // The .NET Foundation licenses this file to you under the MIT license.
7 | // See the LICENSE file in the project root for more information
8 |
9 | #if !NETFRAMEWORK
10 | using System;
11 | using System.Net.Http;
12 |
13 | namespace Elastic.Transport;
14 |
15 | ///
16 | /// Thread-safety: This class is immutable
17 | /// https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs
18 | ///
19 | internal sealed class ExpiredHandlerTrackingEntry
20 | {
21 | private readonly WeakReference _livenessTracker;
22 |
23 | // IMPORTANT: don't cache a reference to `other` or `other.Handler` here.
24 | // We need to allow it to be GC'ed.
25 | public ExpiredHandlerTrackingEntry(ActiveHandlerTrackingEntry other)
26 | {
27 | Key = other.Key;
28 |
29 | _livenessTracker = new WeakReference(other.Handler);
30 | InnerHandler = other.Handler.InnerHandler;
31 | }
32 |
33 | public bool CanDispose => !_livenessTracker.IsAlive;
34 |
35 | public HttpMessageHandler InnerHandler { get; }
36 |
37 | public int Key { get; }
38 | }
39 | #endif
40 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Rules/SniffRule.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using Elastic.Transport.VirtualizedCluster.Components;
7 |
8 | namespace Elastic.Transport.VirtualizedCluster.Rules;
9 |
10 | public interface ISniffRule : IRule
11 | {
12 | /// The new cluster state after the sniff returns
13 | VirtualCluster NewClusterState { get; set; }
14 | }
15 |
16 | public sealed class SniffRule : RuleBase, ISniffRule
17 | {
18 | VirtualCluster ISniffRule.NewClusterState { get; set; }
19 | private ISniffRule Self => this;
20 |
21 | public SniffRule Fails(RuleOption times, RuleOption errorState = null)
22 | {
23 | Self.Times = times;
24 | Self.Succeeds = false;
25 | Self.Return = errorState;
26 | return this;
27 | }
28 |
29 | public SniffRule Succeeds(RuleOption times, VirtualCluster cluster = null)
30 | {
31 | Self.Times = times;
32 | Self.Succeeds = true;
33 | Self.NewClusterState = cluster;
34 | Self.Return = 200;
35 | return this;
36 | }
37 |
38 | public SniffRule SucceedAlways(VirtualCluster cluster = null) => Succeeds(TimesHelper.Always, cluster);
39 |
40 | public SniffRule FailAlways(RuleOption errorState = null) => Fails(TimesHelper.Always, errorState);
41 | }
42 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Audit/ClientCall.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using Elastic.Transport.Diagnostics.Auditing;
8 |
9 | namespace Elastic.Transport.VirtualizedCluster.Audit;
10 |
11 | public sealed class ClientCall : List
12 | {
13 | public ClientCall() { }
14 |
15 | public ClientCall(Func requestOverrides) => RequestOverrides = requestOverrides;
16 |
17 | public Action AssertPoolAfterCall { get; private set; }
18 | public Action AssertResponse { get; private set; }
19 | public Func RequestOverrides { get; }
20 |
21 | public void Add(AuditEvent key, Action value) => Add(new CallTraceState(key) { SimpleAssert = value });
22 |
23 | public void Add(AuditEvent key, int port) => Add(new CallTraceState(key) { Port = port });
24 |
25 | public void Add(AuditEvent key) => Add(new CallTraceState(key));
26 |
27 | public void Add(Action pool) => AssertPoolAfterCall = pool;
28 |
29 | public void Add(AuditEvent key, int port, Action assertResponse)
30 | {
31 | Add(new CallTraceState(key) { Port = port });
32 | AssertResponse = assertResponse;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Serialization/JsonElementExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections.Generic;
6 | using System.Globalization;
7 | using System.Linq;
8 | using System.Text.Json;
9 |
10 | namespace Elastic.Transport.Extensions;
11 |
12 | internal static class JsonElementExtensions
13 | {
14 | ///
15 | /// Fully consumes a json element representing a json object. Meaning it will attempt to unwrap all JsonElement values
16 | /// recursively to their actual types. This should only be used in the context of which is
17 | /// allowed to be slow yet convenient
18 | ///
19 | public static IDictionary ToDictionary(this JsonElement e) =>
20 | e.ValueKind switch
21 | {
22 | JsonValueKind.Object => e.EnumerateObject()
23 | .Aggregate(new Dictionary(), (dict, je) =>
24 | {
25 | dict.Add(je.Name, DynamicValue.ConsumeJsonElement(typeof(object), je.Value));
26 | return dict;
27 | }),
28 | JsonValueKind.Array => e.EnumerateArray()
29 | .Select((je, i) => (i, o: DynamicValue.ConsumeJsonElement(typeof(object), je)))
30 | .Aggregate(new Dictionary(), (dict, t) =>
31 | {
32 | dict.Add(t.i.ToString(CultureInfo.InvariantCulture), t.o);
33 | return dict;
34 | }),
35 | _ => null
36 | };
37 | }
38 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.bat))
6 |
7 | canary.0
8 | 0.2
9 |
10 | latest
11 | true
12 | False
13 | false
14 | true
15 | $(SolutionRoot)\build\keys\keypair.snk
16 | true
17 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.sh))
18 | $(MSBuildThisFileDirectory).artifacts
19 |
20 |
21 |
22 |
23 |
24 |
25 | all
26 | runtime; build; native; contentfiles; analyzers
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Playground/Program.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Elastic.Transport;
6 | using Elastic.Transport.Products.Elasticsearch;
7 | using HttpMethod = Elastic.Transport.HttpMethod;
8 |
9 | var registration = new ElasticsearchProductRegistration(typeof(Elastic.Clients.Elasticsearch.ElasticsearchClient));
10 |
11 | var apiKey = Environment.GetEnvironmentVariable("ELASTIC_API_KEY") ?? throw new Exception();
12 | var url = Environment.GetEnvironmentVariable("ELASTIC_URL") ?? throw new Exception();
13 |
14 | var configuration = new TransportConfiguration(new Uri(url), new ApiKey(apiKey), ElasticsearchProductRegistration.Default)
15 | {
16 | DebugMode = true
17 | };
18 | var transport = new DistributedTransport(configuration);
19 |
20 | var response = transport.Request(HttpMethod.GET, "/does-not-exist");
21 | Console.WriteLine(response.DebugInformation);
22 |
23 | var dynamicResponse = transport.Request(HttpMethod.GET, "/");
24 | Console.WriteLine(dynamicResponse.Body.Get("version.build_flavor"));
25 |
26 | var body = PostData.String("{\"name\": \"test\"}");
27 | var indexResponse = transport.Request(HttpMethod.POST, "/does-not-exist/_doc", body);
28 | Console.WriteLine(indexResponse.DebugInformation);
29 |
30 | Console.WriteLine(registration.DefaultContentType ?? "NOT SPECIFIED");
31 |
32 | public class EsResponse : ElasticsearchResponse;
33 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/TransportClient/HttpMethod.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Runtime.Serialization;
7 |
8 | // ReSharper disable InconsistentNaming
9 |
10 | namespace Elastic.Transport;
11 |
12 | /// Http Method of the API call to be performed
13 | public enum HttpMethod
14 | {
15 | [EnumMember(Value = "GET")]
16 | // These really do not need xmldocs, leave it to the reader if they feel inspired :)
17 | #pragma warning disable 1591
18 | GET,
19 |
20 | [EnumMember(Value = "POST")]
21 | POST,
22 |
23 | [EnumMember(Value = "PUT")]
24 | PUT,
25 |
26 | [EnumMember(Value = "DELETE")]
27 | DELETE,
28 |
29 | [EnumMember(Value = "HEAD")]
30 | HEAD
31 | #pragma warning restore 1591
32 | }
33 |
34 | ///
35 | /// Defines extension methods for .
36 | ///
37 | public static class HttpMethodExtensions
38 | {
39 | ///
40 | /// Returns the string value for a given .
41 | ///
42 | public static string GetStringValue(this HttpMethod httpMethod) =>
43 | httpMethod switch
44 | {
45 | HttpMethod.GET => "GET",
46 | HttpMethod.POST => "POST",
47 | HttpMethod.PUT => "PUT",
48 | HttpMethod.DELETE => "DELETE",
49 | HttpMethod.HEAD => "HEAD",
50 | _ => throw new InvalidOperationException("Unknown enum value.")
51 | };
52 | }
53 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Configuration/Security/BasicAuthenticationCredentials.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Text;
7 |
8 | namespace Elastic.Transport;
9 |
10 | ///
11 | /// Credentials for Basic Authentication.
12 | ///
13 | public sealed class BasicAuthentication : AuthorizationHeader
14 | {
15 | private readonly string _base64String;
16 |
17 | /// The default http header used for basic authentication
18 | public static string BasicAuthenticationScheme { get; } = "Basic";
19 |
20 | ///
21 | public BasicAuthentication(string username, string password)
22 | {
23 | _base64String = GetBase64String($"{username}:{password}");
24 | Username = username;
25 | }
26 |
27 | ///
28 | public override string AuthScheme { get; } = BasicAuthenticationScheme;
29 | internal string Username { get; }
30 |
31 | ///
32 | public override bool TryGetAuthorizationParameters(out string value)
33 | {
34 | value = _base64String;
35 | return true;
36 | }
37 |
38 | /// Get Base64 representation for string
39 | public static string GetBase64String(string header) =>
40 | Convert.ToBase64String(Encoding.UTF8.GetBytes(header));
41 | }
42 |
--------------------------------------------------------------------------------
/.github/workflows/test-windows.yml:
--------------------------------------------------------------------------------
1 | name: Windows tests
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - '*.md'
7 | - '*.asciidoc'
8 | - '.editorconfig'
9 | - 'docs/**'
10 | branches:
11 | - main
12 | tags:
13 | - "*.*.*"
14 | pull_request:
15 | paths-ignore:
16 | - '*.md'
17 | - '*.asciidoc'
18 | - '.editorconfig'
19 | - 'docs/**'
20 |
21 | permissions:
22 | contents: read
23 | checks: write
24 |
25 | concurrency:
26 | group: ${{ github.workflow }}-${{ github.ref }}
27 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
28 |
29 | defaults:
30 | run:
31 | shell: cmd
32 |
33 | jobs:
34 | tests:
35 | runs-on: windows-2022
36 |
37 | steps:
38 | - name: Checkout
39 | uses: actions/checkout@v5
40 | with:
41 | fetch-depth: 1
42 |
43 | - run: |
44 | git fetch --prune --unshallow --tags
45 | echo exit code $?
46 | git tag --list
47 | name: Fetch Tags
48 |
49 | - uses: actions/cache@v4
50 | name: NuGet package cache
51 | with:
52 | path: ~/.nuget/packages
53 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.[cf]sproj*') }}
54 | restore-keys: |
55 | ${{ runner.os }}-nuget
56 |
57 | - name: Setup dotnet
58 | uses: actions/setup-dotnet@v5
59 | with:
60 | global-json-file: ./global.json
61 |
62 | - run: ./build.bat build -s true
63 | name: Build
64 |
65 | - run: ./build.bat test -s true
66 | name: Test
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Elastic.Transport.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net10.0;net481
5 | True
6 | false
7 | CS8002
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 |
17 |
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 | all
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/MetaData/VersionInfo.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Elastic.Transport.Extensions;
6 |
7 | namespace Elastic.Transport;
8 |
9 | ///
10 | ///
11 | ///
12 | public abstract class VersionInfo
13 | {
14 | private readonly SemVersion _version;
15 |
16 | ///
17 | ///
18 | ///
19 | public int Major => _version.Major;
20 |
21 | ///
22 | ///
23 | ///
24 | public int Minor => _version.Minor;
25 |
26 | ///
27 | ///
28 | ///
29 | public int Patch => _version.Patch;
30 |
31 | ///
32 | ///
33 | ///
34 | public string? Prerelease => _version.Prerelease;
35 |
36 | ///
37 | ///
38 | ///
39 | public string? Metadata => _version.Metadata;
40 |
41 | ///
42 | ///
43 | ///
44 | public bool IsPrerelease => !string.IsNullOrEmpty(_version.Prerelease);
45 |
46 | ///
47 | ///
48 | ///
49 | ///
50 | protected VersionInfo(SemVersion version) => _version = version;
51 |
52 | /// Returns the full version as a semantic version number
53 | public override string ToString() => _version.ToString();
54 |
55 | /// Returns the version in a way that safe to emit as telemetry
56 | public string ToMetadataHeaderValue()
57 | {
58 | var prefix = $"{Major}.{Minor}.{Patch}";
59 |
60 | return IsPrerelease ? $"{prefix}p" : prefix;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Serialization/SerializerRegistrationInformation.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport;
8 |
9 | /// Provides some information to the transport auditing and diagnostics infrastructure about the serializer in use and its
10 | public sealed class SerializerRegistrationInformation
11 | {
12 | private readonly string _stringRepresentation;
13 |
14 | ///
15 | public SerializerRegistrationInformation(Type type, string purpose)
16 | {
17 | TypeInformation = type;
18 | Purpose = purpose;
19 | _stringRepresentation = $"{Purpose}: {TypeInformation.FullName}";
20 | }
21 |
22 | /// The type of in use currently
23 | // ReSharper disable once MemberCanBePrivate.Global
24 | public Type TypeInformation { get; }
25 |
26 | ///
27 | /// A string describing the purpose of the serializer emitting this events.
28 | /// In `Elastisearch.Net` this will always be "request/response"
29 | /// Using `Nest` this could also be `source` allowing you to differentiate between the internal and configured source serializer
30 | ///
31 | // ReSharper disable once MemberCanBePrivate.Global
32 | public string Purpose { get; }
33 |
34 | /// A precalculated string representation of the serializer in use
35 | public override string ToString() => _stringRepresentation;
36 | }
37 |
--------------------------------------------------------------------------------
/tests/Elastic.Elasticsearch.IntegrationTests/SecurityClusterTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Elastic.Transport;
6 | using FluentAssertions;
7 | using Xunit;
8 | using Xunit.Abstractions;
9 | using static Elastic.Transport.HttpMethod;
10 |
11 | namespace Elastic.Elasticsearch.IntegrationTests;
12 |
13 | public class SecurityClusterTests : IntegrationTestBase
14 | {
15 | public SecurityClusterTests(SecurityCluster cluster, ITestOutputHelper output) : base(cluster, output) { }
16 |
17 | private static readonly EndpointPath Root = new(GET, "/");
18 |
19 | [Fact]
20 | public async Task AsyncRequestDoesNotThrow()
21 | {
22 | var response = await RequestHandler.RequestAsync(Root);
23 | response.ApiCallDetails.Should().NotBeNull();
24 | response.ApiCallDetails.HasSuccessfulStatusCode.Should().BeTrue();
25 | }
26 |
27 | [Fact]
28 | public void SyncRequestDoesNotThrow()
29 | {
30 | var response = RequestHandler.Request(Root);
31 | response.ApiCallDetails.Should().NotBeNull();
32 | response.ApiCallDetails.HasSuccessfulStatusCode.Should().BeTrue();
33 | }
34 |
35 | [Fact]
36 | public void SyncRequestDoesNotThrowOnBadAuth()
37 | {
38 | var response = RequestHandler.Request(Root, null,
39 | new RequestConfiguration
40 | {
41 | Authentication = new BasicAuthentication("unknown-user", "bad-password")
42 | }
43 | );
44 | response.ApiCallDetails.Should().NotBeNull();
45 | response.ApiCallDetails.HasSuccessfulStatusCode.Should().BeFalse();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Configuration/RequestConfigurationTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Security.Cryptography.X509Certificates;
7 | using System.Threading;
8 | using FluentAssertions;
9 | using Xunit;
10 | #if !NETFRAMEWORK
11 | using Soenneker.Utils.AutoBogus;
12 | #endif
13 |
14 | namespace Elastic.Transport.Tests.Configuration;
15 |
16 | public class RequestConfigurationTests
17 | {
18 | [Fact]
19 | public void CopiesAllDefaults()
20 | {
21 | var config = new RequestConfiguration();
22 | var newConfig = new RequestConfiguration(config);
23 |
24 | config.Should().BeEquivalentTo(newConfig);
25 | }
26 |
27 | [Fact]
28 | public void SameDefaults()
29 | {
30 | IRequestConfiguration config = new RequestConfiguration();
31 | IRequestConfiguration newConfig = new RequestConfigurationDescriptor();
32 |
33 | config.Should().BeEquivalentTo(newConfig);
34 | }
35 |
36 | #if !NETFRAMEWORK
37 | [Fact]
38 | public void CopiesAllProperties()
39 | {
40 | var autoFaker = new AutoFaker();
41 | autoFaker.RuleFor(x => x.ClientCertificates, f => []);
42 |
43 | var config = autoFaker.Generate();
44 | config.Accept.Should().NotBeEmpty();
45 | config.ClientCertificates.Should().NotBeNull();
46 |
47 | IRequestConfiguration newConfig = new RequestConfiguration(config);
48 | config.Should().BeEquivalentTo(newConfig);
49 |
50 | IRequestConfiguration newDescriptor = new RequestConfigurationDescriptor(config);
51 | config.Should().BeEquivalentTo(newDescriptor);
52 | }
53 | #endif
54 | }
55 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Elastic.Transport.IntegrationTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net10.0
5 | True
6 | false
7 | CS8002;xUnit1041
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 |
17 |
18 |
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 | all
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Elastic.Transport.slnx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Products/Elasticsearch/Sniff/SniffParser.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Text.RegularExpressions;
7 | using Elastic.Transport.Extensions;
8 |
9 | namespace Elastic.Transport.Products.Elasticsearch;
10 |
11 | ///
12 | /// Elasticsearch returns addresses in the form of
13 | /// [fqdn]/ip:port number
14 | /// This helper parses it to
15 | ///
16 | public static class SniffParser
17 | {
18 | /// A regular expression that captures fqdn, ip and por
19 | public static Regex AddressRegex { get; } =
20 | new Regex(@"^((?[^/]+)/)?(?[^:]+|\[[\da-fA-F:\.]+\]):(?\d+)$");
21 |
22 | ///
23 | /// Elasticsearch returns addresses in the form of
24 | /// [fqdn]/ip:port number
25 | /// This helper parses it to
26 | ///
27 | public static Uri ParseToUri(string boundAddress, bool forceHttp)
28 | {
29 | if (boundAddress == null) throw new ArgumentNullException(nameof(boundAddress));
30 |
31 | var suffix = forceHttp ? "s" : string.Empty;
32 | var match = AddressRegex.Match(boundAddress);
33 | if (!match.Success) throw new Exception($"Can not parse bound_address: {boundAddress} to Uri");
34 |
35 | var fqdn = match.Groups["fqdn"].Value.Trim();
36 | var ip = match.Groups["ip"].Value.Trim();
37 | var port = match.Groups["port"].Value.Trim();
38 | var host = !fqdn.IsNullOrEmpty() ? fqdn : ip;
39 |
40 | return new Uri($"http{suffix}://{host}:{port}");
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Products/Elasticsearch/Sniff/SniffResponse.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections.Generic;
6 | using System.Collections.ObjectModel;
7 | using System.Linq;
8 | using static Elastic.Transport.Products.Elasticsearch.ElasticsearchNodeFeatures;
9 |
10 | namespace Elastic.Transport.Products.Elasticsearch;
11 |
12 | internal sealed class SniffResponse : TransportResponse
13 | {
14 | // ReSharper disable InconsistentNaming
15 | public string cluster_name { get; set; }
16 |
17 | public Dictionary nodes { get; set; }
18 |
19 | public IEnumerable ToNodes(bool forceHttp = false)
20 | {
21 | foreach (var kv in nodes.Where(n => n.Value.HttpEnabled))
22 | {
23 | var info = kv.Value;
24 | var httpEndpoint = info.http?.publish_address;
25 | if (string.IsNullOrWhiteSpace(httpEndpoint))
26 | httpEndpoint = kv.Value.http?.bound_address.FirstOrDefault();
27 | if (string.IsNullOrWhiteSpace(httpEndpoint))
28 | continue;
29 |
30 | var uri = SniffParser.ParseToUri(httpEndpoint, forceHttp);
31 | var features = new List();
32 | if (info.MasterEligible)
33 | features.Add(MasterEligible);
34 | if (info.HoldsData)
35 | features.Add(HoldsData);
36 | if (info.IngestEnabled)
37 | features.Add(IngestEnabled);
38 | if (info.HttpEnabled)
39 | features.Add(HttpEnabled);
40 |
41 | var node = new Node(uri, features)
42 | {
43 | Name = info.name, Id = kv.Key, Settings = new ReadOnlyDictionary(info.settings)
44 | };
45 | yield return node;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Rules/ClientCallRule.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | #if !NETFRAMEWORK
6 | using System;
7 | using TheException = System.Net.Http.HttpRequestException;
8 | #else
9 | using TheException = System.Net.WebException;
10 | #endif
11 |
12 | namespace Elastic.Transport.VirtualizedCluster.Rules;
13 |
14 | public interface IClientCallRule : IRule { }
15 |
16 | public sealed class ClientCallRule : RuleBase, IClientCallRule
17 | {
18 | private IClientCallRule Self => this;
19 |
20 | public ClientCallRule Fails(RuleOption times, RuleOption errorState = null)
21 | {
22 | Self.Times = times;
23 | Self.Succeeds = false;
24 | Self.Return = errorState ?? new TheException();
25 | return this;
26 | }
27 |
28 | public ClientCallRule Succeeds(RuleOption times, int? validResponseCode = 200)
29 | {
30 | Self.Times = times;
31 | Self.Succeeds = true;
32 | Self.Return = validResponseCode;
33 | return this;
34 | }
35 |
36 | public ClientCallRule AfterSucceeds(RuleOption errorState = null)
37 | {
38 | Self.AfterSucceeds = errorState;
39 | return this;
40 | }
41 |
42 | public ClientCallRule ThrowsAfterSucceeds()
43 | {
44 | Self.AfterSucceeds = new TheException();
45 | return this;
46 | }
47 |
48 | public ClientCallRule SucceedAlways(int? validResponseCode = 200) => Succeeds(TimesHelper.Always, validResponseCode);
49 |
50 | public ClientCallRule FailAlways(RuleOption errorState = null) => Fails(TimesHelper.Always, errorState);
51 | }
52 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Extensions/TaskExtensions.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | // Adapted from https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/src/Shared/TaskExtensions.cs
6 | // Copyright (c) Microsoft Corporation. All rights reserved.
7 | // Licensed under the MIT License.
8 |
9 | using System;
10 | using System.Diagnostics;
11 | using System.Threading.Tasks;
12 |
13 | namespace Elastic.Transport;
14 |
15 | internal static class TaskExtensions
16 | {
17 | [Conditional("DEBUG")]
18 | private static void VerifyTaskCompleted(bool isCompleted)
19 | {
20 | if (!isCompleted)
21 | {
22 | if (Debugger.IsAttached)
23 | {
24 | Debugger.Break();
25 | }
26 | // Throw an InvalidOperationException instead of using
27 | // Debug.Assert because that brings down xUnit immediately
28 | throw new InvalidOperationException("Task is not completed");
29 | }
30 | }
31 |
32 | public static T EnsureCompleted(this ValueTask task)
33 | {
34 | #if DEBUG
35 | VerifyTaskCompleted(task.IsCompleted);
36 | #endif
37 | #pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
38 | return task.GetAwaiter().GetResult();
39 | #pragma warning restore VSTHRD002 // Avoid problematic synchronous waits
40 | }
41 |
42 | public static void EnsureCompleted(this ValueTask task)
43 | {
44 | #if DEBUG
45 | VerifyTaskCompleted(task.IsCompleted);
46 | #endif
47 | #pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
48 | task.GetAwaiter().GetResult();
49 | #pragma warning restore VSTHRD002 // Avoid problematic synchronous waits
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/Auditing/Audit.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using Elastic.Transport.Extensions;
7 |
8 | namespace Elastic.Transport.Diagnostics.Auditing;
9 |
10 | /// An audit of the request made
11 | public sealed class Audit
12 | {
13 | internal Audit(AuditEvent type, DateTimeOffset started)
14 | {
15 | Event = type;
16 | Started = started;
17 | }
18 |
19 | ///
20 | /// The type of audit event.
21 | ///
22 | public AuditEvent Event { get; internal set; }
23 |
24 | ///
25 | /// The node on which the request was made.
26 | ///
27 | public Node? Node { get; internal init; }
28 |
29 | ///
30 | /// The end date and time of the audit.
31 | ///
32 | public DateTimeOffset Ended { get; internal set; }
33 |
34 | ///
35 | /// The start date and time of the audit.
36 | ///
37 | public DateTimeOffset Started { get; }
38 |
39 | ///
40 | /// The exception for the audit, if there was one.
41 | ///
42 | public Exception Exception { get; internal set; }
43 |
44 | ///
45 | /// Returns a string representation of the this audit.
46 | ///
47 | public override string ToString()
48 | {
49 | var took = Ended - Started;
50 | var tookString = string.Empty;
51 | if (took >= TimeSpan.Zero) tookString = $" Took: {took}";
52 |
53 | return Node == null
54 | ? $"Event: {Event.ToStringFast()}{tookString}"
55 | : $"Event: {Event.ToStringFast()} Node: {Node?.Uri} NodeAlive: {Node?.IsAlive}Took: {tookString}";
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Plumbing/Examples/ControllerIntegrationTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Threading.Tasks;
6 | using FluentAssertions;
7 | using Microsoft.AspNetCore.Builder;
8 | using Microsoft.AspNetCore.Http;
9 | using Microsoft.AspNetCore.Mvc;
10 | using Microsoft.AspNetCore.Routing;
11 | using Microsoft.Extensions.Configuration;
12 | using Xunit;
13 |
14 | // Feel free to delete these tests at some point, these are here as examples while we built out our test suite
15 | // A lot of integration tests need to be ported from elastic/elasticsearch-net
16 | namespace Elastic.Transport.IntegrationTests.Plumbing.Examples
17 | {
18 | ///
19 | /// Tests that the test framework loads a controller and the exposed transport can talk to its endpoints.
20 | /// Tests runs against a server that started up once and its server instance shared among many test classes
21 | ///
22 | public class ControllerIntegrationTests(BufferedServerFixture instance)
23 | : AssemblyServerTestsBase(instance)
24 | {
25 | [Fact]
26 | public async Task CanCallIntoController()
27 | {
28 | var response = await RequestHandler.GetAsync("/dummy/20", cancellationToken: TestContext.Current.CancellationToken);
29 | response.ApiCallDetails.HasSuccessfulStatusCode.Should().BeTrue("{0}", response.ApiCallDetails.DebugInformation);
30 | }
31 | }
32 |
33 | [ApiController, Route("[controller]")]
34 | public class DummyController : ControllerBase
35 | {
36 | [HttpGet("{id}")]
37 | public async Task Get(int id) => await Task.FromResult(id * 3);
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/TransportClient/HandlerTracking/ValueStopWatch.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | // Licensed to the .NET Foundation under one or more agreements.
6 | // The .NET Foundation licenses this file to you under the MIT license.
7 | // See the LICENSE file in the project root for more information
8 |
9 | #if !NETFRAMEWORK
10 | using System;
11 | using System.Threading;
12 |
13 | namespace Elastic.Transport;
14 |
15 | ///
16 | /// A convenience API for interacting with System.Threading.Timer in a way
17 | /// that doesn't capture the ExecutionContext. We should be using this (or equivalent)
18 | /// everywhere we use timers to avoid rooting any values stored in asynclocals.
19 | /// https://github.com/dotnet/runtime/blob/master/src/libraries/Common/src/Extensions/ValueStopwatch/ValueStopwatch.cs
20 | ///
21 | internal static class NonCapturingTimer
22 | {
23 | public static Timer Create(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
24 | {
25 | if (callback == null) throw new ArgumentNullException(nameof(callback));
26 |
27 | // Don't capture the current ExecutionContext and its AsyncLocals onto the timer
28 | var restoreFlow = false;
29 | try
30 | {
31 | if (ExecutionContext.IsFlowSuppressed()) return new Timer(callback, state, dueTime, period);
32 |
33 | ExecutionContext.SuppressFlow();
34 | restoreFlow = true;
35 |
36 | return new Timer(callback, state, dueTime, period);
37 | }
38 | finally
39 | {
40 | // Restore the current ExecutionContext
41 | if (restoreFlow) ExecutionContext.RestoreFlow();
42 | }
43 | }
44 | }
45 | #endif
46 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Products/Elasticsearch/ElasticsearchMockProductRegistration.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using Elastic.Transport.Products;
8 | using Elastic.Transport.Products.Elasticsearch;
9 |
10 | namespace Elastic.Transport.VirtualizedCluster.Products.Elasticsearch;
11 |
12 | /// >
13 | public sealed class ElasticsearchMockProductRegistration : MockProductRegistration
14 | {
15 | /// A static instance of to reuse
16 | public static MockProductRegistration Default { get; } = new ElasticsearchMockProductRegistration();
17 |
18 | /// >
19 | public override ProductRegistration ProductRegistration { get; } = ElasticsearchProductRegistration.Default;
20 |
21 | /// >
22 | public override byte[] CreateSniffResponseBytes(IReadOnlyList nodes, string stackVersion, string publishAddressOverride, bool returnFullyQualifiedDomainNames) =>
23 | ElasticsearchSniffResponseFactory.Create(nodes, stackVersion, publishAddressOverride, returnFullyQualifiedDomainNames);
24 |
25 | public override bool IsSniffRequest(Endpoint endpoint) =>
26 | endpoint.PathAndQuery.StartsWith(ElasticsearchProductRegistration.SniffPath, StringComparison.Ordinal);
27 |
28 | public override bool IsPingRequest(Endpoint endpoint) =>
29 | endpoint.Method == HttpMethod.HEAD && (endpoint.PathAndQuery == string.Empty || endpoint.PathAndQuery.StartsWith("?"));
30 | }
31 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Configuration/ConnectionInfo.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | #if !NETFRAMEWORK
7 | using System.Net.Http;
8 | #endif
9 |
10 | namespace Elastic.Transport;
11 |
12 | internal static class ConnectionInfo
13 | {
14 | public static bool UsingCurlHandler
15 | {
16 | get
17 | {
18 | // Not available after .NET 5.0
19 | #if NET6_0_OR_GREATER || NETFRAMEWORK
20 | #pragma warning disable IDE0025 // Use expression body for properties
21 | return false;
22 | #pragma warning restore IDE0025 // Use expression body for properties
23 | #else
24 | var curlHandlerExists = typeof(HttpClientHandler).Assembly.GetType("System.Net.Http.CurlHandler") != null;
25 | if (!curlHandlerExists)
26 | return false;
27 |
28 | var socketsHandlerExists = typeof(HttpClientHandler).Assembly.GetType("System.Net.Http.SocketsHttpHandler") != null;
29 | // running on a .NET core version with CurlHandler, before the existence of SocketsHttpHandler.
30 | // Must be using CurlHandler.
31 | if (!socketsHandlerExists)
32 | return true;
33 |
34 | if (AppContext.TryGetSwitch("System.Net.Http.UseSocketsHttpHandler", out var isEnabled))
35 | return !isEnabled;
36 |
37 | var environmentVariable =
38 | Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER");
39 |
40 | // SocketsHandler exists and no environment variable exists to disable it.
41 | // Must be using SocketsHandler and not CurlHandler
42 | if (environmentVariable == null)
43 | return false;
44 |
45 | return environmentVariable.Equals("false", StringComparison.OrdinalIgnoreCase) ||
46 | environmentVariable.Equals("0");
47 | #endif
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/Auditing/Auditor.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections;
6 | using System.Collections.Generic;
7 | using Elastic.Transport.Extensions;
8 |
9 | namespace Elastic.Transport.Diagnostics.Auditing;
10 |
11 | /// Collects events
12 | public class Auditor : IReadOnlyCollection
13 | {
14 | private readonly DateTimeProvider _dateTimeProvider;
15 | private List? _audits;
16 |
17 | internal Auditor(DateTimeProvider dateTimeProvider) => _dateTimeProvider = dateTimeProvider;
18 |
19 | ///
20 | public IEnumerator GetEnumerator() =>
21 | _audits?.GetEnumerator() ?? (IEnumerator)new EmptyEnumerator();
22 |
23 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
24 |
25 | internal Auditable Add(Auditable auditable)
26 | {
27 | _audits ??= new List();
28 | _audits.Add(auditable.Audit);
29 | return auditable;
30 | }
31 |
32 | internal Auditable Add(AuditEvent type, DateTimeProvider dateTimeProvider, Node? node = null)
33 | {
34 | _audits ??= new List();
35 | var auditable = new Auditable(type, dateTimeProvider, node);
36 | _audits.Add(auditable.Audit);
37 | return auditable;
38 | }
39 |
40 | /// Emits an event that does not need to track a duration
41 | public void Emit(AuditEvent type) => Add(type, _dateTimeProvider).Dispose();
42 |
43 | /// Emits an event that does not need to track a duration
44 | public void Emit(AuditEvent type, Node node) => Add(type, _dateTimeProvider, node).Dispose();
45 |
46 | ///
47 | public int Count => _audits?.Count ?? 0;
48 | }
49 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Configuration/HeadersListTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Linq;
6 | using FluentAssertions;
7 | using Xunit;
8 |
9 | namespace Elastic.Transport.Tests.Configuration
10 | {
11 | public class HeadersListTests
12 | {
13 | [Fact]
14 | public void SupportsEnumerationWhenEmpty()
15 | {
16 | var sut = new HeadersList();
17 |
18 | foreach (var header in sut)
19 | {
20 | }
21 | }
22 |
23 | [Fact]
24 | public void Ctor_SkipsDuplicates_FromSingleEnumerable()
25 | {
26 | var sut = new HeadersList(new[] { "header-one", "header-two", "header-TWO" });
27 |
28 | sut.Count.Should().Be(2);
29 | sut.First().Should().Be("header-one");
30 | sut.Last().Should().Be("header-two");
31 | }
32 |
33 | [Fact]
34 | public void Ctor_SkipsDuplicates_FromSingleEnumerable_AndSingleHeader()
35 | {
36 | var sut = new HeadersList(new[] { "header-one", "header-two" }, "header-TWO");
37 |
38 | sut.Count.Should().Be(2);
39 | sut.First().Should().Be("header-one");
40 | sut.Last().Should().Be("header-two");
41 | }
42 |
43 | [Fact]
44 | public void Ctor_SkipsDuplicates_FromTwoEnumerables()
45 | {
46 | var sut = new HeadersList(new[] { "header-ONE", "header-two" }, new[] { "header-one", "header-THREE", "HEADER-TWO" });
47 |
48 | sut.Count.Should().Be(3);
49 |
50 | var count = 0;
51 | foreach (var header in sut)
52 | {
53 | count++;
54 |
55 | switch (count)
56 | {
57 | case 1:
58 | header.Should().Be("header-ONE");
59 | break;
60 | case 2:
61 | header.Should().Be("header-two");
62 | break;
63 | case 3:
64 | header.Should().Be("header-THREE");
65 | break;
66 | }
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/Dynamic/DynamicResponse.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Diagnostics.CodeAnalysis;
6 | using System.Dynamic;
7 |
8 | namespace Elastic.Transport;
9 |
10 | ///
11 | /// A type of response that makes it easier to work with responses in an untyped fashion.
12 | ///
13 | /// It exposes the body as which is `dynamic` through
14 | ///
15 | /// Since `dynamic` can be scary in .NET this response also exposes a safe traversal mechanism under
16 | /// which support an xpath'esque syntax to fish for values in the returned json.
17 | ///
18 | ///
19 | public sealed class DynamicResponse : TransportResponse
20 | {
21 | ///
22 | public DynamicResponse() { }
23 |
24 | ///
25 | public DynamicResponse(DynamicDictionary dictionary)
26 | {
27 | Body = dictionary;
28 | Dictionary = dictionary;
29 | }
30 |
31 | private DynamicDictionary Dictionary { get; }
32 |
33 | ///
34 | /// Traverses data using path notation.
35 | /// e.g some.deep.nested.json.path
36 | /// A special lookup is available for ANY key using _arbitrary_key_ e.g some.deep._arbitrary_key_.json.path which will traverse into the first key
37 | ///
38 | /// path into the stored object, keys are separated with a dot and the last key is returned as T
39 | ///
40 | /// T or default
41 | public T Get<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(string path) => Dictionary.Get(path);
42 | }
43 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/TransportClient/CertificateHelpers.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Security.Cryptography;
7 | using System.Security.Cryptography.X509Certificates;
8 |
9 | namespace Elastic.Transport;
10 |
11 | internal static class CertificateHelpers
12 | {
13 | private const string _colon = ":";
14 | private const string _hyphen = "-";
15 |
16 | ///
17 | /// Returns the result of validating the fingerprint of a certificate against an expected fingerprint string.
18 | ///
19 | public static bool ValidateCertificateFingerprint(X509Certificate certificate, string expectedFingerprint)
20 | {
21 | string sha256Fingerprint;
22 |
23 | #if !NETFRAMEWORK&& !NETSTANDARD2_0
24 | sha256Fingerprint = certificate.GetCertHashString(HashAlgorithmName.SHA256);
25 | #else
26 | using var alg = SHA256.Create();
27 |
28 | var sha256FingerprintBytes = alg.ComputeHash(certificate.GetRawCertData());
29 | sha256Fingerprint = BitConverter.ToString(sha256FingerprintBytes);
30 | #endif
31 |
32 | sha256Fingerprint = ComparableFingerprint(sha256Fingerprint);
33 | return expectedFingerprint.Equals(sha256Fingerprint, StringComparison.OrdinalIgnoreCase);
34 | }
35 |
36 | ///
37 | /// Cleans the fingerprint by removing colons and dashes so that a comparison can be made
38 | /// without such characters affecting the result.
39 | ///
40 | public static string ComparableFingerprint(string fingerprint)
41 | {
42 | var finalFingerprint = fingerprint;
43 |
44 | if (fingerprint.Contains(_colon))
45 | finalFingerprint = fingerprint.Replace(_colon, string.Empty);
46 | else if (fingerprint.Contains(_hyphen))
47 | finalFingerprint = fingerprint.Replace(_hyphen, string.Empty);
48 |
49 | return finalFingerprint;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/TypedResponseBuilder.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Elastic.Transport;
10 |
11 | ///
12 | /// A builder for a specific type.
13 | ///
14 | ///
15 | public abstract class TypedResponseBuilder : IResponseBuilder
16 | {
17 | bool IResponseBuilder.CanBuild() => typeof(TResponse) == typeof(T);
18 |
19 | ///
20 | protected abstract TResponse? Build(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType, long contentLength);
21 |
22 | T IResponseBuilder.Build(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType, long contentLength) =>
23 | Build(apiCallDetails, boundConfiguration, responseStream, contentType, contentLength) as T;
24 |
25 | ///
26 | protected abstract Task BuildAsync(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream,
27 | string contentType, long contentLength, CancellationToken cancellationToken = default);
28 |
29 | Task IResponseBuilder.BuildAsync(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType,
30 | long contentLength, CancellationToken cancellationToken) =>
31 | BuildAsync(apiCallDetails, boundConfiguration, responseStream, contentType, contentLength, cancellationToken) as Task;
32 | }
33 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Extensions/RuntimeInformation.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | // Licensed to the .NET Foundation under one or more agreements.
6 | // The .NET Foundation licenses this file to you under the MIT license.
7 | // See the LICENSE file in the project root for more information
8 | // https://raw.githubusercontent.com/dotnet/core-setup/master/src/managed/Microsoft.DotNet.PlatformAbstractions/Native/NativeMethods.Windows.cs
9 |
10 | #if NETFRAMEWORK
11 | using System;
12 | using System.Linq;
13 | using System.Reflection;
14 |
15 | namespace Elastic.Transport.Extensions
16 | {
17 | internal static class RuntimeInformation
18 | {
19 | private static string _frameworkDescription;
20 | private static string _osDescription;
21 |
22 | public static string FrameworkDescription
23 | {
24 | get
25 | {
26 | if (_frameworkDescription == null)
27 | {
28 | var assemblyFileVersionAttribute =
29 | ((AssemblyFileVersionAttribute[])Attribute.GetCustomAttributes(
30 | typeof(object).Assembly,
31 | typeof(AssemblyFileVersionAttribute)))
32 | .OrderByDescending(a => a.Version)
33 | .First();
34 | _frameworkDescription = $".NET Framework {assemblyFileVersionAttribute.Version}";
35 | }
36 | return _frameworkDescription;
37 | }
38 | }
39 |
40 | public static string OSDescription
41 | {
42 | get
43 | {
44 | if (_osDescription == null)
45 | {
46 | var platform = (int)Environment.OSVersion.Platform;
47 | var isWindows = platform != 4 && platform != 6 && platform != 128;
48 | if (isWindows)
49 | _osDescription = NativeMethods.Windows.RtlGetVersion() ?? "Microsoft Windows";
50 | else
51 | _osDescription = Environment.OSVersion.VersionString;
52 | }
53 | return _osDescription;
54 | }
55 | }
56 | }
57 | }
58 | #endif
59 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Configuration/TransportConfigurationTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Security.Cryptography.X509Certificates;
7 | using System.Threading;
8 | using FluentAssertions;
9 | using Xunit;
10 | #if !NETFRAMEWORK
11 | using Soenneker.Utils.AutoBogus;
12 | #endif
13 |
14 | namespace Elastic.Transport.Tests.Configuration;
15 |
16 | public class TransportConfigurationTests
17 | {
18 | [Fact]
19 | public void CopiesAllDefaults()
20 | {
21 | var config = new TransportConfiguration();
22 | var newConfig = new TransportConfiguration(config);
23 |
24 | config.Should().BeEquivalentTo(newConfig);
25 | }
26 |
27 | [Fact]
28 | public void SameDefaults()
29 | {
30 | ITransportConfiguration config = new TransportConfiguration();
31 | ITransportConfiguration newConfig = new TransportConfigurationDescriptor();
32 |
33 | config.Should().BeEquivalentTo(newConfig, c => c
34 | .Excluding(p=>p.BootstrapLock)
35 | );
36 |
37 | config.BootstrapLock.CurrentCount.Should().Be(newConfig.BootstrapLock.CurrentCount);
38 | }
39 |
40 | #if !NETFRAMEWORK
41 | [Fact]
42 | public void CopiesAllProperties()
43 | {
44 | var autoFaker = new AutoFaker();
45 | autoFaker.RuleFor(x => x.BootstrapLock, f => new SemaphoreSlim(1, 1));
46 | autoFaker.RuleFor(x => x.ClientCertificates, f => new X509CertificateCollection());
47 |
48 | var config = autoFaker.Generate();
49 | config.Accept.Should().NotBeEmpty();
50 | config.ClientCertificates.Should().NotBeNull();
51 |
52 | ITransportConfiguration newConfig = new TransportConfiguration(config);
53 | config.Should().BeEquivalentTo(newConfig);
54 |
55 | ITransportConfiguration newDescriptor = new TransportConfigurationDescriptor(config);
56 | config.Should().BeEquivalentTo(newDescriptor);
57 | }
58 | #endif
59 | }
60 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/Special/StreamResponseBase.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.IO;
7 | using Elastic.Transport.Extensions;
8 |
9 | namespace Elastic.Transport;
10 |
11 | ///
12 | /// A base class for implementing responses that access the raw response stream.
13 | ///
14 | public abstract class StreamResponseBase : TransportResponse, IDisposable
15 | {
16 | ///
17 | protected internal override bool LeaveOpen => true;
18 |
19 | ///
20 | /// The raw response stream from the HTTP layer.
21 | ///
22 | ///
23 | /// MUST be disposed to release the underlying HTTP connection for reuse.
24 | ///
25 | protected Stream Stream { get; }
26 |
27 | ///
28 | /// Indicates that the response has been disposed and it is not longer safe to access the stream.
29 | ///
30 | protected bool Disposed { get; private set; }
31 |
32 | ///
33 | public StreamResponseBase(Stream responseStream)
34 | {
35 | responseStream.ThrowIfNull(nameof(responseStream));
36 | Stream = responseStream;
37 | }
38 |
39 | ///
40 | /// Disposes the underlying stream.
41 | ///
42 | ///
43 | protected virtual void Dispose(bool disposing)
44 | {
45 | if (!Disposed)
46 | {
47 | if (disposing)
48 | {
49 | Stream?.Dispose();
50 |
51 | if (LinkedDisposables is not null)
52 | {
53 | foreach (var disposable in LinkedDisposables)
54 | disposable?.Dispose();
55 | }
56 | }
57 |
58 | Disposed = true;
59 | }
60 | }
61 |
62 | ///
63 | /// Disposes the underlying stream.
64 | ///
65 | public void Dispose()
66 | {
67 | Dispose(disposing: true);
68 | GC.SuppressFinalize(this);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Elastic.Transport.VirtualizedCluster/Products/MockProductRegistration.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections.Generic;
6 | using Elastic.Transport.Products;
7 | using Elastic.Transport.VirtualizedCluster.Components;
8 |
9 | namespace Elastic.Transport.VirtualizedCluster.Products;
10 |
11 | ///
12 | /// Makes sure is mockable by providing a different sniff response based on the current
13 | ///
14 | public abstract class MockProductRegistration
15 | {
16 | ///
17 | /// Information about the current product we are injecting into
18 | ///
19 | public abstract ProductRegistration ProductRegistration { get; }
20 |
21 | ///
22 | /// Return the sniff response for the product as raw bytes for to return.
23 | ///
24 | /// The nodes we expect to be returned in the response
25 | /// The current version under test
26 | /// Return this hostname instead of some IP
27 | /// If the sniff can return internal + external information return both
28 | public abstract byte[] CreateSniffResponseBytes(IReadOnlyList nodes, string stackVersion, string publishAddressOverride, bool returnFullyQualifiedDomainNames);
29 |
30 | ///
31 | /// see uses this to determine if the current request is a sniff request and should follow
32 | /// the sniffing rules
33 | ///
34 | public abstract bool IsSniffRequest(Endpoint endpoint);
35 |
36 | public abstract bool IsPingRequest(Endpoint endpoint);
37 | }
38 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/NodePool/StickyNodePool.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Threading;
8 | using Elastic.Transport.Diagnostics.Auditing;
9 |
10 | namespace Elastic.Transport;
11 |
12 | ///
13 | /// A connection pool implementation that does not support reseeding and stays on the first reporting true for .
14 | /// This is great if for instance you have multiple proxies that you can fallback on allowing you to seed the proxies in order of preference.
15 | ///
16 | public sealed class StickyNodePool : StaticNodePool
17 | {
18 | ///
19 | public StickyNodePool(IEnumerable uris) : base(uris, false) { }
20 |
21 | ///
22 | public StickyNodePool(IEnumerable nodes) : base(nodes, false) { }
23 |
24 | ///
25 | public override IEnumerable CreateView(Auditor? auditor)
26 | {
27 | var nodes = AliveNodes;
28 |
29 | if (nodes.Count == 0)
30 | {
31 | var globalCursor = Interlocked.Increment(ref GlobalCursor);
32 |
33 | //could not find a suitable node retrying on first node off globalCursor
34 | yield return RetryInternalNodes(globalCursor, auditor);
35 |
36 | yield break;
37 | }
38 |
39 | // If the cursor is greater than the default then it's been
40 | // set already but we now have a live node so we should reset it
41 | if (GlobalCursor > -1)
42 | Interlocked.Exchange(ref GlobalCursor, -1);
43 |
44 | var localCursor = 0;
45 | foreach (var aliveNode in SelectAliveNodes(localCursor, nodes, auditor))
46 | yield return aliveNode;
47 | }
48 |
49 | ///
50 | public override void Reseed(IEnumerable nodes) { }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Plumbing/Stubs/TrackingRequestInvoker.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Net.Http;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace Elastic.Transport.IntegrationTests.Plumbing.Stubs;
11 |
12 | public class TrackingRequestInvoker : IRequestInvoker
13 | {
14 | private readonly Action _response;
15 | private TestableClientHandler _handler;
16 | public int CallCount { get; private set; }
17 | public HttpClientHandler LastHttpClientHandler => (HttpClientHandler)_handler.InnerHandler;
18 |
19 | public ResponseFactory ResponseFactory => _requestInvoker.ResponseFactory;
20 |
21 | private readonly HttpRequestInvoker _requestInvoker;
22 |
23 | public TrackingRequestInvoker(Action response) : this() => _response = response;
24 |
25 | public TrackingRequestInvoker() =>
26 | _requestInvoker = new HttpRequestInvoker((defaultHandler, _) =>
27 | {
28 | _handler = new TestableClientHandler(defaultHandler, _response);
29 | return _handler;
30 | });
31 |
32 | public TResponse Request(Endpoint endpoint, BoundConfiguration boundConfiguration, PostData postData)
33 | where TResponse : TransportResponse, new()
34 | {
35 | CallCount++;
36 | return _requestInvoker.Request(endpoint, boundConfiguration, postData);
37 | }
38 |
39 | public Task RequestAsync(Endpoint endpoint, BoundConfiguration boundConfiguration, PostData postData, CancellationToken cancellationToken)
40 | where TResponse : TransportResponse, new()
41 | {
42 | CallCount++;
43 | return _requestInvoker.RequestAsync(endpoint, boundConfiguration, postData, cancellationToken);
44 | }
45 |
46 | public void Dispose()
47 | {
48 | _handler?.Dispose();
49 | _requestInvoker?.Dispose();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Providers/RecyclableMemoryStreamFactory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.IO;
7 |
8 | namespace Elastic.Transport;
9 |
10 | ///
11 | /// A factory for creating memory streams using a recyclable pool of instances
12 | ///
13 | public sealed class RecyclableMemoryStreamFactory : MemoryStreamFactory
14 | {
15 | private const string TagSource = "Elastic.Transport";
16 | private readonly RecyclableMemoryStreamManager _manager;
17 |
18 | /// Provide a static instance of this stateless class, so it can be reused
19 | public static RecyclableMemoryStreamFactory Default { get; } = new RecyclableMemoryStreamFactory();
20 |
21 | ///
22 | public RecyclableMemoryStreamFactory() => _manager = CreateManager(experimental: false);
23 |
24 | private static RecyclableMemoryStreamManager CreateManager(bool experimental)
25 | {
26 | if (!experimental) return new RecyclableMemoryStreamManager() { AggressiveBufferReturn = true };
27 |
28 | const int blockSize = 1024;
29 | const int largeBufferMultiple = 1024 * 1024;
30 | const int maxBufferSize = 16 * largeBufferMultiple;
31 | return new RecyclableMemoryStreamManager(blockSize, largeBufferMultiple, maxBufferSize)
32 | {
33 | AggressiveBufferReturn = true, MaximumFreeLargePoolBytes = maxBufferSize * 4, MaximumFreeSmallPoolBytes = 100 * blockSize
34 | };
35 | }
36 |
37 | ///
38 | public override MemoryStream Create() => _manager.GetStream(Guid.Empty, TagSource);
39 |
40 | ///
41 | public override MemoryStream Create(byte[] bytes) => _manager.GetStream(bytes);
42 |
43 | ///
44 | public override MemoryStream Create(byte[] bytes, int index, int count) => _manager.GetStream(Guid.Empty, TagSource, bytes, index, count);
45 | }
46 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Extensions/NativeMethods.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | // Licensed to the .NET Foundation under one or more agreements.
6 | // The .NET Foundation licenses this file to you under the MIT license.
7 | // See the LICENSE file in the project root for more information
8 | // https://raw.githubusercontent.com/dotnet/core-setup/master/src/managed/Microsoft.DotNet.PlatformAbstractions/Native/NativeMethods.Windows.cs
9 |
10 | #if NETFRAMEWORK
11 | using System.Runtime.InteropServices;
12 |
13 | namespace Elastic.Transport.Extensions
14 | {
15 | internal static class NativeMethods
16 | {
17 | public static class Windows
18 | {
19 | // This call avoids the shimming Windows does to report old versions
20 | [DllImport("ntdll")]
21 | private static extern int RtlGetVersion(out RTL_OSVERSIONINFOEX lpVersionInformation);
22 |
23 | internal static string RtlGetVersion()
24 | {
25 | var osvi = new RTL_OSVERSIONINFOEX();
26 | osvi.dwOSVersionInfoSize = (uint)Marshal.SizeOf(osvi);
27 | return RtlGetVersion(out osvi) == 0
28 | ? $"Microsoft Windows {osvi.dwMajorVersion}.{osvi.dwMinorVersion}.{osvi.dwBuildNumber}"
29 | : null;
30 | }
31 |
32 | [StructLayout(LayoutKind.Sequential)]
33 | // ReSharper disable once MemberCanBePrivate.Global
34 | // ReSharper disable InconsistentNaming
35 | // ReSharper disable FieldCanBeMadeReadOnly.Global
36 | internal struct RTL_OSVERSIONINFOEX
37 | {
38 | internal uint dwOSVersionInfoSize;
39 | internal uint dwMajorVersion;
40 | internal uint dwMinorVersion;
41 | internal uint dwBuildNumber;
42 | internal uint dwPlatformId;
43 |
44 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
45 | internal string szCSDVersion;
46 | }
47 | // ReSharper restore InconsistentNaming
48 | // ReSharper restore FieldCanBeMadeReadOnly.Global
49 | }
50 | }
51 | }
52 | #endif
53 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/Body/PostData.ByteArray.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Elastic.Transport;
10 |
11 | public abstract partial class PostData
12 | {
13 | ///
14 | /// Create a instance that will write to the output
15 | ///
16 | // ReSharper disable once MemberCanBePrivate.Global
17 | public static PostData Bytes(byte[] bytes) => new PostDataByteArray(bytes);
18 |
19 | private class PostDataByteArray : PostData
20 | {
21 | protected internal PostDataByteArray(byte[] item)
22 | {
23 | WrittenBytes = item;
24 | Type = PostType.ByteArray;
25 | }
26 |
27 | public override void Write(Stream writableStream, ITransportConfiguration settings, bool disableDirectStreaming)
28 | {
29 | if (WrittenBytes == null) return;
30 |
31 | MemoryStream? buffer = null;
32 |
33 | if (!disableDirectStreaming)
34 | writableStream.Write(WrittenBytes, 0, WrittenBytes.Length);
35 | else
36 | buffer = settings.MemoryStreamFactory.Create(WrittenBytes);
37 |
38 | FinishStream(writableStream, buffer, disableDirectStreaming);
39 | }
40 |
41 | public override async Task WriteAsync(Stream writableStream, ITransportConfiguration settings, bool disableDirectStreaming, CancellationToken cancellationToken)
42 | {
43 | if (WrittenBytes == null) return;
44 |
45 | MemoryStream? buffer = null;
46 |
47 | if (!disableDirectStreaming)
48 | await writableStream.WriteAsync(WrittenBytes, 0, WrittenBytes.Length, cancellationToken)
49 | .ConfigureAwait(false);
50 | else
51 | buffer = settings.MemoryStreamFactory.Create(WrittenBytes);
52 |
53 | await FinishStreamAsync(writableStream, buffer, disableDirectStreaming, cancellationToken).ConfigureAwait(false);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Components/NodePool/StaticNodePoolTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Xunit;
6 | using System;
7 | using FluentAssertions;
8 | using System.Linq;
9 |
10 | namespace Elastic.Transport.Tests.Components.NodePool
11 | {
12 | public class StaticNodePoolTests
13 | {
14 | [Fact]
15 | public void MultipleRequests_WhenOnlyASingleEndpointIsConfigured_AndTheEndpointIsUnavailable_DoNotThrowAnException()
16 | {
17 | Node[] nodes = [new Uri("http://localhost:9200")];
18 | var pool = new StaticNodePool(nodes);
19 | var transport = new DistributedTransport(new TransportConfiguration(pool));
20 |
21 | var response = transport.Request(HttpMethod.GET, "/", null, null);
22 |
23 | response.ApiCallDetails.SuccessOrKnownError.Should().BeFalse();
24 | response.ApiCallDetails.AuditTrail.Count.Should().Be(1);
25 |
26 | var audit = response.ApiCallDetails.AuditTrail.First();
27 | audit.Event.Should().Be(Diagnostics.Auditing.AuditEvent.BadRequest);
28 | audit.Node.FailedAttempts.Should().Be(1);
29 | audit.Node.IsAlive.Should().BeFalse();
30 |
31 | response = transport.Request(HttpMethod.GET, "/", null, null);
32 |
33 | response.ApiCallDetails.SuccessOrKnownError.Should().BeFalse();
34 |
35 | var eventCount = 0;
36 |
37 | foreach (var a in response.ApiCallDetails.AuditTrail)
38 | {
39 | eventCount++;
40 |
41 | if (eventCount == 1)
42 | {
43 | a.Event.Should().Be(Diagnostics.Auditing.AuditEvent.AllNodesDead);
44 | }
45 |
46 | if (eventCount == 2)
47 | {
48 | a.Event.Should().Be(Diagnostics.Auditing.AuditEvent.Resurrection);
49 | }
50 |
51 | if (eventCount == 3)
52 | {
53 | a.Event.Should().Be(Diagnostics.Auditing.AuditEvent.BadRequest);
54 | audit.Node.FailedAttempts.Should().Be(2);
55 | audit.Node.IsAlive.Should().BeFalse();
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Responses/Special/BytesResponseBuilder.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Elastic.Transport;
10 |
11 | internal class BytesResponseBuilder : TypedResponseBuilder
12 | {
13 | protected override BytesResponse Build(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType, long contentLength) =>
14 | BuildCoreAsync(false, apiCallDetails, boundConfiguration, responseStream).EnsureCompleted();
15 |
16 | protected override Task BuildAsync(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType, long contentLength, CancellationToken cancellationToken = default) =>
17 | BuildCoreAsync(true, apiCallDetails, boundConfiguration, responseStream, cancellationToken).AsTask();
18 |
19 | private static async ValueTask BuildCoreAsync(bool isAsync, ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, CancellationToken cancellationToken = default)
20 | {
21 | BytesResponse response;
22 |
23 | if (apiCallDetails.ResponseBodyInBytes is not null)
24 | {
25 | response = new BytesResponse(apiCallDetails.ResponseBodyInBytes);
26 | return response;
27 | }
28 |
29 | var tempStream = boundConfiguration.MemoryStreamFactory.Create();
30 | await responseStream.CopyToAsync(tempStream, BufferedResponseHelpers.BufferSize, cancellationToken).ConfigureAwait(false);
31 | apiCallDetails.ResponseBodyInBytes = BufferedResponseHelpers.SwapStreams(ref responseStream, ref tempStream);
32 | response = new BytesResponse(apiCallDetails.ResponseBodyInBytes);
33 |
34 | #if NET6_0_OR_GREATER
35 | await responseStream.DisposeAsync().ConfigureAwait(false);
36 | #else
37 | responseStream.Dispose();
38 | #endif
39 |
40 | return response;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Plumbing/Examples/EndpointIntegrationTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Threading.Tasks;
6 | using FluentAssertions;
7 | using Microsoft.AspNetCore.Builder;
8 | using Microsoft.AspNetCore.Http;
9 | using Microsoft.AspNetCore.Routing;
10 | using Microsoft.Extensions.Configuration;
11 | using Xunit;
12 |
13 | namespace Elastic.Transport.IntegrationTests.Plumbing.Examples
14 | {
15 | ///
16 | /// Spins up a server just for the tests in this class, using a custom startup we attach a special endpoint handler
17 | /// Since it extends it server is only shared between the tests inside this class.
18 | /// The server is also started and stopped after all the tests in this class run.
19 | ///
20 | public class EndpointIntegrationTests(BufferedServerFixture instance)
21 | : ClassServerTestsBase(instance)
22 | {
23 | [Fact]
24 | public async Task CanCallIntoEndpoint()
25 | {
26 | var response = await RequestHandler.GetAsync(BufferedStartup.Endpoint, cancellationToken: TestContext.Current.CancellationToken);
27 | response.ApiCallDetails.HasSuccessfulStatusCode.Should().BeTrue("{0}", response.ApiCallDetails.DebugInformation);
28 | }
29 | }
30 |
31 | public class BufferedServerFixture : TransportTestServer;
32 | public class BufferedStartup : DefaultStartup
33 | {
34 | public BufferedStartup(IConfiguration configuration) : base(configuration) { }
35 |
36 | public static string Endpoint { get; } = "buffered";
37 |
38 | protected override void MapEndpoints(IEndpointRouteBuilder endpoints) =>
39 | endpoints.MapGet("/buffered", async context =>
40 | {
41 | var name = context.Request.RouteValues["id"];
42 | await context.Response.WriteAsync($"Hello {name}!");
43 | await Task.Delay(1);
44 | await context.Response.WriteAsync($"World!");
45 | });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Products/Elasticsearch/ElasticsearchNodeFeatures.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | namespace Elastic.Transport.Products.Elasticsearch;
9 |
10 | ///
11 | /// Encodes the features will register on
12 | /// . These static strings make it easier to inspect if features are enabled
13 | /// using
14 | ///
15 | public static class ElasticsearchNodeFeatures
16 | {
17 | /// Indicates whether this node holds data, defaults to true when unknown/unspecified
18 | public const string HoldsData = "node.data";
19 | /// Whether HTTP is enabled on the node or not
20 | public const string HttpEnabled = "node.http";
21 | /// Indicates whether this node is allowed to run ingest pipelines, defaults to true when unknown/unspecified
22 | public const string IngestEnabled = "node.ingest";
23 | /// Indicates whether this node is master eligible, defaults to true when unknown/unspecified
24 | public const string MasterEligible = "node.master";
25 |
26 | /// The default collection of features, which enables ALL Features
27 | public static readonly IReadOnlyCollection Default =
28 | new[] { HoldsData, MasterEligible, IngestEnabled, HttpEnabled }.ToList().AsReadOnly();
29 |
30 | /// The node only has the and features
31 | public static readonly IReadOnlyCollection MasterEligibleOnly =
32 | new[] { MasterEligible, HttpEnabled }.ToList().AsReadOnly();
33 |
34 | /// The node has all features EXCEPT
35 | // ReSharper disable once UnusedMember.Global
36 | public static readonly IReadOnlyCollection NotMasterEligible =
37 | Default.Except(new[] { MasterEligible }).ToList().AsReadOnly();
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/VolatileUpdates.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Threading;
9 | using FluentAssertions;
10 | using Xunit;
11 |
12 | namespace Elastic.Transport.Tests
13 | {
14 | public class VolatileUpdates
15 | {
16 | private readonly int _numberOfNodes = 10;
17 | private readonly Random _random = new Random();
18 |
19 | private readonly List _update = Enumerable.Range(9200, 10)
20 | .Select(p => new Uri("http://localhost:" + p))
21 | .Select(u => new Node(u))
22 | .ToList();
23 |
24 | [Fact] public void SniffingPoolWithstandsConcurrentReadAndWrites()
25 | {
26 | var uris = Enumerable.Range(9200, _numberOfNodes).Select(p => new Uri("http://localhost:" + p));
27 | var sniffingPool = new SniffingNodePool(uris, false);
28 |
29 | var callSniffing = () => AssertCreateView(sniffingPool);
30 |
31 | callSniffing.Should().NotThrow();
32 | }
33 |
34 | [Fact] public void StaticPoolWithstandsConcurrentReadAndWrites()
35 | {
36 | var uris = Enumerable.Range(9200, _numberOfNodes).Select(p => new Uri("http://localhost:" + p));
37 | var staticPool = new StaticNodePool(uris, false);
38 |
39 | var callStatic = () => AssertCreateView(staticPool);
40 |
41 | callStatic.Should().NotThrow();
42 | }
43 |
44 | private void AssertCreateView(NodePool pool)
45 | {
46 | var threads = Enumerable.Range(0, 50)
47 | .Select(i => CreateReadAndUpdateThread(pool))
48 | .ToList();
49 |
50 | foreach (var t in threads) t.Start();
51 | foreach (var t in threads) t.Join();
52 | }
53 |
54 | private Thread CreateReadAndUpdateThread(NodePool pool) => new Thread(() =>
55 | {
56 | for (var i = 0; i < 1000; i++)
57 | foreach (var _ in CallGetNext(pool))
58 | if (_random.Next(10) % 2 == 0)
59 | pool.Reseed(_update);
60 | });
61 |
62 | private IEnumerable CallGetNext(NodePool pool)
63 | {
64 | foreach (var n in pool.CreateView()) yield return n.Uri.Port;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/Body/PostData.ReadOnlyMemory.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | #if !NETSTANDARD2_0 && !NETFRAMEWORK
6 | using System;
7 | using System.IO;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | namespace Elastic.Transport
12 | {
13 | public abstract partial class PostData
14 | {
15 | ///
16 | /// Create a instance that will write the to the output
17 | ///
18 | public static PostData ReadOnlyMemory(ReadOnlyMemory bytes) => new PostDataReadOnlyMemory(bytes);
19 |
20 | private class PostDataReadOnlyMemory : PostData
21 | {
22 | private readonly ReadOnlyMemory _memoryOfBytes;
23 |
24 | protected internal PostDataReadOnlyMemory(ReadOnlyMemory item)
25 | {
26 | _memoryOfBytes = item;
27 | Type = PostType.ReadOnlyMemory;
28 | }
29 |
30 | public override void Write(Stream writableStream, ITransportConfiguration settings, bool disableDirectStreaming)
31 | {
32 | if (_memoryOfBytes.IsEmpty) return;
33 |
34 | MemoryStream? buffer = null;
35 |
36 | if (!disableDirectStreaming)
37 | writableStream.Write(_memoryOfBytes.Span);
38 | else
39 | {
40 | WrittenBytes ??= _memoryOfBytes.Span.ToArray();
41 | buffer = settings.MemoryStreamFactory.Create(WrittenBytes);
42 | }
43 | FinishStream(writableStream, buffer, disableDirectStreaming);
44 | }
45 |
46 | public override async Task WriteAsync(Stream writableStream, ITransportConfiguration settings, bool disableDirectStreaming, CancellationToken cancellationToken)
47 | {
48 | if (_memoryOfBytes.IsEmpty) return;
49 |
50 | MemoryStream? buffer = null;
51 |
52 | if (!disableDirectStreaming)
53 | writableStream.Write(_memoryOfBytes.Span);
54 | else
55 | {
56 | WrittenBytes ??= _memoryOfBytes.Span.ToArray();
57 | buffer = settings.MemoryStreamFactory.Create(WrittenBytes);
58 | }
59 |
60 | await FinishStreamAsync(writableStream, buffer, disableDirectStreaming, cancellationToken).ConfigureAwait(false);
61 | }
62 | }
63 | }
64 | }
65 | #endif
66 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/Body/PostData.Serializable.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using static Elastic.Transport.SerializationFormatting;
9 |
10 | namespace Elastic.Transport;
11 |
12 | public abstract partial class PostData
13 | {
14 | ///
15 | /// Create a instance that will serialize using
16 | ///
17 | ///
18 | public static PostData Serializable(T data) => new SerializableData(data);
19 |
20 | private class SerializableData : PostData
21 | {
22 | private readonly T _serializable;
23 |
24 | public SerializableData(T item)
25 | {
26 | Type = PostType.Serializable;
27 | _serializable = item;
28 | }
29 |
30 | public static implicit operator SerializableData(T serializableData) => new(serializableData);
31 |
32 | public override void Write(Stream writableStream, ITransportConfiguration settings, bool disableDirectStreaming)
33 | {
34 | MemoryStream buffer = null;
35 | var stream = writableStream;
36 | BufferIfNeeded(settings.MemoryStreamFactory, disableDirectStreaming, ref buffer, ref stream);
37 |
38 | var indent = settings.PrettyJson ? Indented : None;
39 | settings.RequestResponseSerializer.Serialize(_serializable, stream, indent);
40 |
41 | FinishStream(writableStream, buffer, disableDirectStreaming);
42 | }
43 |
44 | public override async Task WriteAsync(Stream writableStream, ITransportConfiguration settings, bool disableDirectStreaming, CancellationToken cancellationToken)
45 | {
46 | MemoryStream buffer = null;
47 | var stream = writableStream;
48 | BufferIfNeeded(settings.MemoryStreamFactory, disableDirectStreaming, ref buffer, ref stream);
49 |
50 | var indent = settings.PrettyJson ? Indented : None;
51 | await settings.RequestResponseSerializer
52 | .SerializeAsync(_serializable, stream, indent, cancellationToken)
53 | .ConfigureAwait(false);
54 |
55 | await FinishStreamAsync(writableStream, buffer, disableDirectStreaming, cancellationToken).ConfigureAwait(false);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.IntegrationTests/Http/ApiCompatibilityHeaderTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using Elastic.Transport.IntegrationTests.Plumbing;
8 | using Elastic.Transport.IntegrationTests.Plumbing.Stubs;
9 | using Elastic.Transport.Products.Elasticsearch;
10 | using FluentAssertions;
11 | using Microsoft.AspNetCore.Mvc;
12 | using Xunit;
13 |
14 | namespace Elastic.Transport.IntegrationTests.Http;
15 |
16 | public class ApiCompatibilityHeaderTests(TestServerFixture instance) : AssemblyServerTestsBase(instance)
17 | {
18 | [Fact]
19 | public async Task AddsExpectedVendorInformationForRestApiCompaitbility()
20 | {
21 | var requestInvoker = new TrackingRequestInvoker(responseMessage =>
22 | {
23 | responseMessage.RequestMessage.Content.Headers.ContentType.MediaType.Should().Be("application/vnd.elasticsearch+json");
24 | var parameter = responseMessage.RequestMessage.Content.Headers.ContentType.Parameters.Single();
25 | parameter.Name.Should().Be("compatible-with");
26 | parameter.Value.Should().Be("8");
27 |
28 | var acceptValues = responseMessage.RequestMessage.Headers.GetValues("Accept");
29 | acceptValues.Single().Replace(" ", "").Should().Be("application/vnd.elasticsearch+json;compatible-with=8");
30 |
31 | var contentTypeValues = responseMessage.RequestMessage.Content.Headers.GetValues("Content-Type");
32 | contentTypeValues.Single().Replace(" ", "").Should().Be("application/vnd.elasticsearch+json;compatible-with=8");
33 | });
34 |
35 | var nodePool = new SingleNodePool(Server.Uri);
36 | var config = new TransportConfiguration(nodePool, requestInvoker, productRegistration: new ElasticsearchProductRegistration(typeof(Clients.Elasticsearch.ElasticsearchClient)));
37 | var transport = new DistributedTransport(config);
38 |
39 | var response = await transport.PostAsync("/metaheader", PostData.String("{}"), cancellationToken: TestContext.Current.CancellationToken);
40 | }
41 | }
42 |
43 | [ApiController, Route("[controller]")]
44 | public class MetaHeaderController : ControllerBase
45 | {
46 | [HttpPost()]
47 | public async Task Post() => await Task.FromResult(100);
48 | }
49 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Responses/Special/VoidResponseBuilderTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using FluentAssertions;
9 | using Xunit;
10 | using static Elastic.Transport.VoidResponse;
11 |
12 | namespace Elastic.Transport.Tests.Responses.Special;
13 |
14 | public class VoidResponseBuilderTests
15 | {
16 | private static readonly byte[] Data = Encoding.UTF8.GetBytes("{\"_index\":\"my-index\",\"_id\":\"pZqC6JIB9RdSpcF8-3lq\",\"_version\":1,\"result\"" +
17 | ":\"created\",\"_shards\":{\"total\":1,\"successful\":1,\"failed\":0},\"_seq_no\":2,\"_primary_term\":1}");
18 |
19 | [Fact]
20 | public async Task ReturnsExpectedResponse()
21 | {
22 | IResponseBuilder sut = new VoidResponseBuilder();
23 |
24 | var config = new TransportConfiguration();
25 | var apiCallDetails = new ApiCallDetails();
26 | var boundConfiguration = new BoundConfiguration(config);
27 | var stream = new MemoryStream(Data);
28 |
29 | var result = await sut.BuildAsync(apiCallDetails, boundConfiguration, stream, BoundConfiguration.DefaultContentType, Data.Length);
30 | result.Body.Should().BeOfType(typeof(VoidBody));
31 |
32 | result = sut.Build(apiCallDetails, boundConfiguration, stream, BoundConfiguration.DefaultContentType, Data.Length);
33 | result.Body.Should().BeOfType(typeof(VoidBody));
34 | }
35 |
36 | [Fact]
37 | public async Task ReturnsExpectedResponse_WhenDisableDirectStreaming()
38 | {
39 | IResponseBuilder sut = new VoidResponseBuilder();
40 |
41 | var config = new TransportConfiguration() { DisableDirectStreaming = true };
42 | var apiCallDetails = new ApiCallDetails() { ResponseBodyInBytes = Data };
43 | var boundConfiguration = new BoundConfiguration(config);
44 | var stream = new MemoryStream(Data);
45 |
46 | var result = await sut.BuildAsync(apiCallDetails, boundConfiguration, stream, BoundConfiguration.DefaultContentType, Data.Length);
47 | result.Body.Should().BeOfType(typeof(VoidBody));
48 |
49 | result = sut.Build(apiCallDetails, boundConfiguration, stream, BoundConfiguration.DefaultContentType, Data.Length);
50 | result.Body.Should().BeOfType(typeof(VoidBody));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/Responses/Dynamic/DynamicResponseBuilderTests.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using FluentAssertions;
9 | using Xunit;
10 |
11 | namespace Elastic.Transport.Tests.Responses.Dynamic;
12 |
13 | public class DynamicResponseBuilderTests
14 | {
15 | [Fact]
16 | public async Task ReturnsExpectedResponse_ForJsonData()
17 | {
18 | IResponseBuilder sut = new DynamicResponseBuilder();
19 |
20 | var config = new TransportConfiguration();
21 | var apiCallDetails = new ApiCallDetails();
22 | var boundConfiguration = new BoundConfiguration(config);
23 |
24 | var data = Encoding.UTF8.GetBytes("{\"_index\":\"my-index\",\"_id\":\"pZqC6JIB9RdSpcF8-3lq\",\"_version\":1,\"result\":\"created\",\"_shards\":{\"total\":1,\"successful\":1,\"failed\":0},\"_seq_no\":2,\"_primary_term\":1}");
25 | var stream = new MemoryStream(data);
26 |
27 | var result = await sut.BuildAsync(apiCallDetails, boundConfiguration, stream, BoundConfiguration.DefaultContentType, data.Length);
28 | result.Body.Get("_index").Should().Be("my-index");
29 |
30 | stream.Position = 0;
31 |
32 | result = sut.Build(apiCallDetails, boundConfiguration, stream, BoundConfiguration.DefaultContentType, data.Length);
33 | result.Body.Get("_index").Should().Be("my-index");
34 | }
35 |
36 | [Fact]
37 | public async Task ReturnsExpectedResponse_ForNonJsonData()
38 | {
39 | IResponseBuilder sut = new DynamicResponseBuilder();
40 |
41 | var config = new TransportConfiguration();
42 | var apiCallDetails = new ApiCallDetails();
43 | var boundConfiguration = new BoundConfiguration(config);
44 |
45 | var data = Encoding.UTF8.GetBytes("This is not JSON");
46 | var stream = new MemoryStream(data);
47 |
48 | var result = await sut.BuildAsync(apiCallDetails, boundConfiguration, stream, "text/plain", data.Length);
49 | result.Body.Get("body").Should().Be("This is not JSON");
50 |
51 | stream.Position = 0;
52 |
53 | result = sut.Build(apiCallDetails, boundConfiguration, stream, "text/plain", data.Length);
54 | result.Body.Get("body").Should().Be("This is not JSON");
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Serialization/IJsonSerializerOptionsProvider.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Text.Json;
8 | using System.Text.Json.Serialization;
9 |
10 | namespace Elastic.Transport;
11 |
12 | ///
13 | /// Provides an instance of to
14 | ///
15 | public interface IJsonSerializerOptionsProvider
16 | {
17 | ///
18 | JsonSerializerOptions CreateJsonSerializerOptions();
19 | }
20 |
21 | ///
22 | /// Default implementation of specialized in providing more converters and
23 | /// altering the shared used by and its derived classes
24 | ///
25 | public class TransportSerializerOptionsProvider : IJsonSerializerOptionsProvider
26 | {
27 | private readonly IReadOnlyCollection? _bakedInConverters;
28 | private readonly IReadOnlyCollection? _userProvidedConverters;
29 | private readonly Action? _mutateOptions;
30 |
31 | ///
32 | public JsonSerializerOptions? CreateJsonSerializerOptions()
33 | {
34 | var options = new JsonSerializerOptions();
35 |
36 | foreach (var converter in _bakedInConverters ?? [])
37 | options.Converters.Add(converter);
38 |
39 | foreach (var converter in _userProvidedConverters ?? [])
40 | options.Converters.Add(converter);
41 |
42 | _mutateOptions?.Invoke(options);
43 |
44 | return options;
45 | }
46 |
47 | ///
48 | public TransportSerializerOptionsProvider() { }
49 |
50 | ///
51 | public TransportSerializerOptionsProvider(
52 | IReadOnlyCollection bakedInConverters,
53 | IReadOnlyCollection? userProvidedConverters,
54 | Action? mutateOptions = null
55 | )
56 | {
57 | _bakedInConverters = bakedInConverters;
58 | _userProvidedConverters = userProvidedConverters;
59 | _mutateOptions = mutateOptions;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/MetaData/MetaDataHeader.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Text;
6 |
7 | namespace Elastic.Transport;
8 |
9 | ///
10 | ///
11 | ///
12 | public sealed class MetaDataHeader
13 | {
14 | private const char _separator = ',';
15 |
16 | private readonly string _headerValue;
17 |
18 | ///
19 | ///
20 | ///
21 | ///
22 | ///
23 | ///
24 | public MetaDataHeader(VersionInfo version, string serviceIdentifier, bool isAsync)
25 | {
26 | if (serviceIdentifier != "et")
27 | TransportVersion = ReflectionVersionInfo.Create().ToString();
28 |
29 | ClientVersion = version.ToMetadataHeaderValue();
30 | RuntimeVersion = new RuntimeVersionInfo().ToString();
31 | ServiceIdentifier = serviceIdentifier;
32 |
33 | // This code is expected to be called infrequently so we're not concerned with over optimising this
34 |
35 | var builder = new StringBuilder(64)
36 | .Append(serviceIdentifier).Append('=').Append(ClientVersion).Append(_separator)
37 | .Append("a=").Append(isAsync ? '1' : '0').Append(_separator)
38 | .Append("net=").Append(RuntimeVersion).Append(_separator)
39 | .Append(_httpClientIdentifier).Append('=').Append(RuntimeVersion);
40 |
41 | if (!string.IsNullOrEmpty(TransportVersion))
42 | builder.Append(_separator).Append("t=").Append(TransportVersion);
43 |
44 | _headerValue = builder.ToString();
45 | }
46 |
47 | private static readonly string _httpClientIdentifier =
48 | #if !NETFRAMEWORK
49 | ConnectionInfo.UsingCurlHandler ? "cu" : "so";
50 | #else
51 | "wr";
52 | #endif
53 |
54 | ///
55 | ///
56 | ///
57 | public string ServiceIdentifier { get; private set; }
58 |
59 | ///
60 | ///
61 | ///
62 | public string ClientVersion { get; private set; }
63 |
64 | ///
65 | ///
66 | ///
67 | public string TransportVersion { get; private set; }
68 |
69 | ///
70 | ///
71 | ///
72 | public string RuntimeVersion { get; private set; }
73 |
74 | ///
75 | ///
76 | ///
77 | ///
78 | public override string ToString() => _headerValue;
79 | }
80 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/NodePool/StickySniffingNodePool.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Threading;
9 | using Elastic.Transport.Diagnostics.Auditing;
10 |
11 | namespace Elastic.Transport;
12 |
13 | ///
14 | /// A connection pool implementation that supports reseeding but stays on the first reporting true for .
15 | /// This is great if for instance you have multiple proxies that you can fallback on allowing you to seed the proxies in order of preference.
16 | ///
17 | public sealed class StickySniffingNodePool : SniffingNodePool
18 | {
19 | ///
20 | public StickySniffingNodePool(IEnumerable uris, Func nodeScorer)
21 | : base(uris.Select(uri => new Node(uri)), nodeScorer ?? DefaultNodeScore) { }
22 |
23 | ///
24 | public StickySniffingNodePool(IEnumerable nodes, Func nodeScorer)
25 | : base(nodes, nodeScorer ?? DefaultNodeScore) { }
26 |
27 | ///
28 | public override bool SupportsPinging => true;
29 |
30 | ///
31 | public override bool SupportsReseeding => true;
32 |
33 | ///
34 | public override IEnumerable CreateView(Auditor? auditor)
35 | {
36 | var nodes = AliveNodes;
37 |
38 | if (nodes.Count == 0)
39 | {
40 | var globalCursor = Interlocked.Increment(ref GlobalCursor);
41 |
42 | //could not find a suitable node retrying on first node off globalCursor
43 | yield return RetryInternalNodes(globalCursor, auditor);
44 |
45 | yield break;
46 | }
47 |
48 | // If the cursor is greater than the default then it's been
49 | // set already but we now have a live node so we should reset it
50 | if (GlobalCursor > -1)
51 | Interlocked.Exchange(ref GlobalCursor, -1);
52 |
53 | var localCursor = 0;
54 | foreach (var aliveNode in SelectAliveNodes(localCursor, nodes, auditor))
55 | yield return aliveNode;
56 | }
57 |
58 | /// Allows subclasses to hook into the parents dispose
59 | private static float DefaultNodeScore(Node node) => 0f;
60 | }
61 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Serialization/LowLevelRequestResponseSerializer.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.Collections.Generic;
6 | using System.Diagnostics.CodeAnalysis;
7 | using System.Text.Json;
8 | using System.Text.Json.Serialization;
9 | using System.Text.Json.Serialization.Metadata;
10 | using Elastic.Transport.Products.Elasticsearch;
11 |
12 | namespace Elastic.Transport;
13 |
14 | ///
15 | /// Default low level request/response-serializer implementation for which serializes using
16 | /// the Microsoft System.Text.Json library
17 | ///
18 | internal sealed class LowLevelRequestResponseSerializer : SystemTextJsonSerializer
19 | {
20 | ///
21 | /// Provides a static reusable reference to an instance of to promote reuse.
22 | ///
23 | internal static readonly LowLevelRequestResponseSerializer Instance = new();
24 |
25 | /// >
26 | public LowLevelRequestResponseSerializer() : this(null) { }
27 |
28 | ///
29 | /// >
30 | ///
31 | /// Add more default converters onto being used
32 | //[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
33 | //[RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
34 |
35 | [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")]
36 | [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")]
37 | public LowLevelRequestResponseSerializer(IReadOnlyCollection? converters)
38 | : base(new TransportSerializerOptionsProvider([
39 | new ExceptionConverter(),
40 | new ErrorCauseConverter(),
41 | new ErrorConverter(),
42 | new DynamicDictionaryConverter()
43 | ], converters, options =>
44 | {
45 | options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
46 | options.TypeInfoResolver = JsonTypeInfoResolver.Combine(new DefaultJsonTypeInfoResolver(), ElasticsearchTransportSerializerContext.Default);
47 | })) { }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Products/Elasticsearch/ElasticsearchConfiguration.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 |
7 | namespace Elastic.Transport.Products.Elasticsearch;
8 |
9 | ///
10 | /// Allows you to control how behaves and where/how it connects to Elasticsearch
11 | ///
12 | public record ElasticsearchConfiguration : TransportConfiguration
13 | {
14 | ///
15 | /// Creates a new instance of
16 | ///
17 | /// The root of the Elastic stack product node we want to connect to. Defaults to http://localhost:9200
18 | public ElasticsearchConfiguration(Uri? uri = null)
19 | : base(new SingleNodePool(uri ?? new Uri("http://localhost:9200")), productRegistration: ElasticsearchProductRegistration.Default ) { }
20 |
21 | ///
22 | /// Sets up the client to communicate to Elastic Cloud using ,
23 | /// documentation for more information on how to get your Cloud ID
24 | ///
25 | public ElasticsearchConfiguration(string cloudId, BasicAuthentication credentials)
26 | : base(new CloudNodePool(cloudId, credentials), productRegistration: ElasticsearchProductRegistration.Default) { }
27 |
28 | ///
29 | /// Sets up the client to communicate to Elastic Cloud using ,
30 | /// documentation for more information on how to get your Cloud ID
31 | ///
32 | public ElasticsearchConfiguration(string cloudId, ApiKey credentials)
33 | : base(new CloudNodePool(cloudId, credentials), productRegistration: ElasticsearchProductRegistration.Default) { }
34 |
35 | /// Sets up the client to communicate to Elastic Cloud.
36 | public ElasticsearchConfiguration(Uri cloudEndpoint, BasicAuthentication credentials)
37 | : base(new CloudNodePool(cloudEndpoint, credentials), productRegistration: ElasticsearchProductRegistration.Default) { }
38 |
39 | /// Sets up the client to communicate to Elastic Cloud.
40 | public ElasticsearchConfiguration(Uri cloudEndpoint, ApiKey credentials)
41 | : base(new CloudNodePool(cloudEndpoint, credentials), productRegistration: ElasticsearchProductRegistration.Default) { }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/tests/Elastic.Transport.Tests/AddressParsing.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using FluentAssertions;
6 | using Elastic.Transport.Products.Elasticsearch;
7 | using Xunit;
8 |
9 | namespace Elastic.Transport.Tests
10 | {
11 | public class AddressParsing
12 | {
13 | [Fact] public void IsMatched()
14 | {
15 | //based on examples from http://www.ietf.org/rfc/rfc2732.txt
16 | var testcases = new[,]
17 | {
18 | {"[::1]:9200", "[::1]", "9200"},
19 | {"192.168.2.1:231", "192.168.2.1", "231"},
20 | {"[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80", "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]", "80"},
21 | {"[1080:0:0:0:8:800:200C:417A]:1234", "[1080:0:0:0:8:800:200C:417A]", "1234"},
22 | {"[3ffe:2a00:100:7031::1]:1", "[3ffe:2a00:100:7031::1]", "1"},
23 | {"[1080::8:800:200C:417A]:123", "[1080::8:800:200C:417A]", "123"},
24 | {"[::192.9.5.5]:12", "[::192.9.5.5]", "12"},
25 | {"[::FFFF:129.144.52.38]:80", "[::FFFF:129.144.52.38]", "80"},
26 | {"[2010:836B:4179::836B:4179]:34533", "[2010:836B:4179::836B:4179]", "34533"}
27 | };
28 |
29 | for (var i = 0; i < testcases.GetLength(0); i++)
30 | {
31 | var address = testcases[i, 0];
32 | var ip = testcases[i, 1];
33 | var port = testcases[i, 2];
34 |
35 | var match = SniffParser.AddressRegex.Match(address);
36 |
37 | match.Success.Should().BeTrue();
38 |
39 | match.Groups["ip"].Value.Should().BeEquivalentTo(ip);
40 | match.Groups["port"].Value.Should().BeEquivalentTo(port);
41 | }
42 | }
43 |
44 | [Fact] public void FqdnIsReadCorrectly()
45 | {
46 | //based on examples from http://www.ietf.org/rfc/rfc2732.txt
47 | var testcases = new[,]
48 | {
49 | {"helloworld/[::1]:9200", "helloworld", "[::1]", "9200"},
50 | {"elastic.co/192.168.2.1:231", "elastic.co", "192.168.2.1", "231"}
51 | };
52 |
53 | for (var i = 0; i < testcases.GetLength(0); i++)
54 | {
55 | var address = testcases[i, 0];
56 | var fqdn = testcases[i, 1];
57 | var ip = testcases[i, 2];
58 | var port = testcases[i, 3];
59 |
60 | var match = SniffParser.AddressRegex.Match(address);
61 |
62 | match.Success.Should().BeTrue();
63 |
64 | match.Groups["fqdn"].Value.Should().BeEquivalentTo(fqdn);
65 | match.Groups["ip"].Value.Should().BeEquivalentTo(ip);
66 | match.Groups["port"].Value.Should().BeEquivalentTo(port);
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/Diagnostic.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Diagnostics;
7 | using System.Diagnostics.CodeAnalysis;
8 |
9 | namespace Elastic.Transport.Diagnostics;
10 |
11 | ///
12 | /// Internal subclass of that implements to
13 | /// make it easier to use.
14 | ///
15 | internal class Diagnostic<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TState> : Diagnostic
16 | {
17 | [RequiresUnreferencedCode(WriteOfTRequiresUnreferencedCode)]
18 | public Diagnostic(string operationName, DiagnosticSource source, TState state)
19 | : base(operationName, source, state) =>
20 | EndState = state;
21 | }
22 |
23 | internal class Diagnostic<
24 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TState,
25 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]TStateEnd> : Activity, IDisposable
26 | {
27 | internal const string WriteOfTRequiresUnreferencedCode = "Only the properties of the T type will be preserved. Properties of referenced types and properties of derived types may be trimmed.";
28 |
29 | public static Diagnostic Default { get; } = new Diagnostic();
30 |
31 | private readonly DiagnosticSource _source;
32 | private TStateEnd _endState;
33 | private readonly bool _default;
34 | private bool _disposed;
35 |
36 | private Diagnostic() : base("__NOOP__") => _default = true;
37 |
38 | [RequiresUnreferencedCode(WriteOfTRequiresUnreferencedCode)]
39 | public Diagnostic(string operationName, DiagnosticSource source, TState state) : base(operationName)
40 | {
41 | _source = source;
42 | _source.StartActivity(SetStartTime(DateTime.UtcNow), state);
43 | }
44 |
45 | public TStateEnd EndState
46 | {
47 | get => _endState;
48 | internal set
49 | {
50 | //do not store state on default instance
51 | if (_default) return;
52 | _endState = value;
53 | }
54 | }
55 |
56 | protected override void Dispose(bool disposing)
57 | {
58 | if (_disposed) return;
59 |
60 | if (disposing)
61 | {
62 | #pragma warning disable IL2026
63 | //_source can be null if Default instance
64 | _source?.StopActivity(SetEndTime(DateTime.UtcNow), EndState);
65 | #pragma warning restore IL2026
66 | }
67 |
68 | _disposed = true;
69 |
70 | base.Dispose(disposing);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/tests/Elastic.Elasticsearch.IntegrationTests/DefaultCluster.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using Elastic.Elasticsearch.Ephemeral;
6 | using Elastic.Elasticsearch.Managed;
7 | using Elastic.Elasticsearch.Xunit;
8 | using Elastic.Transport;
9 | using Elastic.Transport.Products.Elasticsearch;
10 | using Xunit;
11 | using Xunit.Abstractions;
12 | using static Elastic.Elasticsearch.Ephemeral.ClusterAuthentication;
13 |
14 | [assembly: TestFramework("Elastic.Elasticsearch.Xunit.Sdk.ElasticTestFramework", "Elastic.Elasticsearch.Xunit")]
15 |
16 | namespace Elastic.Elasticsearch.IntegrationTests;
17 |
18 | /// Declare our cluster that we want to inject into our test classes
19 | public class DefaultCluster : XunitClusterBase
20 | {
21 | public DefaultCluster() : this(new XunitClusterConfiguration(Version) { StartingPortNumber = 9202, AutoWireKnownProxies = true }) { }
22 |
23 | public DefaultCluster(XunitClusterConfiguration xunitClusterConfiguration) : base(xunitClusterConfiguration) { }
24 |
25 | protected static string Version => "8.7.0";
26 |
27 | public ITransport CreateClient(ITestOutputHelper output) =>
28 | this.GetOrAddClient(cluster =>
29 | {
30 | var nodes = NodesUris();
31 | var nodePool = new StaticNodePool(nodes);
32 | var settings = new TransportConfigurationDescriptor(nodePool, productRegistration: ElasticsearchProductRegistration.Default)
33 | .RequestTimeout(TimeSpan.FromSeconds(5))
34 | .OnRequestCompleted(d =>
35 | {
36 | try
37 | {
38 | output.WriteLine(d.DebugInformation);
39 | }
40 | catch
41 | {
42 | // ignored
43 | }
44 | })
45 | .EnableDebugMode();
46 | if (ClusterConfiguration.Features.HasFlag(ClusterFeatures.Security))
47 | settings = settings.Authentication(new BasicAuthentication(Admin.Username, Admin.Password));
48 | if (cluster.DetectedProxy != DetectedProxySoftware.None)
49 | settings = settings.Proxy(new Uri("http://localhost:8080"));
50 | if (ClusterConfiguration.Features.HasFlag(ClusterFeatures.SSL))
51 | settings = settings.ServerCertificateValidationCallback(CertificateValidations.AllowAll);
52 |
53 | return new DistributedTransport(settings);
54 | });
55 | }
56 |
57 | public class SecurityCluster : DefaultCluster
58 | {
59 | public SecurityCluster() : base(new XunitClusterConfiguration(Version, ClusterFeatures.Security | ClusterFeatures.SSL | ClusterFeatures.XPack)
60 | {
61 | StartingPortNumber = 9202
62 | }) { }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Configuration/UserAgent.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Reflection;
7 |
8 | #if NETFRAMEWORK
9 |
10 | using Elastic.Transport.Extensions;
11 |
12 | #else
13 |
14 | using System.Runtime.InteropServices;
15 |
16 | #endif
17 |
18 | namespace Elastic.Transport;
19 |
20 | ///
21 | /// Represents the user agent string. Two constructors exists, one to aid with constructing elastic clients standard compliant
22 | /// user agents and one free form to allow any custom string to be set.
23 | ///
24 | public sealed class UserAgent
25 | {
26 | private readonly string _toString;
27 |
28 | private UserAgent(string reposName, Type typeVersionLookup, string[]? metadata = null)
29 | {
30 | var version = typeVersionLookup.Assembly
31 | .GetCustomAttribute()
32 | .InformationalVersion;
33 |
34 | var meta = string.Join("; ", metadata ?? []);
35 | var assemblyName = typeVersionLookup.Assembly.GetName().Name;
36 |
37 | _toString = $"{reposName}/{version} ({RuntimeInformation.OSDescription}; {RuntimeInformation.FrameworkDescription}; {assemblyName}{meta.Trim()})";
38 | }
39 |
40 | private UserAgent(string fullUserAgentString) => _toString = fullUserAgentString;
41 |
42 | /// Create a user agent that adheres to the minimum information needed to be elastic standard compliant
43 | /// The repos name uniquely identifies the origin of the client
44 | ///
45 | /// Use 's assembly
46 | /// to inject version information into the header
47 | ///
48 | public static UserAgent Create(string reposName, Type typeVersionLookup) => new UserAgent(reposName, typeVersionLookup);
49 |
50 | ///
51 | public static UserAgent Create(string reposName, Type typeVersionLookup, string[] metadata) => new UserAgent(reposName, typeVersionLookup, metadata);
52 |
53 | /// Create a user string that does not confirm to elastic client standards
54 | public static UserAgent Create(string fullUserAgentString) => new UserAgent(fullUserAgentString);
55 |
56 | /// The pre=calculated string representation of this instance
57 | ///
58 | public override string ToString() => _toString;
59 | }
60 |
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Elastic and contributors
6 | Elasticsearch BV
7 | Apache-2.0
8 | https://github.com/elastic/elastic-transport-net
9 | https://github.com/elastic/elastic-transport-net
10 | https://github.com/elastic/elastic-transport-net/releases
11 |
12 | true
13 | $(SolutionRoot)\build\keys\keypair.snk
14 |
15 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
16 | true
17 | nuget-icon.png
18 |
19 | true
20 | true
21 | 002400000480000094000000060200000024000052534131000400000100010015b0fa59d868c7f3ea2ae67567b19e102465745f01b430a38a42b92fd41a0f5869bec1f2b33b589d78662af432fe6b789ef72d4738f7b1a86264d7aeb5185ed8995b2bb104e7c5c58845f1a618be829e410fa34a6bd7d714ece191ed68a66333a83ae7456ee32e9aeb54bc1d7410ae8c344367257e9001abb5e96ce1f1d97696
22 |
23 |
24 |
25 |
26 | nuget-icon.png
27 | True
28 | nuget-icon.png
29 |
30 |
31 | True
32 | license.txt
33 |
34 |
35 |
36 |
37 |
38 | $(NoWarn);IDT001;IDT002
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Diagnostics/TcpStats.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Collections.ObjectModel;
8 | using System.Net.NetworkInformation;
9 |
10 | namespace Elastic.Transport.Diagnostics;
11 |
12 | ///
13 | /// Gets statistics about TCP connections
14 | ///
15 | internal static class TcpStats
16 | {
17 | private static readonly int StateLength = Enum.GetNames(typeof(TcpState)).Length;
18 | private static readonly ReadOnlyDictionary Empty = new(new Dictionary());
19 |
20 | ///
21 | /// Gets the active TCP connections
22 | ///
23 | /// TcpConnectionInformation[]
24 | /// Can return `null` when there is a permissions issue retrieving TCP connections.
25 | public static TcpConnectionInformation[]? GetActiveTcpConnections()
26 | {
27 | try
28 | {
29 | return IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections();
30 | }
31 | catch (NetworkInformationException) // host might not allow this information to be fetched.
32 | {
33 | // ignored
34 | }
35 |
36 | return null;
37 | }
38 |
39 | ///
40 | /// Gets the sum for each state of the active TCP connections
41 | ///
42 | public static ReadOnlyDictionary GetStates()
43 | {
44 | var connections = GetActiveTcpConnections();
45 | if (connections is null)
46 | {
47 | return Empty;
48 | }
49 |
50 | var states = new Dictionary(StateLength);
51 |
52 | for (var index = 0; index < connections.Length; index++)
53 | {
54 | var connection = connections[index];
55 | if (states.TryGetValue(connection.State, out var count))
56 | states[connection.State] = ++count;
57 | else
58 | states.Add(connection.State, 1);
59 | }
60 |
61 | return new ReadOnlyDictionary(states);
62 | }
63 |
64 | ///
65 | /// Gets the TCP statistics for a given network interface component
66 | ///
67 | public static TcpStatistics GetTcpStatistics(NetworkInterfaceComponent version)
68 | {
69 | var properties = IPGlobalProperties.GetIPGlobalProperties();
70 | switch (version)
71 | {
72 | case NetworkInterfaceComponent.IPv4:
73 | return properties.GetTcpIPv4Statistics();
74 | case NetworkInterfaceComponent.IPv6:
75 | return properties.GetTcpIPv6Statistics();
76 | default:
77 | throw new ArgumentException("version");
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Requests/Body/PostData.String.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using Elastic.Transport.Extensions;
9 |
10 | namespace Elastic.Transport;
11 |
12 | public abstract partial class PostData
13 | {
14 | ///
15 | /// Create a instance that will write to the output
16 | ///
17 | // ReSharper disable once MemberCanBePrivate.Global
18 | public static PostData String(string serializedString) => new PostDataString(serializedString);
19 |
20 | ///
21 | /// string implicitly converts to so you do not have to use the static
22 | /// factory method
23 | ///
24 | public static implicit operator PostData(string literalString) => String(literalString);
25 |
26 | private class PostDataString : PostData
27 | {
28 | private readonly string _literalString;
29 |
30 | protected internal PostDataString(string item)
31 | {
32 | _literalString = item;
33 | Type = PostType.LiteralString;
34 | }
35 |
36 | public override void Write(Stream writableStream, ITransportConfiguration settings, bool disableDirectStreaming)
37 | {
38 | if (string.IsNullOrEmpty(_literalString)) return;
39 |
40 | MemoryStream? buffer = null;
41 |
42 | var stringBytes = WrittenBytes ?? _literalString.Utf8Bytes();
43 | WrittenBytes ??= stringBytes;
44 | if (!disableDirectStreaming)
45 | writableStream.Write(stringBytes, 0, stringBytes.Length);
46 | else
47 | buffer = settings.MemoryStreamFactory.Create(stringBytes);
48 |
49 | FinishStream(writableStream, buffer, disableDirectStreaming);
50 | }
51 |
52 | public override async Task WriteAsync(Stream writableStream, ITransportConfiguration settings, bool disableDirectStreaming, CancellationToken cancellationToken)
53 | {
54 | if (string.IsNullOrEmpty(_literalString)) return;
55 |
56 | MemoryStream? buffer = null;
57 |
58 | var stringBytes = WrittenBytes ?? _literalString.Utf8Bytes();
59 | WrittenBytes ??= stringBytes;
60 | if (!disableDirectStreaming)
61 | await writableStream.WriteAsync(stringBytes, 0, stringBytes.Length, cancellationToken)
62 | .ConfigureAwait(false);
63 | else
64 | buffer = settings.MemoryStreamFactory.Create(stringBytes);
65 |
66 | await FinishStreamAsync(writableStream, buffer, disableDirectStreaming, cancellationToken).ConfigureAwait(false);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Elastic.Transport/Components/Serialization/Converters/DynamicDictionaryConverter.cs:
--------------------------------------------------------------------------------
1 | // Licensed to Elasticsearch B.V under one or more agreements.
2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3 | // See the LICENSE file in the project root for more information
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Diagnostics.CodeAnalysis;
8 | using System.Globalization;
9 | using System.Text.Json;
10 | using System.Text.Json.Serialization;
11 | using System.Text.Json.Serialization.Metadata;
12 | using Elastic.Transport.Products.Elasticsearch;
13 | using JsonSerializer = System.Text.Json.JsonSerializer;
14 |
15 | namespace Elastic.Transport;
16 |
17 | internal class DynamicDictionaryConverter : JsonConverter
18 | {
19 | public override DynamicDictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
20 | {
21 | if (reader.TokenType == JsonTokenType.StartArray)
22 | {
23 | var array = JsonSerializer.Deserialize