Skip to content

Commit 5b9b8d3

Browse files
authored
Rework HttpClient content buffering (#109642)
* Rework HttpClient response buffering * Fix string preamble detection order * Less var * Lower initial buffer size for chunked responses to 16 KB * Apply some style changes
1 parent e7d837d commit 5b9b8d3

File tree

6 files changed

+638
-443
lines changed

6 files changed

+638
-443
lines changed

src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,12 @@ private async Task<string> GetStringAsyncCore(HttpRequestMessage request, Cancel
199199

200200
// Since the underlying byte[] will never be exposed, we use an ArrayPool-backed
201201
// stream to which we copy all of the data from the response.
202-
using Stream responseStream = c.TryReadAsStream() ?? await c.ReadAsStreamAsync(cts.Token).ConfigureAwait(false);
203-
using var buffer = new HttpContent.LimitArrayPoolWriteStream(_maxResponseContentBufferSize, (int)c.Headers.ContentLength.GetValueOrDefault());
202+
using var buffer = new HttpContent.LimitArrayPoolWriteStream(
203+
_maxResponseContentBufferSize,
204+
c.Headers.ContentLength.GetValueOrDefault(),
205+
getFinalSizeFromPool: true);
204206

207+
using Stream responseStream = c.TryReadAsStream() ?? await c.ReadAsStreamAsync(cts.Token).ConfigureAwait(false);
205208
try
206209
{
207210
await responseStream.CopyToAsync(buffer, cts.Token).ConfigureAwait(false);
@@ -211,14 +214,8 @@ private async Task<string> GetStringAsyncCore(HttpRequestMessage request, Cancel
211214
throw HttpContent.WrapStreamCopyException(e);
212215
}
213216

214-
if (buffer.Length > 0)
215-
{
216-
// Decode and return the data from the buffer.
217-
return HttpContent.ReadBufferAsString(buffer.GetBuffer(), c.Headers);
218-
}
219-
220-
// No content to return.
221-
return string.Empty;
217+
// Decode and return the data from the buffer.
218+
return HttpContent.ReadBufferAsString(buffer, c.Headers);
222219
}
223220
catch (Exception e)
224221
{
@@ -272,17 +269,16 @@ private async Task<byte[]> GetByteArrayAsyncCore(HttpRequestMessage request, Can
272269
responseContentTelemetryStarted = true;
273270
}
274271

275-
// If we got a content length, then we assume that it's correct and create a MemoryStream
276-
// to which the content will be transferred. That way, assuming we actually get the exact
277-
// amount we were expecting, we can simply return the MemoryStream's underlying buffer.
272+
// If we got a content length, then we assume that it's correct. If that's the case,
273+
// we can opportunistically allocate the exact-sized buffer while buffering the content.
278274
// If we didn't get a content length, then we assume we're going to have to grow
279275
// the buffer potentially several times and that it's unlikely the underlying buffer
280276
// at the end will be the exact size needed, in which case it's more beneficial to use
281277
// ArrayPool buffers and copy out to a new array at the end.
282-
long? contentLength = c.Headers.ContentLength;
283-
using Stream buffer = contentLength.HasValue ?
284-
new HttpContent.LimitMemoryStream(_maxResponseContentBufferSize, (int)contentLength.GetValueOrDefault()) :
285-
new HttpContent.LimitArrayPoolWriteStream(_maxResponseContentBufferSize);
278+
using var buffer = new HttpContent.LimitArrayPoolWriteStream(
279+
_maxResponseContentBufferSize,
280+
c.Headers.ContentLength.GetValueOrDefault(),
281+
getFinalSizeFromPool: false);
286282

287283
using Stream responseStream = c.TryReadAsStream() ?? await c.ReadAsStreamAsync(cts.Token).ConfigureAwait(false);
288284
try
@@ -294,10 +290,7 @@ private async Task<byte[]> GetByteArrayAsyncCore(HttpRequestMessage request, Can
294290
throw HttpContent.WrapStreamCopyException(e);
295291
}
296292

297-
return
298-
buffer.Length == 0 ? Array.Empty<byte>() :
299-
buffer is HttpContent.LimitMemoryStream lms ? lms.GetSizedBuffer() :
300-
((HttpContent.LimitArrayPoolWriteStream)buffer).ToArray();
293+
return buffer.ToArray();
301294
}
302295
catch (Exception e)
303296
{

0 commit comments

Comments
 (0)