Skip to content
Permalink
Browse files

WebSocketTransport initialization, shutdown and extension (#951)

  • Loading branch information...
ggeurts authored and kblok committed Mar 6, 2019
1 parent 644c8e0 commit 8e61e2cdeb70b4911007ba021a9f0356521e75ad
@@ -16,7 +16,7 @@ only_commits:
- appveyor-fullframework.yml
before_build:
- ps: >-
nuget.exe restore .\lib\PuppeteerSharp.AspNetFramework.sln
dotnet restore .\lib\PuppeteerSharp.AspNetFramework.sln
New-SelfSignedCertificate -Subject "localhost" -FriendlyName "Puppeteer" -CertStoreLocation "cert:\CurrentUser\My"
@@ -1,27 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Hosting;
using Microsoft.Extensions.Logging;
using PuppeteerSharp.Transport;

namespace PuppeteerSharp.AspNetFramework
{
public class AspNetWebSocketTransport : WebSocketTransport
{
#region Static fields

/// <summary>
/// Gets a <see cref="TransportFactory"/> that creates <see cref="AspNetWebSocketTransport"/> instances.
/// </summary>
public static readonly TransportFactory AspNetTransportFactory = CreateAspNetTransport;

/// <summary>
/// Initializes a new instance of the <see cref="PuppeteerSharp.AspNet.AspNetWebSocketTransport"/> class.
/// Gets a <see cref="TransportTaskScheduler"/> that uses ASP.NET <see cref="HostingEnvironment.QueueBackgroundWorkItem(Func{CancellationToken,Task})"/>
/// for scheduling of tasks.
/// </summary>
public AspNetWebSocketTransport() : base(false)
public static readonly TransportTaskScheduler AspNetTransportScheduler = ScheduleBackgroundTask;

#endregion

#region Static methods

private static async Task<IConnectionTransport> CreateAspNetTransport(Uri url, IConnectionOptions connectionOptions, CancellationToken cancellationToken)
{
var webSocketFactory = connectionOptions.WebSocketFactory ?? DefaultWebSocketFactory;
var webSocket = await webSocketFactory(url, connectionOptions, cancellationToken);
return new AspNetWebSocketTransport(webSocket, connectionOptions.EnqueueTransportMessages);
}

public override async Task InitializeAsync(string url, IConnectionOptions connectionOptions, ILoggerFactory loggerFactory = null)
private static void ScheduleBackgroundTask(Func<CancellationToken, Task> taskFactory, CancellationToken cancellationToken)
{
await base.InitializeAsync(url, connectionOptions, loggerFactory).ConfigureAwait(false);
HostingEnvironment.QueueBackgroundWorkItem((cts) => GetResponseAsync());
Task ExecuteAsync(CancellationToken hostingCancellationToken)
=> taskFactory(CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, hostingCancellationToken).Token);
HostingEnvironment.QueueBackgroundWorkItem(ExecuteAsync);
}

#endregion

#region Constructor(s)

/// <inheritdoc />
public AspNetWebSocketTransport(WebSocket client, bool queueRequests)
: base(client, AspNetTransportScheduler, queueRequests)
{ }

#endregion
}
}
@@ -9,7 +9,7 @@
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MicrosoftExtensions.Logging.Xunit" Version="1.0.0" />
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" Condition="'$(TargetFramework).StartsWith(`net4`)' == True" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0005" />
@@ -1,5 +1,7 @@
using System.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
using PuppeteerSharp.Transport;
using Xunit;
using Xunit.Abstractions;

@@ -87,7 +89,7 @@ public async Task ShouldSupportCustomWebSocket()
WebSocketFactory = (uri, socketOptions, cancellationToken) =>
{
customSocketCreated = true;
return Connection.DefaultWebSocketFactory(uri, socketOptions, cancellationToken);
return WebSocketTransport.DefaultWebSocketFactory(uri, socketOptions, cancellationToken);
}
};

@@ -96,5 +98,25 @@ public async Task ShouldSupportCustomWebSocket()
Assert.True(customSocketCreated);
}
}

[Fact]
public async Task ShouldSupportCustomTransport()
{
var customTransportCreated = false;
var options = new ConnectOptions()
{
BrowserWSEndpoint = Browser.WebSocketEndpoint,
TransportFactory = (url, opt, cancellationToken) =>
{
customTransportCreated = true;
return WebSocketTransport.DefaultTransportFactory(url, opt, cancellationToken);
}
};

using (await Puppeteer.ConnectAsync(options, TestConstants.LoggerFactory))
{
Assert.True(customTransportCreated);
}
}
}
}
@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using PuppeteerSharp.Helpers;
using PuppeteerSharp.Transport;
using Xunit;
using Xunit.Abstractions;

