-
Notifications
You must be signed in to change notification settings - Fork 1k
Expand file tree
/
Copy pathWebSocketServerHandler.cs
More file actions
160 lines (139 loc) · 5.47 KB
/
WebSocketServerHandler.cs
File metadata and controls
160 lines (139 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// 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;
}
}
}
}