Skip to content

Commit

Permalink
WebSocket support. (#400)
Browse files Browse the repository at this point in the history
  • Loading branch information
StormHub authored and nayato committed Aug 13, 2018
1 parent 9e3a841 commit 2c0c109
Show file tree
Hide file tree
Showing 119 changed files with 9,718 additions and 6 deletions.
14 changes: 14 additions & 0 deletions DotNetty.sln
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Codecs.Http", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Codecs.Http.Tests", "test\DotNetty.Codecs.Http.Tests\DotNetty.Codecs.Http.Tests.csproj", "{16C89E7C-1575-4685-8DFA-8E7E2C6101BF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSockets.Server", "examples\WebSockets.Server\WebSockets.Server.csproj", "{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSockets.Client", "examples\WebSockets.Client\WebSockets.Client.csproj", "{3326DB6E-023E-483F-9A1C-5905D3091B57}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -273,6 +277,14 @@ Global
{16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Release|Any CPU.Build.0 = Release|Any CPU
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Release|Any CPU.Build.0 = Release|Any CPU
{3326DB6E-023E-483F-9A1C-5905D3091B57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3326DB6E-023E-483F-9A1C-5905D3091B57}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3326DB6E-023E-483F-9A1C-5905D3091B57}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3326DB6E-023E-483F-9A1C-5905D3091B57}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -319,6 +331,8 @@ Global
{A7CACAE7-66E7-43DA-948B-28EB0DDDB582} = {F716F1EF-81EF-4020-914A-5422A13A9E13}
{5F68A5B1-7907-4B16-8AFE-326E9DD7D65B} = {126EA539-4B28-4B07-8B5D-D1D7F794D189}
{16C89E7C-1575-4685-8DFA-8E7E2C6101BF} = {541093F6-616E-43D9-B671-FCD1F9C0A181}
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A} = {F716F1EF-81EF-4020-914A-5422A13A9E13}
{3326DB6E-023E-483F-9A1C-5905D3091B57} = {F716F1EF-81EF-4020-914A-5422A13A9E13}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
{9FE6A783-C20D-4097-9988-4178E2C4CE75} = {126EA539-4B28-4B07-8B5D-D1D7F794D189}
Expand Down
9 changes: 9 additions & 0 deletions examples/Examples.Common/ClientSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,14 @@ public static bool IsSsl
public static int Port => int.Parse(ExampleHelper.Configuration["port"]);

public static int Size => int.Parse(ExampleHelper.Configuration["size"]);

public static bool UseLibuv
{
get
{
string libuv = ExampleHelper.Configuration["libuv"];
return !string.IsNullOrEmpty(libuv) && bool.Parse(libuv);
}
}
}
}
139 changes: 139 additions & 0 deletions examples/WebSockets.Client/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace WebSockets.Client
{
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using DotNetty.Buffers;
using DotNetty.Codecs.Http;
using DotNetty.Codecs.Http.WebSockets;
using DotNetty.Codecs.Http.WebSockets.Extensions.Compression;
using DotNetty.Handlers.Tls;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using DotNetty.Transport.Libuv;
using Examples.Common;

class Program
{
static async Task RunClientAsync()
{
var builder = new UriBuilder
{
Scheme = ClientSettings.IsSsl ? "wss" : "ws",
Host = ClientSettings.Host.ToString(),
Port = ClientSettings.Port
};

string path = ExampleHelper.Configuration["path"];
if (!string.IsNullOrEmpty(path))
{
builder.Path = path;
}

Uri uri = builder.Uri;
ExampleHelper.SetConsoleLogger();

bool useLibuv = ClientSettings.UseLibuv;
Console.WriteLine("Transport type : " + (useLibuv ? "Libuv" : "Socket"));

IEventLoopGroup group;
if (useLibuv)
{
group = new EventLoopGroup();
}
else
{
group = new MultithreadEventLoopGroup();
}

X509Certificate2 cert = null;
string targetHost = null;
if (ClientSettings.IsSsl)
{
cert = new X509Certificate2(Path.Combine(ExampleHelper.ProcessDirectory, "dotnetty.com.pfx"), "password");
targetHost = cert.GetNameInfo(X509NameType.DnsName, false);
}
try
{
var bootstrap = new Bootstrap();
bootstrap
.Group(group)
.Option(ChannelOption.TcpNodelay, true);
if (useLibuv)
{
bootstrap.Channel<TcpChannel>();
}
else
{
bootstrap.Channel<TcpSocketChannel>();
}

// Connect with V13 (RFC 6455 aka HyBi-17). You can change it to V08 or V00.
// If you change it to V00, ping is not supported and remember to change
// HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline.
var handler =new WebSocketClientHandler(
WebSocketClientHandshakerFactory.NewHandshaker(
uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders()));

bootstrap.Handler(new ActionChannelInitializer<IChannel>(channel =>
{
IChannelPipeline pipeline = channel.Pipeline;
if (cert != null)
{
pipeline.AddLast("tls", new TlsHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => true), new ClientTlsSettings(targetHost)));
}

pipeline.AddLast(
new HttpClientCodec(),
new HttpObjectAggregator(8192),
WebSocketClientCompressionHandler.Instance,
handler);
}));

IChannel ch = await bootstrap.ConnectAsync(new IPEndPoint(ClientSettings.Host, ClientSettings.Port));
await handler.HandshakeCompletion;

Console.WriteLine("WebSocket handshake completed.\n");
Console.WriteLine("\t[bye]:Quit \n\t [ping]:Send ping frame\n\t Enter any text and Enter: Send text frame");
while (true)
{
string msg = Console.ReadLine();
if (msg == null)
{
break;
}
else if ("bye".Equals(msg.ToLower()))
{
await ch.WriteAndFlushAsync(new CloseWebSocketFrame());
break;
}
else if ("ping".Equals(msg.ToLower()))
{
var frame = new PingWebSocketFrame(Unpooled.WrappedBuffer(new byte[] { 8, 1, 8, 1 }));
await ch.WriteAndFlushAsync(frame);
}
else
{
WebSocketFrame frame = new TextWebSocketFrame(msg);
await ch.WriteAndFlushAsync(frame);
}
}

await ch.CloseAsync();
}
finally
{
await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
}
}

