Browse Source

first commit

pull/123/head
notgiven688 3 years ago
commit
ac7428ec61
197 changed files with 44380 additions and 0 deletions
  1. +32
    -0
      monero.license
  2. +26
    -0
      server/Server.sln
  3. +26
    -0
      server/Server.userprefs
  4. +50
    -0
      server/Server/DataStructures.cs
  5. +162
    -0
      server/Server/EmptyWebsocket.cs
  6. +19
    -0
      server/Server/Execute.cs
  7. +31
    -0
      server/Server/Extensions.cs
  8. +137
    -0
      server/Server/Firewall.cs
  9. +78
    -0
      server/Server/Fleck/BufferPool.cs
  10. +20
    -0
      server/Server/Fleck/ConnectionNotAvailableException.cs
  11. +141
    -0
      server/Server/Fleck/Fleck.csproj
  12. +44
    -0
      server/Server/Fleck/FleckLog.cs
  13. +15
    -0
      server/Server/Fleck/FrameType.cs
  14. +46
    -0
      server/Server/Fleck/HandlerFactory.cs
  15. +56
    -0
      server/Server/Fleck/Handlers/ComposableHandler.cs
  16. +116
    -0
      server/Server/Fleck/Handlers/Draft76Handler.cs
  17. +32
    -0
      server/Server/Fleck/Handlers/FlashSocketPolicyRequestHandler.cs
  18. +208
    -0
      server/Server/Fleck/Handlers/Hybi13Handler.cs
  19. +14
    -0
      server/Server/Fleck/HandshakeException.cs
  20. +13
    -0
      server/Server/Fleck/Helpers/MonoHelper.cs
  21. +41
    -0
      server/Server/Fleck/IntExtensions.cs
  22. +16
    -0
      server/Server/Fleck/Interfaces/IHandler.cs
  23. +31
    -0
      server/Server/Fleck/Interfaces/ISocket.cs
  24. +23
    -0
      server/Server/Fleck/Interfaces/IWebSocketConnection.cs
  25. +19
    -0
      server/Server/Fleck/Interfaces/IWebSocketConnectionInfo.cs
  26. +9
    -0
      server/Server/Fleck/Interfaces/IWebSocketServer.cs
  27. +257
    -0
      server/Server/Fleck/QueuedStream.cs
  28. +21
    -0
      server/Server/Fleck/ReadState.cs
  29. +69
    -0
      server/Server/Fleck/RequestParser.cs
  30. +211
    -0
      server/Server/Fleck/SocketWrapper.cs
  31. +14
    -0
      server/Server/Fleck/SubProtocolNegotiationFailureException.cs
  32. +22
    -0
      server/Server/Fleck/SubProtocolNegotiator.cs
  33. +285
    -0
      server/Server/Fleck/WebSocketConnection.cs
  34. +63
    -0
      server/Server/Fleck/WebSocketConnectionInfo.cs
  35. +23
    -0
      server/Server/Fleck/WebSocketException.cs
  36. +48
    -0
      server/Server/Fleck/WebSocketHttpRequest.cs
  37. +219
    -0
      server/Server/Fleck/WebSocketServer.cs
  38. +29
    -0
      server/Server/Fleck/WebSocketStatusCodes.cs
  39. +333
    -0
      server/Server/JSONParser.cs
  40. +415
    -0
      server/Server/PoolConnection.cs
  41. +1282
    -0
      server/Server/Program.cs
  42. +22
    -0
      server/Server/Properties/AssemblyInfo.cs
  43. +94
    -0
      server/Server/Server.csproj
  44. +23
    -0
      server/Server/Server.sln
  45. +1
    -0
      server/Server/Server.userprefs
  46. +23
    -0
      server/libhash/aeon/Makefile
  47. +185
    -0
      server/libhash/aeon/aesb.c
  48. +355
    -0
      server/libhash/aeon/blake256.c
  49. +73
    -0
      server/libhash/aeon/blake256.h
  50. +170
    -0
      server/libhash/aeon/chacha8.c
  51. +86
    -0
      server/libhash/aeon/chacha8.h
  52. +8
    -0
      server/libhash/aeon/cn.h
  53. BIN
      server/libhash/aeon/cn_hash.so
  54. +872
    -0
      server/libhash/aeon/crypto-ops-data.c
  55. +2925
    -0
      server/libhash/aeon/crypto-ops.c
  56. +145
    -0
      server/libhash/aeon/crypto-ops.h
  57. +220
    -0
      server/libhash/aeon/crypto.h
  58. +62
    -0
      server/libhash/aeon/generic-ops.h
  59. +360
    -0
      server/libhash/aeon/groestl.c
  60. +88
    -0
      server/libhash/aeon/groestl.h
  61. +66
    -0
      server/libhash/aeon/groestl_tables.h
  62. +38
    -0
      server/libhash/aeon/hash-extra-blake.c
  63. +38
    -0
      server/libhash/aeon/hash-extra-groestl.c
  64. +42
    -0
      server/libhash/aeon/hash-extra-jh.c
  65. +40
    -0
      server/libhash/aeon/hash-extra-skein.c
  66. +91
    -0
      server/libhash/aeon/hash-ops.h
  67. +50
    -0
      server/libhash/aeon/hash.c
  68. +81
    -0
      server/libhash/aeon/hash.h
  69. +58
    -0
      server/libhash/aeon/initializer.h
  70. +233
    -0
      server/libhash/aeon/int-util.h
  71. +367
    -0
      server/libhash/aeon/jh.c
  72. +21
    -0
      server/libhash/aeon/jh.h
  73. +112
    -0
      server/libhash/aeon/keccak.c
  74. +26
    -0
      server/libhash/aeon/keccak.h
  75. BIN
      server/libhash/aeon/libhash.so
  76. +53
    -0
      server/libhash/aeon/main.c
  77. +50
    -0
      server/libhash/aeon/oaes_config.h
  78. +1500
    -0
      server/libhash/aeon/oaes_lib.c
  79. +215
    -0
      server/libhash/aeon/oaes_lib.h
  80. +143
    -0
      server/libhash/aeon/random.c
  81. +35
    -0
      server/libhash/aeon/random.h
  82. +2036
    -0
      server/libhash/aeon/skein.c
  83. +47
    -0
      server/libhash/aeon/skein.h
  84. +218
    -0
      server/libhash/aeon/skein_port.h
  85. +1037
    -0
      server/libhash/aeon/slow-hash.c
  86. +8
    -0
      server/libhash/aeon/slow-hash.h
  87. +113
    -0
      server/libhash/aeon/tree-hash.c
  88. +23
    -0
      server/libhash/xmr/Makefile
  89. +185
    -0
      server/libhash/xmr/aesb.c
  90. +355
    -0
      server/libhash/xmr/blake256.c
  91. +73
    -0
      server/libhash/xmr/blake256.h
  92. +170
    -0
      server/libhash/xmr/chacha8.c
  93. +86
    -0
      server/libhash/xmr/chacha8.h
  94. +8
    -0
      server/libhash/xmr/cn.h
  95. BIN
      server/libhash/xmr/cn_hash.so
  96. +872
    -0
      server/libhash/xmr/crypto-ops-data.c
  97. +2925
    -0
      server/libhash/xmr/crypto-ops.c
  98. +145
    -0
      server/libhash/xmr/crypto-ops.h
  99. +220
    -0
      server/libhash/xmr/crypto.h
  100. +62
    -0
      server/libhash/xmr/generic-ops.h

