├── .ci
└── make.sh
├── .editorconfig
├── .gitattributes
├── .github
├── add-license-headers.sh
├── check-license-headers.sh
├── license-header.txt
└── workflows
│ ├── ci.yml
│ ├── license.yml
│ └── test-windows.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── Directory.Build.props
├── Elastic.Transport.sln
├── Elastic.Transport.sln.DotSettings
├── NOTICE.txt
├── Playground
├── Playground.csproj
└── Program.cs
├── README.md
├── benchmarks
├── Elastic.Transport.Benchmarks
│ ├── Elastic.Transport.Benchmarks.csproj
│ ├── Program.cs
│ └── TransportBenchmarks.cs
└── Elastic.Transport.Profiling
│ ├── Elastic.Transport.Profiling.csproj
│ └── Program.cs
├── build.bat
├── build.sh
├── build
├── keys
│ ├── keypair.snk
│ └── public.snk
└── scripts
│ ├── CommandLine.fs
│ ├── Paths.fs
│ ├── Program.fs
│ ├── Targets.fs
│ └── scripts.fsproj
├── contributing.md
├── dotnet-tools.json
├── examples
└── transport-aot-example
│ ├── Program.cs
│ └── transport-aot-example.csproj
├── global.json
├── license.txt
├── nuget-icon.png
├── nuget.config
├── request-pipeline.png
├── src
├── Directory.Build.props
├── Elastic.Transport.VirtualizedCluster
│ ├── Audit
│ │ ├── Auditor.cs
│ │ ├── CallTraceState.cs
│ │ └── ClientCall.cs
│ ├── Components
│ │ ├── ExposingPipelineFactory.cs
│ │ ├── SealedVirtualCluster.cs
│ │ ├── VirtualCluster.cs
│ │ ├── VirtualClusterConnection.cs
│ │ └── VirtualizedCluster.cs
│ ├── Elastic.Transport.VirtualizedCluster.csproj
│ ├── Extensions
│ │ └── NumericExtensions.cs
│ ├── Products
│ │ ├── Elasticsearch
│ │ │ ├── ElasticsearchMockProductRegistration.cs
│ │ │ ├── ElasticsearchSniffResponseFactory.cs
│ │ │ └── ElasticsearchVirtualCluster.cs
│ │ └── MockProductRegistration.cs
│ ├── Providers
│ │ └── TestableDateTimeProvider.cs
│ ├── Rules
│ │ ├── ClientCallRule.cs
│ │ ├── PingRule.cs
│ │ ├── RuleBase.cs
│ │ ├── RuleOption.cs
│ │ ├── SniffRule.cs
│ │ └── TimesHelper.cs
│ └── Setup.cs
└── Elastic.Transport
│ ├── .editorconfig
│ ├── Components
│ ├── Endpoint.cs
│ ├── NodePool
│ │ ├── CloudNodePool.cs
│ │ ├── Node.cs
│ │ ├── NodePool.cs
│ │ ├── SingleNodePool.cs
│ │ ├── SniffingNodePool.cs
│ │ ├── StaticNodePool.cs
│ │ ├── StickyNodePool.cs
│ │ └── StickySniffingNodePool.cs
│ ├── Pipeline
│ │ ├── BoundConfiguration.cs
│ │ ├── DefaultResponseBuilder.cs
│ │ ├── PipelineException.cs
│ │ ├── PipelineFailure.cs
│ │ ├── RequestPipeline.cs
│ │ └── RequestPipelineStatics.cs
│ ├── Providers
│ │ ├── DateTimeProvider.cs
│ │ ├── DefaultDateTimeProvider.cs
│ │ ├── DefaultMemoryStreamFactory.cs
│ │ ├── DefaultRequestPipelineFactory.cs
│ │ ├── MemoryStreamFactory.cs
│ │ ├── RecyclableMemoryStream.cs
│ │ ├── RecyclableMemoryStreamFactory.cs
│ │ ├── RecyclableMemoryStreamManager-Events.cs
│ │ ├── RecyclableMemoryStreamManager.cs
│ │ └── RequestPipelineFactory.cs
│ ├── Serialization
│ │ ├── Converters
│ │ │ ├── DynamicDictionaryConverter.cs
│ │ │ ├── ErrorCauseConverter.cs
│ │ │ └── ExceptionConverter.cs
│ │ ├── IJsonSerializerOptionsProvider.cs
│ │ ├── JsonElementExtensions.cs
│ │ ├── LowLevelRequestResponseSerializer.cs
│ │ ├── SerializationFormatting.cs
│ │ ├── Serializer.cs
│ │ ├── SerializerRegistrationInformation.cs
│ │ ├── SystemTextJsonSerializer.cs
│ │ └── TransportSerializerExtensions.cs
│ └── TransportClient
│ │ ├── CertificateHelpers.cs
│ │ ├── CertificateValidations.cs
│ │ ├── Content
│ │ └── RequestDataContent.cs
│ │ ├── HandlerTracking
│ │ ├── ActiveHandlerTrackingEntry.cs
│ │ ├── ExpiredHandlerTrackingEntry.cs
│ │ ├── LifetimeTrackingHttpMessageHandler.cs
│ │ ├── RequestDataHttpClientFactory.cs
│ │ └── ValueStopWatch.cs
│ │ ├── HttpMethod.cs
│ │ ├── HttpRequestInvoker-FullFramework.cs
│ │ ├── HttpRequestInvoker.cs
│ │ ├── HttpWebRequestInvoker.cs
│ │ ├── IRequestInvoker.cs
│ │ ├── InMemoryRequestInvoker.cs
│ │ ├── RequestInvokerHelpers.cs
│ │ └── WebProxy.cs
│ ├── Configuration
│ ├── ConnectionInfo.cs
│ ├── HeadersList.cs
│ ├── IRequestConfiguration.cs
│ ├── ITransportConfiguration.cs
│ ├── RequestConfiguration.cs
│ ├── RequestConfigurationDescriptor.cs
│ ├── Security
│ │ ├── ApiKey.cs
│ │ ├── AuthorizationHeader.cs
│ │ ├── Base64ApiKey.cs
│ │ └── BasicAuthenticationCredentials.cs
│ ├── TransportConfiguration.cs
│ ├── TransportConfigurationDescriptor.cs
│ └── UserAgent.cs
│ ├── Diagnostics
│ ├── AuditDiagnosticObserver.cs
│ ├── Auditing
│ │ ├── Audit.cs
│ │ ├── AuditEvent.cs
│ │ ├── Auditable.cs
│ │ └── Auditor.cs
│ ├── Diagnostic.cs
│ ├── DiagnosticSources.cs
│ ├── HttpConnectionDiagnosticObserver.cs
│ ├── OpenTelemetry
│ │ ├── OpenTelemetry.cs
│ │ ├── OpenTelemetryAttributes.cs
│ │ └── SemanticConventions.cs
│ ├── RequestPipelineDiagnosticObserver.cs
│ ├── SerializerDiagnosticObserver.cs
│ ├── TcpStats.cs
│ ├── ThreadpoolStats.cs
│ └── TypedDiagnosticObserver.cs
│ ├── DistributedTransport.cs
│ ├── Elastic.Transport.csproj
│ ├── Exceptions
│ ├── TransportException.cs
│ └── UnexpectedTransportException.cs
│ ├── Extensions
│ ├── .editorconfig
│ ├── EmptyEnumerator.cs
│ ├── EmptyReadonly.cs
│ ├── Extensions.cs
│ ├── Fluent.cs
│ ├── NameValueCollectionExtensions.cs
│ ├── NativeMethods.cs
│ ├── RuntimeInformation.cs
│ ├── SemVersion.cs
│ ├── StringExtensions.cs
│ └── TaskExtensions.cs
│ ├── ITransport.cs
│ ├── ITransportHttpMethodExtensions.cs
│ ├── IsExternalInit.cs
│ ├── Products
│ ├── .editorconfig
│ ├── DefaultProductRegistration.cs
│ ├── Elasticsearch
│ │ ├── ElasticsearchConfiguration.cs
│ │ ├── ElasticsearchErrorExtensions.cs
│ │ ├── ElasticsearchNodeFeatures.cs
│ │ ├── ElasticsearchProductRegistration.cs
│ │ ├── ElasticsearchResponse.cs
│ │ ├── ElasticsearchResponseBuilder.cs
│ │ ├── ErrorSerializationContext.cs
│ │ ├── Failures
│ │ │ ├── ElasticsearchServerError.cs
│ │ │ ├── Error.cs
│ │ │ ├── ErrorCause.cs
│ │ │ └── ShardFailure.cs
│ │ └── Sniff
│ │ │ ├── NodeInfo.cs
│ │ │ ├── NodeInfoHttp.cs
│ │ │ ├── SniffParser.cs
│ │ │ └── SniffResponse.cs
│ └── ProductRegistration.cs
│ ├── Properties
│ └── ClsCompliancy.cs
│ ├── Requests
│ ├── Body
│ │ ├── PostData.ByteArray.cs
│ │ ├── PostData.MultiJson.cs
│ │ ├── PostData.ReadOnlyMemory.cs
│ │ ├── PostData.Serializable.cs
│ │ ├── PostData.Streamable.cs
│ │ ├── PostData.String.cs
│ │ ├── PostData.cs
│ │ └── PostType.cs
│ ├── IUrlParameter.cs
│ ├── MetaData
│ │ ├── DefaultMetaHeaderProvider.cs
│ │ ├── MetaDataHeader.cs
│ │ ├── MetaHeaderProvider.cs
│ │ ├── ReflectionVersionInfo.cs
│ │ ├── RequestMetaData.cs
│ │ ├── RequestMetaDataExtensions.cs
│ │ ├── RuntimeVersionInfo.cs
│ │ └── VersionInfo.cs
│ ├── RequestParameters.cs
│ └── UrlFormatter.cs
│ └── Responses
│ ├── BufferedResponseHelpers.cs
│ ├── DefaultResponseFactory.cs
│ ├── Dynamic
│ ├── DynamicDictionary.cs
│ ├── DynamicResponse.cs
│ ├── DynamicResponseBuilder.cs
│ └── DynamicValue.cs
│ ├── ErrorResponse.cs
│ ├── HttpDetails
│ └── ApiCallDetails.cs
│ ├── IResponseBuilder.cs
│ ├── ResponseFactory.cs
│ ├── ResponseStatics.cs
│ ├── Special
│ ├── BytesResponse.cs
│ ├── BytesResponseBuilder.cs
│ ├── StreamResponse.cs
│ ├── StreamResponseBase.cs
│ ├── StreamResponseBuilder.cs
│ ├── StringResponse.cs
│ ├── StringResponseBuilder.cs
│ ├── VoidResponse.cs
│ └── VoidResponseBuilder.cs
│ ├── TestableResponseFactory.cs
│ ├── TransportResponse.cs
│ └── TypedResponseBuilder.cs
└── tests
├── Elastic.Elasticsearch.IntegrationTests
├── DefaultCluster.cs
├── DefaultClusterTests.cs
├── Elastic.Elasticsearch.IntegrationTests.csproj
├── IntegrationTestBase.cs
└── SecurityClusterTests.cs
├── Elastic.Transport.IntegrationTests
├── Elastic.Transport.IntegrationTests.csproj
├── Http
│ ├── ApiCompatibilityHeaderTests.cs
│ └── TransferEncodingChunckedTests.cs
├── OpenTelemetry
│ └── OpenTelemetryTests.cs
├── Plumbing
│ ├── AssemblyServerTestsBase.cs
│ ├── ClassServerTestsBase.cs
│ ├── DefaultStartup.cs
│ ├── Examples
│ │ ├── ControllerIntegrationTests.cs
│ │ └── EndpointIntegrationTests.cs
│ ├── Stubs
│ │ ├── TestableClientHandler.cs
│ │ └── TrackingRequestInvoker.cs
│ ├── TransportTestServer.cs
│ └── WebHostExtensions.cs
└── Responses
│ └── SpecialisedResponseTests.cs
├── Elastic.Transport.Tests.Shared
├── Elastic.Transport.Tests.Shared.csproj
├── TrackDisposeStream.cs
└── TrackingMemoryStreamFactory.cs
└── Elastic.Transport.Tests
├── AddressParsing.cs
├── CodeStandards
└── NamingConventions.doc.cs
├── Components
├── NodePool
│ └── StaticNodePoolTests.cs
├── Serialization
│ ├── LowLevelRequestResponseSerializerTests.cs
│ └── SerializationTestBase.cs
└── TransportClient
│ └── RequestInvokerTests.cs
├── Configuration
├── HeadersListTests.cs
├── RequestConfigurationTests.cs
└── TransportConfigurationTests.cs
├── Elastic.Transport.Tests.csproj
├── InstantiationsTests.cs
├── OpenTelemetryTests.cs
├── Plumbing
├── InMemoryConnectionFactory.cs
└── TestResponse.cs
├── ResponseFactoryDisposeTests.cs
├── Responses
├── Dynamic
│ └── DynamicResponseBuilderTests.cs
├── Special
│ ├── BytesResponseBuilderTests.cs
│ ├── StreamResponseBuilderTests.cs
│ ├── StringResponseBuilderTests.cs
│ └── VoidResponseBuilderTests.cs
└── TestableResponseFactory.cs
├── UsageTests.cs
├── VirtualClusterTests.cs
└── VolatileUpdates.cs
/.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
--------------------------------------------------------------------------------
/.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"
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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
--------------------------------------------------------------------------------
/.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@v4
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@v4
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
67 |
68 | - name: Test Results
69 | if: success() || failure()
70 | uses: mikepenz/action-junit-report@v4
71 | with:
72 | report_paths: 'build/output/junit-*.xml'
73 | github_token: ${{ secrets.GITHUB_TOKEN }}
74 | fail_on_failure: true
75 | require_tests: true
76 | check_name: Test Results
--------------------------------------------------------------------------------
/.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
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | Location: https://www.elastic.co/community/codeofconduct
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.bat))
6 |
7 | canary
8 | 0.1
9 |
10 | latest
11 | true
12 | False
13 | false
14 | true
15 | $(SolutionRoot)\build\keys\keypair.snk
16 |
17 |
18 |
19 |
20 |
21 |
22 | all
23 | runtime; build; native; contentfiles; analyzers
24 |
25 |
26 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Playground/Playground.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | enable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/benchmarks/Elastic.Transport.Benchmarks/Elastic.Transport.Benchmarks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | false
6 | net8.0
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
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.Columns;
6 | using BenchmarkDotNet.Configs;
7 | using BenchmarkDotNet.Diagnosers;
8 | using BenchmarkDotNet.Reports;
9 | using BenchmarkDotNet.Running;
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/benchmarks/Elastic.Transport.Profiling/Elastic.Transport.Profiling.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/build.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | dotnet run --project build/scripts -- %*
3 |
4 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | dotnet run --project build/scripts -- "$@"
4 |
--------------------------------------------------------------------------------
/build/keys/keypair.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elastic/elastic-transport-net/0701e2c43ae9c50c5b74af899cff141875d7285a/build/keys/keypair.snk
--------------------------------------------------------------------------------
/build/keys/public.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elastic/elastic-transport-net/0701e2c43ae9c50c5b74af899cff141875d7285a/build/keys/public.snk
--------------------------------------------------------------------------------
/build/scripts/CommandLine.fs:
--------------------------------------------------------------------------------
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 | module CommandLine
6 |
7 | open Argu
8 | open Microsoft.FSharp.Reflection
9 |
10 | type Arguments =
11 | | [] Clean
12 | | [] Build
13 | | [] Test
14 |
15 | | [] PristineCheck
16 | | [] GeneratePackages
17 | | [] ValidatePackages
18 | | [] GenerateReleaseNotes
19 | | [] GenerateApiChanges
20 | | [] Release
21 |
22 | | [] CreateReleaseOnGithub
23 | | [] Publish
24 |
25 | | [] SingleTarget of bool
26 | | [] Token of string
27 | | [] CleanCheckout of bool
28 | with
29 | interface IArgParserTemplate with
30 | member this.Usage =
31 | match this with
32 | | Clean -> "clean known output locations"
33 | | Build -> "Run build"
34 | | Test -> "Runs build then tests"
35 | | Release -> "runs build, tests, and create and validates the packages shy of publishing them"
36 | | Publish -> "Runs the full release"
37 |
38 | | SingleTarget _ -> "Runs the provided sub command without running their dependencies"
39 | | Token _ -> "Token to be used to authenticate with github"
40 | | CleanCheckout _ -> "Skip the clean checkout check that guards the release/publish targets"
41 |
42 | | PristineCheck
43 | | GeneratePackages
44 | | ValidatePackages
45 | | GenerateReleaseNotes
46 | | GenerateApiChanges
47 | | CreateReleaseOnGithub
48 | -> "Undocumented, dependent target"
49 | member this.Name =
50 | match FSharpValue.GetUnionFields(this, typeof) with
51 | | case, _ -> case.Name.ToLowerInvariant()
52 |
--------------------------------------------------------------------------------
/build/scripts/Paths.fs:
--------------------------------------------------------------------------------
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 | module Paths
6 |
7 | open System
8 | open System.IO
9 |
10 | let ToolName = "elastic-transport-net"
11 | let Repository = sprintf "elastic/%s" ToolName
12 | let MainTFM = "netstandard2.0"
13 | let SignKey = "069ca2728db333c1"
14 |
15 | let ValidateAssemblyName = false
16 | let IncludeGitHashInInformational = true
17 | let GenerateApiChanges = false
18 |
19 | let Root =
20 | let mutable dir = DirectoryInfo(".")
21 | while dir.GetFiles("*.sln").Length = 0 do dir <- dir.Parent
22 | Environment.CurrentDirectory <- dir.FullName
23 | dir
24 |
25 | let RootRelative path = Path.GetRelativePath(Root.FullName, path)
26 |
27 | let Output = DirectoryInfo(Path.Combine(Root.FullName, "build", "output"))
28 |
29 | let ToolProject = DirectoryInfo(Path.Combine(Root.FullName, "src", ToolName))
30 |
--------------------------------------------------------------------------------
/build/scripts/Program.fs:
--------------------------------------------------------------------------------
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 | module Program
6 |
7 | open Argu
8 | open Bullseye
9 | open ProcNet
10 | open CommandLine
11 |
12 | []
13 | let main argv =
14 | let parser = ArgumentParser.Create(programName = "./build.sh")
15 | let parsed =
16 | try
17 | let parsed = parser.ParseCommandLine(inputs = argv, raiseOnUsage = true)
18 | let arguments = parsed.GetSubCommand()
19 | Some (parsed, arguments)
20 | with e ->
21 | printfn "%s" e.Message
22 | None
23 |
24 | match parsed with
25 | | None -> 2
26 | | Some (parsed, arguments) ->
27 |
28 | let target = arguments.Name
29 |
30 | Targets.Setup parsed arguments
31 | let swallowTypes = [typeof; typeof]
32 |
33 | Targets.RunTargetsAndExit
34 | ([target], (fun e -> swallowTypes |> List.contains (e.GetType()) ), ":")
35 | 0
36 |
37 |
--------------------------------------------------------------------------------
/build/scripts/scripts.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "minver-cli": {
6 | "version": "2.5.0",
7 | "commands": [
8 | "minver"
9 | ]
10 | },
11 | "assembly-differ": {
12 | "version": "0.15.0",
13 | "commands": [
14 | "assembly-differ"
15 | ]
16 | },
17 | "release-notes": {
18 | "version": "0.6.0",
19 | "commands": [
20 | "release-notes"
21 | ]
22 | },
23 | "nupkg-validator": {
24 | "version": "0.6.0",
25 | "commands": [
26 | "nupkg-validator"
27 | ]
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/examples/transport-aot-example/transport-aot-example.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | transport_aot_example
7 | enable
8 | enable
9 | true
10 | false
11 | true
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "8.0.100",
4 | "rollForward": "latestMinor",
5 | "allowPrerelease": false
6 | }
7 | }
--------------------------------------------------------------------------------
/nuget-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elastic/elastic-transport-net/0701e2c43ae9c50c5b74af899cff141875d7285a/nuget-icon.png
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/request-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elastic/elastic-transport-net/0701e2c43ae9c50c5b74af899cff141875d7285a/request-pipeline.png
--------------------------------------------------------------------------------
/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.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 |
--------------------------------------------------------------------------------
/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.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.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 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/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.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.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.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/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.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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | resharper_check_namespace_highlighting=do_not_show
3 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 | internal MemoryStreamFactory() { }
15 |
16 | ///
17 | /// Creates a memory stream
18 | ///
19 | public abstract MemoryStream Create();
20 |
21 | ///
22 | /// Creates a memory stream with the bytes written to the stream
23 | ///
24 | public abstract MemoryStream Create(byte[] bytes);
25 |
26 | ///
27 | /// Creates a memory stream with the bytes written to the stream
28 | ///
29 | public abstract MemoryStream Create(byte[] bytes, int index, int count);
30 | }
31 |
--------------------------------------------------------------------------------
/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/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/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