Yes, Puppeteer-Sharp can handle WebSocket communication in webpages, but it requires using the Chrome DevTools Protocol directly rather than high-level API methods. Puppeteer-Sharp, as a .NET port of Node.js Puppeteer, leverages the Chrome DevTools Protocol which has built-in support for WebSocket monitoring and interaction.
WebSocket Support Overview
While Puppeteer-Sharp doesn't provide dedicated WebSocket methods like page.websocket()
, you can:
- Monitor WebSocket connections and their lifecycle
- Intercept WebSocket frames (sent and received messages)
- Observe connection events (handshake, close, errors)
- Access frame data including payload content and metadata
Basic WebSocket Monitoring
Here's how to set up basic WebSocket monitoring in Puppeteer-Sharp:
using PuppeteerSharp;
using Newtonsoft.Json.Linq;
using System;
using System.Threading.Tasks;
class Program
{
public static async Task Main(string[] args)
{
// Download browser if needed
await new BrowserFetcher().DownloadAsync();
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = false, // Set to true for headless operation
Args = new[] { "--disable-web-security" } // Optional: for testing
});
var page = await browser.NewPageAsync();
// Enable network domain to capture WebSocket events
await page.Client.SendAsync("Network.enable");
// Listen for WebSocket connection events
page.Client.MessageReceived += OnWebSocketEvent;
// Navigate to a page with WebSocket functionality
await page.GoToAsync("wss://echo.websocket.org");
// Wait for interactions
await Task.Delay(10000);
await browser.CloseAsync();
}
private static void OnWebSocketEvent(object sender, MessageEventArgs e)
{
switch (e.MessageID)
{
case "Network.webSocketCreated":
Console.WriteLine($"WebSocket created: {e.MessageData}");
break;
case "Network.webSocketFrameSent":
var sentFrame = e.MessageData.ToObject<WebSocketFrame>();
Console.WriteLine($"Frame sent: {sentFrame.Response.PayloadData}");
break;
case "Network.webSocketFrameReceived":
var receivedFrame = e.MessageData.ToObject<WebSocketFrame>();
Console.WriteLine($"Frame received: {receivedFrame.Response.PayloadData}");
break;
case "Network.webSocketClosed":
Console.WriteLine("WebSocket connection closed");
break;
}
}
}
Advanced WebSocket Interaction
For more sophisticated WebSocket handling, you can create a dedicated WebSocket monitor class:
using PuppeteerSharp;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class WebSocketMonitor
{
private readonly IPage _page;
private readonly List<WebSocketMessage> _messages;
public WebSocketMonitor(IPage page)
{
_page = page;
_messages = new List<WebSocketMessage>();
}
public async Task StartMonitoringAsync()
{
await _page.Client.SendAsync("Network.enable");
_page.Client.MessageReceived += HandleWebSocketEvent;
}
private void HandleWebSocketEvent(object sender, MessageEventArgs e)
{
var timestamp = DateTime.UtcNow;
switch (e.MessageID)
{
case "Network.webSocketCreated":
var created = e.MessageData.ToObject<JObject>();
Console.WriteLine($"[{timestamp}] WebSocket created - URL: {created["url"]}");
break;
case "Network.webSocketFrameSent":
var sentFrame = e.MessageData.ToObject<JObject>();
var sentMessage = new WebSocketMessage
{
Timestamp = timestamp,
Direction = "Sent",
Data = sentFrame["response"]["payloadData"]?.ToString(),
OpCode = sentFrame["response"]["opcode"]?.ToObject<int>() ?? 0
};
_messages.Add(sentMessage);
LogMessage(sentMessage);
break;
case "Network.webSocketFrameReceived":
var receivedFrame = e.MessageData.ToObject<JObject>();
var receivedMessage = new WebSocketMessage
{
Timestamp = timestamp,
Direction = "Received",
Data = receivedFrame["response"]["payloadData"]?.ToString(),
OpCode = receivedFrame["response"]["opcode"]?.ToObject<int>() ?? 0
};
_messages.Add(receivedMessage);
LogMessage(receivedMessage);
break;
case "Network.webSocketFrameError":
var error = e.MessageData.ToObject<JObject>();
Console.WriteLine($"[{timestamp}] WebSocket error: {error["errorMessage"]}");
break;
}
}
private void LogMessage(WebSocketMessage message)
{
var opCodeText = GetOpCodeText(message.OpCode);
Console.WriteLine($"[{message.Timestamp}] {message.Direction} ({opCodeText}): {message.Data}");
}
private string GetOpCodeText(int opCode)
{
return opCode switch
{
1 => "Text",
2 => "Binary",
8 => "Close",
9 => "Ping",
10 => "Pong",
_ => $"Unknown({opCode})"
};
}
public List<WebSocketMessage> GetMessages() => new List<WebSocketMessage>(_messages);
}
public class WebSocketMessage
{
public DateTime Timestamp { get; set; }
public string Direction { get; set; }
public string Data { get; set; }
public int OpCode { get; set; }
}
Usage Example with Real-Time Data
Here's a practical example for monitoring a real-time chat application:
public static async Task MonitorChatApplication()
{
var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = false });
var page = await browser.NewPageAsync();
var monitor = new WebSocketMonitor(page);
await monitor.StartMonitoringAsync();
// Navigate to a chat application
await page.GoToAsync("https://chat-app-example.com");
// Wait for the page to load and WebSocket to connect
await page.WaitForSelectorAsync("#chat-input");
// Send a test message
await page.TypeAsync("#chat-input", "Hello WebSocket!");
await page.ClickAsync("#send-button");
// Monitor for 30 seconds
await Task.Delay(30000);
// Get all captured messages
var messages = monitor.GetMessages();
Console.WriteLine($"Captured {messages.Count} WebSocket messages");
await browser.CloseAsync();
}
Data Models for WebSocket Events
Define proper models to handle WebSocket frame data:
public class WebSocketFrame
{
public WebSocketResponse Response { get; set; }
}
public class WebSocketResponse
{
public string PayloadData { get; set; }
public int OpCode { get; set; }
public bool Mask { get; set; }
}
public class WebSocketCreated
{
public string RequestId { get; set; }
public string Url { get; set; }
public string Initiator { get; set; }
}
Important Considerations
Performance Impact
WebSocket monitoring can generate significant output for busy applications. Consider implementing filtering:
private bool ShouldLogMessage(WebSocketMessage message)
{
// Filter out ping/pong frames for cleaner output
return message.OpCode != 9 && message.OpCode != 10;
}
Security and CORS
Some websites may block WebSocket monitoring due to security policies. You might need to:
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Args = new[]
{
"--disable-web-security",
"--disable-features=VizDisplayCompositor"
}
});
Error Handling
Always implement proper error handling for WebSocket events:
try
{
var data = e.MessageData.ToObject<WebSocketFrame>();
// Process frame data
}
catch (Exception ex)
{
Console.WriteLine($"Error processing WebSocket frame: {ex.Message}");
}
Limitations
- No direct sending: You cannot send WebSocket frames directly through Puppeteer-Sharp
- Read-only monitoring: The API is primarily for observation, not manipulation
- Protocol dependency: Features depend on Chrome DevTools Protocol capabilities
For sending WebSocket messages, you'll need to use JavaScript evaluation:
await page.EvaluateExpressionAsync(@"
if (window.websocket) {
window.websocket.send('Hello from Puppeteer-Sharp!');
}
");
WebSocket handling in Puppeteer-Sharp provides powerful monitoring capabilities for testing real-time applications, debugging WebSocket issues, and automating interactions with WebSocket-based services.