+ 32
- 0
monero.license View File

@ -0,0 +1,32 @@
Copyright (c) 2014-2018, The Monero Project
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Parts of the project are originally copyright (c) 2012-2013 The Cryptonote
developers

+ 26
- 0
server/Server.sln View File

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
Release_XMR|Any CPU = Release_XMR|Any CPU
Release_AEON|Any CPU = Release_AEON|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}.Debug|x86.ActiveCfg = Debug|x86
{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}.Debug|x86.Build.0 = Debug|x86
{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}.Release_AEON|Any CPU.ActiveCfg = Release_AEON|Any CPU
{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}.Release_AEON|Any CPU.Build.0 = Release_AEON|Any CPU
{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}.Release_XMR|Any CPU.ActiveCfg = Release_XMR|Any CPU
{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}.Release_XMR|Any CPU.Build.0 = Release_XMR|Any CPU
{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}.Release|x86.ActiveCfg = Release|x86
{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = server\server.csproj
EndGlobalSection
EndGlobal

+ 26
- 0
server/Server.userprefs View File

@ -0,0 +1,26 @@
<Properties StartupConfiguration="{DC564972-9DEF-4897-A8F5-C4C21CEBDE2F}|Default" StartupItem="Server/Server.csproj">
<MonoDevelop.Ide.Workbench ActiveDocument="Server/PoolConnection.cs">
<Files>
<File FileName="Server/Program.cs" Line="1" Column="1" />
<File FileName="Server/PoolConnection.cs" Line="410" Column="4" />
<File FileName="Server/JSONParser.cs" Line="1" Column="1" />
<File FileName="Server/Extensions.cs" Line="1" Column="1" />
<File FileName="Server/EmptyWebsocket.cs" Line="1" Column="1" />
<File FileName="Server/Execute.cs" Line="1" Column="1" />
<File FileName="Server/DataStructures.cs" Line="1" Column="1" />
<File FileName="Server/Firewall.cs" Line="1" Column="1" />
<File FileName="Server/Properties/AssemblyInfo.cs" Line="1" Column="1" />
<File FileName="Server/Fleck/IntExtensions.cs" Line="1" Column="1" />
<File FileName="Server/Fleck/WebSocketServer.cs" Line="1" Column="1" />
</Files>
</MonoDevelop.Ide.Workbench>
<MonoDevelop.Ide.Workspace ActiveConfiguration="Release|x86" />
<MonoDevelop.Ide.DebuggingService.Breakpoints>
<BreakpointStore />
</MonoDevelop.Ide.DebuggingService.Breakpoints>
<MonoDevelop.Ide.DebuggingService.PinnedWatches>
<Watch file="testproj/Program.cs" line="302" offsetX="509" offsetY="5419" expression="forward" liveUpdate="False" />
</MonoDevelop.Ide.DebuggingService.PinnedWatches>
<MultiItemStartupConfigurations />
<MonoDevelop.Ide.ItemProperties.server PreferredExecutionTarget="MonoDevelop.Default" />
</Properties>

+ 50
- 0
server/Server/DataStructures.cs View File

@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Collections.Concurrent;
namespace Server
{
public class CcDictionary<T, V> : ConcurrentDictionary<T, V>
{
public bool TryRemove(T item)
{
V dummy;
return this.TryRemove(item, out dummy);
}
}
public class CcQueue<T> : ConcurrentQueue<T>
{
}
public class CcHashset<T>
{
ConcurrentDictionary<T, byte> dictionary = new ConcurrentDictionary<T, byte>();
public bool Add(T item)
{
return dictionary.TryAdd(item, byte.MaxValue);
}
public ICollection<T> Values
{
get { return dictionary.Keys; }
}
public bool Contains(T item)
{
return dictionary.ContainsKey(item);
}
public bool TryRemove(T item)
{
byte dummy;
return dictionary.TryRemove(item, out dummy);
}
}
}

+ 162
- 0
server/Server/EmptyWebsocket.cs View File

@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using Fleck;
namespace Server
{
public class EmptyConnectionInfo : IWebSocketConnectionInfo
{
#region IWebSocketConnectionInfo implementation
public string SubProtocol {
get {
throw new NotImplementedException ();
}
}
public string Origin {
get {
throw new NotImplementedException ();
}
}
public string Host {
get {
throw new NotImplementedException ();
}
}
public string Path {
get {
throw new NotImplementedException ();
}
}
public string ClientIpAddress {
get {
return "127.0.0.1";
}
}
public int ClientPort {
get {
throw new NotImplementedException ();
}
}
public IDictionary<string, string> Cookies {
get {
throw new NotImplementedException ();
}
}
public IDictionary<string, string> Headers {
get {
throw new NotImplementedException ();
}
}
public Guid Id {
get {
return Guid.Empty;
}
}
public string NegotiatedSubProtocol {
get {
throw new NotImplementedException ();
}
}
#endregion
}
public class EmptyWebsocket : IWebSocketConnection
{
private static EmptyConnectionInfo eci =
new EmptyConnectionInfo();
#region IWebSocketConnection implementation
public System.Threading.Tasks.Task Send (string message)
{
//throw new NotImplementedException ();
return null;
}
public System.Threading.Tasks.Task Send (byte[] message)
{
throw new NotImplementedException ();
}
public System.Threading.Tasks.Task SendPing (byte[] message)
{
throw new NotImplementedException ();
}
public System.Threading.Tasks.Task SendPong (byte[] message)
{
throw new NotImplementedException ();
}
public void Close ()
{
}
public Action OnOpen {
get {
throw new NotImplementedException ();
}
set {
throw new NotImplementedException ();
}
}
public Action OnClose {
get {
throw new NotImplementedException ();
}
set {
throw new NotImplementedException ();
}
}
public Action<string> OnMessage {
get {
throw new NotImplementedException ();
}
set {
throw new NotImplementedException ();
}
}
public Action<byte[]> OnBinary {
get {
throw new NotImplementedException ();
}
set {
throw new NotImplementedException ();
}
}
public Action<byte[]> OnPing {
get {
throw new NotImplementedException ();
}
set {
throw new NotImplementedException ();
}
}
public Action<byte[]> OnPong {
get {
throw new NotImplementedException ();
}
set {
throw new NotImplementedException ();
}
}
public Action<Exception> OnError {
get {
throw new NotImplementedException ();
}
set {
throw new NotImplementedException ();
}
}
public IWebSocketConnectionInfo ConnectionInfo {
get {
return EmptyWebsocket.eci;
}
}
public bool IsAvailable {
get {
return false;
}
}
#endregion
}
}

+ 19
- 0
server/Server/Execute.cs View File

@ -0,0 +1,19 @@
using System;
namespace Server
{
public static class Execute
{
public static void IgnoreExceptions(Action a)
{
try
{
a();
}
catch
{
}
}
}
}

+ 31
- 0
server/Server/Extensions.cs View File

@ -0,0 +1,31 @@
namespace System.Net.Sockets
{
public static class SocketExtensions
{
private const int BytesPerLong = 4;
private const int BitsPerByte = 8;
public static void SetKeepAlive(this Socket socket, UInt32 keepAliveInterval, UInt32 retryInterval)
{
int size = sizeof(UInt32);
UInt32 on = 1;
byte[] inArray = new byte[size * 3];
Array.Copy(BitConverter.GetBytes(on), 0, inArray, 0, size);
Array.Copy(BitConverter.GetBytes(keepAliveInterval), 0, inArray, size, size);
Array.Copy(BitConverter.GetBytes(retryInterval), 0, inArray, size * 2, size);
socket.IOControl(IOControlCode.KeepAliveValues, inArray, null);
}
}
public static class ObjectExtensionClass
{
public static string GetString(this object input)
{
return input == null ? string.Empty : input.ToString ();
}
}
}

+ 137
- 0
server/Server/Firewall.cs View File

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Text;
using System.IO;
namespace Server
{
public static class Firewall
{
public enum UpdateEntry
{
SolvedJob, AuthSuccess, AuthFailure, WrongHash, Handshake
}
private class Entry
{
public string Address;
public Entry(string adr)
{
Address = adr;
}
public int SolvedJobs = 0;
public int WrongHash = 0;
public int AuthSuccess = 0;
public int AuthFailure = 0;
public int Handshake = 0;
public DateTime FirstSeen = DateTime.Now;
}
private static ConcurrentDictionary<string,Entry> entries = new ConcurrentDictionary<string, Entry>();
public const int CheckTimeInHeartbeats = 6*10; // every 10min
private static int HeartBeats = 0;
private static void AddToIpTables(string ip)
{
WriteTextAsync ("ip_list", ip + Environment.NewLine);
}
private static async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.ASCII.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Append, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
};
}
public static void Update(string ip, UpdateEntry update)
{
Entry entry = null;
if (entries.TryGetValue (ip, out entry)) {
if (update == UpdateEntry.SolvedJob)
entry.SolvedJobs++;
else if (update == UpdateEntry.AuthFailure)
entry.AuthFailure++;
else if (update == UpdateEntry.AuthSuccess)
entry.AuthSuccess++;
else if (update == UpdateEntry.WrongHash)
entry.WrongHash++;
else if (update == UpdateEntry.Handshake)
entry.Handshake++;
} else
{
entries.TryAdd(ip,new Entry(ip));
}
}
public static void Heartbeat()
{
HeartBeats++;
Entry dummy;
List<Entry> entrylst = new List<Entry>(entries.Values);
foreach(Entry entry in entrylst)
{
// decide here...
if (entry.AuthSuccess == 0 && entry.SolvedJobs == 0
&& entry.AuthFailure > 20) {
AddToIpTables (entry.Address);
entries.TryRemove (entry.Address, out dummy);
Console.WriteLine ("Added {0} to iptables (rule #1)", entry.Address);
} else if (entry.AuthFailure > 500 && entry.AuthSuccess < 500)
{
AddToIpTables (entry.Address);
entries.TryRemove (entry.Address, out dummy);
Console.WriteLine ("Added {0} to iptables (rule #2)", entry.Address);
}
else if (entry.AuthSuccess + entry.AuthFailure > 1000 && entry.SolvedJobs < 3)
{
AddToIpTables (entry.Address);
entries.TryRemove (entry.Address, out dummy);
Console.WriteLine ("Added {0} to iptables (rule #3)", entry.Address);
}
else if (entry.AuthSuccess + entry.AuthFailure > 4000)
{
AddToIpTables (entry.Address);
entries.TryRemove (entry.Address, out dummy);
Console.WriteLine ("Added {0} to iptables (rule #4)", entry.Address);
}
else if (entry.WrongHash > 0 && entry.AuthSuccess < 5)
{
AddToIpTables (entry.Address);
entries.TryRemove (entry.Address, out dummy);
Console.WriteLine ("Added {0} to iptables (rule #5)", entry.Address);
}
else if (entry.AuthSuccess + entry.AuthFailure > 2000 && entry.Handshake < 1)
{
AddToIpTables (entry.Address);
entries.TryRemove (entry.Address, out dummy);
Console.WriteLine ("Added {0} to iptables (rule #6)", entry.Address);
}
}
if ((HeartBeats % CheckTimeInHeartbeats) == 0) {
entries.Clear ();
}
}
}
}

