Hosting the server
editStart the server
PlaywrightServer listens on a TCP port of its own. StartAsync starts it and WsEndpoint is the address the clients connect to:
await using var server = new PlaywrightServer(); // no DocumentLoader: the pages use page.SetContentAsync() and the routes
await server.StartAsync();
using var playwright = await Microsoft.Playwright.Playwright.CreateAsync();
await using var browser = await playwright.Chromium.ConnectAsync(server.WsEndpoint.ToString());
var page = await browser.NewPageAsync();
await page.SetContentAsync("""
<label for=name>Name</label><input id=name>
<button onclick="document.querySelector('output').textContent = 'Hello ' + document.querySelector('input').value">Greet</button>
<output></output>
""");
await page.GetByLabel("Name").FillAsync("World");
await page.GetByRole(AriaRole.Button, new() { Name = "Greet" }).ClickAsync();
await Assertions.Expect(page.Locator("output")).ToHaveTextAsync("Hello World");
var screenshot = await page.ScreenshotAsync();| PlaywrightServerOptions | Default | Effect |
|---|---|---|
| BindAddress, Port, Path | loopback, a free port, / | Where the server listens |
| DocumentLoader | none | The network requests of the pages (see below) |
| ScriptEngineOptions | the default limits | The limits of the scripts |
| UseSystemFonts | false | The pages can use the fonts installed on the machine |
| BrowserName | chromium | The name of the browser reported to the clients |
| AllowAnyClientVersion | false | Accepts the clients of another version of Playwright |
| MaxConnections | none | The number of clients connected at the same time |
Let the pages use the network
By default, nothing is fetched: the pages only use the content of SetContentAsync() and the responses of the routes (page.RouteAsync()). Set a DocumentLoader to let them load documents and resources:
await using var server = new PlaywrightServer(new PlaywrightServerOptions
{
DocumentLoader = DocumentLoader.Create(), // the pages can load any URL
});
await server.StartAsync();
using var playwright = await Microsoft.Playwright.Playwright.CreateAsync();
await using var browser = await playwright.Chromium.ConnectAsync(server.WsEndpoint.ToString());
var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com/");
await Assertions.Expect(page.GetByRole(AriaRole.Heading, new() { Level = 1 })).ToHaveTextAsync("Example Domain");
await page.ScreenshotAsync(new() { Path = "example.png", FullPage = true });The requests of the pages go through the loader: filter them as for any page (see Network requests). The data: and blob: URLs are not reported to the clients, and the bodies of the responses are buffered.
Host the server in ASP.NET Core
An application can give the server the WebSockets it accepts, instead of a port of its own:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
await using var server = new PlaywrightServer(new PlaywrightServerOptions { DocumentLoader = DocumentLoader.Create() });
app.UseWebSockets();
app.Map("/playwright", async context =>
{
// The protocol changes with each minor version of Playwright: the clients must use the version of the server
if (!context.WebSockets.IsWebSocketRequest || !PlaywrightServer.IsCompatibleClient(context.Request.Headers.UserAgent))
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await server.HandleConnectionAsync(webSocket, context.RequestAborted);
});
await app.RunAsync();PlaywrightServer.IsCompatibleClient checks the version of the client from its User-Agent header before the connection is accepted.
Security
The scripts of the pages run in the process of the server, and a client can make the server request any URL that the DocumentLoader allows. Expose the server to trusted clients only: the default BindAddress is the loopback address.