Skip to content

Commit db68934

Browse files
committed
Do not set HttpRequestMessage content when no PostData (#3925)
This commit changes the HttpConnection to not set Content on HttpRequestMessage when there is no PostData. The check is performed inside of Request and RequestAsync to avoid creating any content instance and allocations. Pass RequestData through to _onStreamAvailable delegates so that HttpCompression, ConnectionSettings and PostData can be accessed on the passed instance. Fixes #3907 (cherry picked from commit 90c2bf7)
1 parent 5b4e8d6 commit db68934

File tree

3 files changed

+56
-18
lines changed

3 files changed

+56
-18
lines changed

src/Elasticsearch.Net/Connection/Content/RequestDataContent.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ namespace Elasticsearch.Net
2525
internal class RequestDataContent : HttpContent
2626
{
2727
private readonly RequestData _requestData;
28-
private readonly Func<PostData, CompleteTaskOnCloseStream, RequestDataContent, TransportContext, Task> _onStreamAvailable;
29-
28+
private readonly Func<RequestData, CompleteTaskOnCloseStream, RequestDataContent, TransportContext, Task> _onStreamAvailable;
3029

3130
public RequestDataContent(RequestData requestData)
3231
{
@@ -35,14 +34,17 @@ public RequestDataContent(RequestData requestData)
3534
if (requestData.HttpCompression)
3635
Headers.ContentEncoding.Add("gzip");
3736

38-
Task OnStreamAvailable(PostData data, Stream stream, HttpContent content, TransportContext context)
37+
Task OnStreamAvailable(RequestData data, Stream stream, HttpContent content, TransportContext context)
3938
{
40-
if (_requestData.HttpCompression)
39+
if (data.HttpCompression)
4140
stream = new GZipStream(stream, CompressionMode.Compress, false);
41+
4242
using(stream)
43-
data.Write(stream, requestData.ConnectionSettings);
43+
data.PostData.Write(stream, data.ConnectionSettings);
44+
4445
return Task.CompletedTask;
4546
}
47+
4648
_onStreamAvailable = OnStreamAvailable;
4749
}
4850
public RequestDataContent(RequestData requestData, CancellationToken token)
@@ -52,13 +54,15 @@ public RequestDataContent(RequestData requestData, CancellationToken token)
5254
if (requestData.HttpCompression)
5355
Headers.ContentEncoding.Add("gzip");
5456

55-
async Task OnStreamAvailable(PostData data, Stream stream, HttpContent content, TransportContext context)
57+
async Task OnStreamAvailable(RequestData data, Stream stream, HttpContent content, TransportContext context)
5658
{
57-
if (_requestData.HttpCompression)
59+
if (data.HttpCompression)
5860
stream = new GZipStream(stream, CompressionMode.Compress, false);
61+
5962
using (stream)
60-
await data.WriteAsync(stream, requestData.ConnectionSettings, token).ConfigureAwait(false);
63+
await data.PostData.WriteAsync(stream, data.ConnectionSettings, token).ConfigureAwait(false);
6164
}
65+
6266
_onStreamAvailable = OnStreamAvailable;
6367
}
6468

@@ -73,14 +77,9 @@ async Task OnStreamAvailable(PostData data, Stream stream, HttpContent content,
7377
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is passed as task result.")]
7478
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
7579
{
76-
77-
var data = _requestData.PostData;
78-
if (data == null) return;
79-
8080
var serializeToStreamTask = new TaskCompletionSource<bool>();
81-
8281
var wrappedStream = new CompleteTaskOnCloseStream(stream, serializeToStreamTask);
83-
await _onStreamAvailable(data, wrappedStream, this, context).ConfigureAwait(false);
82+
await _onStreamAvailable(_requestData, wrappedStream, this, context).ConfigureAwait(false);
8483
await serializeToStreamTask.Task.ConfigureAwait(false);
8584
}
8685

@@ -113,7 +112,6 @@ protected override void Dispose(bool disposing)
113112
base.Dispose();
114113
}
115114

116-
117115
public override void Close() => _serializeToStreamTask.TrySetResult(true);
118116
}
119117

@@ -195,6 +193,8 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As
195193
public override void EndWrite(IAsyncResult asyncResult) => _innerStream.EndWrite(asyncResult);
196194

197195
public override void WriteByte(byte value) => _innerStream.WriteByte(value);
196+
197+
public override void Close() => _innerStream.Close();
198198
}
199199
}
200200
}

src/Elasticsearch.Net/Connection/HttpConnection.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ public virtual TResponse Request<TResponse>(RequestData requestData)
5757
try
5858
{
5959
var requestMessage = CreateHttpRequestMessage(requestData);
60-
SetContent(requestMessage, requestData);
60+
61+
if (requestData.PostData != null)
62+
SetContent(requestMessage, requestData);
63+
6164
using(requestMessage?.Content ?? (IDisposable)Stream.Null)
6265
using (var d = DiagnosticSource.Diagnose<RequestData, int?>(DiagnosticSources.HttpConnection.SendAndReceiveHeaders, requestData))
6366
{
@@ -107,8 +110,11 @@ public virtual async Task<TResponse> RequestAsync<TResponse>(RequestData request
107110
try
108111
{
109112
var requestMessage = CreateHttpRequestMessage(requestData);
110-
SetAsyncContent(requestMessage, requestData, cancellationToken);
111-
using(requestMessage?.Content ?? (IDisposable)Stream.Null)
113+
114+
if (requestData.PostData != null)
115+
SetAsyncContent(requestMessage, requestData, cancellationToken);
116+
117+
using(requestMessage?.Content ?? (IDisposable)Stream.Null)
112118
using (var d = DiagnosticSource.Diagnose<RequestData, int?>(DiagnosticSources.HttpConnection.SendAndReceiveHeaders, requestData))
113119
{
114120
responseMessage = await client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Net;
3+
using Elastic.Xunit.XunitPlumbing;
4+
using FluentAssertions;
5+
using Nest;
6+
using Tests.Core.ManagedElasticsearch.Clusters;
7+
using Tests.Domain;
8+
9+
namespace Tests.Reproduce
10+
{
11+
public class GithubIssue3907 : IClusterFixture<ReadOnlyCluster>
12+
{
13+
private readonly IntrusiveOperationCluster _cluster;
14+
15+
// use intrusive operation cluster because we're changing the underlying http handler
16+
// and this cluster runs with a max concurrency of 1, so changing http handler
17+
// will not affect other integration tests
18+
public GithubIssue3907(IntrusiveOperationCluster cluster) => _cluster = cluster;
19+
20+
[I]
21+
public void NotUsingSocketsHttpHandlerDoesNotCauseException()
22+
{
23+
AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
24+
25+
var response = _cluster.Client.Indices.Exists("non_existent_index");
26+
response.ApiCall.HttpStatusCode.Should().Be(404);
27+
response.OriginalException.Should().BeNull();
28+
29+
AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", true);
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)