+ 78
- 0
server/Server/Fleck/BufferPool.cs View File

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
namespace Fleck
{
public static class BufferPool
{
public static int WebsocketClosed = 0;
public static int WebsocketCreated = 0;
private static Queue<byte[]> pooledBuffers = new Queue<byte[]>();
private static HashSet<byte[]> hs = new HashSet<byte[]>();
const int BufferSize = 1024 * 4;
public static int Created = 0;
public static int Returned = 0;
public static void InitBufferPool (int size = 10000)
{
while (size-- > 0) {
pooledBuffers.Enqueue(new byte[BufferSize]);
hs.Add (new byte[BufferSize]);
}
}
public static void ReturnBuffer(byte[] buffer)
{
lock (pooledBuffers)
{
if (hs.Contains (buffer))
return;
Returned++;
hs.Add (buffer);
System.Array.Clear (buffer, 0, BufferSize);
pooledBuffers.Enqueue (buffer);
}
}
public static byte[] RequestBuffer( byte[] data)
{
var retval = RequestBuffer ();
System.Array.Copy (data, retval, data.Length);
return retval;
}
public static byte[] RequestBuffer()
{
lock (pooledBuffers) {
Created++;
if (pooledBuffers.Count == 0) return new byte[BufferSize];
byte[] retval = pooledBuffers.Dequeue ();
hs.Remove (retval);
return retval;
}
}
}
}

