-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from Garados007/websocket
Websocket
- Loading branch information
Showing
15 changed files
with
888 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
using MaxLib.WebServer.WebSocket; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace MaxLib.WebServer.Test.WebSocket | ||
{ | ||
[TestClass] | ||
public class FrameParsing | ||
{ | ||
[TestMethod] | ||
public async Task ReadSingleFrameUnmaskedTextMessage() | ||
{ | ||
var m = new MemoryStream(new byte[] { 0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f }); | ||
var frame = await Frame.TryRead(m); | ||
Assert.IsNotNull(frame); | ||
Assert.IsTrue(frame!.FinalFrame); | ||
Assert.AreEqual(OpCode.Text, frame.OpCode); | ||
Assert.IsFalse(frame.HasMaskingKey); | ||
Assert.AreEqual("Hello", frame.TextPayload); | ||
} | ||
|
||
[TestMethod] | ||
public async Task ReadSingleFrameMaskedTextMessage() | ||
{ | ||
var m = new MemoryStream(new byte[] { 0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58 }); | ||
var frame = await Frame.TryRead(m); | ||
Assert.IsNotNull(frame); | ||
Assert.IsTrue(frame!.FinalFrame); | ||
Assert.AreEqual(OpCode.Text, frame.OpCode); | ||
Assert.IsTrue(frame.HasMaskingKey); | ||
frame.UnapplyMask(); | ||
Assert.IsFalse(frame.HasMaskingKey); | ||
Assert.AreEqual("Hello", frame.TextPayload); | ||
} | ||
|
||
[TestMethod] | ||
public async Task ReadFragmentedUnmaskedTextMessage() | ||
{ | ||
var m = new MemoryStream(new byte[] { 0x01, 0x03, 0x48, 0x65, 0x6c }); | ||
var frame = await Frame.TryRead(m); | ||
Assert.IsNotNull(frame); | ||
Assert.IsFalse(frame!.FinalFrame); | ||
Assert.AreEqual(OpCode.Text, frame.OpCode); | ||
Assert.IsFalse(frame.HasMaskingKey); | ||
Assert.AreEqual("Hel", frame.TextPayload); | ||
|
||
m = new MemoryStream(new byte[] { 0x80, 0x02, 0x6c, 0x6f }); | ||
frame = await Frame.TryRead(m); | ||
Assert.IsNotNull(frame); | ||
Assert.IsTrue(frame!.FinalFrame); | ||
Assert.AreEqual(OpCode.Continuation, frame.OpCode); | ||
Assert.IsFalse(frame.HasMaskingKey); | ||
Assert.AreEqual("lo", frame.TextPayload); | ||
} | ||
|
||
|
||
[TestMethod] | ||
public async Task ReadUnmaskedPingAndMaskedPongMessage() | ||
{ | ||
var m = new MemoryStream(new byte[] { 0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f }); | ||
var frame = await Frame.TryRead(m); | ||
Assert.IsNotNull(frame); | ||
Assert.IsTrue(frame!.FinalFrame); | ||
Assert.AreEqual(OpCode.Ping, frame.OpCode); | ||
Assert.IsFalse(frame.HasMaskingKey); | ||
Assert.AreEqual("Hello", frame.TextPayload); | ||
|
||
m = new MemoryStream(new byte[] { 0x8a, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58 }); | ||
frame = await Frame.TryRead(m); | ||
Assert.IsNotNull(frame); | ||
Assert.IsTrue(frame!.FinalFrame); | ||
Assert.AreEqual(OpCode.Pong, frame.OpCode); | ||
Assert.IsTrue(frame.HasMaskingKey); | ||
frame.UnapplyMask(); | ||
Assert.IsFalse(frame.HasMaskingKey); | ||
Assert.AreEqual("Hello", frame.TextPayload); | ||
} | ||
|
||
[TestMethod] | ||
public async Task Read256ByteUnmaskedMessage() | ||
{ | ||
Memory<byte> data = new byte[4 + 256]; | ||
(new byte[] { 0x82, 0x7E, 0x01, 0x00 }).CopyTo(data[..4]); | ||
var m = new MemoryStream(data.ToArray()); | ||
var frame = await Frame.TryRead(m); | ||
Assert.IsNotNull(frame); | ||
Assert.IsTrue(frame!.FinalFrame); | ||
Assert.AreEqual(OpCode.Binary, frame.OpCode); | ||
Assert.IsFalse(frame.HasMaskingKey); | ||
Assert.AreEqual(256, frame.Payload.Length); | ||
} | ||
|
||
[TestMethod] | ||
public async Task Read64KiByteUnmaskedMessage() | ||
{ | ||
Memory<byte> data = new byte[10 + 65536]; | ||
(new byte[] { 0x82, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 }).CopyTo(data[..10]); | ||
var m = new MemoryStream(data.ToArray()); | ||
var frame = await Frame.TryRead(m); | ||
Assert.IsNotNull(frame); | ||
Assert.IsTrue(frame!.FinalFrame); | ||
Assert.AreEqual(OpCode.Binary, frame.OpCode); | ||
Assert.IsFalse(frame.HasMaskingKey); | ||
Assert.AreEqual(65536, frame.Payload.Length); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using System.IO; | ||
using System.Threading.Tasks; | ||
|
||
#nullable enable | ||
|
||
namespace MaxLib.WebServer.WebSocket.Echo | ||
{ | ||
public class EchoConnection : WebSocketConnection | ||
{ | ||
public EchoConnection(Stream networkStream) | ||
: base(networkStream) | ||
{ | ||
} | ||
|
||
protected override async Task ReceiveClose(CloseReason? reason, string? info) | ||
{ | ||
WebServerLog.Add(ServerLogType.Information, GetType(), "WebSocket", $"client close websocket ({reason}): {info}"); | ||
if (!SendCloseSignal) | ||
await Close(); | ||
} | ||
|
||
protected override async Task ReceivedFrame(Frame frame) | ||
{ | ||
await SendFrame(new Frame | ||
{ | ||
OpCode = frame.OpCode, | ||
Payload = frame.Payload | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System.IO; | ||
|
||
#nullable enable | ||
|
||
namespace MaxLib.WebServer.WebSocket.Echo | ||
{ | ||
public class EchoEndpoint : WebSocketEndpoint<EchoConnection> | ||
{ | ||
public override string? Protocol => null; | ||
|
||
protected override EchoConnection CreateConnection(Stream stream, HttpRequestHeader header) | ||
{ | ||
return new EchoConnection(stream); | ||
} | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
MaxLib.WebServer.WebSocket.Echo/MaxLib.WebServer.WebSocket.Echo.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>netcoreapp3.1</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\MaxLib.WebServer\MaxLib.WebServer.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using MaxLib.WebServer.Services; | ||
using System; | ||
|
||
#nullable enable | ||
|
||
namespace MaxLib.WebServer.WebSocket.Echo | ||
{ | ||
class Program | ||
{ | ||
static void Main() | ||
{ | ||
WebServerLog.LogAdded += WebServerLog_LogAdded; | ||
var server = new Server(new WebServerSettings(8000, 5000)); | ||
// add services | ||
server.AddWebService(new HttpRequestParser()); | ||
server.AddWebService(new HttpHeaderPostParser()); | ||
server.AddWebService(new HttpHeaderSpecialAction()); | ||
server.AddWebService(new HttpResponseCreator()); | ||
server.AddWebService(new HttpSender()); | ||
// setup web socket | ||
var websocket = new WebSocketService(); | ||
websocket.Add(new EchoEndpoint()); | ||
server.AddWebService(websocket); | ||
// start server | ||
server.Start(); | ||
// wait for console quit | ||
while (Console.ReadKey().Key != ConsoleKey.Q) ; | ||
// close | ||
server.Stop(); | ||
websocket.Dispose(); | ||
} | ||
|
||
private static void WebServerLog_LogAdded(ServerLogItem item) | ||
{ | ||
Console.WriteLine($"[{item.Date}] [{item.Type}] ({item.InfoType}) {item.SenderType}: {item.Information}"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
#nullable enable | ||
|
||
namespace MaxLib.WebServer.WebSocket | ||
{ | ||
public enum CloseReason : ushort | ||
{ | ||
/// <summary> | ||
/// 1000 indicates a normal closure, meaning that the purpose for | ||
/// which the connection was established has been fulfilled. | ||
/// </summary> | ||
NormalClose = 1000, | ||
/// <summary> | ||
/// 1001 indicates that an endpoint is "going away", such as a server | ||
/// going down or a browser having navigated away from a page. | ||
/// </summary> | ||
GoingAway = 1001, | ||
/// <summary> | ||
/// 1002 indicates that an endpoint is terminating the connection due | ||
/// to a protocol error. | ||
/// </summary> | ||
ProtocolError = 1002, | ||
/// <summary> | ||
/// 1003 indicates that an endpoint is terminating the connection | ||
/// because it has received a type of data it cannot accept (e.g., an | ||
/// endpoint that understands only text data MAY send this if it | ||
/// receives a binary message). | ||
/// </summary> | ||
CannotAccept = 1003, | ||
/// <summary> | ||
/// 1007 indicates that an endpoint is terminating the connection | ||
/// because it has received data within a message that was not | ||
/// consistent with the type of the message (e.g., non-UTF-8 [RFC3629] | ||
/// data within a text message). | ||
/// </summary> | ||
InvalidMessageContent = 1007, | ||
/// <summary> | ||
/// 1008 indicates that an endpoint is terminating the connection | ||
/// because it has received a message that violates its policy. This | ||
/// is a generic status code that can be returned when there is no | ||
/// other more suitable status code (e.g., 1003 or 1009) or if there | ||
/// is a need to hide specific details about the policy. | ||
/// </summary> | ||
PolicyError = 1008, | ||
/// <summary> | ||
/// 1009 indicates that an endpoint is terminating the connection | ||
/// because it has received a message that is too big for it to | ||
/// process. | ||
/// </summary> | ||
TooBigMessage = 1009, | ||
/// <summary> | ||
/// 1010 indicates that an endpoint (client) is terminating the | ||
/// connection because it has expected the server to negotiate one or | ||
/// more extension, but the server didn't return them in the response | ||
/// message of the WebSocket handshake. The list of extensions that | ||
/// are needed SHOULD appear in the /reason/ part of the Close frame. | ||
/// Note that this status code is not used by the server, because it | ||
/// can fail the WebSocket handshake instead. | ||
/// </summary> | ||
MissingExtension = 1010, | ||
/// <summary> | ||
/// 1011 indicates that a server is terminating the connection because | ||
/// it encountered an unexpected condition that prevented it from | ||
/// fulfilling the request. | ||
/// </summary> | ||
UnexpectedCondition = 1011, | ||
} | ||
} |
Oops, something went wrong.