selenium
selenium copied to clipboard
[🐛 Bug]: "System.IO.IOException : Cannot access a closed stream" when disposing DevToolsSession
What happened?
This bug happens pretty rarely, but it does. I ran the following test with [Repeat(1000)]
to reproduce it:
using System.Collections.Concurrent;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.DevTools;
using OpenQA.Selenium.DevTools.V127;
using OpenQA.Selenium.DevTools.V127.Network;
using OpenQA.Selenium.DevTools.V127.Fetch;
using EnableCommandSettings = OpenQA.Selenium.DevTools.V127.Fetch.EnableCommandSettings;
using RequestPattern = OpenQA.Selenium.DevTools.V127.Fetch.RequestPattern;
namespace TestProject2;
internal class DevToolsIssue
{
private static bool _disposing;
private static int _repeatCounter;
[Test, Repeat(1000)]
public void EventHandlersCanRunAfterDevToolsIsDisposed()
{
TestContext.Progress.WriteLine($"Repeat={_repeatCounter++}");
_disposing = false;
var executingHandlers = new CountdownEvent(1);
using var driver = new ChromeDriver();
var caughtExceptions = new ConcurrentQueue<Exception>();
using (var devToolsSession = driver.GetDevToolsSession(new DevToolsOptions { ProtocolVersion = 127 }))
{
var networkAdapter = new NetworkAdapter(devToolsSession);
var fetch = new FetchAdapter(devToolsSession);
var network = new V127Network(networkAdapter, fetch);
var enableCommandSettings = new EnableCommandSettings
{
Patterns = new RequestPattern[]
{
new()
{
RequestStage = RequestStage.Request,
UrlPattern = "*"
}
}
};
fetch.Enable(enableCommandSettings);
network.RequestPaused += async (_, args) =>
{
if (!executingHandlers.TryAddCount())
return;
try
{
// Do some stuff...
await TestContext.Progress.WriteLineAsync($"ContinueRequestWithoutModification when _disposing={_disposing}");
await network.ContinueRequestWithoutModification(args.RequestData);
}
catch (Exception ex)
{
caughtExceptions.Enqueue(ex);
}
finally
{
executingHandlers.Signal();
}
};
driver.Url = "https://www.google.com";
executingHandlers.Signal();
executingHandlers.Wait();
} // Disposing DevToolsSession
if (!caughtExceptions.IsEmpty)
throw new AggregateException(caughtExceptions);
}
}
And I got the following exception:
System.IO.IOException : Cannot access a closed stream.
at System.Net.Http.HttpConnection.RawConnectionStream.WriteAsync(ReadOnlyMemory`1 buffer, CancellationToken cancellationToken)
at System.Net.WebSockets.ManagedWebSocket.SendFrameLockAcquiredNonCancelableAsync(MessageOpcode opcode, Boolean endOfMessage, Boolean disableCompression, ReadOnlyMemory`1 payloadBuffer)
at System.Net.WebSockets.ManagedWebSocket.SendFrameAsync(MessageOpcode opcode, Boolean endOfMessage, Boolean disableCompression, ReadOnlyMemory`1 payloadBuffer, CancellationToken cancellationToken)
at System.Net.WebSockets.ManagedWebSocket.SendCloseFrameAsync(WebSocketCloseStatus closeStatus, String closeStatusDescription, CancellationToken cancellationToken)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at System.Net.WebSockets.ManagedWebSocket.SendCloseFrameAsync(WebSocketCloseStatus closeStatus, String closeStatusDescription, CancellationToken cancellationToken)
at System.Net.WebSockets.ManagedWebSocket.CloseOutputAsyncCore(WebSocketCloseStatus closeStatus, String statusDescription, CancellationToken cancellationToken)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at System.Net.WebSockets.ManagedWebSocket.CloseOutputAsyncCore(WebSocketCloseStatus closeStatus, String statusDescription, CancellationToken cancellationToken)
at System.Net.WebSockets.ManagedWebSocket.CloseOutputAsync(WebSocketCloseStatus closeStatus, String statusDescription, CancellationToken cancellationToken)
at System.Net.WebSockets.ClientWebSocket.CloseOutputAsync(WebSocketCloseStatus closeStatus, String statusDescription, CancellationToken cancellationToken)
at OpenQA.Selenium.DevTools.WebSocketConnection.ReceiveData()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining)
at System.Threading.Tasks.Task.RunContinuations(Object continuationObject)
at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
at System.Threading.Tasks.ValueTask`1.ValueTaskSourceAsTask.<>c.<.cctor>b__4_0(Object state)
at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TResult](Memory`1 payloadBuffer, CancellationToken cancellationToken)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.WebSockets.ManagedWebSocket.EnsureBufferContainsAsync(Int32 minimumRequiredBytes, CancellationToken cancellationToken, Boolean throwOnPrematureClosure)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining)
at System.Threading.Tasks.Task.RunContinuations(Object continuationObject)
at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
at System.Net.Http.HttpConnection.RawConnectionStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining)
at System.Threading.Tasks.Task.RunContinuations(Object continuationObject)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetExistingTaskResult(Task`1 task, TResult result)
at System.Net.Http.HttpConnection.ReadBufferedAsyncCore(Memory`1 destination)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.InvokeContinuation(Action`1 continuation, Object state, Boolean forceAsync, Boolean requiresExecutionContextFlow)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.OnCompleted(SocketAsyncEventArgs _)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pNativeOverlapped)
--- End of stack trace from previous location ---
at System.Net.WebSockets.ManagedWebSocket.SendCloseFrameAsync(WebSocketCloseStatus closeStatus, String closeStatusDescription, CancellationToken cancellationToken)
at System.Net.WebSockets.ManagedWebSocket.CloseOutputAsyncCore(WebSocketCloseStatus closeStatus, String statusDescription, CancellationToken cancellationToken)
at OpenQA.Selenium.DevTools.WebSocketConnection.ReceiveData()
at OpenQA.Selenium.DevTools.WebSocketConnection.<Start>b__22_0()
at OpenQA.Selenium.DevTools.WebSocketConnection.Stop()
at OpenQA.Selenium.DevTools.DevToolsSession.TerminateSocketConnection()
at OpenQA.Selenium.DevTools.DevToolsSession.<Dispose>b__43_0()
at OpenQA.Selenium.DevTools.DevToolsSession.Dispose(Boolean disposing)
at OpenQA.Selenium.DevTools.DevToolsSession.Dispose()
at TestProject2.DevToolsIssue.EventHandlersCanRunAfterDevToolsIsDisposed()
How can we reproduce the issue?
Just paste the above code to a new nUnit project and run the test.
Relevant log output
14:19:20.338 TRACE SeleniumManager: Driver path: chromedriver.EXE
14:19:20.338 TRACE SeleniumManager: Browser path: C:\Program Files\Google\Chrome\Application\chrome.exe
14:19:20.864 DEBUG HttpCommandExecutor: Executing command: []: newSession {"capabilities":{"firstMatch":[{"browserName":"chrome","goog:chromeOptions":{"binary":"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"}}]}}
14:19:20.864 TRACE HttpCommandExecutor: >> Method: POST, RequestUri: 'http://localhost:52938/session', Version: 1.1, Content: System.Net.Http.ByteArrayContent, Headers:
{
Accept: application/json; charset=utf-8
User-Agent: selenium/4.23.0
User-Agent: (.net windows)
Content-Type: application/json; charset=utf-8
Content-Length: 151
}
{"capabilities":{"firstMatch":[{"browserName":"chrome","goog:chromeOptions":{"binary":"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"}}]}}
14:19:21.677 TRACE HttpCommandExecutor: << StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Cache-Control: no-cache
Content-Length: 883
Content-Type: application/json; charset=utf-8
}
14:19:21.678 DEBUG HttpCommandExecutor: Response: (3eb6914a3922045bf8e623a3e8dc17bc Success: System.Collections.Generic.Dictionary`2[System.String,System.Object])
14:19:25.763 DEBUG HttpCommandExecutor: Executing command: [3eb6914a3922045bf8e623a3e8dc17bc]: get {"url":"https://www.google.com"}
14:19:25.763 TRACE HttpCommandExecutor: >> Method: POST, RequestUri: 'http://localhost:52938/session/3eb6914a3922045bf8e623a3e8dc17bc/url', Version: 1.1, Content: System.Net.Http.ByteArrayContent, Headers:
{
Accept: application/json; charset=utf-8
User-Agent: selenium/4.23.0
User-Agent: (.net windows)
Content-Type: application/json; charset=utf-8
Content-Length: 32
}
{"url":"https://www.google.com"}
14:19:26.371 TRACE HttpCommandExecutor: << StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Cache-Control: no-cache
Content-Length: 14
Content-Type: application/json; charset=utf-8
}
14:19:26.371 DEBUG HttpCommandExecutor: Response: ( Success: )
14:19:26.388 DEBUG HttpCommandExecutor: Executing command: [3eb6914a3922045bf8e623a3e8dc17bc]: quit {}
14:19:26.389 TRACE HttpCommandExecutor: >> Method: DELETE, RequestUri: 'http://localhost:52938/session/3eb6914a3922045bf8e623a3e8dc17bc', Version: 1.1, Content: <null>, Headers:
{
User-Agent: selenium/4.23.0
User-Agent: (.net windows)
Accept: application/json
Accept: image/png
}
14:19:26.491 TRACE HttpCommandExecutor: << StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Cache-Control: no-cache
Content-Length: 14
Content-Type: application/json; charset=utf-8
}
14:19:26.491 DEBUG HttpCommandExecutor: Response: ( Success: )
Operating System
Windows 11
Selenium version
C# 11 (dotnet 8), Selenium.WebDriver 4.23.0
What are the browser(s) and version(s) where you see this issue?
Chrome 128
What are the browser driver(s) and version(s) where you see this issue?
ChromeDriver 128.0.6613.8600
Are you using Selenium Grid?
No response