@@ -410,7 +409,7 @@ public async Task ShouldSupportCustomWebSocket()
options.WebSocketFactory = (uri, socketOptions, cancellationToken) =>
{
customSocketCreated = true;
return Connection.DefaultWebSocketFactory(uri, socketOptions, cancellationToken);
return WebSocketTransport.DefaultWebSocketFactory(uri, socketOptions, cancellationToken);
};

using (await Puppeteer.LaunchAsync(options, TestConstants.LoggerFactory))
@@ -419,6 +418,23 @@ public async Task ShouldSupportCustomWebSocket()
}
}

[Fact]
public async Task ShouldSupportCustomTransport()
{
var customTransportCreated = false;
var options = TestConstants.DefaultBrowserOptions();
options.TransportFactory = (url, opt, cancellationToken) =>
{
customTransportCreated = true;
return WebSocketTransport.DefaultTransportFactory(url, opt, cancellationToken);
};

using (await Puppeteer.LaunchAsync(options, TestConstants.LoggerFactory))
{
Assert.True(customTransportCreated);
}
}

private Process GetTestAppProcess(string appName, string arguments)
{
var process = new Process();
@@ -1,12 +1,9 @@
using System;
using System.Net.WebSockets;
using PuppeteerSharp.Transport;

namespace PuppeteerSharp
{
using System.Threading;
using System.Threading.Tasks;
using PuppeteerSharp.Transport;

/// <summary>
/// Options for connecting to an existing browser.
/// </summary>
@@ -48,7 +45,7 @@ public class ConnectOptions : IBrowserOptions, IConnectionOptions
/// Optional factory for <see cref="WebSocket"/> implementations.
/// If <see cref="Transport"/> is set this property will be ignored.
/// </summary>
public Func<Uri, IConnectionOptions, CancellationToken, Task<WebSocket>> WebSocketFactory { get; set; }
public WebSocketFactory WebSocketFactory { get; set; }

/// <summary>
/// Gets or sets the default Viewport.
@@ -59,7 +56,14 @@ public class ConnectOptions : IBrowserOptions, IConnectionOptions
/// <summary>
/// Optional connection transport.
/// </summary>
[Obsolete("Use " + nameof(TransportFactory) + " instead")]
public IConnectionTransport Transport { get; set; }

/// <summary>
/// Optional factory for <see cref="IConnectionTransport"/> implementations.
/// </summary>
public TransportFactory TransportFactory { get; set; }

/// <summary>
/// If not <see cref="Transport"/> is set this will be use to determine is the default <see cref="WebSocketTransport"/> will enqueue messages.
/// </summary>
@@ -1,7 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
@@ -270,21 +269,19 @@ private void ProcessIncomingMessage(ConnectionResponse obj)
/// <summary>
/// Gets default web socket factory implementation.
/// </summary>
public static readonly Func<Uri, IConnectionOptions, CancellationToken, Task<WebSocket>> DefaultWebSocketFactory = async (uri, options, cancellationToken) =>
{
var result = new ClientWebSocket();
result.Options.KeepAliveInterval = TimeSpan.Zero;
await result.ConnectAsync(uri, cancellationToken).ConfigureAwait(false);
return result;
};
[Obsolete("Use " + nameof(WebSocketTransport) + "." + nameof(WebSocketTransport.DefaultWebSocketFactory) + " instead")]
public static readonly WebSocketFactory DefaultWebSocketFactory = WebSocketTransport.DefaultWebSocketFactory;

internal static async Task<Connection> Create(string url, IConnectionOptions connectionOptions, ILoggerFactory loggerFactory = null)
internal static async Task<Connection> Create(string url, IConnectionOptions connectionOptions, ILoggerFactory loggerFactory = null, CancellationToken cancellationToken = default)
{
var transport = connectionOptions.Transport ?? new WebSocketTransport();
connectionOptions.WebSocketFactory = connectionOptions.WebSocketFactory ?? DefaultWebSocketFactory;

await transport.InitializeAsync(url, connectionOptions).ConfigureAwait(false);

#pragma warning disable 618
var transport = connectionOptions.Transport;
#pragma warning restore 618
if (transport == null)
{
var transportFactory = connectionOptions.TransportFactory ?? WebSocketTransport.DefaultTransportFactory;
transport = await transportFactory(new Uri(url), connectionOptions, cancellationToken);
}
return new Connection(url, connectionOptions.SlowMo, transport, loggerFactory);
}

@@ -1,7 +1,5 @@
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using PuppeteerSharp.Transport;

namespace PuppeteerSharp
@@ -26,13 +24,19 @@ public interface IConnectionOptions
/// Optional factory for <see cref="WebSocket"/> implementations.
/// If <see cref="Transport"/> is set this property will be ignored.
/// </summary>
Func<Uri, IConnectionOptions, CancellationToken, Task<WebSocket>> WebSocketFactory { get; set; }
WebSocketFactory WebSocketFactory { get; set; }

/// <summary>
/// Optional connection transport.
/// Optional connection transport factory.
/// </summary>
[Obsolete("Use " + nameof(TransportFactory) + " instead")]
IConnectionTransport Transport { get; set; }

/// <summary>
/// Optional factory for <see cref="IConnectionTransport"/> implementations.
/// </summary>
TransportFactory TransportFactory { get; set; }

/// <summary>
/// If not <see cref="Transport"/> is set this will be use to determine is the default <see cref="WebSocketTransport"/> will enqueue messages.
/// </summary>
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using PuppeteerSharp.Transport;

namespace PuppeteerSharp
@@ -110,13 +108,19 @@ public string[] IgnoredDefaultArgs
/// Optional factory for <see cref="WebSocket"/> implementations.
/// If <see cref="Transport"/> is set this property will be ignored.
/// </summary>
public Func<Uri, IConnectionOptions, CancellationToken, Task<WebSocket>> WebSocketFactory { get; set; }
public WebSocketFactory WebSocketFactory { get; set; }

/// <summary>
/// Optional connection transport.
/// </summary>
[Obsolete("Use " + nameof(TransportFactory) + " instead")]
public IConnectionTransport Transport { get; set; }

/// <summary>
/// Optional factory for <see cref="IConnectionTransport"/> implementations.
/// </summary>
public TransportFactory TransportFactory { get; set; }

/// <summary>
/// Gets or sets the default Viewport.
/// </summary>
@@ -1,7 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace PuppeteerSharp.Transport
{
@@ -10,14 +8,6 @@ namespace PuppeteerSharp.Transport
/// </summary>
public interface IConnectionTransport : IDisposable
{
/// <summary>
/// Initialize the Transport
/// </summary>
/// <param name="url">Chromium URL</param>
/// <param name="connectionOptions">Connection options</param>
/// <param name="loggerFactory">Logger factory</param>
Task InitializeAsync(string url, IConnectionOptions connectionOptions, ILoggerFactory loggerFactory = null);

/// <summary>
/// Gets a value indicating whether this <see cref="PuppeteerSharp.Transport.IConnectionTransport"/> is closed.
/// </summary>
@@ -0,0 +1,15 @@
namespace PuppeteerSharp.Transport
{
using System;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// Delegate for creation of <see cref="IConnectionTransport"/> instances.
/// </summary>
/// <param name="url">Chromium URL</param>
/// <param name="options">Connection options</param>
/// <param name="cancellationToken">A cancellation token</param>
/// <returns>A <see cref="Task{IConnectionTransport}"/> instance for the asynchronous socket create and connect operation.</returns>
public delegate Task<IConnectionTransport> TransportFactory(Uri url, IConnectionOptions options, CancellationToken cancellationToken);
}
@@ -0,0 +1,13 @@
namespace PuppeteerSharp.Transport
{
using System;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// Delegate for scheduling of long-running transport tasks
/// </summary>
/// <param name="taskFactory">Delegate that creates the task to be scheduled.</param>
/// <param name="cancellationToken">Cancellation token for the task to be scheduled.</param>
public delegate void TransportTaskScheduler(Func<CancellationToken, Task> taskFactory, CancellationToken cancellationToken);
}
@@ -0,0 +1,16 @@
namespace PuppeteerSharp.Transport
{
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// Delegate for creation of <see cref="WebSocket"/> instances.
/// </summary>
/// <param name="url">Chromium URL</param>
/// <param name="options">Connection options</param>
/// <param name="cancellationToken">A cancellation token</param>
/// <returns>A <see cref="Task{WebSocket}"/> instance for the asynchronous socket create and connect operation.</returns>
public delegate Task<WebSocket> WebSocketFactory(Uri url, IConnectionOptions options, CancellationToken cancellationToken);
}
Oops, something went wrong.

0 comments on commit 8e61e2c

Please sign in to comment.
You can’t perform that action at this time.