Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit ec6a664

Browse files
author
Cesar Blum Silveira
committed
Refactor non-body response handling.
- Add functional tests - Remove BadHttpResponse, ResponseRejectionReasons and TestFrameProtectedMembers
1 parent f698fa7 commit ec6a664

File tree

9 files changed

+197
-228
lines changed

9 files changed

+197
-228
lines changed

src/Microsoft.AspNetCore.Server.Kestrel/BadHttpResponse.cs

Lines changed: 0 additions & 75 deletions
This file was deleted.

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Frame.cs

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using System.IO;
78
using System.Linq;
89
using System.Net;
@@ -172,7 +173,7 @@ public int StatusCode
172173
{
173174
if (HasResponseStarted)
174175
{
175-
BadHttpResponse.ThrowException(ResponseRejectionReasons.ValueCannotBeSetResponseStarted, ResponseRejectionParameter.StatusCode);
176+
ThrowResponseAlreadyStartedException(nameof(StatusCode));
176177
}
177178

178179
_statusCode = value;
@@ -190,7 +191,7 @@ public string ReasonPhrase
190191
{
191192
if (HasResponseStarted)
192193
{
193-
BadHttpResponse.ThrowException(ResponseRejectionReasons.ValueCannotBeSetResponseStarted, ResponseRejectionParameter.ReasonPhrase);
194+
ThrowResponseAlreadyStartedException(nameof(ReasonPhrase));
194195
}
195196

196197
_reasonPhrase = value;
@@ -426,7 +427,7 @@ public void OnStarting(Func<object, Task> callback, object state)
426427
{
427428
if (HasResponseStarted)
428429
{
429-
BadHttpResponse.ThrowException(ResponseRejectionReasons.OnStartingCannotBeSetResponseStarted, ResponseRejectionParameter.OnStarting);
430+
ThrowResponseAlreadyStartedException(nameof(OnStarting));
430431
}
431432

432433
if (_onStarting == null)
@@ -513,7 +514,7 @@ public void Write(ArraySegment<byte> data)
513514
{
514515
ProduceStartAndFireOnStarting().GetAwaiter().GetResult();
515516

516-
if (_canHaveBody)
517+
if (_canHaveBody || StatusCode == 101)
517518
{
518519
if (_autoChunk)
519520
{
@@ -541,7 +542,7 @@ public Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationTo
541542
return WriteAsyncAwaited(data, cancellationToken);
542543
}
543544

544-
if (_canHaveBody)
545+
if (_canHaveBody || StatusCode == 101)
545546
{
546547
if (_autoChunk)
547548
{
@@ -567,7 +568,7 @@ public async Task WriteAsyncAwaited(ArraySegment<byte> data, CancellationToken c
567568
{
568569
await ProduceStartAndFireOnStarting();
569570

570-
if (_canHaveBody)
571+
if (_canHaveBody || StatusCode == 101)
571572
{
572573
if (_autoChunk)
573574
{
@@ -587,7 +588,6 @@ public async Task WriteAsyncAwaited(ArraySegment<byte> data, CancellationToken c
587588
HandleNonBodyResponseWrite(data.Count);
588589
return;
589590
}
590-
591591
}
592592

593593
private void WriteChunked(ArraySegment<byte> data)
@@ -700,11 +700,11 @@ protected Task ProduceEnd()
700700
return TaskCache.CompletedTask;
701701
}
702702

703-
// If the request was rejected, StatusCode has already been set by SetBadRequestState
703+
// If the request was rejected, the error state has already been set by SetBadRequestState
704704
if (!_requestRejected)
705705
{
706706
// 500 Internal Server Error
707-
ErrorResetHeadersToDefaults(statusCode: 500);
707+
SetErrorResponseHeaders(statusCode: 500);
708708
}
709709
}
710710

@@ -758,19 +758,19 @@ private void CreateResponseHeader(
758758
bool appCompleted)
759759
{
760760
var responseHeaders = FrameResponseHeaders;
761-
762761
var hasConnection = responseHeaders.HasConnection;
763762

764-
// Set whether response can have body
765-
_canHaveBody = StatusCanHaveBody(StatusCode) && Method != "HEAD";
766-
767763
var end = SocketOutput.ProducingStart();
764+
768765
if (_keepAlive && hasConnection)
769766
{
770767
var connectionValue = responseHeaders.HeaderConnection.ToString();
771768
_keepAlive = connectionValue.Equals("keep-alive", StringComparison.OrdinalIgnoreCase);
772769
}
773-
770+
771+
// Set whether response can have body
772+
_canHaveBody = StatusCanHaveBody(StatusCode) && Method != "HEAD";
773+
774774
if (_canHaveBody)
775775
{
776776
if (!responseHeaders.HasTransferEncoding && !responseHeaders.HasContentLength)
@@ -781,7 +781,7 @@ private void CreateResponseHeader(
781781
// the headers we can safely set the Content-Length to 0.
782782
responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
783783
}
784-
else
784+
else if (_keepAlive)
785785
{
786786
// Note for future reference: never change this to set _autoChunk to true on HTTP/1.0
787787
// connections, even if we were to infer the client supports it because an HTTP/1.0 request
@@ -805,7 +805,7 @@ private void CreateResponseHeader(
805805
else
806806
{
807807
// Don't set the Content-Length or Transfer-Encoding headers
808-
// automatically for HEAD requests or 101, 204, 205, 304 responses.
808+
// automatically for HEAD requests or 204, 205, 304 responses.
809809
if (responseHeaders.HasTransferEncoding)
810810
{
811811
RejectNonBodyTransferEncodingResponse(appCompleted);
@@ -1277,9 +1277,14 @@ public bool StatusCanHaveBody(int statusCode)
12771277
statusCode != 304;
12781278
}
12791279

1280+
private void ThrowResponseAlreadyStartedException(string value)
1281+
{
1282+
throw new InvalidOperationException($"{value} cannot be set, response has already started.");
1283+
}
1284+
12801285
private void RejectNonBodyTransferEncodingResponse(bool appCompleted)
12811286
{
1282-
var ex = BadHttpResponse.GetException(ResponseRejectionReasons.TransferEncodingSetOnNonBodyResponse, StatusCode);
1287+
var ex = new InvalidOperationException($"Transfer-Encoding set on a {StatusCode} non-body request.");
12831288
if (!appCompleted)
12841289
{
12851290
// Back out of header creation surface exeception in user code
@@ -1289,18 +1294,22 @@ private void RejectNonBodyTransferEncodingResponse(bool appCompleted)
12891294
else
12901295
{
12911296
ReportApplicationError(ex);
1297+
12921298
// 500 Internal Server Error
1293-
ErrorResetHeadersToDefaults(statusCode: 500);
1299+
SetErrorResponseHeaders(statusCode: 500);
12941300
}
12951301
}
12961302

1297-
private void ErrorResetHeadersToDefaults(int statusCode)
1303+
private void SetErrorResponseHeaders(int statusCode)
12981304
{
1299-
// Setting status code will throw if response has already started
1300-
if (!HasResponseStarted)
1305+
Debug.Assert(!HasResponseStarted, $"{nameof(SetErrorResponseHeaders)} called after response had already started.");
1306+
1307+
StatusCode = statusCode;
1308+
ReasonPhrase = null;
1309+
1310+
if (FrameResponseHeaders == null)
13011311
{
1302-
StatusCode = statusCode;
1303-
ReasonPhrase = null;
1312+
InitializeHeaders();
13041313
}
13051314

13061315
var responseHeaders = FrameResponseHeaders;
@@ -1325,8 +1334,8 @@ public void HandleNonBodyResponseWrite(int count)
13251334
}
13261335
else
13271336
{
1328-
// Throw Exception for 101, 204, 205, 304 responses.
1329-
BadHttpResponse.ThrowException(ResponseRejectionReasons.WriteToNonBodyResponse, StatusCode);
1337+
// Throw Exception for 204, 205, 304 responses.
1338+
throw new InvalidOperationException($"Write to non-body {StatusCode} response.");
13301339
}
13311340
}
13321341

@@ -1365,7 +1374,11 @@ public void SetBadRequestState(RequestRejectionReason reason)
13651374

13661375
public void SetBadRequestState(BadHttpRequestException ex)
13671376
{
1368-
ErrorResetHeadersToDefaults(statusCode: ex.StatusCode);
1377+
// Setting status code will throw if response has already started
1378+
if (!HasResponseStarted)
1379+
{
1380+
SetErrorResponseHeaders(statusCode: ex.StatusCode);
1381+
}
13691382

13701383
_keepAlive = false;
13711384
_requestProcessingStopping = true;

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/FrameHeaders.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ StringValues IHeaderDictionary.this[string key]
2929
{
3030
if (_isReadOnly)
3131
{
32-
BadHttpResponse.ThrowException(ResponseRejectionReasons.HeadersReadonlyResponseStarted);
32+
ThrowHeadersReadOnlyException();
3333
}
3434
SetValueFast(key, value);
3535
}
@@ -48,6 +48,11 @@ StringValues IDictionary<string, StringValues>.this[string key]
4848
}
4949
}
5050

51+
protected void ThrowHeadersReadOnlyException()
52+
{
53+
throw new InvalidOperationException("Headers are read-only, response has already started.");
54+
}
55+
5156
protected void ThrowArgumentException()
5257
{
5358
throw new ArgumentException();
@@ -139,7 +144,7 @@ void IDictionary<string, StringValues>.Add(string key, StringValues value)
139144
{
140145
if (_isReadOnly)
141146
{
142-
BadHttpResponse.ThrowException(ResponseRejectionReasons.HeadersReadonlyResponseStarted);
147+
ThrowHeadersReadOnlyException();
143148
}
144149
AddValueFast(key, value);
145150
}
@@ -148,7 +153,7 @@ void ICollection<KeyValuePair<string, StringValues>>.Clear()
148153
{
149154
if (_isReadOnly)
150155
{
151-
BadHttpResponse.ThrowException(ResponseRejectionReasons.HeadersReadonlyResponseStarted);
156+
ThrowHeadersReadOnlyException();
152157
}
153158
ClearFast();
154159
}
@@ -195,7 +200,7 @@ bool IDictionary<string, StringValues>.Remove(string key)
195200
{
196201
if (_isReadOnly)
197202
{
198-
BadHttpResponse.ThrowException(ResponseRejectionReasons.HeadersReadonlyResponseStarted);
203+
ThrowHeadersReadOnlyException();
199204
}
200205
return RemoveFast(key);
201206
}

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ResponseRejectionReasons.cs

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)