Skip to content

Align HttpListener.GetContext() exception across platforms for Stop/Abort/Close#123360

Merged
wfurt merged 9 commits intomainfrom
copilot/fix-http-listener-exception
Mar 4, 2026
Merged

Align HttpListener.GetContext() exception across platforms for Stop/Abort/Close#123360
wfurt merged 9 commits intomainfrom
copilot/fix-http-listener-exception

Conversation

Copy link
Contributor

Copilot AI commented Jan 19, 2026

Description

When HttpListener.GetContext() is called after Stop(), Abort(), or Close(), Windows throws HttpListenerException with ErrorCode = 995 (SocketError.OperationAborted), but Linux throws ErrorCode = 500 (HttpStatusCode.InternalServerError). This breaks cross-platform code that handles these scenarios.

Changes

ListenerAsyncResult.Managed.cs

  • Changed error code from HttpStatusCode.InternalServerError to SocketError.OperationAborted when ObjectDisposedException is caught during GetContext() (line 71)
  • Added using System.Net.Sockets; directive

HttpListenerTests.cs

  • Added parameterized test using xUnit's [Theory] and [InlineData] to verify error code is 995 on all platforms for all termination scenarios (Stop, Abort, Close)
  • Test method: GetContext_TerminationMethodCalled_ThrowsHttpListenerExceptionWithOperationAborted
  • Used TaskCompletionSource for proper test synchronization to avoid race conditions in CI where termination could be called before GetContext() enters its blocking state

Code Path Analysis

  • On Linux, all three termination methods (Stop(), Abort(), Close()) call Cleanup() which creates a single ObjectDisposedException("listener") for pending operations
  • On Windows, all three methods close the request queue handle, causing the OS to return ERROR_OPERATION_ABORTED
  • The _inGet flag ensures the fix only affects synchronous GetContext() calls, not async BeginGetContext/EndGetContext

Example

var listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8080/");
listener.Start();

var task = Task.Run(() => listener.GetContext());
listener.Stop(); // or Abort() or Close()

try 
{
    task.Wait();
}
catch (AggregateException ex) when (ex.InnerException is HttpListenerException hle)
{
    // Before: hle.ErrorCode == 500 on Linux, 995 on Windows
    // After:  hle.ErrorCode == 995 on both platforms (all scenarios)
    Assert.Equal((int)SocketError.OperationAborted, hle.ErrorCode);
}

Microsoft Reviewers

[CC @wfurt as requested]

Checklist

  • Code builds and tests pass locally
  • Added/updated tests for the changes
  • Follows existing code style and conventions
  • Documentation updated if needed
Original prompt

This section details on the original issue you should resolve

<issue_title>System.Net.HttpListener.GetContext() throws HttpListenerException with ErrorCode=HttpStatusCode.InternalServerError on Linux after Stop()</issue_title>
<issue_description>### Description

When System.Net.HttpListener.GetContext() is called after System.Net.HttpListener.Stop() on Linux, HttpListenerException with ErrorCode=HttpStatusCode.InternalServerError is thrown.

On Windows it's ErrorCode=SocketError.OperationAborted, and imo they should be aligned for all OSes.

Here is non-Windows code that throws:

if (_inGet && (exc is ObjectDisposedException))
_exception = new HttpListenerException((int)HttpStatusCode.InternalServerError, SR.net_listener_close);

Reproduction Steps

.

Expected behavior

Same HttpListenerException.ExitCode on all OSes or System.InvalidOperationException, as API declares:

https://learn.microsoft.com/en-us/dotnet/api/system.net.httplistener.getcontext?view=net-10.0#exceptions

InvalidOperationException
This object has not been started or is currently stopped.

Actual behavior

When System.Net.HttpListener.GetContext() is called after System.Net.HttpListener.Stop() on Linux, HttpListenerException with ErrorCode=HttpStatusCode.InternalServerError is thrown.

On Windows it's ErrorCode=SocketError.OperationAborted.

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response</issue_description>

<agent_instructions>can you look into this @copilot and also write a test.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@wfurt seems like good cleanup.

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

…r Stop()

Change the error code from HttpStatusCode.InternalServerError (500) to
SocketError.OperationAborted (995) when GetContext() is called after Stop()
on Linux to match Windows behavior.

Add test to verify the error code is consistent across platforms.

Co-authored-by: wfurt <14356188+wfurt@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix HttpListener exception handling on Linux Align HttpListener.GetContext() exception across platforms when called after Stop() Jan 19, 2026
Copilot AI requested a review from wfurt January 19, 2026 20:04
@stephentoub
Copy link
Member

/azp list

@azure-pipelines
Copy link

CI/CD Pipelines for this repository:

@stephentoub
Copy link
Member

/azp run runtime-libraries-coreclr outerloop-linux

@stephentoub stephentoub marked this pull request as ready for review January 20, 2026 02:36
Copilot AI review requested due to automatic review settings January 20, 2026 02:36
@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@stephentoub
Copy link
Member

/azp run runtime-libraries-coreclr outerloop-windows

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@stephentoub stephentoub enabled auto-merge (squash) January 20, 2026 02:40
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 PR aligns the exception behavior of HttpListener.GetContext() across platforms. Previously, when called after Stop(), Windows threw HttpListenerException with ErrorCode = 995 (SocketError.OperationAborted), while Linux threw ErrorCode = 500 (HttpStatusCode.InternalServerError). The fix ensures both platforms consistently use SocketError.OperationAborted.

Changes:

  • Updated Linux implementation to throw HttpListenerException with SocketError.OperationAborted error code (995) instead of HttpStatusCode.InternalServerError (500)
  • Added test to verify cross-platform consistency of error code

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
ListenerAsyncResult.Managed.cs Changed error code from HttpStatusCode.InternalServerError to SocketError.OperationAborted when ObjectDisposedException is caught; added required using directive
HttpListenerTests.cs Added test to verify GetContext() throws HttpListenerException with error code 995 when called after Stop()

Use TaskCompletionSource to ensure GetContext() is called before Stop()
to avoid race condition where Stop() could be called first, causing
InvalidOperationException instead of HttpListenerException.

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
auto-merge was automatically disabled January 20, 2026 02:49

Head branch was pushed to by a user without write access

Copilot AI requested a review from stephentoub January 20, 2026 02:51
Add comprehensive tests to verify that GetContext() throws HttpListenerException
with SocketError.OperationAborted for all termination scenarios: Stop(), Abort(),
and Close(). All three methods call Cleanup() which creates ObjectDisposedException
for pending async operations, which is now consistently converted to OperationAborted.

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title Align HttpListener.GetContext() exception across platforms when called after Stop() Align HttpListener.GetContext() exception across platforms for Stop/Abort/Close Jan 20, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @karelz, @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Copy link
Member

@rzikm rzikm left a comment

Choose a reason for hiding this comment

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

LGTM

Consolidated three nearly identical tests into a single parameterized test
using xUnit's [Theory] and [InlineData] attributes to test Stop(), Abort(),
and Close() scenarios with the same logic.

Co-authored-by: wfurt <14356188+wfurt@users.noreply.github.com>
Copy link
Member

@wfurt wfurt left a comment

Choose a reason for hiding this comment

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

LGTM

@wfurt wfurt merged commit e4c1aa0 into main Mar 4, 2026
87 of 90 checks passed
@wfurt wfurt deleted the copilot/fix-http-listener-exception branch March 4, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Net.HttpListener.GetContext() throws HttpListenerException with ErrorCode=HttpStatusCode.InternalServerError on Linux after Stop()

5 participants