+ 20
- 0
server/Server/Fleck/ConnectionNotAvailableException.cs View File

@ -0,0 +1,20 @@
using System;
namespace Fleck
{
public class ConnectionNotAvailableException : Exception
{
public ConnectionNotAvailableException() : base()
{
}
public ConnectionNotAvailableException(string message) : base(message)
{
}
public ConnectionNotAvailableException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

+ 141
- 0
server/Server/Fleck/Fleck.csproj View File

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8B12D929-AFA9-4307-BEFF-2ED0F1070FEE}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Fleck</RootNamespace>
<AssemblyName>Fleck</AssemblyName>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>False</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>True</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_XMR|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release_XMR</OutputPath>
<WarningLevel>4</WarningLevel>
<DefineConstants>TRACE</DefineConstants>
<DebugSymbols>true</DebugSymbols>
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_AEON|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release_AEON</OutputPath>
<WarningLevel>4</WarningLevel>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<Choose>
<When Condition="Exists('$(SolutionDir)\CommonAssemblyInfo.cs')">
<ItemGroup>
<Compile Include="$(SolutionDir)\CommonAssemblyInfo.cs">
<Link>Properties\CommonAssemblyInfo.cs</Link>
</Compile>
</ItemGroup>
</When>
</Choose>
<ItemGroup>
<Compile Include="ConnectionNotAvailableException.cs" />
<Compile Include="Handlers\ComposableHandler.cs" />
<Compile Include="Handlers\Draft76Handler.cs" />
<Compile Include="Handlers\FlashSocketPolicyRequestHandler.cs" />
<Compile Include="Handlers\Hybi13Handler.cs" />
<Compile Include="Interfaces\ISocket.cs" />
<Compile Include="Interfaces\IWebSocketConnection.cs" />
<Compile Include="FleckLog.cs" />
<Compile Include="Interfaces\IWebSocketServer.cs" />
<Compile Include="Interfaces\IWebSocketConnectionInfo.cs" />
<Compile Include="SocketWrapper.cs" />
<Compile Include="WebSocketConnectionInfo.cs" />
<Compile Include="WebSocketServer.cs" />
<Compile Include="WebSocketHttpRequest.cs" />
<Compile Include="RequestParser.cs" />
<Compile Include="HandlerFactory.cs" />
<Compile Include="WebSocketConnection.cs" />
<Compile Include="WebSocketException.cs" />
<Compile Include="IntExtensions.cs" />
<Compile Include="HandshakeException.cs" />
<Compile Include="FrameType.cs" />
<Compile Include="ReadState.cs" />
<Compile Include="Interfaces\IHandler.cs" />
<Compile Include="WebSocketStatusCodes.cs" />
<Compile Include="SubProtocolNegotiator.cs" />
<Compile Include="SubProtocolNegotiationFailureException.cs" />
<Compile Include="QueuedStream.cs" />
<Compile Include="Helpers\MonoHelper.cs" />
<Compile Include="Firewall.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<ItemGroup />
<ItemGroup>
<Folder Include="Helpers\" />
</ItemGroup>
</Project>

+ 44
- 0
server/Server/Fleck/FleckLog.cs View File

@ -0,0 +1,44 @@
using System;
namespace Fleck
{
public enum LogLevel
{
Debug,
Info,
Warn,
Error
}
public class FleckLog
{
public static LogLevel Level = LogLevel.Info;
public static Action<LogLevel, string, Exception> LogAction = (level, message, ex) =>
{
if (level >= Level)
Console.WriteLine("{0} [{1}] {2} {3}", DateTime.Now, level, message, ex);
};
public static void Warn(string message, Exception ex = null)
{
LogAction(LogLevel.Warn, message, ex);
}
public static void Error(string message, Exception ex = null)
{
LogAction(LogLevel.Error, message, ex);
}
public static void Debug(string message, Exception ex = null)
{
LogAction(LogLevel.Debug, message, ex);
}
public static void Info(string message, Exception ex = null)
{
LogAction(LogLevel.Info, message, ex);
}
}
}

+ 15
- 0
server/Server/Fleck/FrameType.cs View File

@ -0,0 +1,15 @@
using System;
namespace Fleck
{
public enum FrameType : byte
{
Continuation,
Text,
Binary,
Close = 8,
Ping = 9,
Pong = 10,
}
}

+ 46
- 0
server/Server/Fleck/HandlerFactory.cs View File

@ -0,0 +1,46 @@
using System;
using Fleck.Handlers;
namespace Fleck
{
public class HandlerFactory
{
public static IHandler BuildHandler(WebSocketHttpRequest request, Action<string> onMessage, Action onClose, Action<byte[]> onBinary, Action<byte[]> onPing, Action<byte[]> onPong)
{
var version = GetVersion(request);
switch (version)
{
case "76":
return Draft76Handler.Create(request, onMessage);
case "7":
case "8":
case "13":
return Hybi13Handler.Create(request, onMessage, onClose, onBinary, onPing, onPong);
case "policy-file-request":
return FlashSocketPolicyRequestHandler.Create(request);
}
throw new WebSocketException(WebSocketStatusCodes.UnsupportedDataType);
}
public static string GetVersion(WebSocketHttpRequest request)
{
string version;
if (request.Headers.TryGetValue("Sec-WebSocket-Version", out version))
return version;
if (request.Headers.TryGetValue("Sec-WebSocket-Draft", out version))
return version;
if (request.Headers.ContainsKey("Sec-WebSocket-Key1"))
return "76";
if ((request.Body != null) && request.Body.ToLower().Contains("policy-file-request"))
return "policy-file-request";
return "75";
}
}
}

+ 56
- 0
server/Server/Fleck/Handlers/ComposableHandler.cs View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
namespace Fleck.Handlers
{
public class ComposableHandler : IHandler
{
public Func<string, byte[]> Handshake = s => new byte[0];
public Func<string, byte[]> TextFrame = x => new byte[0];
public Func<byte[], byte[]> BinaryFrame = x => new byte[0];
public Action<List<byte>> ReceiveData = delegate { };
public Func<byte[], byte[]> PingFrame = i => new byte[0];
public Func<byte[], byte[]> PongFrame = i => new byte[0];
public Func<int, byte[]> CloseFrame = i => new byte[0];
private readonly List<byte> _data = new List<byte>();
public byte[] CreateHandshake(string subProtocol = null)
{
return Handshake(subProtocol);
}
public void Receive(IEnumerable<byte> data)
{
_data.AddRange(data);
ReceiveData(_data);
}
public byte[] FrameText(string text)
{
return TextFrame(text);
}
public byte[] FrameBinary(byte[] bytes)
{
return BinaryFrame(bytes);
}
public byte[] FramePing(byte[] bytes)
{
return PingFrame(bytes);
}
public byte[] FramePong(byte[] bytes)
{
return PongFrame(bytes);
}
public byte[] FrameClose(int code)
{
return CloseFrame(code);
}
}
}

+ 116
- 0
server/Server/Fleck/Handlers/Draft76Handler.cs View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace Fleck.Handlers
{
public static class Draft76Handler
{
private const byte End = 255;
private const byte Start = 0;
private const int MaxSize = 1024 * 1024 * 5;
public static IHandler Create(WebSocketHttpRequest request, Action<string> onMessage)
{
return new ComposableHandler
{
TextFrame = Draft76Handler.FrameText,
Handshake = sub => Draft76Handler.Handshake(request, sub),
ReceiveData = data => ReceiveData(onMessage, data)
};
}
public static void ReceiveData(Action<string> onMessage, List<byte> data)
{
while (data.Count > 0)
{
if (data[0] != Start)
throw new WebSocketException(WebSocketStatusCodes.InvalidFramePayloadData);
var endIndex = data.IndexOf(End);
if (endIndex < 0)
return;
if (endIndex > MaxSize)
throw new WebSocketException(WebSocketStatusCodes.MessageTooBig);
var bytes = data.Skip(1).Take(endIndex - 1).ToArray();
data.RemoveRange(0, endIndex + 1);
var message = Encoding.UTF8.GetString(bytes);
onMessage(message);
}
}
public static byte[] FrameText(string data)
{
byte[] bytes = Encoding.UTF8.GetBytes(data);
// wrap the array with the wrapper bytes
var wrappedBytes = new byte[bytes.Length + 2];
wrappedBytes[0] = Start;
wrappedBytes[wrappedBytes.Length - 1] = End;
Array.Copy(bytes, 0, wrappedBytes, 1, bytes.Length);
return wrappedBytes;
}
public static byte[] Handshake(WebSocketHttpRequest request, string subProtocol)
{
FleckLog.Debug("Building Draft76 Response");
var builder = new StringBuilder();
builder.Append("HTTP/1.1 101 WebSocket Protocol Handshake\r\n");
builder.Append("Upgrade: WebSocket\r\n");
builder.Append("Connection: Upgrade\r\n");
builder.AppendFormat("Sec-WebSocket-Origin: {0}\r\n", request["Origin"]);
builder.AppendFormat("Sec-WebSocket-Location: {0}://{1}{2}\r\n", request.Scheme, request["Host"], request.Path);
if (subProtocol != null)
builder.AppendFormat("Sec-WebSocket-Protocol: {0}\r\n", subProtocol);
builder.Append("\r\n");
var key1 = request["Sec-WebSocket-Key1"];
var key2 = request["Sec-WebSocket-Key2"];
var challenge = new ArraySegment<byte>(request.Bytes, request.Bytes.Length - 8, 8);
var answerBytes = CalculateAnswerBytes(key1, key2, challenge);
byte[] byteResponse = Encoding.ASCII.GetBytes(builder.ToString());
int byteResponseLength = byteResponse.Length;
Array.Resize(ref byteResponse, byteResponseLength + answerBytes.Length);
Array.Copy(answerBytes, 0, byteResponse, byteResponseLength, answerBytes.Length);
return byteResponse;
}
public static byte[] CalculateAnswerBytes(string key1, string key2, ArraySegment<byte> challenge)
{
byte[] result1Bytes = ParseKey(key1);
byte[] result2Bytes = ParseKey(key2);
var rawAnswer = new byte[16];
Array.Copy(result1Bytes, 0, rawAnswer, 0, 4);
Array.Copy(result2Bytes, 0, rawAnswer, 4, 4);
Array.Copy(challenge.Array, challenge.Offset, rawAnswer, 8, 8);
return MD5.Create().ComputeHash(rawAnswer);
}
private static byte[] ParseKey(string key)
{
int spaces = key.Count(x => x == ' ');
var digits = new String(key.Where(Char.IsDigit).ToArray());
var value = (Int32)(Int64.Parse(digits) / spaces);
byte[] result = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
Array.Reverse(result);
return result;
}
}
}

+ 32
- 0
server/Server/Fleck/Handlers/FlashSocketPolicyRequestHandler.cs View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Fleck.Handlers
{
public class FlashSocketPolicyRequestHandler
{
public static string PolicyResponse =
"<?xml version=\"1.0\"?>\n" +
"<cross-domain-policy>\n" +
" <allow-access-from domain=\"*\" to-ports=\"*\"/>\n" +
" <site-control permitted-cross-domain-policies=\"all\"/>\n" +
"</cross-domain-policy>\n" +
"\0";
public static IHandler Create(WebSocketHttpRequest request)
{
return new ComposableHandler
{
Handshake = sub => FlashSocketPolicyRequestHandler.Handshake(request, sub),
};
}
public static byte[] Handshake(WebSocketHttpRequest request, string subProtocol)
{
FleckLog.Debug("Building Flash Socket Policy Response");
return Encoding.UTF8.GetBytes(PolicyResponse);
}
}
}

+ 208
- 0
server/Server/Fleck/Handlers/Hybi13Handler.cs View File

@ -0,0 +1,208 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace Fleck.Handlers
{
public static class Hybi13Handler
{
public static IHandler Create(WebSocketHttpRequest request, Action<string> onMessage, Action onClose, Action<byte[]> onBinary, Action<byte[]> onPing, Action<byte[]> onPong)
{
var readState = new ReadState();
return new ComposableHandler
{
Handshake = sub => Hybi13Handler.BuildHandshake(request, sub),
TextFrame = s => Hybi13Handler.FrameData(Encoding.UTF8.GetBytes(s), FrameType.Text),
BinaryFrame = s => Hybi13Handler.FrameData(s, FrameType.Binary),
PingFrame = s => Hybi13Handler.FrameData(s, FrameType.Ping),
PongFrame = s => Hybi13Handler.FrameData(s, FrameType.Pong),
CloseFrame = i => Hybi13Handler.FrameData(i.ToBigEndianBytes<ushort>(), FrameType.Close),
ReceiveData = d => Hybi13Handler.ReceiveData(d, readState, (op, data) => Hybi13Handler.ProcessFrame(op, data, onMessage, onClose, onBinary, onPing, onPong))
};
}
public static byte[] FrameData(byte[] payload, FrameType frameType)
{
var memoryStream = new MemoryStream();
byte op = (byte)((byte)frameType + 128);
memoryStream.WriteByte(op);
if (payload.Length > UInt16.MaxValue) {
memoryStream.WriteByte(127);
var lengthBytes = payload.Length.ToBigEndianBytes<ulong>();
memoryStream.Write(lengthBytes, 0, lengthBytes.Length);
} else if (payload.Length > 125) {
memoryStream.WriteByte(126);
var lengthBytes = payload.Length.ToBigEndianBytes<ushort>();
memoryStream.Write(lengthBytes, 0, lengthBytes.Length);
} else {
memoryStream.WriteByte((byte)payload.Length);
}
memoryStream.Write(payload, 0, payload.Length);
return memoryStream.ToArray();
}
public static void ReceiveData(List<byte> data, ReadState readState, Action<FrameType, byte[]> processFrame)
{
while (data.Count >= 2)
{
var isFinal = (data[0] & 128) != 0;
var reservedBits = (data[0] & 112);
var frameType = (FrameType)(data[0] & 15);
var isMasked = (data[1] & 128) != 0;
var length = (data[1] & 127);
if (!isMasked
|| !Enum.IsDefined(typeof(FrameType), frameType)
|| reservedBits != 0 //Must be zero per spec 5.2
|| (frameType == FrameType.Continuation && !readState.FrameType.HasValue))
throw new WebSocketException(WebSocketStatusCodes.ProtocolError);
var index = 2;
int payloadLength;
if (length == 127)
{
if (data.Count < index + 8)
return; //Not complete
payloadLength = data.Skip(index).Take(8).ToArray().ToLittleEndianInt();
index += 8;
}
else if (length == 126)
{
if (data.Count < index + 2)
return; //Not complete
payloadLength = data.Skip(index).Take(2).ToArray().ToLittleEndianInt();
index += 2;
}
else
{
payloadLength = length;
}
if (data.Count < index + 4)
return; //Not complete
var maskBytes = data.Skip(index).Take(4).ToArray();
index += 4;
if (data.Count < index + payloadLength)
return; //Not complete
var payload = data
.Skip(index)
.Take(payloadLength)
.Select((x, i) => (byte)(x ^ maskBytes[i % 4]));
readState.Data.AddRange(payload);
data.RemoveRange(0, index + payloadLength);
if (frameType != FrameType.Continuation)
readState.FrameType = frameType;
if (isFinal && readState.FrameType.HasValue)
{
var stateData = readState.Data.ToArray();
var stateFrameType = readState.FrameType;
readState.Clear();
processFrame(stateFrameType.Value, stateData);
}
}
}
public static void ProcessFrame(FrameType frameType, byte[] data, Action<string> onMessage, Action onClose, Action<byte[]> onBinary, Action<byte[]> onPing, Action<byte[]> onPong)
{
switch (frameType)
{
case FrameType.Close:
if (data.Length == 1 || data.Length>125)
throw new WebSocketException(WebSocketStatusCodes.ProtocolError);
if (data.Length >= 2)
{
var closeCode = (ushort)data.Take(2).ToArray().ToLittleEndianInt();
if (!WebSocketStatusCodes.ValidCloseCodes.Contains(closeCode) && (closeCode < 3000 || closeCode > 4999))
throw new WebSocketException(WebSocketStatusCodes.ProtocolError);
}
if (data.Length > 2)
ReadUTF8PayloadData(data.Skip(2).ToArray());
onClose();
break;
case FrameType.Binary:
onBinary(data);
break;
case FrameType.Ping:
onPing(data);
break;
case FrameType.Pong:
onPong(data);
break;
case FrameType.Text:
onMessage(ReadUTF8PayloadData(data));
break;
default:
FleckLog.Debug("Received unhandled " + frameType);
break;
}
}
public static byte[] BuildHandshake(WebSocketHttpRequest request, string subProtocol)
{
FleckLog.Debug("Building Hybi-14 Response");
var builder = new StringBuilder();
builder.Append("HTTP/1.1 101 Switching Protocols\r\n");
builder.Append("Upgrade: websocket\r\n");
builder.Append("Connection: Upgrade\r\n");
if (subProtocol != null)
builder.AppendFormat("Sec-WebSocket-Protocol: {0}\r\n", subProtocol);
var responseKey = CreateResponseKey(request["Sec-WebSocket-Key"]);
builder.AppendFormat("Sec-WebSocket-Accept: {0}\r\n", responseKey);
builder.Append("\r\n");
return Encoding.ASCII.GetBytes(builder.ToString());
}
private const string WebSocketResponseGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
public static string CreateResponseKey(string requestKey)
{
var combined = requestKey + WebSocketResponseGuid;
var bytes = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(combined));
return Convert.ToBase64String(bytes);
}
private static string ReadUTF8PayloadData(byte[] bytes)
{
var encoding = new UTF8Encoding(false, true);
try
{
return encoding.GetString(bytes);
}
catch(ArgumentException)
{
throw new WebSocketException(WebSocketStatusCodes.InvalidFramePayloadData);
}
}
}
}