static void Main() => RunClientAsync().Wait();
}
}
85 changes: 85 additions & 0 deletions examples/WebSockets.Client/WebSocketClientHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace WebSockets.Client
{
using System;
using System.Text;
using System.Threading.Tasks;
using DotNetty.Codecs.Http;
using DotNetty.Codecs.Http.WebSockets;
using DotNetty.Common.Concurrency;
using DotNetty.Common.Utilities;
using DotNetty.Transport.Channels;

public class WebSocketClientHandler : SimpleChannelInboundHandler<object>
{
readonly WebSocketClientHandshaker handshaker;
readonly TaskCompletionSource completionSource;

public WebSocketClientHandler(WebSocketClientHandshaker handshaker)
{
this.handshaker = handshaker;
this.completionSource = new TaskCompletionSource();
}

public Task HandshakeCompletion => this.completionSource.Task;

public override void ChannelActive(IChannelHandlerContext ctx) =>
this.handshaker.HandshakeAsync(ctx.Channel).LinkOutcome(this.completionSource);

public override void ChannelInactive(IChannelHandlerContext context)
{
Console.WriteLine("WebSocket Client disconnected!");
}

protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
{
IChannel ch = ctx.Channel;
if (!this.handshaker.IsHandshakeComplete)
{
try
{
this.handshaker.FinishHandshake(ch, (IFullHttpResponse)msg);
Console.WriteLine("WebSocket Client connected!");
this.completionSource.TryComplete();
}
catch (WebSocketHandshakeException e)
{
Console.WriteLine("WebSocket Client failed to connect");
this.completionSource.TrySetException(e);
}

return;
}


if (msg is IFullHttpResponse response)
{
throw new InvalidOperationException(
$"Unexpected FullHttpResponse (getStatus={response.Status}, content={response.Content.ToString(Encoding.UTF8)})");
}

if (msg is TextWebSocketFrame textFrame)
{
Console.WriteLine($"WebSocket Client received message: {textFrame.Text()}");
}
else if (msg is PongWebSocketFrame)
{
Console.WriteLine("WebSocket Client received pong");
}
else if (msg is CloseWebSocketFrame)
{
Console.WriteLine("WebSocket Client received closing");
ch.CloseAsync();
}
}

public override void ExceptionCaught(IChannelHandlerContext ctx, Exception exception)
{
Console.WriteLine("Exception: " + exception);
this.completionSource.TrySetException(exception);
ctx.CloseAsync();
}
}
}
31 changes: 31 additions & 0 deletions examples/WebSockets.Client/WebSockets.Client.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp1.1;net451</TargetFrameworks>
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">1.6.1</NetStandardImplicitPackageVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net451' ">
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\shared\dotnetty.com.pfx" Link="dotnetty.com.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\DotNetty.Buffers\DotNetty.Buffers.csproj" />
<ProjectReference Include="..\..\src\DotNetty.Codecs.Http\DotNetty.Codecs.Http.csproj" />
<ProjectReference Include="..\..\src\DotNetty.Codecs\DotNetty.Codecs.csproj" />
<ProjectReference Include="..\..\src\DotNetty.Common\DotNetty.Common.csproj" />
<ProjectReference Include="..\..\src\DotNetty.Handlers\DotNetty.Handlers.csproj" />
<ProjectReference Include="..\..\src\DotNetty.Transport.Libuv\DotNetty.Transport.Libuv.csproj" />
<ProjectReference Include="..\..\src\DotNetty.Transport\DotNetty.Transport.csproj" />
<ProjectReference Include="..\Examples.Common\Examples.Common.csproj" />
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions examples/WebSockets.Client/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"ssl": "false",
"host": "127.0.0.1",
"port": "8080",
"path": "/websocket",
"libuv": "true"
}
Loading

0 comments on commit 2c0c109

Please sign in to comment.