Skip to content

Commit 329e03d

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 6fe6961 commit 329e03d

File tree

3 files changed

+58
-18
lines changed

3 files changed

+58
-18
lines changed

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

Lines changed: 17 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,12 +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
{
39+
if (data.HttpCompression)
40+
stream = new GZipStream(stream, CompressionMode.Compress, false);
41+
4042
using(stream)
41-
data.Write(stream, requestData.ConnectionSettings);
43+
data.PostData.Write(stream, data.ConnectionSettings);
44+
4245
return Task.CompletedTask;
4346
}
47+
4448
_onStreamAvailable = OnStreamAvailable;
4549
}
4650
public RequestDataContent(RequestData requestData, CancellationToken token)
@@ -50,11 +54,15 @@ public RequestDataContent(RequestData requestData, CancellationToken token)
5054
if (requestData.HttpCompression)
5155
Headers.ContentEncoding.Add("gzip");
5256

53-
async Task OnStreamAvailable(PostData data, Stream stream, HttpContent content, TransportContext context)
57+
async Task OnStreamAvailable(RequestData data, Stream stream, HttpContent content, TransportContext context)
5458
{
59+
if (data.HttpCompression)
60+
stream = new GZipStream(stream, CompressionMode.Compress, false);
61+
5562
using (stream)
56-
await data.WriteAsync(stream, requestData.ConnectionSettings, token).ConfigureAwait(false);
63+
await data.PostData.WriteAsync(stream, data.ConnectionSettings, token).ConfigureAwait(false);
5764
}
65+
5866
_onStreamAvailable = OnStreamAvailable;
5967
}
6068

@@ -69,16 +77,9 @@ async Task OnStreamAvailable(PostData data, Stream stream, HttpContent content,
6977
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is passed as task result.")]
7078
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
7179
{
72-
73-
var data = _requestData.PostData;
74-
if (data == null) return;
75-
7680
var serializeToStreamTask = new TaskCompletionSource<bool>();
77-
78-
if (_requestData.HttpCompression)
79-
stream = new GZipStream(stream, CompressionMode.Compress, false);
8081
var wrappedStream = new CompleteTaskOnCloseStream(stream, serializeToStreamTask);
81-
await _onStreamAvailable(data, wrappedStream, this, context).ConfigureAwait(false);
82+
await _onStreamAvailable(_requestData, wrappedStream, this, context).ConfigureAwait(false);
8283
await serializeToStreamTask.Task.ConfigureAwait(false);
8384
}
8485

@@ -111,7 +112,6 @@ protected override void Dispose(bool disposing)
111112
base.Dispose();
112113
}
113114

114-
115115
public override void Close() => _serializeToStreamTask.TrySetResult(true);
116116
}
117117

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

195195
public override void WriteByte(byte value) => _innerStream.WriteByte(value);
196+
197+
public override void Close() => _innerStream.Close();
196198
}
197199
}
198200
}

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)