Skip to content

Add non-throwing parser path for bad request handling#65256

Merged
adityamandaleeka merged 11 commits intodotnet:mainfrom
adityamandaleeka:nonthrow_parser
Feb 5, 2026
Merged

Add non-throwing parser path for bad request handling#65256
adityamandaleeka merged 11 commits intodotnet:mainfrom
adityamandaleeka:nonthrow_parser

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?

@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.

Copy link
Member

@BrennanConroy BrennanConroy left a comment

Choose a reason for hiding this comment

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

Lots of comments not copied to the Try* methods, could we add some/most/all of them back?

Only looked through Http1Connection, will continue on HttpParser

{
// Record OtherError for unexpected exceptions that escape the parser
KestrelMetrics.AddConnectionEndReason(MetricsContext, ConnectionEndReason.OtherError);
throw;
Copy link
Member

Choose a reason for hiding this comment

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

Should this also set the bad request state, end connection, then return instead of 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.

Maybe... but I think we should keep this as a rethrow. This catch block is for truly unexpected exceptions (OOM, etc.) which we can't "handle" and send a bad request exception, or InvalidOperationException from invalid header characters (null/CR/LF). The outer catch in ProcessRequestsAsync will log them appropriately. We could potentially convert InvalidOperationException to a bad request, but that would be a behavioral change - the current behavior of logging and closing the connection is reasonable.


// Extract error detail from buffer if available, but only when logging is enabled
// to avoid leaking internal details in production.
if (Log.IsEnabled(LogLevel.Information) &&
Copy link
Member

Choose a reason for hiding this comment

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

Some of our exception logic uses HttpParser._showErrorDetails and some of it uses LogLevel.Information. Seems like we're losing that differentiator.

return false;
}

public bool TakeStartLine(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.

Delete method? Also delete TakeMessageHeaders? (will need to update Http1ChunkedEncodingMessageBody)

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do this as followup once this PR is in.

@adityamandaleeka adityamandaleeka merged commit d9712bb into dotnet:main Feb 5, 2026
25 checks passed
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.

5 participants