mirror of
https://github.com/Azure/DotNetty.git
synced 2026-10-19 06:31:20 +08:00
161 lines
5.5 KiB
C#
161 lines
5.5 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
namespace WebSockets.Server
|
|
{
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using DotNetty.Buffers;
|
|
using DotNetty.Codecs.Http;
|
|
using DotNetty.Codecs.Http.WebSockets;
|
|
using DotNetty.Common.Utilities;
|
|
using DotNetty.Transport.Channels;
|
|
using Examples.Common;
|
|
|
|
using static DotNetty.Codecs.Http.HttpVersion;
|
|
using static DotNetty.Codecs.Http.HttpResponseStatus;
|
|
|
|
public sealed class WebSocketServerHandler : SimpleChannelInboundHandler<object>
|
|
{
|
|
const string WebsocketPath = "/websocket";
|
|
|
|
WebSocketServerHandshaker handshaker;
|
|
|
|
protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
|
|
{
|
|
if (msg is IFullHttpRequest request)
|
|
{
|
|
this.HandleHttpRequest(ctx, request);
|
|
}
|
|
else if (msg is WebSocketFrame frame)
|
|
{
|
|
this.HandleWebSocketFrame(ctx, frame);
|
|
}
|
|
}
|
|
|
|
public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();
|
|
|
|
void HandleHttpRequest(IChannelHandlerContext ctx, IFullHttpRequest req)
|
|
{
|
|
// Handle a bad request.
|
|
if (!req.Result.IsSuccess)
|
|
{
|
|
SendHttpResponse(ctx, req, new DefaultFullHttpResponse(Http11, BadRequest));
|
|
return;
|
|
}
|
|
|
|
// Allow only GET methods.
|
|
if (!Equals(req.Method, HttpMethod.Get))
|
|
{
|
|
SendHttpResponse(ctx, req, new DefaultFullHttpResponse(Http11, Forbidden));
|
|
return;
|
|
}
|
|
|
|
// Send the demo page and favicon.ico
|
|
if ("/".Equals(req.Uri))
|
|
{
|
|
IByteBuffer content = WebSocketServerBenchmarkPage.GetContent(GetWebSocketLocation(req));
|
|
var res = new DefaultFullHttpResponse(Http11, OK, content);
|
|
|
|
res.Headers.Set(HttpHeaderNames.ContentType, "text/html; charset=UTF-8");
|
|
HttpUtil.SetContentLength(res, content.ReadableBytes);
|
|
|
|
SendHttpResponse(ctx, req, res);
|
|
return;
|
|
}
|
|
if ("/favicon.ico".Equals(req.Uri))
|
|
{
|
|
var res = new DefaultFullHttpResponse(Http11, NotFound);
|
|
SendHttpResponse(ctx, req, res);
|
|
return;
|
|
}
|
|
|
|
// Handshake
|
|
var wsFactory = new WebSocketServerHandshakerFactory(
|
|
GetWebSocketLocation(req), null, true, 5 * 1024 * 1024);
|
|
this.handshaker = wsFactory.NewHandshaker(req);
|
|
if (this.handshaker == null)
|
|
{
|
|
WebSocketServerHandshakerFactory.SendUnsupportedVersionResponse(ctx.Channel);
|
|
}
|
|
else
|
|
{
|
|
this.handshaker.HandshakeAsync(ctx.Channel, req);
|
|
}
|
|
}
|
|
|
|
void HandleWebSocketFrame(IChannelHandlerContext ctx, WebSocketFrame frame)
|
|
{
|
|
// Check for closing frame
|
|
if (frame is CloseWebSocketFrame)
|
|
{
|
|
this.handshaker.CloseAsync(ctx.Channel, (CloseWebSocketFrame)frame.Retain());
|
|
return;
|
|
}
|
|
|
|
if (frame is PingWebSocketFrame)
|
|
{
|
|
ctx.WriteAsync(new PongWebSocketFrame((IByteBuffer)frame.Content.Retain()));
|
|
return;
|
|
}
|
|
|
|
if (frame is TextWebSocketFrame)
|
|
{
|
|
// Echo the frame
|
|
ctx.WriteAsync(frame.Retain());
|
|
return;
|
|
}
|
|
|
|
if (frame is BinaryWebSocketFrame)
|
|
{
|
|
// Echo the frame
|
|
ctx.WriteAsync(frame.Retain());
|
|
}
|
|
}
|
|
|
|
static void SendHttpResponse(IChannelHandlerContext ctx, IFullHttpRequest req, IFullHttpResponse res)
|
|
{
|
|
// Generate an error page if response getStatus code is not OK (200).
|
|
if (res.Status.Code != 200)
|
|
{
|
|
IByteBuffer buf = Unpooled.CopiedBuffer(Encoding.UTF8.GetBytes(res.Status.ToString()));
|
|
res.Content.WriteBytes(buf);
|
|
buf.Release();
|
|
HttpUtil.SetContentLength(res, res.Content.ReadableBytes);
|
|
}
|
|
|
|
// Send the response and close the connection if necessary.
|
|
Task task = ctx.Channel.WriteAndFlushAsync(res);
|
|
if (!HttpUtil.IsKeepAlive(req) || res.Status.Code != 200)
|
|
{
|
|
task.ContinueWith((t, c) => ((IChannelHandlerContext)c).CloseAsync(),
|
|
ctx, TaskContinuationOptions.ExecuteSynchronously);
|
|
}
|
|
}
|
|
|
|
public override void ExceptionCaught(IChannelHandlerContext ctx, Exception e)
|
|
{
|
|
Console.WriteLine($"{nameof(WebSocketServerHandler)} {0}", e);
|
|
ctx.CloseAsync();
|
|
}
|
|
|
|
static string GetWebSocketLocation(IFullHttpRequest req)
|
|
{
|
|
bool result = req.Headers.TryGet(HttpHeaderNames.Host, out ICharSequence value);
|
|
Debug.Assert(result, "Host header does not exist.");
|
|
string location= value.ToString() + WebsocketPath;
|
|
|
|
if (ServerSettings.IsSsl)
|
|
{
|
|
return "wss://" + location;
|
|
}
|
|
else
|
|
{
|
|
return "ws://" + location;
|
|
}
|
|
}
|
|
}
|
|
}
|