Skip to content

Commit

Permalink
add state checking in fastcgi record handler
Browse files Browse the repository at this point in the history
  • Loading branch information
funcmike committed Aug 26, 2024
1 parent a9741e6 commit ae076b5
Showing 1 changed file with 29 additions and 8 deletions.
37 changes: 29 additions & 8 deletions src/ReverseProxy/Forwarder/FastCgiHttpMessageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ private static async ValueTask FlushAsync(ConnectionContext connectionContext, C
private interface FastCgiRecordHandler
{
bool TryOnFastCgiRecord(ref FastCgiRecord fastCgiRecord);
void EndOfData();
}

private interface IFastCgiContentDataWriter
Expand All @@ -225,6 +226,7 @@ public static async Task ProcessAsync(FastCgiRecordHandler handler, ConnectionCo
{
if (buffer.Length == 0)
{
handler.EndOfData();
return;
}

Expand Down Expand Up @@ -340,10 +342,17 @@ private sealed class HttpResponseReaderFastCgiRecordHandler : FastCgiRecordHandl
private readonly HttpResponseReader _reader;
private readonly ILogger _logger;

private bool _headersDone;
private StateType _state = StateType.Headers;
private RentedMemorySegment<byte>? _start;
private RentedMemorySegment<byte>? _end;

private enum StateType
{
Headers,
Body,
Finished,
}

public HttpResponseReaderFastCgiRecordHandler(HttpRequestMessage request, ILogger logger)
{
Result = new HttpResponseMessage { RequestMessage = request, StatusCode = HttpStatusCode.OK };
Expand All @@ -355,7 +364,7 @@ public bool TryOnFastCgiRecord(ref FastCgiRecord fastCgiRecord)
{
switch (fastCgiRecord.Header.Type)
{
case FastCgiRecordHeader.RecordType.Stdout when _headersDone:
case FastCgiRecordHeader.RecordType.Stdout when _state == StateType.Body:
{
// TODO: Idea to optimize it further.
// After headers are done it could act as stream and lazy parse rest of the data on the fly.
Expand All @@ -382,12 +391,12 @@ public bool TryOnFastCgiRecord(ref FastCgiRecord fastCgiRecord)

return true;
}
case FastCgiRecordHeader.RecordType.Stdout:
case FastCgiRecordHeader.RecordType.Stdout when _state == StateType.Headers:
{
var reader = new SequenceReader<byte>(new ReadOnlySequence<byte>(fastCgiRecord.ContentData.Memory));
if (_reader.ParseHttpHeaders(ref reader))
{
_headersDone = true;
_state = StateType.Body;

var left = fastCgiRecord.ContentData.Memory.Slice((int)reader.Consumed);
if (left.Length > 0)
Expand All @@ -408,7 +417,7 @@ public bool TryOnFastCgiRecord(ref FastCgiRecord fastCgiRecord)
fastCgiRecord.Dispose();
return true;
}
case FastCgiRecordHeader.RecordType.EndRequest:
case FastCgiRecordHeader.RecordType.EndRequest when _state == StateType.Body:
{
HttpContent content;
if (_start is null)
Expand All @@ -433,20 +442,28 @@ public bool TryOnFastCgiRecord(ref FastCgiRecord fastCgiRecord)
Result.Content = content;

fastCgiRecord.Dispose();
_state = StateType.Finished;
return false;
}
default:
{
Result.StatusCode = HttpStatusCode.InternalServerError;
Result.Content = new StringContent($"received unexpected fastcgi record type: {fastCgiRecord.Header.Type}");
fastCgiRecord.Dispose();
_start?.Dispose();
_end?.Dispose();
return false;
_logger.LogDebug(message: "received unexpected fastcgi record {recordType}", fastCgiRecord.Header.Type);
throw new BadFastCgiResponseException(FastCgiCoreExpStrings.BadResponse_UnexpectedRecord, FastCgiResponseRejectionReason.UnexpectedRecord);
}
}
}

public void EndOfData()
{

if (_state == StateType.Finished) { return;}
_logger.LogDebug(message: "response finished when in wrong {state}", _state);
throw new BadFastCgiResponseException(FastCgiCoreExpStrings.BadResponse_EndRequestNotReceived, FastCgiResponseRejectionReason.EndRequestNotReceived);
}

private sealed class HttpResponseReader(HttpResponseMessage response) : IHttpHeadersHandler, IHttpRequestLineHandler
{
private readonly HttpParser<HttpResponseReader> _httpParser = new();
Expand Down Expand Up @@ -802,6 +819,8 @@ private enum FastCgiResponseRejectionReason
UnrecognizedFastCgiVersion,
UnrecognizedRequestType,
UnrecognizedStatusCode,
UnexpectedRecord,
EndRequestNotReceived,
IncompleteRecord,
}

Expand All @@ -822,6 +841,8 @@ private static class FastCgiCoreExpStrings
internal static readonly string BadResponse_UnrecognizedFastCgiVersion = "BadResponse_UnrecognizedFastCgiVersion";
internal static readonly string BadResponse_UnrecognizedRequestType = "BadResponse_UnrecognizedRequestType";
internal static readonly string BadResponse_UnrecognizedStatusCode = "BadResponse_UnrecognizedStatusCode";
internal static readonly string BadResponse_UnexpectedRecord = "BadResponse_UnexpectedRecord";
internal static readonly string BadResponse_EndRequestNotReceived = "BadResponse_EndRequestNotReceived";
internal static readonly string BadResponse_IncompleteRecord = "BadResponse_IncompleteRecord";
}

Expand Down

0 comments on commit ae076b5

Please sign in to comment.