Skip to content

Commit fb513a5

Browse files
Don't send body in HEAD response when using PipeWriter.Advance before headers flushed (#59725)
1 parent 52088f2 commit fb513a5

File tree

6 files changed

+302
-94
lines changed

6 files changed

+302
-94
lines changed

Diff for: src/Servers/Kestrel/Core/src/Internal/Http/Http1OutputProducer.cs

+32-28
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ internal class Http1OutputProducer : IHttpOutputProducer, IDisposable
5151
// Once write or flush is called, we modify the _currentChunkMemory to prepend the size of data written
5252
// and append the end terminator.
5353

54-
private bool _autoChunk;
54+
private ResponseBodyMode _responseBodyMode;
5555

5656
private bool _writeStreamSuffixCalled;
5757

@@ -121,7 +121,7 @@ public ValueTask<FlushResult> WriteStreamSuffixAsync()
121121
{
122122
if (!_writeStreamSuffixCalled)
123123
{
124-
if (_autoChunk)
124+
if (_responseBodyMode == ResponseBodyMode.Chunked)
125125
{
126126
var writer = new BufferWriter<PipeWriter>(_pipeWriter);
127127
result = WriteAsyncInternal(ref writer, EndChunkedResponseBytes);
@@ -147,7 +147,7 @@ public ValueTask<FlushResult> FlushAsync(CancellationToken cancellationToken = d
147147
return new ValueTask<FlushResult>(new FlushResult(false, true));
148148
}
149149

150-
if (_autoChunk)
150+
if (_responseBodyMode == ResponseBodyMode.Chunked)
151151
{
152152
if (_advancedBytesForChunk > 0)
153153
{
@@ -173,7 +173,7 @@ static ValueTask<FlushResult> FlushAsyncChunked(Http1OutputProducer producer, Ca
173173
// Local function so in the common-path the stack space for BufferWriter isn't reserved and cleared when it isn't used.
174174

175175
Debug.Assert(!producer._pipeWriterCompleted);
176-
Debug.Assert(producer._autoChunk && producer._advancedBytesForChunk > 0);
176+
Debug.Assert(producer._responseBodyMode == ResponseBodyMode.Chunked && producer._advancedBytesForChunk > 0);
177177

178178
var writer = new BufferWriter<PipeWriter>(producer._pipeWriter);
179179
producer.WriteCurrentChunkMemoryToPipeWriter(ref writer);
@@ -203,7 +203,7 @@ public Memory<byte> GetMemory(int sizeHint = 0)
203203
{
204204
return LeasedMemory(sizeHint);
205205
}
206-
else if (_autoChunk)
206+
else if (_responseBodyMode == ResponseBodyMode.Chunked)
207207
{
208208
return GetChunkedMemory(sizeHint);
209209
}
@@ -228,7 +228,7 @@ public Span<byte> GetSpan(int sizeHint = 0)
228228
{
229229
return LeasedMemory(sizeHint).Span;
230230
}
231-
else if (_autoChunk)
231+
else if (_responseBodyMode == ResponseBodyMode.Chunked)
232232
{
233233
return GetChunkedMemory(sizeHint).Span;
234234
}
@@ -262,7 +262,7 @@ public void Advance(int bytes)
262262
_position += bytes;
263263
}
264264
}
265-
else if (_autoChunk)
265+
else if (_responseBodyMode == ResponseBodyMode.Chunked)
266266
{
267267
if (_advancedBytesForChunk > _currentChunkMemory.Length - _currentMemoryPrefixBytes - EndChunkLength - bytes)
268268
{
@@ -333,7 +333,7 @@ private void CommitChunkInternal(ref BufferWriter<PipeWriter> writer, ReadOnlySp
333333
writer.Commit();
334334
}
335335

336-
public void WriteResponseHeaders(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, bool appComplete)
336+
public void WriteResponseHeaders(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, ResponseBodyMode responseBodyMode, bool appComplete)
337337
{
338338
lock (_contextLock)
339339
{
@@ -346,11 +346,11 @@ public void WriteResponseHeaders(int statusCode, string? reasonPhrase, HttpRespo
346346

347347
var buffer = _pipeWriter;
348348
var writer = new BufferWriter<PipeWriter>(buffer);
349-
WriteResponseHeadersInternal(ref writer, statusCode, reasonPhrase, responseHeaders, autoChunk);
349+
WriteResponseHeadersInternal(ref writer, statusCode, reasonPhrase, responseHeaders, responseBodyMode);
350350
}
351351
}
352352

353-
private void WriteResponseHeadersInternal(ref BufferWriter<PipeWriter> writer, int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk)
353+
private void WriteResponseHeadersInternal(ref BufferWriter<PipeWriter> writer, int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, ResponseBodyMode responseBodyMode)
354354
{
355355
writer.Write(HttpVersion11Bytes);
356356
var statusBytes = ReasonPhrases.ToStatusBytes(statusCode, reasonPhrase);
@@ -360,7 +360,8 @@ private void WriteResponseHeadersInternal(ref BufferWriter<PipeWriter> writer, i
360360

361361
writer.Commit();
362362

363-
_autoChunk = autoChunk;
363+
Debug.Assert(responseBodyMode != ResponseBodyMode.Uninitialized);
364+
_responseBodyMode = responseBodyMode;
364365
WriteDataWrittenBeforeHeaders(ref writer);
365366
_unflushedBytes += writer.BytesCommitted;
366367

@@ -373,11 +374,11 @@ private void WriteDataWrittenBeforeHeaders(ref BufferWriter<PipeWriter> writer)
373374
{
374375
foreach (var segment in _completedSegments)
375376
{
376-
if (_autoChunk)
377+
if (_responseBodyMode == ResponseBodyMode.Chunked)
377378
{
378379
CommitChunkInternal(ref writer, segment.Span);
379380
}
380-
else
381+
else if (_responseBodyMode == ResponseBodyMode.ContentLength)
381382
{
382383
writer.Write(segment.Span);
383384
writer.Commit();
@@ -391,16 +392,19 @@ private void WriteDataWrittenBeforeHeaders(ref BufferWriter<PipeWriter> writer)
391392

392393
if (!_currentSegment.IsEmpty)
393394
{
394-
var segment = _currentSegment.Slice(0, _position);
395-
396-
if (_autoChunk)
395+
if (_responseBodyMode != ResponseBodyMode.Disabled)
397396
{
398-
CommitChunkInternal(ref writer, segment.Span);
399-
}
400-
else
401-
{
402-
writer.Write(segment.Span);
403-
writer.Commit();
397+
var segment = _currentSegment.Slice(0, _position);
398+
399+
if (_responseBodyMode == ResponseBodyMode.Chunked)
400+
{
401+
CommitChunkInternal(ref writer, segment.Span);
402+
}
403+
else if (_responseBodyMode == ResponseBodyMode.ContentLength)
404+
{
405+
writer.Write(segment.Span);
406+
writer.Commit();
407+
}
404408
}
405409

406410
_position = 0;
@@ -491,7 +495,7 @@ public ValueTask<FlushResult> Write100ContinueAsync()
491495
return WriteAsync(ContinueBytes);
492496
}
493497

494-
public ValueTask<FlushResult> FirstWriteAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> buffer, CancellationToken cancellationToken)
498+
public ValueTask<FlushResult> FirstWriteAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, ResponseBodyMode responseBodyMode, ReadOnlySpan<byte> buffer, CancellationToken cancellationToken)
495499
{
496500
lock (_contextLock)
497501
{
@@ -505,13 +509,13 @@ public ValueTask<FlushResult> FirstWriteAsync(int statusCode, string? reasonPhra
505509
// Uses same BufferWriter to write response headers and response
506510
var writer = new BufferWriter<PipeWriter>(_pipeWriter);
507511

508-
WriteResponseHeadersInternal(ref writer, statusCode, reasonPhrase, responseHeaders, autoChunk);
512+
WriteResponseHeadersInternal(ref writer, statusCode, reasonPhrase, responseHeaders, responseBodyMode);
509513

510514
return WriteAsyncInternal(ref writer, buffer, cancellationToken);
511515
}
512516
}
513517

514-
public ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> buffer, CancellationToken cancellationToken)
518+
public ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, ResponseBodyMode responseBodyMode, ReadOnlySpan<byte> buffer, CancellationToken cancellationToken)
515519
{
516520
lock (_contextLock)
517521
{
@@ -525,7 +529,7 @@ public ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string? rea
525529
// Uses same BufferWriter to write response headers and chunk
526530
var writer = new BufferWriter<PipeWriter>(_pipeWriter);
527531

528-
WriteResponseHeadersInternal(ref writer, statusCode, reasonPhrase, responseHeaders, autoChunk);
532+
WriteResponseHeadersInternal(ref writer, statusCode, reasonPhrase, responseHeaders, responseBodyMode);
529533

530534
CommitChunkInternal(ref writer, buffer);
531535

@@ -541,7 +545,7 @@ public void Reset()
541545
Debug.Assert(_completedSegments == null || _completedSegments.Count == 0);
542546
// Cleared in sequential address ascending order
543547
_currentMemoryPrefixBytes = 0;
544-
_autoChunk = false;
548+
_responseBodyMode = ResponseBodyMode.Uninitialized;
545549
_writeStreamSuffixCalled = false;
546550
_currentChunkMemoryUpdated = false;
547551
_startCalled = false;
@@ -570,7 +574,7 @@ private ValueTask<FlushResult> WriteAsyncInternal(
570574
ReadOnlySpan<byte> buffer,
571575
CancellationToken cancellationToken = default)
572576
{
573-
if (_autoChunk)
577+
if (_responseBodyMode == ResponseBodyMode.Chunked)
574578
{
575579
if (_advancedBytesForChunk > 0)
576580
{

0 commit comments

Comments
 (0)