+ 14
- 0
server/Server/Fleck/HandshakeException.cs View File

@ -0,0 +1,14 @@
using System;
namespace Fleck
{
public class HandshakeException : Exception
{
public HandshakeException() : base() { }
public HandshakeException(string message) : base(message) {}
public HandshakeException(string message, Exception innerException) : base(message, innerException) {}
}
}

+ 13
- 0
server/Server/Fleck/Helpers/MonoHelper.cs View File

@ -0,0 +1,13 @@
using System;
namespace Fleck.Helpers
{
public static class MonoHelper
{
public static bool IsRunningOnMono ()
{
return Type.GetType ("Mono.Runtime") != null;
}
}
}

+ 41
- 0
server/Server/Fleck/IntExtensions.cs View File

@ -0,0 +1,41 @@
using System;
namespace Fleck
{
public static class IntExtensions
{
public static byte[] ToBigEndianBytes<T>(this int source)
{
byte[] bytes;
var type = typeof(T);
if (type == typeof(ushort))
bytes = BitConverter.GetBytes((ushort)source);
else if (type == typeof(ulong))
bytes = BitConverter.GetBytes((ulong)source);
else if (type == typeof(int))
bytes = BitConverter.GetBytes(source);
else
throw new InvalidCastException("Cannot be cast to T");
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return bytes;
}
public static int ToLittleEndianInt(this byte[] source)
{
if(BitConverter.IsLittleEndian)
Array.Reverse(source);
if(source.Length == 2)
return BitConverter.ToUInt16(source, 0);
if(source.Length == 8)
return (int)BitConverter.ToUInt64(source, 0);
throw new ArgumentException("Unsupported Size");
}
}
}

