Skip to content

Conversation

@adityamandaleeka
Copy link
Member

@adityamandaleeka adityamandaleeka commented Jan 29, 2026

Introduces a non-throwing code path for HTTP/1.1 request parsing. Instead of using try/catch to handle malformed requests, the parser now returns a result struct indicating success, incomplete, or error states.

Exception handling is expensive, and in scenarios with many malformed requests (port scanning, malicious traffic, misconfigured clients), throwing BadHttpRequestException on every parse failure added unnecessary overhead. This eliminates exceptions from the normal bad-request handling path.

In my testing using a client sending bad requests, throughput is up ~20% on an Azure Linux VM. When pinning to 2 cores on my physical machine (to make it CPU bound), I can see RPS increase by over 40%, and a perf trace shows that the time spent in exception handling goes from ~10% to ~0.2%. There's no impact on valid requests.

Copilot AI review requested due to automatic review settings January 29, 2026 02:05
@github-actions github-actions bot added the area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions label Jan 29, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces a non-throwing code path for HTTP/1.1 request parsing to improve performance when handling malformed requests. Instead of relying on exception handling for bad requests (which is expensive), the parser now returns a structured result indicating success, incomplete, or error states.

Changes:

  • Added HttpParseResult struct to represent parsing outcomes without exceptions
  • Implemented non-throwing parser methods (TryParseRequestLine, TryParseHeaders) that return parse results
  • Modified Http1Connection.TryParseRequest to use the non-throwing path and only create exceptions when needed
  • Added tests to verify error offset/length extraction works correctly with multi-segment buffers

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Servers/Kestrel/Core/src/Internal/Http/IHttpParser.cs Adds HttpParseResult struct for non-throwing parse operations
src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs Implements TryParseRequestLine and TryParseHeaders methods with error offset tracking, refactors existing throwing methods to call non-throwing versions
src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs Adds TryParseRequestNoThrow and CreateBadRequestException methods, updates TryParseRequest to use non-throwing path
src/Servers/Kestrel/Core/test/HttpParserTests.cs Adds tests for multi-segment buffer error detail extraction and error offset calculation with consumed bytes
src/Servers/Kestrel/Core/test/Http1/Http1ConnectionTests.cs Updates test to expect BadHttpRequestException instead of InvalidOperationException for invalid headers

@JamesNK
Copy link
Member

JamesNK commented Jan 29, 2026

What is the impact on successful requests?

}
}

#pragma warning disable CS0618 // Type or member is obsolete
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#pragma warning disable CS0618 // Type or member is obsolete

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed because BadHttpRequestException is obsolete.

#pragma warning restore CS0618 // Type or member is obsolete

#pragma warning disable CS0618 // Type or member is obsolete
private void OnBadRequest(ReadOnlySequence<byte> requestData, BadHttpRequestException ex)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking into code more, I spotted that BadHttpRequestException is mostly used for flow-control, and we probably can:

  1. eliminate _requestRejectedException completely (I did not see any usage, where we need Exception -> we only use field to determine if request is already rejected, or to get the ex.StatusCode.

  2. dont even allocate the badRequestException at all, and rely on ErrorReason

BadHttpRequestException being obsoleted is another hint to explore this

/// <summary>
/// Non-throwing version of ParseRequest. Returns HttpParseResult instead of throwing.
/// </summary>
private HttpParseResult TryParseRequestNoThrow(ref SequenceReader<byte> reader)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: not sure we need to name it NoThrow. You are simply improving the code and once it is all non-throwing it will not add more context to the code

// In this case, there is an ongoing request but the start line/header parsing has timed out, so send
// a 408 response.
KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestHeadersTimeout);
var ex = KestrelBadHttpRequestException.GetException(RequestRejectionReason.RequestHeadersTimeout);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're losing the stack trace. Might want to use ExceptionDispatchInfo.SetCurrentStackTrace(ex) to preserve that without throwing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's much value in that, given that the reason is already included. Using SetCurrentStackTrace would add stack walk overhead on each bad request for no benefit.

@adityamandaleeka
Copy link
Member Author

What is the impact on successful requests?

There's no impact on valid requests. Updated original comment to clarify that as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions Perf

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants