Skip to content

Commit ec46abd

Browse files
authored
Fix process leaks (#82)
1 parent fa3b627 commit ec46abd

File tree

5 files changed

+203
-41
lines changed

5 files changed

+203
-41
lines changed

lib/PuppeteerSharp.Tests/Puppeteer/PuppeteerLaunchTests.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
namespace PuppeteerSharp.Tests.Puppeteer
1010
{
1111
[Collection("PuppeteerLoaderFixture collection")]
12-
public class PuppeteerLaunchTests
12+
public class PuppeteerLaunchTests : PuppeteerBaseTest
1313
{
1414
[Fact]
1515
public async Task ShouldSupportIgnoreHTTPSErrorsOption()
@@ -94,11 +94,12 @@ public async Task UserDataDirOption()
9494
var options = TestConstants.DefaultBrowserOptions();
9595
options.UserDataDir = userDataDir;
9696

97-
var browser = await PuppeteerSharp.Puppeteer.LaunchAsync(options, TestConstants.ChromiumRevision);
97+
var launcher = new Launcher();
98+
var browser = await launcher.LaunchAsync(options, TestConstants.ChromiumRevision);
9899
Assert.True(Directory.GetFiles(userDataDir).Length > 0);
99100
await browser.CloseAsync();
100101
Assert.True(Directory.GetFiles(userDataDir).Length > 0);
101-
// Directory.Delete(userDataDir, true);
102+
await launcher.TryDeleteUserDataDir();
102103
}
103104

104105
[Fact]
@@ -108,11 +109,12 @@ public async Task UserDataDirArgument()
108109
var options = TestConstants.DefaultBrowserOptions();
109110
options.Args = options.Args.Concat(new[] { $"--user-data-dir={userDataDir}" }).ToArray();
110111

111-
var browser = await PuppeteerSharp.Puppeteer.LaunchAsync(options, TestConstants.ChromiumRevision);
112+
var launcher = new Launcher();
113+
var browser = await launcher.LaunchAsync(options, TestConstants.ChromiumRevision);
112114
Assert.True(Directory.GetFiles(userDataDir).Length > 0);
113115
await browser.CloseAsync();
114116
Assert.True(Directory.GetFiles(userDataDir).Length > 0);
115-
// Directory.Delete(userDataDir, true);
117+
await launcher.TryDeleteUserDataDir();
116118
}
117119

118120
[Fact]
@@ -121,5 +123,21 @@ public void ShouldReturnTheDefaultChromeArguments()
121123
var args = PuppeteerSharp.Puppeteer.DefaultArgs;
122124
Assert.Contains("--no-first-run", args);
123125
}
126+
127+
[Fact]
128+
public async Task ChromeShouldBeClosed()
129+
{
130+
var options = TestConstants.DefaultBrowserOptions();
131+
var launcher = new Launcher();
132+
var browser = await launcher.LaunchAsync(options, TestConstants.ChromiumRevision);
133+
var page = await browser.NewPageAsync();
134+
135+
var response = await page.GoToAsync("https://www.google.com");
136+
Assert.Equal(response.Status.ToString(), "OK");
137+
138+
await browser.CloseAsync();
139+
140+
Assert.True(launcher.IsChromeClosed);
141+
}
124142
}
125143
}

lib/PuppeteerSharp.Tests/TestConstants.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public static class TestConstants
1616
Headless = Convert.ToBoolean(Environment.GetEnvironmentVariable("HEADLESS") ?? "true"),
1717
Args = new[] { "--no-sandbox" },
1818
Timeout = 0,
19-
KeepAliveInterval = 120
19+
KeepAliveInterval = 120,
20+
LogProcess = true
2021
};
2122
}
2223
}

lib/PuppeteerSharp/Connection.cs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public Connection(string url, int delay, ClientWebSocket ws)
3434
private Dictionary<string, Session> _sessions;
3535
private bool _closed = false;
3636
private TaskCompletionSource<bool> _connectionCloseTask;
37+
38+
private bool _closeMessageSent;
39+
private const string CloseMessage = "Browser.close";
3740
#endregion
3841

3942
#region Properties
@@ -59,6 +62,14 @@ public async Task<dynamic> SendAsync(string method, dynamic args = null)
5962
var buffer = new ArraySegment<Byte>(encoded, 0, encoded.Length);
6063
QueueId(id);
6164
await WebSocket.SendAsync(buffer, WebSocketMessageType.Text, true, default(CancellationToken));
65+
66+
//I don't know if this will be the final solution.
67+
//For now this will prevent the WebSocket from failing after the process is killed by the close method.
68+
if (method == CloseMessage)
69+
{
70+
_closeMessageSent = true;
71+
}
72+
6273
return await _responses[id].Task;
6374
}
6475

@@ -80,13 +91,16 @@ public async Task<Session> CreateSession(string targetId)
8091

8192
private void OnClose()
8293
{
83-
_closed = true;
84-
_connectionCloseTask.SetResult(true);
94+
if (!_closed)
95+
{
96+
_closed = true;
97+
_connectionCloseTask.SetResult(true);
8598

86-
Closed?.Invoke(this, new EventArgs());
99+
Closed?.Invoke(this, new EventArgs());
87100

88-
_responses.Clear();
89-
_sessions.Clear();
101+
_responses.Clear();
102+
_sessions.Clear();
103+
}
90104
}
91105

92106
/// <summary>
@@ -102,6 +116,7 @@ private async Task<object> GetResponseAsync()
102116
{
103117
if (_closed)
104118
{
119+
OnClose();
105120
return null;
106121
}
107122

@@ -119,10 +134,23 @@ await Task.WhenAny(
119134

120135
if (_closed)
121136
{
137+
OnClose();
122138
return null;
123139
}
124140

125-
var result = socketTask.Result;
141+
WebSocketReceiveResult result = null;
142+
try
143+
{
144+
result = socketTask.Result;
145+
}
146+
catch (AggregateException) when (_closeMessageSent)
147+
{
148+
if (!_closed)
149+
{
150+
OnClose();
151+
return null;
152+
}
153+
}
126154

127155
endOfMessage = result.EndOfMessage;
128156

lib/PuppeteerSharp/LaunchOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,7 @@ public class LaunchOptions
2828
public bool Devtools { get; set; }
2929

3030
public int KeepAliveInterval { get; set; } = 30;
31+
32+
public bool LogProcess { get; set; }
3133
}
3234
}

0 commit comments

Comments
 (0)