+ 16
- 0
server/Server/Fleck/Interfaces/IHandler.cs View File

@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace Fleck
{
public interface IHandler
{
byte[] CreateHandshake(string subProtocol = null);
void Receive(IEnumerable<byte> data);
byte[] FrameText(string text);
byte[] FrameBinary(byte[] bytes);
byte[] FramePing(byte[] bytes);
byte[] FramePong(byte[] bytes);
byte[] FrameClose(int code);
}
}

+ 31
- 0
server/Server/Fleck/Interfaces/ISocket.cs View File

@ -0,0 +1,31 @@
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace Fleck
{
public interface ISocket
{
bool Connected { get; }
string RemoteIpAddress { get; }
int RemotePort { get; }
Stream Stream { get; }
bool NoDelay { get; set; }
EndPoint LocalEndPoint { get; }
Task<ISocket> Accept(Action<ISocket> callback, Action<Exception> error);
Task Send(byte[] buffer, Action callback, Action<Exception> error);
Task<int> Receive(byte[] buffer, Action<int> callback, Action<Exception> error, int offset = 0);
Task Authenticate(X509Certificate2 certificate, SslProtocols enabledSslProtocols, Action callback, Action<Exception> error);
void Dispose();
void Close();
void Bind(EndPoint ipLocal);
void Listen(int backlog);
}
}

