diff --git a/build/scripts/Tooling.fs b/build/scripts/Tooling.fs index de3fe42ca06..46cff86ddac 100644 --- a/build/scripts/Tooling.fs +++ b/build/scripts/Tooling.fs @@ -16,7 +16,7 @@ module Tooling = let startArgs = StartArguments(bin, args |> List.toArray) if (Option.isSome workinDir) then startArgs.WorkingDirectory <- Option.defaultValue "" workinDir - startArgs.WaitForStreamReadersTimeout <- Nullable() + if Commandline.isMono then startArgs.WaitForStreamReadersTimeout <- Nullable() let result = Proc.StartRedirected(startArgs, timeout, LineHighlightWriter()) if not result.Completed then failwithf "process failed to complete within %O: %s" timeout bin if not result.ExitCode.HasValue then failwithf "process yielded no exit code: %s" bin diff --git a/build/scripts/scripts.fsproj b/build/scripts/scripts.fsproj index 46645d1cc08..e8e847be840 100644 --- a/build/scripts/scripts.fsproj +++ b/build/scripts/scripts.fsproj @@ -69,7 +69,7 @@ - + diff --git a/src/Elasticsearch.Net/Connection/HttpWebRequestConnection.cs b/src/Elasticsearch.Net/Connection/HttpWebRequestConnection.cs index b2c524e112c..78946b36d7e 100644 --- a/src/Elasticsearch.Net/Connection/HttpWebRequestConnection.cs +++ b/src/Elasticsearch.Net/Connection/HttpWebRequestConnection.cs @@ -20,6 +20,8 @@ static HttpWebRequestConnection() { //Not available under mono if (!IsMono) HttpWebRequest.DefaultMaximumErrorResponseLength = -1; + + } internal static bool IsMono { get; } = Type.GetType("Mono.Runtime") != null; @@ -244,10 +246,13 @@ protected virtual void SetBasicAuthenticationIfNeeded(HttpWebRequest request, Re // 2 - Specified at the global IConnectionSettings level // 3 - Specified with the URI (lowest precedence) - var userInfo = Uri.UnescapeDataString(requestData.Uri.UserInfo); + string userInfo = null; + if (!string.IsNullOrEmpty(requestData.Uri.UserInfo)) + userInfo = Uri.UnescapeDataString(requestData.Uri.UserInfo); + else if (requestData.BasicAuthorizationCredentials != null) + userInfo = + $"{requestData.BasicAuthorizationCredentials.Username}:{requestData.BasicAuthorizationCredentials.Password.CreateString()}"; - if (requestData.BasicAuthorizationCredentials != null) - userInfo = requestData.BasicAuthorizationCredentials.ToString(); if (!string.IsNullOrWhiteSpace(userInfo)) request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(userInfo)); diff --git a/src/Elasticsearch.Net/Transport/Pipeline/RequestData.cs b/src/Elasticsearch.Net/Transport/Pipeline/RequestData.cs index c022e9cd08e..8be0ac16781 100644 --- a/src/Elasticsearch.Net/Transport/Pipeline/RequestData.cs +++ b/src/Elasticsearch.Net/Transport/Pipeline/RequestData.cs @@ -12,6 +12,7 @@ namespace Elasticsearch.Net public class RequestData { public const string MimeType = "application/json"; + public const string MimeTypeTextPlain = "text/plain"; public const string OpaqueIdHeader = "X-Opaque-Id"; public const string RunAsSecurityHeader = "es-security-runas-user"; diff --git a/src/Nest/Cluster/NodesHotThreads/NodesHotThreadsRequest.cs b/src/Nest/Cluster/NodesHotThreads/NodesHotThreadsRequest.cs index 238054c0dfd..da206e6176b 100644 --- a/src/Nest/Cluster/NodesHotThreads/NodesHotThreadsRequest.cs +++ b/src/Nest/Cluster/NodesHotThreads/NodesHotThreadsRequest.cs @@ -1,4 +1,5 @@ -using Elasticsearch.Net.Specification.NodesApi; +using Elasticsearch.Net; +using Elasticsearch.Net.Specification.NodesApi; namespace Nest { @@ -7,12 +8,16 @@ public partial interface INodesHotThreadsRequest { } public partial class NodesHotThreadsRequest { + protected override string ContentType => RequestData.MimeTypeTextPlain; + protected sealed override void RequestDefaults(NodesHotThreadsRequestParameters parameters) => parameters.CustomResponseBuilder = NodeHotThreadsResponseBuilder.Instance; } public partial class NodesHotThreadsDescriptor { + protected override string ContentType => RequestData.MimeTypeTextPlain; + protected sealed override void RequestDefaults(NodesHotThreadsRequestParameters parameters) => parameters.CustomResponseBuilder = NodeHotThreadsResponseBuilder.Instance; } diff --git a/src/Nest/CommonAbstractions/Request/RequestBase.cs b/src/Nest/CommonAbstractions/Request/RequestBase.cs index a87ebb1cc63..168ce838f92 100644 --- a/src/Nest/CommonAbstractions/Request/RequestBase.cs +++ b/src/Nest/CommonAbstractions/Request/RequestBase.cs @@ -9,6 +9,9 @@ namespace Nest [InterfaceDataContract] public interface IRequest { + [IgnoreDataMember] + string ContentType { get; } + [IgnoreDataMember] HttpMethod HttpMethod { get; } @@ -58,6 +61,10 @@ protected RequestBase(Func pathSelector) [IgnoreDataMember] HttpMethod IRequest.HttpMethod => HttpMethod; + [IgnoreDataMember] + string IRequest.ContentType => ContentType; + protected virtual string ContentType { get; } = null; + private readonly TParameters _parameters; [IgnoreDataMember] diff --git a/src/Nest/ElasticClient.cs b/src/Nest/ElasticClient.cs index 827e645810b..a12bfef5501 100644 --- a/src/Nest/ElasticClient.cs +++ b/src/Nest/ElasticClient.cs @@ -38,9 +38,11 @@ protected CatResponse DoCat(TRequest where TRequest : class, IRequest { if (typeof(TCatRecord) == typeof(CatHelpRecord)) + { request.RequestParameters.CustomResponseBuilder = CatHelpResponseBuilder.Instance; - else - request.RequestParameters.CustomResponseBuilder = CatResponseBuilder.Instance; + return DoRequest>(request, request.RequestParameters, r => ElasticClient.ForceTextPlain(r)); + } + request.RequestParameters.CustomResponseBuilder = CatResponseBuilder.Instance; return DoRequest>(request, request.RequestParameters, r => ElasticClient.ForceJson(r)); } @@ -50,9 +52,11 @@ protected Task> DoCatAsync { if (typeof(TCatRecord) == typeof(CatHelpRecord)) + { request.RequestParameters.CustomResponseBuilder = CatHelpResponseBuilder.Instance; - else - request.RequestParameters.CustomResponseBuilder = CatResponseBuilder.Instance; + return DoRequestAsync>(request, request.RequestParameters, ct, r => ElasticClient.ForceTextPlain(r)); + } + request.RequestParameters.CustomResponseBuilder = CatResponseBuilder.Instance; return DoRequestAsync>(request, request.RequestParameters, ct, r => ElasticClient.ForceJson(r)); } @@ -103,6 +107,7 @@ internal TResponse DoRequest(TRequest p, IRequestParameters where TResponse : class, IElasticsearchResponse, new() { if (forceConfiguration != null) ForceConfiguration(p, forceConfiguration); + if (p.ContentType != null) ForceContentType(p, p.ContentType); var url = p.GetUrl(ConnectionSettings); var b = (p.HttpMethod == HttpMethod.GET || p.HttpMethod == HttpMethod.HEAD) ? null : new SerializableData(p); @@ -120,6 +125,7 @@ internal Task DoRequestAsync( where TResponse : class, IElasticsearchResponse, new() { if (forceConfiguration != null) ForceConfiguration(p, forceConfiguration); + if (p.ContentType != null) ForceContentType(p, p.ContentType); var url = p.GetUrl(ConnectionSettings); var b = (p.HttpMethod == HttpMethod.GET || p.HttpMethod == HttpMethod.HEAD) ? null : new SerializableData(p); @@ -130,16 +136,29 @@ internal Task DoRequestAsync( private static void ForceConfiguration(IRequest request, Action forceConfiguration) { if (forceConfiguration == null) return; + var configuration = request.RequestParameters.RequestConfiguration ?? new RequestConfiguration(); forceConfiguration(configuration); request.RequestParameters.RequestConfiguration = configuration; } + private void ForceContentType(TRequest request, string contentType) where TRequest : class, IRequest + { + var configuration = request.RequestParameters.RequestConfiguration ?? new RequestConfiguration(); + configuration.Accept = contentType; + configuration.ContentType = contentType; + request.RequestParameters.RequestConfiguration = configuration; + } internal static void ForceJson(IRequestConfiguration requestConfiguration) { requestConfiguration.Accept = RequestData.MimeType; requestConfiguration.ContentType = RequestData.MimeType; } + internal static void ForceTextPlain(IRequestConfiguration requestConfiguration) + { + requestConfiguration.Accept = RequestData.MimeTypeTextPlain; + requestConfiguration.ContentType = RequestData.MimeTypeTextPlain; + } internal IRequestParameters ResponseBuilder(SourceRequestParameters parameters, CustomResponseBuilderBase builder) { diff --git a/src/Tests/Tests.Benchmarking/Tests.Benchmarking.csproj b/src/Tests/Tests.Benchmarking/Tests.Benchmarking.csproj index ec0457babbc..cd8ae8e940b 100644 --- a/src/Tests/Tests.Benchmarking/Tests.Benchmarking.csproj +++ b/src/Tests/Tests.Benchmarking/Tests.Benchmarking.csproj @@ -13,7 +13,7 @@ - + \ No newline at end of file diff --git a/src/Tests/Tests.Core/Tests.Core.csproj b/src/Tests/Tests.Core/Tests.Core.csproj index 1347370874f..42e64ca0130 100644 --- a/src/Tests/Tests.Core/Tests.Core.csproj +++ b/src/Tests/Tests.Core/Tests.Core.csproj @@ -13,10 +13,10 @@ - + - + diff --git a/src/Tests/Tests.Domain/Tests.Domain.csproj b/src/Tests/Tests.Domain/Tests.Domain.csproj index f7399111d23..eec73dc3eb5 100644 --- a/src/Tests/Tests.Domain/Tests.Domain.csproj +++ b/src/Tests/Tests.Domain/Tests.Domain.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/Tests/Tests/ClientConcepts/Troubleshooting/DiagnosticSource.doc.cs b/src/Tests/Tests/ClientConcepts/Troubleshooting/DiagnosticSource.doc.cs index 82009dec774..da2e757fc95 100644 --- a/src/Tests/Tests/ClientConcepts/Troubleshooting/DiagnosticSource.doc.cs +++ b/src/Tests/Tests/ClientConcepts/Troubleshooting/DiagnosticSource.doc.cs @@ -26,27 +26,27 @@ namespace Tests.ClientConcepts.Troubleshooting * * To aid with their discover the topics you can subscribe on and the event names they emit are exposed as * strongly typed strings under `Elasticsearch.Net.Diagnostics.DiagnosticSources` - * + * */ public class DiagnosticSourceUsageDocumentation : IClusterFixture { private readonly ReadOnlyCluster _cluster; public DiagnosticSourceUsageDocumentation(ReadOnlyCluster cluster) => _cluster = cluster; - - + + /** * Subscribing to DiagnosticSources means implementing `IObserver` * or use `.Subscribe(observer, filter)` to opt in to the correct topic. * * Here we choose the more verbose `IObserver<>` implementation. - * + * */ private class ListenerObserver : IObserver, IDisposable { private long _messagesWrittenToConsole = 0; public long MessagesWrittenToConsole => _messagesWrittenToConsole; - + public Exception SeenException { get; private set; } public void OnError(Exception error) => SeenException = error; @@ -56,12 +56,11 @@ private class ListenerObserver : IObserver, IDisposable private void WriteToConsole(string eventName, T data) { var a = Activity.Current; - Console.WriteLine($"{eventName?.PadRight(30)} {a.Id?.PadRight(32)} {a.ParentId?.PadRight(32)} {data?.ToString().PadRight(10)}"); Interlocked.Increment(ref _messagesWrittenToConsole); } - + private List Disposables { get; } = new List(); - + /** * By inspecting the name we selectively subscribe only to topics `Elasticsearch.Net` emits. * @@ -72,7 +71,7 @@ private void WriteToConsole(string eventName, T data) * * Therefor each topic we ship with has a dedicated `Observer` implementation that takes an `onNext` lambda * which is typed to the context object we actually emit. - * + * */ public void OnNext(DiagnosticListener value) { @@ -83,24 +82,24 @@ void TrySubscribe(string sourceName, Func var subscription = value.Subscribe(listener()); Disposables.Add(subscription); } - - TrySubscribe(DiagnosticSources.AuditTrailEvents.SourceName, + + TrySubscribe(DiagnosticSources.AuditTrailEvents.SourceName, () => new AuditDiagnosticObserver(v => WriteToConsole(v.EventName, v.Audit))); - - TrySubscribe(DiagnosticSources.Serializer.SourceName, + + TrySubscribe(DiagnosticSources.Serializer.SourceName, () => new SerializerDiagnosticObserver(v => WriteToConsole(v.EventName, v.Registration))); /** * RequestPipeline emits a different context object for the start of the `Activity` then it does * for the end of the `Activity` therefor `RequestPipelineDiagnosticObserver` accepts two `onNext` lambda's. * One for the `.Start` events and one for the `.Stop` events. */ - TrySubscribe(DiagnosticSources.RequestPipeline.SourceName, + TrySubscribe(DiagnosticSources.RequestPipeline.SourceName, () => new RequestPipelineDiagnosticObserver( v => WriteToConsole(v.EventName, v.RequestData), v => WriteToConsole(v.EventName, v.Response) )); - - TrySubscribe(DiagnosticSources.HttpConnection.SourceName, + + TrySubscribe(DiagnosticSources.HttpConnection.SourceName, () => new HttpConnectionDiagnosticObserver( v => WriteToConsole(v.EventName, v.RequestData), v => WriteToConsole(v.EventName, v.StatusCode) @@ -122,7 +121,7 @@ [I] public void SubscribeToTopics() using(var listenerObserver = new ListenerObserver()) using (var subscription = DiagnosticListener.AllListeners.Subscribe(listenerObserver)) { - + /** * We'll use a Sniffing connection pool here since it sniffs on startup and pings before * first usage, so our diagnostics are involved enough to showcase most topics. diff --git a/src/Tests/Tests/Framework/EndpointTests/CoordinatedIntegrationTestBase.cs b/src/Tests/Tests/Framework/EndpointTests/CoordinatedIntegrationTestBase.cs index 200059ae1df..ecaaaad97b9 100644 --- a/src/Tests/Tests/Framework/EndpointTests/CoordinatedIntegrationTestBase.cs +++ b/src/Tests/Tests/Framework/EndpointTests/CoordinatedIntegrationTestBase.cs @@ -1,8 +1,12 @@ using System; +using System.Diagnostics; +using System.Runtime.ExceptionServices; using System.Threading.Tasks; using Elastic.Managed.Ephemeral; using Elastic.Xunit.XunitPlumbing; +using Elasticsearch.Net; using Nest; +using Tests.Core.Client; using Tests.Core.ManagedElasticsearch.Clusters; using Tests.Framework.EndpointTests.TestState; using Tests.Framework.Extensions; @@ -53,7 +57,23 @@ private async Task AssertOnAllResponses(string name, LazyResponses re if (!_coordinatedUsage.MethodIsolatedValues.TryGetValue(key, out var isolatedValue)) throw new Exception($"{name} is not a request observed and so no call isolated values could be located for it"); - assert(isolatedValue, response); + var r = response; + if (TestClient.Configuration.RunIntegrationTests && !r.IsValid && r.ApiCall.OriginalException != null + && !(r.ApiCall.OriginalException is ElasticsearchClientException)) + { + var e = ExceptionDispatchInfo.Capture(r.ApiCall.OriginalException.Demystify()); + throw new ResponseAssertionException(e.SourceException, r).Demystify(); + } + + try + { + assert(isolatedValue, response); + } + catch (Exception e) + { + var ex = ExceptionDispatchInfo.Capture(e.Demystify()); + throw new ResponseAssertionException(ex.SourceException, r).Demystify(); + } } } diff --git a/src/Tests/Tests/Indices/IndexManagement/RolloverIndex/RolloverIndexApiTests.cs b/src/Tests/Tests/Indices/IndexManagement/RolloverIndex/RolloverIndexApiTests.cs index 28c380e85ad..01af09c6849 100644 --- a/src/Tests/Tests/Indices/IndexManagement/RolloverIndex/RolloverIndexApiTests.cs +++ b/src/Tests/Tests/Indices/IndexManagement/RolloverIndex/RolloverIndexApiTests.cs @@ -53,9 +53,9 @@ public RolloverIndexApiTests(WritableCluster cluster, EndpointUsage usage) } } }, - aliases = new + aliases = new Dictionary { - new_projects = new { } + { CallIsolatedValue + "-new_projects", new { } } } }; @@ -83,7 +83,7 @@ public RolloverIndexApiTests(WritableCluster cluster, EndpointUsage usage) ) ) .Aliases(a => a - .Alias("new_projects") + .Alias(CallIsolatedValue + "-new_projects") ); protected override RolloverIndexRequest Initializer => new RolloverIndexRequest(CallIsolatedValue + "-alias", CallIsolatedValue + "-new") @@ -120,7 +120,7 @@ public RolloverIndexApiTests(WritableCluster cluster, EndpointUsage usage) }, Aliases = new Aliases { - { "new_projects", new Alias() } + { CallIsolatedValue + "-new_projects", new Alias() } } }; @@ -138,7 +138,7 @@ protected override void OnBeforeCall(IElasticClient client) .IndexMany(Project.Generator.Generate(1200)) ); someDocs.ShouldBeValid(); - + } protected override LazyResponses ClientUsage() => Calls( diff --git a/src/Tests/Tests/XPack/Security/User/PutUser/PutUserApiTests.cs b/src/Tests/Tests/XPack/Security/User/PutUser/PutUserApiTests.cs index b8a22609136..5080ef98c3a 100644 --- a/src/Tests/Tests/XPack/Security/User/PutUser/PutUserApiTests.cs +++ b/src/Tests/Tests/XPack/Security/User/PutUser/PutUserApiTests.cs @@ -72,7 +72,8 @@ protected override LazyResponses ClientUsage() => Calls( protected override PutUserDescriptor NewDescriptor() => new PutUserDescriptor(CallIsolatedValue); - protected override void ExpectResponse(PutUserResponse response) => response.Created.Should().BeTrue(); + protected override void ExpectResponse(PutUserResponse response) => + response.Created.Should().BeTrue("{0}", response.DebugInformation); } public class PutUserRunAsApiTests : PutUserApiTests