+ 23
- 0
server/Server/Fleck/Interfaces/IWebSocketConnection.cs View File

@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
namespace Fleck
{
public interface IWebSocketConnection
{
Action OnOpen { get; set; }
Action OnClose { get; set; }
Action<string> OnMessage { get; set; }
Action<byte[]> OnBinary { get; set; }
Action<byte[]> OnPing { get; set; }
Action<byte[]> OnPong { get; set; }
Action<Exception> OnError { get; set; }
Task Send(string message);
Task Send(byte[] message);
Task SendPing(byte[] message);
Task SendPong(byte[] message);
void Close();
IWebSocketConnectionInfo ConnectionInfo { get; }
bool IsAvailable { get; }
}
}

+ 19
- 0
server/Server/Fleck/Interfaces/IWebSocketConnectionInfo.cs View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System;
namespace Fleck
{
public interface IWebSocketConnectionInfo
{
string SubProtocol { get; }
string Origin { get; }
string Host { get; }
string Path { get; }
string ClientIpAddress { get; }
int ClientPort { get; }
IDictionary<string, string> Cookies { get; }
IDictionary<string, string> Headers { get; }
Guid Id { get; }
string NegotiatedSubProtocol { get; }
}
}

+ 9
- 0
server/Server/Fleck/Interfaces/IWebSocketServer.cs View File

@ -0,0 +1,9 @@
using System;
namespace Fleck
{
public interface IWebSocketServer : IDisposable
{
void Start(Action<IWebSocketConnection> config);
}
}

+ 257
- 0
server/Server/Fleck/QueuedStream.cs View File

@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace Fleck
{
/// <summary>
/// Wraps a stream and queues multiple write operations.
/// Useful for wrapping SslStream as it does not support multiple simultaneous write operations.
/// </summary>
public class QueuedStream : Stream
{
readonly Stream _stream;
readonly Queue<WriteData> _queue = new Queue<WriteData>();
int _pendingWrite;
bool _disposed;
public QueuedStream(Stream stream)
{
_stream = stream;
}
public override bool CanRead
{
get { return _stream.CanRead; }
}
public override bool CanSeek
{
get { return _stream.CanSeek; }
}
public override bool CanWrite
{
get { return _stream.CanWrite; }
}
public override long Length
{
get { return _stream.Length; }
}
public override long Position
{
get { return _stream.Position; }
set { _stream.Position = value; }
}
public override int Read(byte[] buffer, int offset, int count)
{
return _stream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return _stream.Seek(offset, origin);
}
public override void SetLength(long value)
{
_stream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException("QueuedStream does not support synchronous write operations yet.");
}
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
return _stream.BeginRead(buffer, offset, count, callback, state);
}
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
lock (_queue)
{
var data = new WriteData(buffer, offset, count, callback, state);
if (_pendingWrite > 0)
{
_queue.Enqueue(data);
return data.AsyncResult;
}
return BeginWriteInternal(buffer, offset, count, callback, state, data);
}
}
public override int EndRead(IAsyncResult asyncResult)
{
return _stream.EndRead(asyncResult);
}
public override void EndWrite(IAsyncResult asyncResult)
{
if (asyncResult is QueuedWriteResult)
{
var queuedResult = asyncResult as QueuedWriteResult;
if (queuedResult.Exception != null) throw queuedResult.Exception;
var ar = queuedResult.ActualResult;
if (ar == null)
{
throw new NotSupportedException(
"QueuedStream does not support synchronous write operations. Please wait for callback to be invoked before calling EndWrite.");
}
// EndWrite on actual stream should already be invoked.
}
else
{
throw new ArgumentException();
}
}
public override void Flush()
{
_stream.Flush();
}
public override void Close()
{
_stream.Close();
}
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_stream.Dispose();
}
_disposed = true;
}
//_queue.Clear ();
foreach (var item in _queue) {
item.NullBuffer ();
}
_queue.Clear ();
base.Dispose(disposing);
}
IAsyncResult BeginWriteInternal(byte[] buffer, int offset, int count, AsyncCallback callback, object state, WriteData queued)
{
_pendingWrite++;
var result = _stream.BeginWrite(buffer, offset, count, ar =>
{
// callback can be executed even before return value of BeginWriteInternal is set to this property
queued.AsyncResult.ActualResult = ar;
try
{
// so that we can call BeginWrite again
_stream.EndWrite(ar);
}
catch (Exception exc)
{
queued.AsyncResult.Exception = exc;
}
// one down, another is good to go
lock (_queue)
{
_pendingWrite--;
while (_queue.Count > 0)
{
var data = _queue.Dequeue();
try
{
data.AsyncResult.ActualResult = BeginWriteInternal(data.Buffer, data.Offset, data.Count, data.Callback, data.State, data);
break;
}
catch (Exception exc)
{
_pendingWrite--;
data.AsyncResult.Exception = exc;
data.Callback(data.AsyncResult);
}
}
callback(queued.AsyncResult);
}
}, state);
// always return the wrapped async result.
// this is especially important if the underlying stream completed the operation synchronously (hence "result.CompletedSynchronously" is true!)
queued.AsyncResult.ActualResult = result;
return queued.AsyncResult;
}
#region Nested type: WriteData