mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-04 03:27:21 -05:00 
			
		
		
		
	Remove SocketHttpListener
This commit is contained in:
		
							parent
							
								
									848cfc32cc
								
							
						
					
					
						commit
						77addb2283
					
				@ -9,7 +9,6 @@
 | 
				
			|||||||
    <ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj" />
 | 
					    <ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
 | 
					    <ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
 | 
					    <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\SocketHttpListener\SocketHttpListener.csproj" />
 | 
					 | 
				
			||||||
    <ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
 | 
					    <ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj" />
 | 
					    <ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
 | 
					    <ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
 | 
				
			||||||
@ -40,7 +39,6 @@
 | 
				
			|||||||
    <PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
 | 
					    <PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
 | 
				
			||||||
    <PackageReference Include="sharpcompress" Version="0.22.0" />
 | 
					    <PackageReference Include="sharpcompress" Version="0.22.0" />
 | 
				
			||||||
    <PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
 | 
					    <PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
 | 
				
			||||||
    <PackageReference Include="System.Buffers" Version="4.5.0" />
 | 
					 | 
				
			||||||
    <PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
 | 
					    <PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -23,9 +23,8 @@ namespace Emby.Server.Implementations.SocketSharp
 | 
				
			|||||||
        /// <value>The web socket.</value>
 | 
					        /// <value>The web socket.</value>
 | 
				
			||||||
        private readonly WebSocket _webSocket;
 | 
					        private readonly WebSocket _webSocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
 | 
					 | 
				
			||||||
        private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
 | 
					        private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
 | 
				
			||||||
        private bool _disposed = false;
 | 
					        private bool _disposed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public SharpWebSocket(WebSocket socket, ILogger logger)
 | 
					        public SharpWebSocket(WebSocket socket, ILogger logger)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Http.Extensions;
 | 
				
			|||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
using Microsoft.Extensions.Primitives;
 | 
					using Microsoft.Extensions.Primitives;
 | 
				
			||||||
using Microsoft.Net.Http.Headers;
 | 
					using Microsoft.Net.Http.Headers;
 | 
				
			||||||
using SocketHttpListener.Net;
 | 
					 | 
				
			||||||
using IHttpFile = MediaBrowser.Model.Services.IHttpFile;
 | 
					using IHttpFile = MediaBrowser.Model.Services.IHttpFile;
 | 
				
			||||||
using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
 | 
					using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
 | 
				
			||||||
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
 | 
					using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
 | 
					
 | 
				
			||||||
 | 
					Microsoft Visual Studio Solution File, Format Version 12.00
 | 
				
			||||||
# Visual Studio 15
 | 
					# Visual Studio 15
 | 
				
			||||||
VisualStudioVersion = 15.0.26730.3
 | 
					VisualStudioVersion = 15.0.26730.3
 | 
				
			||||||
MinimumVisualStudioVersion = 10.0.40219.1
 | 
					MinimumVisualStudioVersion = 10.0.40219.1
 | 
				
			||||||
@ -36,8 +37,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Dlna", "Emby.Dlna\Emby
 | 
				
			|||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{CB7F2326-6497-4A3D-BA03-48513B17A7BE}"
 | 
					Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{CB7F2326-6497-4A3D-BA03-48513B17A7BE}"
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SocketHttpListener", "SocketHttpListener\SocketHttpListener.csproj", "{1D74413B-E7CF-455B-B021-F52BDF881542}"
 | 
					 | 
				
			||||||
EndProject
 | 
					 | 
				
			||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Notifications", "Emby.Notifications\Emby.Notifications.csproj", "{2E030C33-6923-4530-9E54-FA29FA6AD1A9}"
 | 
					Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Notifications", "Emby.Notifications\Emby.Notifications.csproj", "{2E030C33-6923-4530-9E54-FA29FA6AD1A9}"
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Naming", "Emby.Naming\Emby.Naming.csproj", "{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}"
 | 
					Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Naming", "Emby.Naming\Emby.Naming.csproj", "{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}"
 | 
				
			||||||
@ -56,7 +55,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 | 
				
			|||||||
		SharedVersion.cs = SharedVersion.cs
 | 
							SharedVersion.cs = SharedVersion.cs
 | 
				
			||||||
	EndProjectSection
 | 
						EndProjectSection
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Drawing.Skia", "Jellyfin.Drawing.Skia\Jellyfin.Drawing.Skia.csproj", "{154872D9-6C12-4007-96E3-8F70A58386CE}"
 | 
					Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Drawing.Skia", "Jellyfin.Drawing.Skia\Jellyfin.Drawing.Skia.csproj", "{154872D9-6C12-4007-96E3-8F70A58386CE}"
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
Global
 | 
					Global
 | 
				
			||||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
						GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
				
			||||||
@ -132,10 +131,6 @@ Global
 | 
				
			|||||||
		{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
							{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
		{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
							{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
		{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
							{CB7F2326-6497-4A3D-BA03-48513B17A7BE}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
		{1D74413B-E7CF-455B-B021-F52BDF881542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
					 | 
				
			||||||
		{1D74413B-E7CF-455B-B021-F52BDF881542}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
					 | 
				
			||||||
		{1D74413B-E7CF-455B-B021-F52BDF881542}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
					 | 
				
			||||||
		{1D74413B-E7CF-455B-B021-F52BDF881542}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
					 | 
				
			||||||
		{2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
							{2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
		{2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
							{2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
		{2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
							{2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
				
			|||||||
@ -1,17 +0,0 @@
 | 
				
			|||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Contains the values that indicate whether the byte order is a Little-endian or Big-endian.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public enum ByteOrder : byte
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Indicates a Little-endian.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Little,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Indicates a Big-endian.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Big
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,79 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Contains the event data associated with a <see cref="WebSocket.OnClose"/> event.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <remarks>
 | 
					 | 
				
			||||||
    /// A <see cref="WebSocket.OnClose"/> event occurs when the WebSocket connection has been closed.
 | 
					 | 
				
			||||||
    /// If you would like to get the reason for the close, you should access the <see cref="Code"/> or
 | 
					 | 
				
			||||||
    /// <see cref="Reason"/> property.
 | 
					 | 
				
			||||||
    /// </remarks>
 | 
					 | 
				
			||||||
    public class CloseEventArgs : EventArgs
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Private Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private bool _clean;
 | 
					 | 
				
			||||||
        private ushort _code;
 | 
					 | 
				
			||||||
        private string _reason;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal CloseEventArgs(PayloadData payload)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var data = payload.ApplicationData;
 | 
					 | 
				
			||||||
            var len = data.Length;
 | 
					 | 
				
			||||||
            _code = len > 1
 | 
					 | 
				
			||||||
                    ? data.SubArray(0, 2).ToUInt16(ByteOrder.Big)
 | 
					 | 
				
			||||||
                    : (ushort)CloseStatusCode.NoStatusCode;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _reason = len > 2
 | 
					 | 
				
			||||||
                      ? GetUtf8String(data.SubArray(2, len - 2))
 | 
					 | 
				
			||||||
                      : string.Empty;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string GetUtf8String(byte[] bytes)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the status code for the close.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <value>
 | 
					 | 
				
			||||||
        /// A <see cref="ushort"/> that represents the status code for the close if any.
 | 
					 | 
				
			||||||
        /// </value>
 | 
					 | 
				
			||||||
        public ushort Code => _code;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the reason for the close.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <value>
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the reason for the close if any.
 | 
					 | 
				
			||||||
        /// </value>
 | 
					 | 
				
			||||||
        public string Reason => _reason;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets a value indicating whether the WebSocket connection has been closed cleanly.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <value>
 | 
					 | 
				
			||||||
        /// <c>true</c> if the WebSocket connection has been closed cleanly; otherwise, <c>false</c>.
 | 
					 | 
				
			||||||
        /// </value>
 | 
					 | 
				
			||||||
        public bool WasClean
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get => _clean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            internal set => _clean = value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,94 +0,0 @@
 | 
				
			|||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Contains the values of the status code for the WebSocket connection close.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <remarks>
 | 
					 | 
				
			||||||
    ///   <para>
 | 
					 | 
				
			||||||
    ///   The values of the status code are defined in
 | 
					 | 
				
			||||||
    ///   <see href="http://tools.ietf.org/html/rfc6455#section-7.4">Section 7.4</see>
 | 
					 | 
				
			||||||
    ///   of RFC 6455.
 | 
					 | 
				
			||||||
    ///   </para>
 | 
					 | 
				
			||||||
    ///   <para>
 | 
					 | 
				
			||||||
    ///   "Reserved value" must not be set as a status code in a close control frame
 | 
					 | 
				
			||||||
    ///   by an endpoint. It's designated for use in applications expecting a status
 | 
					 | 
				
			||||||
    ///   code to indicate that the connection was closed due to the system grounds.
 | 
					 | 
				
			||||||
    ///   </para>
 | 
					 | 
				
			||||||
    /// </remarks>
 | 
					 | 
				
			||||||
    public enum CloseStatusCode : ushort
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1000.
 | 
					 | 
				
			||||||
        /// Indicates a normal close.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Normal = 1000,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1001.
 | 
					 | 
				
			||||||
        /// Indicates that an endpoint is going away.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Away = 1001,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1002.
 | 
					 | 
				
			||||||
        /// Indicates that an endpoint is terminating the connection due to a protocol error.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        ProtocolError = 1002,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1003.
 | 
					 | 
				
			||||||
        /// Indicates that an endpoint is terminating the connection because it has received
 | 
					 | 
				
			||||||
        /// an unacceptable type message.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        IncorrectData = 1003,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1004.
 | 
					 | 
				
			||||||
        /// Still undefined. Reserved value.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Undefined = 1004,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1005.
 | 
					 | 
				
			||||||
        /// Indicates that no status code was actually present. Reserved value.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        NoStatusCode = 1005,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1006.
 | 
					 | 
				
			||||||
        /// Indicates that the connection was closed abnormally. Reserved value.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Abnormal = 1006,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1007.
 | 
					 | 
				
			||||||
        /// Indicates that an endpoint is terminating the connection because it has received
 | 
					 | 
				
			||||||
        /// a message that contains a data that isn't consistent with the type of the message.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        InconsistentData = 1007,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1008.
 | 
					 | 
				
			||||||
        /// Indicates that an endpoint is terminating the connection because it has received
 | 
					 | 
				
			||||||
        /// a message that violates its policy.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        PolicyViolation = 1008,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1009.
 | 
					 | 
				
			||||||
        /// Indicates that an endpoint is terminating the connection because it has received
 | 
					 | 
				
			||||||
        /// a message that is too big to process.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        TooBig = 1009,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1010.
 | 
					 | 
				
			||||||
        /// Indicates that the 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 handshake response.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        IgnoreExtension = 1010,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1011.
 | 
					 | 
				
			||||||
        /// Indicates that the server is terminating the connection because it has encountered
 | 
					 | 
				
			||||||
        /// an unexpected condition that prevented it from fulfilling the request.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        ServerError = 1011,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to close status 1015.
 | 
					 | 
				
			||||||
        /// Indicates that the connection was closed due to a failure to perform a TLS handshake.
 | 
					 | 
				
			||||||
        /// Reserved value.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        TlsHandshakeFailure = 1015
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Contains the values of the compression method used to compress the message on the WebSocket
 | 
					 | 
				
			||||||
    /// connection.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <remarks>
 | 
					 | 
				
			||||||
    /// The values of the compression method are defined in
 | 
					 | 
				
			||||||
    /// <see href="http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-09">Compression
 | 
					 | 
				
			||||||
    /// Extensions for WebSocket</see>.
 | 
					 | 
				
			||||||
    /// </remarks>
 | 
					 | 
				
			||||||
    public enum CompressionMethod : byte
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Indicates non compression.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        None,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Indicates using DEFLATE.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Deflate
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,42 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Contains the event data associated with a <see cref="WebSocket.OnError"/> event.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <remarks>
 | 
					 | 
				
			||||||
    /// A <see cref="WebSocket.OnError"/> event occurs when the <see cref="WebSocket"/> gets an error.
 | 
					 | 
				
			||||||
    /// If you would like to get the error message, you should access the <see cref="Message"/>
 | 
					 | 
				
			||||||
    /// property.
 | 
					 | 
				
			||||||
    /// </remarks>
 | 
					 | 
				
			||||||
    public class ErrorEventArgs : EventArgs
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Private Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private string _message;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal ErrorEventArgs(string message)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _message = message;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the error message.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <value>
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the error message.
 | 
					 | 
				
			||||||
        /// </value>
 | 
					 | 
				
			||||||
        public string Message => _message;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,946 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using System.IO.Compression;
 | 
					 | 
				
			||||||
using System.Net;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Services;
 | 
					 | 
				
			||||||
using WebSocketState = System.Net.WebSockets.WebSocketState;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Provides a set of static methods for the websocket-sharp.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public static class Ext
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Private Const Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private const string _tspecials = "()<>@,;:\\\"/[]?={} \t";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Private Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static MemoryStream compress(this Stream stream)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var output = new MemoryStream();
 | 
					 | 
				
			||||||
            if (stream.Length == 0)
 | 
					 | 
				
			||||||
                return output;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            stream.Position = 0;
 | 
					 | 
				
			||||||
            using (var ds = new DeflateStream(output, CompressionMode.Compress, true))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                stream.CopyTo(ds);
 | 
					 | 
				
			||||||
                //ds.Close(); // "BFINAL" set to 1.
 | 
					 | 
				
			||||||
                output.Position = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return output;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static byte[] decompress(this byte[] value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (value.Length == 0)
 | 
					 | 
				
			||||||
                return value;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            using (var input = new MemoryStream(value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return input.decompressToArray();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static MemoryStream decompress(this Stream stream)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var output = new MemoryStream();
 | 
					 | 
				
			||||||
            if (stream.Length == 0)
 | 
					 | 
				
			||||||
                return output;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            stream.Position = 0;
 | 
					 | 
				
			||||||
            using (var ds = new DeflateStream(stream, CompressionMode.Decompress, true))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ds.CopyTo(output, true);
 | 
					 | 
				
			||||||
                return output;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static byte[] decompressToArray(this Stream stream)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            using (var decomp = stream.decompress())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return decomp.ToArray();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static async Task<byte[]> ReadBytesAsync(this Stream stream, byte[] buffer, int offset, int length)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var len = await stream.ReadAsync(buffer, offset, length).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            if (len < 1)
 | 
					 | 
				
			||||||
                return buffer.SubArray(0, offset);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var tmp = 0;
 | 
					 | 
				
			||||||
            while (len < length)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                tmp = await stream.ReadAsync(buffer, offset + len, length - len).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                if (tmp < 1)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                len += tmp;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return len < length
 | 
					 | 
				
			||||||
                   ? buffer.SubArray(0, offset + len)
 | 
					 | 
				
			||||||
                   : buffer;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static async Task<bool> ReadBytesAsync(this Stream stream, byte[] buffer, int offset, int length, Stream dest)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var bytes = await stream.ReadBytesAsync(buffer, offset, length).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            var len = bytes.Length;
 | 
					 | 
				
			||||||
            dest.Write(bytes, 0, len);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return len == offset + length;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static async Task<byte[]> AppendAsync(this ushort code, string reason)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            using (var buffer = new MemoryStream())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var tmp = code.ToByteArrayInternally(ByteOrder.Big);
 | 
					 | 
				
			||||||
                await buffer.WriteAsync(tmp, 0, 2).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                if (reason != null && reason.Length > 0)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    tmp = Encoding.UTF8.GetBytes(reason);
 | 
					 | 
				
			||||||
                    await buffer.WriteAsync(tmp, 0, tmp.Length).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return buffer.ToArray();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string CheckIfClosable(this WebSocketState state)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return state == WebSocketState.CloseSent
 | 
					 | 
				
			||||||
                   ? "While closing the WebSocket connection."
 | 
					 | 
				
			||||||
                   : state == WebSocketState.Closed
 | 
					 | 
				
			||||||
                     ? "The WebSocket connection has already been closed."
 | 
					 | 
				
			||||||
                     : null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string CheckIfOpen(this WebSocketState state)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return state == WebSocketState.Connecting
 | 
					 | 
				
			||||||
                   ? "A WebSocket connection isn't established."
 | 
					 | 
				
			||||||
                   : state == WebSocketState.CloseSent
 | 
					 | 
				
			||||||
                     ? "While closing the WebSocket connection."
 | 
					 | 
				
			||||||
                     : state == WebSocketState.Closed
 | 
					 | 
				
			||||||
                       ? "The WebSocket connection has already been closed."
 | 
					 | 
				
			||||||
                       : null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string CheckIfValidControlData(this byte[] data, string paramName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return data.Length > 125
 | 
					 | 
				
			||||||
                   ? string.Format("'{0}' length must be less.", paramName)
 | 
					 | 
				
			||||||
                   : null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static Stream Compress(this Stream stream, CompressionMethod method)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return method == CompressionMethod.Deflate
 | 
					 | 
				
			||||||
                   ? stream.compress()
 | 
					 | 
				
			||||||
                   : stream;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static bool Contains<T>(this IEnumerable<T> source, Func<T, bool> condition)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            foreach (T elm in source)
 | 
					 | 
				
			||||||
                if (condition(elm))
 | 
					 | 
				
			||||||
                    return true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static void CopyTo(this Stream src, Stream dest, bool setDefaultPosition)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var readLen = 0;
 | 
					 | 
				
			||||||
            var bufferLen = 256;
 | 
					 | 
				
			||||||
            var buffer = new byte[bufferLen];
 | 
					 | 
				
			||||||
            while ((readLen = src.Read(buffer, 0, bufferLen)) > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                dest.Write(buffer, 0, readLen);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (setDefaultPosition)
 | 
					 | 
				
			||||||
                dest.Position = 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static byte[] Decompress(this byte[] value, CompressionMethod method)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return method == CompressionMethod.Deflate
 | 
					 | 
				
			||||||
                   ? value.decompress()
 | 
					 | 
				
			||||||
                   : value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return method == CompressionMethod.Deflate
 | 
					 | 
				
			||||||
                   ? stream.decompressToArray()
 | 
					 | 
				
			||||||
                   : stream.ToByteArray();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Determines whether the specified <see cref="int"/> equals the specified <see cref="char"/>,
 | 
					 | 
				
			||||||
        /// and invokes the specified Action<int> delegate at the same time.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// <c>true</c> if <paramref name="value"/> equals <paramref name="c"/>;
 | 
					 | 
				
			||||||
        /// otherwise, <c>false</c>.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="value">
 | 
					 | 
				
			||||||
        /// An <see cref="int"/> to compare.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="c">
 | 
					 | 
				
			||||||
        /// A <see cref="char"/> to compare.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="action">
 | 
					 | 
				
			||||||
        /// An Action<int> delegate that references the method(s) called at
 | 
					 | 
				
			||||||
        /// the same time as comparing. An <see cref="int"/> parameter to pass to
 | 
					 | 
				
			||||||
        /// the method(s) is <paramref name="value"/>.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <exception cref="ArgumentOutOfRangeException">
 | 
					 | 
				
			||||||
        /// <paramref name="value"/> isn't between 0 and 255.
 | 
					 | 
				
			||||||
        /// </exception>
 | 
					 | 
				
			||||||
        internal static bool EqualsWith(this int value, char c, Action<int> action)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (value < 0 || value > 255)
 | 
					 | 
				
			||||||
                throw new ArgumentOutOfRangeException(nameof(value));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            action(value);
 | 
					 | 
				
			||||||
            return value == c - 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string GetMessage(this CloseStatusCode code)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return code == CloseStatusCode.ProtocolError
 | 
					 | 
				
			||||||
                   ? "A WebSocket protocol error has occurred."
 | 
					 | 
				
			||||||
                   : code == CloseStatusCode.IncorrectData
 | 
					 | 
				
			||||||
                     ? "An incorrect data has been received."
 | 
					 | 
				
			||||||
                     : code == CloseStatusCode.Abnormal
 | 
					 | 
				
			||||||
                       ? "An exception has occurred."
 | 
					 | 
				
			||||||
                       : code == CloseStatusCode.InconsistentData
 | 
					 | 
				
			||||||
                         ? "An inconsistent data has been received."
 | 
					 | 
				
			||||||
                         : code == CloseStatusCode.PolicyViolation
 | 
					 | 
				
			||||||
                           ? "A policy violation has occurred."
 | 
					 | 
				
			||||||
                           : code == CloseStatusCode.TooBig
 | 
					 | 
				
			||||||
                             ? "A too big data has been received."
 | 
					 | 
				
			||||||
                             : code == CloseStatusCode.IgnoreExtension
 | 
					 | 
				
			||||||
                               ? "WebSocket client did not receive expected extension(s)."
 | 
					 | 
				
			||||||
                               : code == CloseStatusCode.ServerError
 | 
					 | 
				
			||||||
                                 ? "WebSocket server got an internal error."
 | 
					 | 
				
			||||||
                                 : code == CloseStatusCode.TlsHandshakeFailure
 | 
					 | 
				
			||||||
                                   ? "An error has occurred while handshaking."
 | 
					 | 
				
			||||||
                                   : string.Empty;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string GetNameInternal(this string nameAndValue, string separator)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var i = nameAndValue.IndexOf(separator);
 | 
					 | 
				
			||||||
            return i > 0
 | 
					 | 
				
			||||||
                   ? nameAndValue.Substring(0, i).Trim()
 | 
					 | 
				
			||||||
                   : null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string GetValueInternal(this string nameAndValue, string separator)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var i = nameAndValue.IndexOf(separator);
 | 
					 | 
				
			||||||
            return i >= 0 && i < nameAndValue.Length - 1
 | 
					 | 
				
			||||||
                   ? nameAndValue.Substring(i + 1).Trim()
 | 
					 | 
				
			||||||
                   : null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static bool IsCompressionExtension(this string value, CompressionMethod method)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return value.StartsWith(method.ToExtensionString());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static bool IsPortNumber(this int value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return value > 0 && value < 65536;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static bool IsReserved(this ushort code)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return code == (ushort)CloseStatusCode.Undefined ||
 | 
					 | 
				
			||||||
                   code == (ushort)CloseStatusCode.NoStatusCode ||
 | 
					 | 
				
			||||||
                   code == (ushort)CloseStatusCode.Abnormal ||
 | 
					 | 
				
			||||||
                   code == (ushort)CloseStatusCode.TlsHandshakeFailure;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static bool IsReserved(this CloseStatusCode code)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return code == CloseStatusCode.Undefined ||
 | 
					 | 
				
			||||||
                   code == CloseStatusCode.NoStatusCode ||
 | 
					 | 
				
			||||||
                   code == CloseStatusCode.Abnormal ||
 | 
					 | 
				
			||||||
                   code == CloseStatusCode.TlsHandshakeFailure;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static bool IsText(this string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var len = value.Length;
 | 
					 | 
				
			||||||
            for (var i = 0; i < len; i++)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                char c = value[i];
 | 
					 | 
				
			||||||
                if (c < 0x20 && !"\r\n\t".Contains(c))
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (c == 0x7f)
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (c == '\n' && ++i < len)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    c = value[i];
 | 
					 | 
				
			||||||
                    if (!" \t".Contains(c))
 | 
					 | 
				
			||||||
                        return false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static bool IsToken(this string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            foreach (char c in value)
 | 
					 | 
				
			||||||
                if (c < 0x20 || c >= 0x7f || _tspecials.Contains(c))
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string Quote(this string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return value.IsToken()
 | 
					 | 
				
			||||||
                   ? value
 | 
					 | 
				
			||||||
                   : string.Format("\"{0}\"", value.Replace("\"", "\\\""));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static Task<byte[]> ReadBytesAsync(this Stream stream, int length)
 | 
					 | 
				
			||||||
            => stream.ReadBytesAsync(new byte[length], 0, length);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static async Task<byte[]> ReadBytesAsync(this Stream stream, long length, int bufferLength)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            using (var result = new MemoryStream())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var count = length / bufferLength;
 | 
					 | 
				
			||||||
                var rem = (int)(length % bufferLength);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var buffer = new byte[bufferLength];
 | 
					 | 
				
			||||||
                var end = false;
 | 
					 | 
				
			||||||
                for (long i = 0; i < count; i++)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (!await stream.ReadBytesAsync(buffer, 0, bufferLength, result).ConfigureAwait(false))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        end = true;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (!end && rem > 0)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    await stream.ReadBytesAsync(new byte[rem], 0, rem, result).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return result.ToArray();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string RemovePrefix(this string value, params string[] prefixes)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var i = 0;
 | 
					 | 
				
			||||||
            foreach (var prefix in prefixes)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (value.StartsWith(prefix))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    i = prefix.Length;
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return i > 0
 | 
					 | 
				
			||||||
                   ? value.Substring(i)
 | 
					 | 
				
			||||||
                   : value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static T[] Reverse<T>(this T[] array)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var len = array.Length;
 | 
					 | 
				
			||||||
            T[] reverse = new T[len];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var end = len - 1;
 | 
					 | 
				
			||||||
            for (var i = 0; i <= end; i++)
 | 
					 | 
				
			||||||
                reverse[i] = array[end - i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return reverse;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static IEnumerable<string> SplitHeaderValue(
 | 
					 | 
				
			||||||
          this string value, params char[] separator)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var len = value.Length;
 | 
					 | 
				
			||||||
            var separators = new string(separator);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var buffer = new StringBuilder(32);
 | 
					 | 
				
			||||||
            var quoted = false;
 | 
					 | 
				
			||||||
            var escaped = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            char c;
 | 
					 | 
				
			||||||
            for (var i = 0; i < len; i++)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                c = value[i];
 | 
					 | 
				
			||||||
                if (c == '"')
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (escaped)
 | 
					 | 
				
			||||||
                        escaped = !escaped;
 | 
					 | 
				
			||||||
                    else
 | 
					 | 
				
			||||||
                        quoted = !quoted;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (c == '\\')
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (i < len - 1 && value[i + 1] == '"')
 | 
					 | 
				
			||||||
                        escaped = true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (separators.Contains(c))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (!quoted)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        yield return buffer.ToString();
 | 
					 | 
				
			||||||
                        buffer.Length = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                buffer.Append(c);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (buffer.Length > 0)
 | 
					 | 
				
			||||||
                yield return buffer.ToString();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static byte[] ToByteArray(this Stream stream)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            using (var output = new MemoryStream())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                stream.Position = 0;
 | 
					 | 
				
			||||||
                stream.CopyTo(output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return output.ToArray();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static byte[] ToByteArrayInternally(this ushort value, ByteOrder order)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var bytes = BitConverter.GetBytes(value);
 | 
					 | 
				
			||||||
            if (!order.IsHostOrder())
 | 
					 | 
				
			||||||
                Array.Reverse(bytes);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return bytes;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static byte[] ToByteArrayInternally(this ulong value, ByteOrder order)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var bytes = BitConverter.GetBytes(value);
 | 
					 | 
				
			||||||
            if (!order.IsHostOrder())
 | 
					 | 
				
			||||||
                Array.Reverse(bytes);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return bytes;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string ToExtensionString(
 | 
					 | 
				
			||||||
          this CompressionMethod method, params string[] parameters)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (method == CompressionMethod.None)
 | 
					 | 
				
			||||||
                return string.Empty;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var m = string.Format("permessage-{0}", method.ToString().ToLowerInvariant());
 | 
					 | 
				
			||||||
            if (parameters == null || parameters.Length == 0)
 | 
					 | 
				
			||||||
                return m;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return string.Format("{0}; {1}", m, parameters.ToString("; "));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static ushort ToUInt16(this byte[] src, ByteOrder srcOrder)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            src.ToHostOrder(srcOrder);
 | 
					 | 
				
			||||||
            return BitConverter.ToUInt16(src, 0);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static ulong ToUInt64(this byte[] src, ByteOrder srcOrder)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            src.ToHostOrder(srcOrder);
 | 
					 | 
				
			||||||
            return BitConverter.ToUInt64(src, 0);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string TrimEndSlash(this string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            value = value.TrimEnd('/');
 | 
					 | 
				
			||||||
            return value.Length > 0
 | 
					 | 
				
			||||||
                   ? value
 | 
					 | 
				
			||||||
                   : "/";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static string Unquote(this string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var start = value.IndexOf('\"');
 | 
					 | 
				
			||||||
            var end = value.LastIndexOf('\"');
 | 
					 | 
				
			||||||
            if (start < end)
 | 
					 | 
				
			||||||
                value = value.Substring(start + 1, end - start - 1).Replace("\\\"", "\"");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return value.Trim();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static void WriteBytes(this Stream stream, byte[] value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            using (var src = new MemoryStream(value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                src.CopyTo(stream);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Determines whether the specified <see cref="string"/> contains any of characters
 | 
					 | 
				
			||||||
        /// in the specified array of <see cref="char"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>;
 | 
					 | 
				
			||||||
        /// otherwise, <c>false</c>.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="value">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> to test.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="chars">
 | 
					 | 
				
			||||||
        /// An array of <see cref="char"/> that contains characters to find.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static bool Contains(this string value, params char[] chars)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return chars == null || chars.Length == 0
 | 
					 | 
				
			||||||
                   ? true
 | 
					 | 
				
			||||||
                   : value == null || value.Length == 0
 | 
					 | 
				
			||||||
                     ? false
 | 
					 | 
				
			||||||
                     : value.IndexOfAny(chars) != -1;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Determines whether the specified <see cref="QueryParamCollection"/> contains the entry
 | 
					 | 
				
			||||||
        /// with the specified <paramref name="name"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// <c>true</c> if <paramref name="collection"/> contains the entry
 | 
					 | 
				
			||||||
        /// with <paramref name="name"/>; otherwise, <c>false</c>.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="collection">
 | 
					 | 
				
			||||||
        /// A <see cref="QueryParamCollection"/> to test.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="name">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the key of the entry to find.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static bool Contains(this QueryParamCollection collection, string name)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return collection == null || collection.Count == 0
 | 
					 | 
				
			||||||
                   ? false
 | 
					 | 
				
			||||||
                   : collection[name] != null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Determines whether the specified <see cref="QueryParamCollection"/> contains the entry
 | 
					 | 
				
			||||||
        /// with the specified both <paramref name="name"/> and <paramref name="value"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// <c>true</c> if <paramref name="collection"/> contains the entry
 | 
					 | 
				
			||||||
        /// with both <paramref name="name"/> and <paramref name="value"/>;
 | 
					 | 
				
			||||||
        /// otherwise, <c>false</c>.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="collection">
 | 
					 | 
				
			||||||
        /// A <see cref="QueryParamCollection"/> to test.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="name">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the key of the entry to find.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="value">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the value of the entry to find.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static bool Contains(this QueryParamCollection collection, string name, string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (collection == null || collection.Count == 0)
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var values = collection[name];
 | 
					 | 
				
			||||||
            if (values == null)
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            foreach (var v in values.Split(','))
 | 
					 | 
				
			||||||
                if (v.Trim().Equals(value, StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                    return true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Emits the specified <c>EventHandler<TEventArgs></c> delegate
 | 
					 | 
				
			||||||
        /// if it isn't <see langword="null"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="eventHandler">
 | 
					 | 
				
			||||||
        /// An <c>EventHandler<TEventArgs></c> to emit.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="sender">
 | 
					 | 
				
			||||||
        /// An <see cref="object"/> from which emits this <paramref name="eventHandler"/>.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="e">
 | 
					 | 
				
			||||||
        /// A <c>TEventArgs</c> that represents the event data.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <typeparam name="TEventArgs">
 | 
					 | 
				
			||||||
        /// The type of the event data generated by the event.
 | 
					 | 
				
			||||||
        /// </typeparam>
 | 
					 | 
				
			||||||
        public static void Emit<TEventArgs>(
 | 
					 | 
				
			||||||
          this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e)
 | 
					 | 
				
			||||||
          where TEventArgs : EventArgs
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (eventHandler != null)
 | 
					 | 
				
			||||||
                eventHandler(sender, e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the description of the specified HTTP status <paramref name="code"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the description of the HTTP status code.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="code">
 | 
					 | 
				
			||||||
        /// One of <see cref="HttpStatusCode"/> enum values, indicates the HTTP status codes.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static string GetDescription(this HttpStatusCode code)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return ((int)code).GetStatusDescription();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the description of the specified HTTP status <paramref name="code"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the description of the HTTP status code.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="code">
 | 
					 | 
				
			||||||
        /// An <see cref="int"/> that represents the HTTP status code.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static string GetStatusDescription(this int code)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            switch (code)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                case 100: return "Continue";
 | 
					 | 
				
			||||||
                case 101: return "Switching Protocols";
 | 
					 | 
				
			||||||
                case 102: return "Processing";
 | 
					 | 
				
			||||||
                case 200: return "OK";
 | 
					 | 
				
			||||||
                case 201: return "Created";
 | 
					 | 
				
			||||||
                case 202: return "Accepted";
 | 
					 | 
				
			||||||
                case 203: return "Non-Authoritative Information";
 | 
					 | 
				
			||||||
                case 204: return "No Content";
 | 
					 | 
				
			||||||
                case 205: return "Reset Content";
 | 
					 | 
				
			||||||
                case 206: return "Partial Content";
 | 
					 | 
				
			||||||
                case 207: return "Multi-Status";
 | 
					 | 
				
			||||||
                case 300: return "Multiple Choices";
 | 
					 | 
				
			||||||
                case 301: return "Moved Permanently";
 | 
					 | 
				
			||||||
                case 302: return "Found";
 | 
					 | 
				
			||||||
                case 303: return "See Other";
 | 
					 | 
				
			||||||
                case 304: return "Not Modified";
 | 
					 | 
				
			||||||
                case 305: return "Use Proxy";
 | 
					 | 
				
			||||||
                case 307: return "Temporary Redirect";
 | 
					 | 
				
			||||||
                case 400: return "Bad Request";
 | 
					 | 
				
			||||||
                case 401: return "Unauthorized";
 | 
					 | 
				
			||||||
                case 402: return "Payment Required";
 | 
					 | 
				
			||||||
                case 403: return "Forbidden";
 | 
					 | 
				
			||||||
                case 404: return "Not Found";
 | 
					 | 
				
			||||||
                case 405: return "Method Not Allowed";
 | 
					 | 
				
			||||||
                case 406: return "Not Acceptable";
 | 
					 | 
				
			||||||
                case 407: return "Proxy Authentication Required";
 | 
					 | 
				
			||||||
                case 408: return "Request Timeout";
 | 
					 | 
				
			||||||
                case 409: return "Conflict";
 | 
					 | 
				
			||||||
                case 410: return "Gone";
 | 
					 | 
				
			||||||
                case 411: return "Length Required";
 | 
					 | 
				
			||||||
                case 412: return "Precondition Failed";
 | 
					 | 
				
			||||||
                case 413: return "Request Entity Too Large";
 | 
					 | 
				
			||||||
                case 414: return "Request-Uri Too Long";
 | 
					 | 
				
			||||||
                case 415: return "Unsupported Media Type";
 | 
					 | 
				
			||||||
                case 416: return "Requested Range Not Satisfiable";
 | 
					 | 
				
			||||||
                case 417: return "Expectation Failed";
 | 
					 | 
				
			||||||
                case 422: return "Unprocessable Entity";
 | 
					 | 
				
			||||||
                case 423: return "Locked";
 | 
					 | 
				
			||||||
                case 424: return "Failed Dependency";
 | 
					 | 
				
			||||||
                case 500: return "Internal Server Error";
 | 
					 | 
				
			||||||
                case 501: return "Not Implemented";
 | 
					 | 
				
			||||||
                case 502: return "Bad Gateway";
 | 
					 | 
				
			||||||
                case 503: return "Service Unavailable";
 | 
					 | 
				
			||||||
                case 504: return "Gateway Timeout";
 | 
					 | 
				
			||||||
                case 505: return "Http Version Not Supported";
 | 
					 | 
				
			||||||
                case 507: return "Insufficient Storage";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return string.Empty;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Determines whether the specified <see cref="ByteOrder"/> is host
 | 
					 | 
				
			||||||
        /// (this computer architecture) byte order.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// <c>true</c> if <paramref name="order"/> is host byte order;
 | 
					 | 
				
			||||||
        /// otherwise, <c>false</c>.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="order">
 | 
					 | 
				
			||||||
        /// One of the <see cref="ByteOrder"/> enum values, to test.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static bool IsHostOrder(this ByteOrder order)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // true : !(true ^ true)  or !(false ^ false)
 | 
					 | 
				
			||||||
            // false: !(true ^ false) or !(false ^ true)
 | 
					 | 
				
			||||||
            return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Determines whether the specified <see cref="string"/> is a predefined scheme.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// <c>true</c> if <paramref name="value"/> is a predefined scheme; otherwise, <c>false</c>.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="value">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> to test.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static bool IsPredefinedScheme(this string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (value == null || value.Length < 2)
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var c = value[0];
 | 
					 | 
				
			||||||
            if (c == 'h')
 | 
					 | 
				
			||||||
                return value == "http" || value == "https";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (c == 'w')
 | 
					 | 
				
			||||||
                return value == "ws" || value == "wss";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (c == 'f')
 | 
					 | 
				
			||||||
                return value == "file" || value == "ftp";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (c == 'n')
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                c = value[1];
 | 
					 | 
				
			||||||
                return c == 'e'
 | 
					 | 
				
			||||||
                       ? value == "news" || value == "net.pipe" || value == "net.tcp"
 | 
					 | 
				
			||||||
                       : value == "nntp";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return (c == 'g' && value == "gopher") || (c == 'm' && value == "mailto");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Determines whether the specified <see cref="string"/> is a URI string.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// <c>true</c> if <paramref name="value"/> may be a URI string; otherwise, <c>false</c>.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="value">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> to test.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static bool MaybeUri(this string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (value == null || value.Length == 0)
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var i = value.IndexOf(':');
 | 
					 | 
				
			||||||
            if (i == -1)
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (i >= 10)
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return value.Substring(0, i).IsPredefinedScheme();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Retrieves a sub-array from the specified <paramref name="array"/>.
 | 
					 | 
				
			||||||
        /// A sub-array starts at the specified element position.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// An array of T that receives a sub-array, or an empty array of T if any problems
 | 
					 | 
				
			||||||
        /// with the parameters.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="array">
 | 
					 | 
				
			||||||
        /// An array of T that contains the data to retrieve a sub-array.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="startIndex">
 | 
					 | 
				
			||||||
        /// An <see cref="int"/> that contains the zero-based starting position of a sub-array
 | 
					 | 
				
			||||||
        /// in <paramref name="array"/>.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="length">
 | 
					 | 
				
			||||||
        /// An <see cref="int"/> that contains the number of elements to retrieve a sub-array.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <typeparam name="T">
 | 
					 | 
				
			||||||
        /// The type of elements in the <paramref name="array"/>.
 | 
					 | 
				
			||||||
        /// </typeparam>
 | 
					 | 
				
			||||||
        public static T[] SubArray<T>(this T[] array, int startIndex, int length)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (array == null || array.Length == 0)
 | 
					 | 
				
			||||||
                return new T[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (startIndex < 0 || length <= 0)
 | 
					 | 
				
			||||||
                return new T[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (startIndex + length > array.Length)
 | 
					 | 
				
			||||||
                return new T[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (startIndex == 0 && array.Length == length)
 | 
					 | 
				
			||||||
                return array;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            T[] subArray = new T[length];
 | 
					 | 
				
			||||||
            Array.Copy(array, startIndex, subArray, 0, length);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return subArray;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Converts the order of the specified array of <see cref="byte"/> to the host byte order.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// An array of <see cref="byte"/> converted from <paramref name="src"/>.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="src">
 | 
					 | 
				
			||||||
        /// An array of <see cref="byte"/> to convert.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="srcOrder">
 | 
					 | 
				
			||||||
        /// One of the <see cref="ByteOrder"/> enum values, indicates the byte order of
 | 
					 | 
				
			||||||
        /// <paramref name="src"/>.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <exception cref="ArgumentNullException">
 | 
					 | 
				
			||||||
        /// <paramref name="src"/> is <see langword="null"/>.
 | 
					 | 
				
			||||||
        /// </exception>
 | 
					 | 
				
			||||||
        public static void ToHostOrder(this byte[] src, ByteOrder srcOrder)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (src == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                throw new ArgumentNullException(nameof(src));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (src.Length > 1 && !srcOrder.IsHostOrder())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Array.Reverse(src);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Converts the specified <paramref name="array"/> to a <see cref="string"/> that
 | 
					 | 
				
			||||||
        /// concatenates the each element of <paramref name="array"/> across the specified
 | 
					 | 
				
			||||||
        /// <paramref name="separator"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> converted from <paramref name="array"/>,
 | 
					 | 
				
			||||||
        /// or <see cref="String.Empty"/> if <paramref name="array"/> is empty.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="array">
 | 
					 | 
				
			||||||
        /// An array of T to convert.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="separator">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the separator string.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <typeparam name="T">
 | 
					 | 
				
			||||||
        /// The type of elements in <paramref name="array"/>.
 | 
					 | 
				
			||||||
        /// </typeparam>
 | 
					 | 
				
			||||||
        /// <exception cref="ArgumentNullException">
 | 
					 | 
				
			||||||
        /// <paramref name="array"/> is <see langword="null"/>.
 | 
					 | 
				
			||||||
        /// </exception>
 | 
					 | 
				
			||||||
        public static string ToString<T>(this T[] array, string separator)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (array == null)
 | 
					 | 
				
			||||||
                throw new ArgumentNullException(nameof(array));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var len = array.Length;
 | 
					 | 
				
			||||||
            if (len == 0)
 | 
					 | 
				
			||||||
                return string.Empty;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (separator == null)
 | 
					 | 
				
			||||||
                separator = string.Empty;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var buff = new StringBuilder(64);
 | 
					 | 
				
			||||||
            (len - 1).Times(i => buff.AppendFormat("{0}{1}", array[i].ToString(), separator));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            buff.Append(array[len - 1].ToString());
 | 
					 | 
				
			||||||
            return buff.ToString();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Executes the specified <c>Action<int></c> delegate <paramref name="n"/> times.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="n">
 | 
					 | 
				
			||||||
        /// An <see cref="int"/> is the number of times to execute.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="action">
 | 
					 | 
				
			||||||
        /// An <c>Action<int></c> delegate that references the method(s) to execute.
 | 
					 | 
				
			||||||
        /// An <see cref="int"/> parameter to pass to the method(s) is the zero-based count of
 | 
					 | 
				
			||||||
        /// iteration.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static void Times(this int n, Action<int> action)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (n > 0 && action != null)
 | 
					 | 
				
			||||||
                for (int i = 0; i < n; i++)
 | 
					 | 
				
			||||||
                    action(i);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Converts the specified <see cref="string"/> to a <see cref="Uri"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// A <see cref="Uri"/> converted from <paramref name="uriString"/>, or <see langword="null"/>
 | 
					 | 
				
			||||||
        /// if <paramref name="uriString"/> isn't successfully converted.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="uriString">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> to convert.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static Uri ToUri(this string uriString)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return Uri.TryCreate(
 | 
					 | 
				
			||||||
                     uriString, uriString.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out var res)
 | 
					 | 
				
			||||||
                   ? res
 | 
					 | 
				
			||||||
                   : null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// URL-decodes the specified <see cref="string"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns>
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that receives the decoded string, or the <paramref name="value"/>
 | 
					 | 
				
			||||||
        /// if it's <see langword="null"/> or empty.
 | 
					 | 
				
			||||||
        /// </returns>
 | 
					 | 
				
			||||||
        /// <param name="value">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> to decode.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public static string UrlDecode(this string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return value == null || value.Length == 0
 | 
					 | 
				
			||||||
                   ? value
 | 
					 | 
				
			||||||
                   : WebUtility.UrlDecode(value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,8 +0,0 @@
 | 
				
			|||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    internal enum Fin : byte
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        More = 0x0,
 | 
					 | 
				
			||||||
        Final = 0x1
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,70 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Services;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    internal abstract class HttpBase
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Private Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private QueryParamCollection _headers;
 | 
					 | 
				
			||||||
        private Version _version;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Protected Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        protected const string CrLf = "\r\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Protected Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        protected HttpBase(Version version, QueryParamCollection headers)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _version = version;
 | 
					 | 
				
			||||||
            _headers = headers;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public QueryParamCollection Headers => _headers;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Version ProtocolVersion => _version;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Private Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static Encoding getEncoding(string contentType)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (contentType == null || contentType.Length == 0)
 | 
					 | 
				
			||||||
                return Encoding.UTF8;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var i = contentType.IndexOf("charset=", StringComparison.Ordinal);
 | 
					 | 
				
			||||||
            if (i == -1)
 | 
					 | 
				
			||||||
                return Encoding.UTF8;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var charset = contentType.Substring(i + 8);
 | 
					 | 
				
			||||||
            i = charset.IndexOf(';');
 | 
					 | 
				
			||||||
            if (i != -1)
 | 
					 | 
				
			||||||
                charset = charset.Substring(0, i).TrimEnd();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return Encoding.GetEncoding(charset.Trim('"'));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public byte[] ToByteArray()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return Encoding.UTF8.GetBytes(ToString());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,122 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using System.Net;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Services;
 | 
					 | 
				
			||||||
using SocketHttpListener.Net;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    // TODO what is the point of this class?
 | 
					 | 
				
			||||||
    internal class HttpResponse : HttpBase
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Private Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private string _code;
 | 
					 | 
				
			||||||
        private string _reason;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Private Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private HttpResponse(string code, string reason, Version version, QueryParamCollection headers)
 | 
					 | 
				
			||||||
            : base(version, headers)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _code = code;
 | 
					 | 
				
			||||||
            _reason = reason;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal HttpResponse(HttpStatusCode code)
 | 
					 | 
				
			||||||
            : this(code, code.GetDescription())
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal HttpResponse(HttpStatusCode code, string reason)
 | 
					 | 
				
			||||||
            : this(((int)code).ToString(), reason, HttpVersion.Version11, new QueryParamCollection())
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Headers["Server"] = "websocket-sharp/1.0";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public CookieCollection Cookies => GetCookies(Headers, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static CookieCollection GetCookies(QueryParamCollection headers, bool response)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var name = response ? "Set-Cookie" : "Cookie";
 | 
					 | 
				
			||||||
            return headers == null || !headers.Contains(name)
 | 
					 | 
				
			||||||
                   ? new CookieCollection()
 | 
					 | 
				
			||||||
                   : CookieHelper.Parse(headers[name], response);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsProxyAuthenticationRequired => _code == "407";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsUnauthorized => _code == "401";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsWebSocketResponse
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var headers = Headers;
 | 
					 | 
				
			||||||
                return ProtocolVersion > HttpVersion.Version10 &&
 | 
					 | 
				
			||||||
                       _code == "101" &&
 | 
					 | 
				
			||||||
                       headers.Contains("Upgrade", "websocket") &&
 | 
					 | 
				
			||||||
                       headers.Contains("Connection", "Upgrade");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public string Reason => _reason;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public string StatusCode => _code;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static HttpResponse CreateCloseResponse(HttpStatusCode code)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var res = new HttpResponse(code);
 | 
					 | 
				
			||||||
            res.Headers["Connection"] = "close";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return res;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void SetCookies(CookieCollection cookies)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (cookies == null || cookies.Count == 0)
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var headers = Headers;
 | 
					 | 
				
			||||||
            var sorted = cookies.OfType<Cookie>().OrderBy(i => i.Name).ToList();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            foreach (var cookie in sorted)
 | 
					 | 
				
			||||||
                headers.Add("Set-Cookie", cookie.ToString());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override string ToString()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var output = new StringBuilder(64);
 | 
					 | 
				
			||||||
            output.AppendFormat("HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var headers = Headers;
 | 
					 | 
				
			||||||
            foreach (var key in headers.Keys)
 | 
					 | 
				
			||||||
                output.AppendFormat("{0}: {1}{2}", key, headers[key], CrLf);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            output.Append(CrLf);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return output.ToString();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,8 +0,0 @@
 | 
				
			|||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    internal enum Mask : byte
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Unmask = 0x0,
 | 
					 | 
				
			||||||
        Mask = 0x1
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,84 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Contains the event data associated with a <see cref="WebSocket.OnMessage"/> event.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <remarks>
 | 
					 | 
				
			||||||
    /// A <see cref="WebSocket.OnMessage"/> event occurs when the <see cref="WebSocket"/> receives
 | 
					 | 
				
			||||||
    /// a text or binary data frame.
 | 
					 | 
				
			||||||
    /// If you want to get the received data, you access the <see cref="Data"/> or
 | 
					 | 
				
			||||||
    /// <see cref="RawData"/> property.
 | 
					 | 
				
			||||||
    /// </remarks>
 | 
					 | 
				
			||||||
    public class MessageEventArgs : EventArgs
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Private Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private string _data;
 | 
					 | 
				
			||||||
        private Opcode _opcode;
 | 
					 | 
				
			||||||
        private byte[] _rawData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal MessageEventArgs(Opcode opcode, byte[] data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _opcode = opcode;
 | 
					 | 
				
			||||||
            _rawData = data;
 | 
					 | 
				
			||||||
            _data = convertToString(opcode, data);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal MessageEventArgs(Opcode opcode, PayloadData payload)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _opcode = opcode;
 | 
					 | 
				
			||||||
            _rawData = payload.ApplicationData;
 | 
					 | 
				
			||||||
            _data = convertToString(opcode, _rawData);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the received data as a <see cref="string"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <value>
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that contains the received data.
 | 
					 | 
				
			||||||
        /// </value>
 | 
					 | 
				
			||||||
        public string Data => _data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the received data as an array of <see cref="byte"/>.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <value>
 | 
					 | 
				
			||||||
        /// An array of <see cref="byte"/> that contains the received data.
 | 
					 | 
				
			||||||
        /// </value>
 | 
					 | 
				
			||||||
        public byte[] RawData => _rawData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the type of the received data.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <value>
 | 
					 | 
				
			||||||
        /// One of the <see cref="Opcode"/> values, indicates the type of the received data.
 | 
					 | 
				
			||||||
        /// </value>
 | 
					 | 
				
			||||||
        public Opcode Type => _opcode;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Private Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string convertToString(Opcode opcode, byte[] data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return data.Length == 0
 | 
					 | 
				
			||||||
                   ? string.Empty
 | 
					 | 
				
			||||||
                   : opcode == Opcode.Text
 | 
					 | 
				
			||||||
                     ? Encoding.UTF8.GetString(data, 0, data.Length)
 | 
					 | 
				
			||||||
                     : opcode.ToString();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,141 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Globalization;
 | 
					 | 
				
			||||||
using System.Net;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener.Net
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public static class CookieHelper
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        internal static CookieCollection Parse(string value, bool response)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return response
 | 
					 | 
				
			||||||
                ? parseResponse(value)
 | 
					 | 
				
			||||||
                : null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string[] splitCookieHeaderValue(string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return new List<string>(value.SplitHeaderValue(',', ';')).ToArray();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static CookieCollection parseResponse(string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var cookies = new CookieCollection();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Cookie cookie = null;
 | 
					 | 
				
			||||||
            var pairs = splitCookieHeaderValue(value);
 | 
					 | 
				
			||||||
            for (int i = 0; i < pairs.Length; i++)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var pair = pairs[i].Trim();
 | 
					 | 
				
			||||||
                if (pair.Length == 0)
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (pair.StartsWith("version", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.Version = int.Parse(pair.GetValueInternal("=").Trim('"'));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("expires", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var buffer = new StringBuilder(pair.GetValueInternal("="), 32);
 | 
					 | 
				
			||||||
                    if (i < pairs.Length - 1)
 | 
					 | 
				
			||||||
                        buffer.AppendFormat(", {0}", pairs[++i].Trim());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (!DateTime.TryParseExact(
 | 
					 | 
				
			||||||
                      buffer.ToString(),
 | 
					 | 
				
			||||||
                      new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" },
 | 
					 | 
				
			||||||
                      new CultureInfo("en-US"),
 | 
					 | 
				
			||||||
                      DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal,
 | 
					 | 
				
			||||||
                      out var expires))
 | 
					 | 
				
			||||||
                        expires = DateTime.Now;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (cookie != null && cookie.Expires == DateTime.MinValue)
 | 
					 | 
				
			||||||
                        cookie.Expires = expires.ToLocalTime();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("max-age", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var max = int.Parse(pair.GetValueInternal("=").Trim('"'));
 | 
					 | 
				
			||||||
                    var expires = DateTime.Now.AddSeconds((double)max);
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.Expires = expires;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("path", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.Path = pair.GetValueInternal("=");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("domain", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.Domain = pair.GetValueInternal("=");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("port", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var port = pair.Equals("port", StringComparison.OrdinalIgnoreCase)
 | 
					 | 
				
			||||||
                               ? "\"\""
 | 
					 | 
				
			||||||
                               : pair.GetValueInternal("=");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.Port = port;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("comment", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.Comment = pair.GetValueInternal("=").UrlDecode();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("commenturl", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.CommentUri = pair.GetValueInternal("=").Trim('"').ToUri();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("discard", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.Discard = true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("secure", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.Secure = true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (pair.StartsWith("httponly", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookie.HttpOnly = true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (cookie != null)
 | 
					 | 
				
			||||||
                        cookies.Add(cookie);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    string name;
 | 
					 | 
				
			||||||
                    string val = string.Empty;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    var pos = pair.IndexOf('=');
 | 
					 | 
				
			||||||
                    if (pos == -1)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        name = pair;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if (pos == pair.Length - 1)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        name = pair.Substring(0, pos).TrimEnd(' ');
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        name = pair.Substring(0, pos).TrimEnd(' ');
 | 
					 | 
				
			||||||
                        val = pair.Substring(pos + 1).TrimStart(' ');
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    cookie = new Cookie(name, val);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (cookie != null)
 | 
					 | 
				
			||||||
                cookies.Add(cookie);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return cookies;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,43 +0,0 @@
 | 
				
			|||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Contains the values of the opcode that indicates the type of a WebSocket frame.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <remarks>
 | 
					 | 
				
			||||||
    /// The values of the opcode are defined in
 | 
					 | 
				
			||||||
    /// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2</see> of RFC 6455.
 | 
					 | 
				
			||||||
    /// </remarks>
 | 
					 | 
				
			||||||
    public enum Opcode : byte
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to numeric value 0.
 | 
					 | 
				
			||||||
        /// Indicates a continuation frame.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Cont = 0x0,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to numeric value 1.
 | 
					 | 
				
			||||||
        /// Indicates a text frame.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Text = 0x1,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to numeric value 2.
 | 
					 | 
				
			||||||
        /// Indicates a binary frame.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Binary = 0x2,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to numeric value 8.
 | 
					 | 
				
			||||||
        /// Indicates a connection close frame.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Close = 0x8,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to numeric value 9.
 | 
					 | 
				
			||||||
        /// Indicates a ping frame.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Ping = 0x9,
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Equivalent to numeric value 10.
 | 
					 | 
				
			||||||
        /// Indicates a pong frame.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        Pong = 0xa
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,130 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    internal class PayloadData : IEnumerable<byte>
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Private Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private byte[] _applicationData;
 | 
					 | 
				
			||||||
        private byte[] _extensionData;
 | 
					 | 
				
			||||||
        private bool _masked;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Const Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public const ulong MaxLength = long.MaxValue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public PayloadData()
 | 
					 | 
				
			||||||
          : this(new byte[0], new byte[0], false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public PayloadData(byte[] applicationData)
 | 
					 | 
				
			||||||
          : this(new byte[0], applicationData, false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public PayloadData(string applicationData)
 | 
					 | 
				
			||||||
          : this(new byte[0], Encoding.UTF8.GetBytes(applicationData), false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public PayloadData(byte[] applicationData, bool masked)
 | 
					 | 
				
			||||||
          : this(new byte[0], applicationData, masked)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public PayloadData(byte[] extensionData, byte[] applicationData, bool masked)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _extensionData = extensionData;
 | 
					 | 
				
			||||||
            _applicationData = applicationData;
 | 
					 | 
				
			||||||
            _masked = masked;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal bool ContainsReservedCloseStatusCode =>
 | 
					 | 
				
			||||||
            _applicationData.Length > 1 &&
 | 
					 | 
				
			||||||
            _applicationData.SubArray(0, 2).ToUInt16(ByteOrder.Big).IsReserved();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public byte[] ApplicationData => _applicationData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public byte[] ExtensionData => _extensionData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsMasked => _masked;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ulong Length => (ulong)(_extensionData.Length + _applicationData.Length);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Private Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void mask(byte[] src, byte[] key)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            for (long i = 0; i < src.Length; i++)
 | 
					 | 
				
			||||||
                src[i] = (byte)(src[i] ^ key[i % 4]);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public IEnumerator<byte> GetEnumerator()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            foreach (byte b in _extensionData)
 | 
					 | 
				
			||||||
                yield return b;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            foreach (byte b in _applicationData)
 | 
					 | 
				
			||||||
                yield return b;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void Mask(byte[] maskingKey)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_extensionData.Length > 0)
 | 
					 | 
				
			||||||
                mask(_extensionData, maskingKey);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (_applicationData.Length > 0)
 | 
					 | 
				
			||||||
                mask(_applicationData, maskingKey);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _masked = !_masked;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public byte[] ToByteArray()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return _extensionData.Length > 0
 | 
					 | 
				
			||||||
                   ? new List<byte>(this).ToArray()
 | 
					 | 
				
			||||||
                   : _applicationData;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override string ToString()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return BitConverter.ToString(ToByteArray());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Explicitly Implemented Interface Members
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        IEnumerator IEnumerable.GetEnumerator()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GetEnumerator();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,21 +0,0 @@
 | 
				
			|||||||
using System.Reflection;
 | 
					 | 
				
			||||||
using System.Resources;
 | 
					 | 
				
			||||||
using System.Runtime.InteropServices;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// General Information about an assembly is controlled through the following
 | 
					 | 
				
			||||||
// set of attributes. Change these attribute values to modify the information
 | 
					 | 
				
			||||||
// associated with an assembly.
 | 
					 | 
				
			||||||
[assembly: AssemblyTitle("SocketHttpListener")]
 | 
					 | 
				
			||||||
[assembly: AssemblyDescription("")]
 | 
					 | 
				
			||||||
[assembly: AssemblyConfiguration("")]
 | 
					 | 
				
			||||||
[assembly: AssemblyCompany("Jellyfin Project")]
 | 
					 | 
				
			||||||
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
 | 
					 | 
				
			||||||
[assembly: AssemblyCopyright("Copyright ©  2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
 | 
					 | 
				
			||||||
[assembly: AssemblyTrademark("")]
 | 
					 | 
				
			||||||
[assembly: AssemblyCulture("")]
 | 
					 | 
				
			||||||
[assembly: NeutralResourcesLanguage("en")]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Setting ComVisible to false makes the types in this assembly not visible
 | 
					 | 
				
			||||||
// to COM components.  If you need to access a type in this assembly from
 | 
					 | 
				
			||||||
// COM, set the ComVisible attribute to true on that type.
 | 
					 | 
				
			||||||
[assembly: ComVisible(false)]
 | 
					 | 
				
			||||||
@ -1,8 +0,0 @@
 | 
				
			|||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    internal enum Rsv : byte
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Off = 0x0,
 | 
					 | 
				
			||||||
        On = 0x1
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,18 +0,0 @@
 | 
				
			|||||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <ItemGroup>
 | 
					 | 
				
			||||||
    <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
 | 
					 | 
				
			||||||
    <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
 | 
					 | 
				
			||||||
  </ItemGroup>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <ItemGroup>
 | 
					 | 
				
			||||||
    <Compile Include="..\SharedVersion.cs" />
 | 
					 | 
				
			||||||
  </ItemGroup>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <PropertyGroup>
 | 
					 | 
				
			||||||
    <TargetFramework>netstandard2.0</TargetFramework>
 | 
					 | 
				
			||||||
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 | 
					 | 
				
			||||||
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
 | 
					 | 
				
			||||||
  </PropertyGroup>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</Project>
 | 
					 | 
				
			||||||
@ -1,74 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using System.Net.Sockets;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public class SocketStream : Stream
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private readonly Socket _socket;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public SocketStream(Socket socket, bool ownsSocket)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _socket = socket;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override void Flush()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override bool CanRead => true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override bool CanSeek => false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override bool CanWrite => true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override long Length => throw new NotImplementedException();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override long Position
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get => throw new NotImplementedException();
 | 
					 | 
				
			||||||
            set => throw new NotImplementedException();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override void Write(byte[] buffer, int offset, int count)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _socket.Send(buffer, offset, count, SocketFlags.None);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return _socket.BeginSend(buffer, offset, count, SocketFlags.None, callback, state);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override void EndWrite(IAsyncResult asyncResult)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _socket.EndSend(asyncResult);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override void SetLength(long value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            throw new NotImplementedException();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override long Seek(long offset, SeekOrigin origin)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            throw new NotImplementedException();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override int Read(byte[] buffer, int offset, int count)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return _socket.Receive(buffer, offset, count, SocketFlags.None);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return _socket.BeginReceive(buffer, offset, count, SocketFlags.None, callback, state);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override int EndRead(IAsyncResult asyncResult)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return _socket.EndReceive(asyncResult);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,777 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using System.Net;
 | 
					 | 
				
			||||||
using System.Net.Sockets;
 | 
					 | 
				
			||||||
using System.Net.WebSockets;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using System.Threading;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
using WebSocketState = System.Net.WebSockets.WebSocketState;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Implements the WebSocket interface.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <remarks>
 | 
					 | 
				
			||||||
    /// The WebSocket class provides a set of methods and properties for two-way communication using
 | 
					 | 
				
			||||||
    /// the WebSocket protocol (<see href="http://tools.ietf.org/html/rfc6455">RFC 6455</see>).
 | 
					 | 
				
			||||||
    /// </remarks>
 | 
					 | 
				
			||||||
    public class WebSocket : IDisposable
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Private Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private Action _closeContext;
 | 
					 | 
				
			||||||
        private CompressionMethod _compression;
 | 
					 | 
				
			||||||
        private WebSocketContext _context;
 | 
					 | 
				
			||||||
        private CookieCollection _cookies;
 | 
					 | 
				
			||||||
        private AutoResetEvent _exitReceiving;
 | 
					 | 
				
			||||||
        private object _forConn;
 | 
					 | 
				
			||||||
        private readonly SemaphoreSlim _forEvent = new SemaphoreSlim(1, 1);
 | 
					 | 
				
			||||||
        private object _forMessageEventQueue;
 | 
					 | 
				
			||||||
        private readonly SemaphoreSlim _forSend = new SemaphoreSlim(1, 1);
 | 
					 | 
				
			||||||
        private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 | 
					 | 
				
			||||||
        private Queue<MessageEventArgs> _messageEventQueue;
 | 
					 | 
				
			||||||
        private string _protocol;
 | 
					 | 
				
			||||||
        private volatile WebSocketState _readyState;
 | 
					 | 
				
			||||||
        private AutoResetEvent _receivePong;
 | 
					 | 
				
			||||||
        private bool _secure;
 | 
					 | 
				
			||||||
        private Stream _stream;
 | 
					 | 
				
			||||||
        private const string _version = "13";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal const int FragmentLength = 1016; // Max value is int.MaxValue - 14.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // As server
 | 
					 | 
				
			||||||
        internal WebSocket(string protocol)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _protocol = protocol;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void SetContext(HttpListenerWebSocketContext context, Action closeContextFn, Stream stream)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _context = context;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _closeContext = closeContextFn;
 | 
					 | 
				
			||||||
            _secure = context.IsSecureConnection;
 | 
					 | 
				
			||||||
            _stream = stream;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            init();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // In the .NET Framework, this pulls the value from a P/Invoke.  Here we just hardcode it to a reasonable default.
 | 
					 | 
				
			||||||
        public static TimeSpan DefaultKeepAliveInterval => TimeSpan.FromSeconds(30);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the state of the WebSocket connection.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <value>
 | 
					 | 
				
			||||||
        /// One of the <see cref="WebSocketState"/> enum values, indicates the state of the WebSocket
 | 
					 | 
				
			||||||
        /// connection. The default value is <see cref="WebSocketState.Connecting"/>.
 | 
					 | 
				
			||||||
        /// </value>
 | 
					 | 
				
			||||||
        public WebSocketState ReadyState => _readyState;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Events
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Occurs when the WebSocket connection has been closed.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public event EventHandler<CloseEventArgs> OnClose;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Occurs when the <see cref="WebSocket"/> gets an error.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public event EventHandler<ErrorEventArgs> OnError;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Occurs when the <see cref="WebSocket"/> receives a message.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public event EventHandler<MessageEventArgs> OnMessage;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Occurs when the WebSocket connection has been established.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public event EventHandler OnOpen;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Private Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task CloseAsync(CloseStatusCode code, string reason, bool wait)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CloseAsync(new PayloadData(
 | 
					 | 
				
			||||||
                await ((ushort)code).AppendAsync(reason).ConfigureAwait(false)),
 | 
					 | 
				
			||||||
                !code.IsReserved(),
 | 
					 | 
				
			||||||
                wait).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task CloseAsync(PayloadData payload, bool send, bool wait)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            lock (_forConn)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (_readyState == WebSocketState.CloseSent || _readyState == WebSocketState.Closed)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _readyState = WebSocketState.CloseSent;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var e = new CloseEventArgs(payload)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                WasClean = await CloseHandshakeAsync(
 | 
					 | 
				
			||||||
                  send ? WebSocketFrame.CreateCloseFrame(Mask.Unmask, payload).ToByteArray() : null,
 | 
					 | 
				
			||||||
                  wait ? 1000 : 0).ConfigureAwait(false)
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _readyState = WebSocketState.Closed;
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                OnClose.Emit(this, e);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                error("An exception has occurred while OnClose.", ex);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task<bool> CloseHandshakeAsync(byte[] frameAsBytes, int millisecondsTimeout)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var sent = frameAsBytes != null && await WriteBytesAsync(frameAsBytes).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            var received =
 | 
					 | 
				
			||||||
              millisecondsTimeout == 0 ||
 | 
					 | 
				
			||||||
              (sent && _exitReceiving != null && _exitReceiving.WaitOne(millisecondsTimeout));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            closeServerResources();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (_receivePong != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _receivePong.Dispose();
 | 
					 | 
				
			||||||
                _receivePong = null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (_exitReceiving != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _exitReceiving.Dispose();
 | 
					 | 
				
			||||||
                _exitReceiving = null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var result = sent && received;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return result;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // As server
 | 
					 | 
				
			||||||
        private void closeServerResources()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_closeContext == null)
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _closeContext();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (SocketException)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // it could be unable to send the handshake response
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _closeContext = null;
 | 
					 | 
				
			||||||
            _stream = null;
 | 
					 | 
				
			||||||
            _context = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task<bool> ConcatenateFragmentsIntoAsync(Stream dest)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            while (true)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var frame = await WebSocketFrame.ReadAsync(_stream, true).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                if (frame.IsFinal)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    /* FINAL */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // CONT
 | 
					 | 
				
			||||||
                    if (frame.IsContinuation)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        dest.WriteBytes(frame.PayloadData.ApplicationData);
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // PING
 | 
					 | 
				
			||||||
                    if (frame.IsPing)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        processPingFrame(frame);
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // PONG
 | 
					 | 
				
			||||||
                    if (frame.IsPong)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        processPongFrame(frame);
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // CLOSE
 | 
					 | 
				
			||||||
                    if (frame.IsClose)
 | 
					 | 
				
			||||||
                        return await ProcessCloseFrameAsync(frame).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    /* MORE */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // CONT
 | 
					 | 
				
			||||||
                    if (frame.IsContinuation)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        dest.WriteBytes(frame.PayloadData.ApplicationData);
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // ?
 | 
					 | 
				
			||||||
                return await ProcessUnsupportedFrameAsync(
 | 
					 | 
				
			||||||
                  frame,
 | 
					 | 
				
			||||||
                  CloseStatusCode.IncorrectData,
 | 
					 | 
				
			||||||
                  "An incorrect data has been received while receiving fragmented data.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // As server
 | 
					 | 
				
			||||||
        private HttpResponse createHandshakeCloseResponse(HttpStatusCode code)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var res = HttpResponse.CreateCloseResponse(code);
 | 
					 | 
				
			||||||
            res.Headers["Sec-WebSocket-Version"] = _version;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return res;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private MessageEventArgs dequeueFromMessageEventQueue()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            lock (_forMessageEventQueue)
 | 
					 | 
				
			||||||
                return _messageEventQueue.Count > 0
 | 
					 | 
				
			||||||
                       ? _messageEventQueue.Dequeue()
 | 
					 | 
				
			||||||
                       : null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void enqueueToMessageEventQueue(MessageEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            lock (_forMessageEventQueue)
 | 
					 | 
				
			||||||
                _messageEventQueue.Enqueue(e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void error(string message, Exception exception)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (exception != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    message += ". Exception.Message: " + exception.Message;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                OnError.Emit(this, new ErrorEventArgs(message));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void error(string message)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                OnError.Emit(this, new ErrorEventArgs(message));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void init()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _compression = CompressionMethod.None;
 | 
					 | 
				
			||||||
            _cookies = new CookieCollection();
 | 
					 | 
				
			||||||
            _forConn = new object();
 | 
					 | 
				
			||||||
            _messageEventQueue = new Queue<MessageEventArgs>();
 | 
					 | 
				
			||||||
            _forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot;
 | 
					 | 
				
			||||||
            _readyState = WebSocketState.Connecting;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task OpenAsync()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                startReceiving();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await ProcessExceptionAsync(ex, "An exception has occurred while opening.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await _forEvent.WaitAsync().ConfigureAwait(false);
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                OnOpen?.Invoke(this, EventArgs.Empty);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await ProcessExceptionAsync(ex, "An exception has occurred while OnOpen.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            finally
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _forEvent.Release();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task<bool> ProcessCloseFrameAsync(WebSocketFrame frame)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var payload = frame.PayloadData;
 | 
					 | 
				
			||||||
            await CloseAsync(payload, !payload.ContainsReservedCloseStatusCode, false).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private bool processDataFrame(WebSocketFrame frame)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var e = frame.IsCompressed
 | 
					 | 
				
			||||||
                    ? new MessageEventArgs(
 | 
					 | 
				
			||||||
                        frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression))
 | 
					 | 
				
			||||||
                    : new MessageEventArgs(frame.Opcode, frame.PayloadData);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            enqueueToMessageEventQueue(e);
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task ProcessExceptionAsync(Exception exception, string message)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var code = CloseStatusCode.Abnormal;
 | 
					 | 
				
			||||||
            var reason = message;
 | 
					 | 
				
			||||||
            if (exception is WebSocketException)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var wsex = (WebSocketException)exception;
 | 
					 | 
				
			||||||
                code = wsex.Code;
 | 
					 | 
				
			||||||
                reason = wsex.Message;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            error(message ?? code.GetMessage(), exception);
 | 
					 | 
				
			||||||
            if (_readyState == WebSocketState.Connecting)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await CloseAsync(HttpStatusCode.BadRequest).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await CloseAsync(code, reason ?? code.GetMessage(), false).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private Task<bool> ProcessFragmentedFrameAsync(WebSocketFrame frame)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return frame.IsContinuation // Not first fragment
 | 
					 | 
				
			||||||
                   ? Task.FromResult(true)
 | 
					 | 
				
			||||||
                   : ProcessFragmentsAsync(frame);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task<bool> ProcessFragmentsAsync(WebSocketFrame first)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            using (var buff = new MemoryStream())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                buff.WriteBytes(first.PayloadData.ApplicationData);
 | 
					 | 
				
			||||||
                if (!await ConcatenateFragmentsIntoAsync(buff).ConfigureAwait(false))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                byte[] data;
 | 
					 | 
				
			||||||
                if (_compression != CompressionMethod.None)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    data = buff.DecompressToArray(_compression);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    data = buff.ToArray();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                enqueueToMessageEventQueue(new MessageEventArgs(first.Opcode, data));
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private bool processPingFrame(WebSocketFrame frame)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private bool processPongFrame(WebSocketFrame frame)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _receivePong.Set();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task<bool> ProcessUnsupportedFrameAsync(WebSocketFrame frame, CloseStatusCode code, string reason)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await ProcessExceptionAsync(new WebSocketException(code, reason), null).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private Task<bool> ProcessWebSocketFrameAsync(WebSocketFrame frame)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // TODO: @bond change to if/else chain
 | 
					 | 
				
			||||||
            return frame.IsCompressed && _compression == CompressionMethod.None
 | 
					 | 
				
			||||||
                   ? ProcessUnsupportedFrameAsync(
 | 
					 | 
				
			||||||
                       frame,
 | 
					 | 
				
			||||||
                       CloseStatusCode.IncorrectData,
 | 
					 | 
				
			||||||
                       "A compressed data has been received without available decompression method.")
 | 
					 | 
				
			||||||
                   : frame.IsFragmented
 | 
					 | 
				
			||||||
                     ? ProcessFragmentedFrameAsync(frame)
 | 
					 | 
				
			||||||
                     : frame.IsData
 | 
					 | 
				
			||||||
                       ? Task.FromResult(processDataFrame(frame))
 | 
					 | 
				
			||||||
                       : frame.IsPing
 | 
					 | 
				
			||||||
                         ? Task.FromResult(processPingFrame(frame))
 | 
					 | 
				
			||||||
                         : frame.IsPong
 | 
					 | 
				
			||||||
                           ? Task.FromResult(processPongFrame(frame))
 | 
					 | 
				
			||||||
                           : frame.IsClose
 | 
					 | 
				
			||||||
                             ? ProcessCloseFrameAsync(frame)
 | 
					 | 
				
			||||||
                             : ProcessUnsupportedFrameAsync(frame, CloseStatusCode.PolicyViolation, null);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task<bool> SendAsync(Opcode opcode, Stream stream)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await _forSend.WaitAsync().ConfigureAwait(false);
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var src = stream;
 | 
					 | 
				
			||||||
                var compressed = false;
 | 
					 | 
				
			||||||
                var sent = false;
 | 
					 | 
				
			||||||
                try
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (_compression != CompressionMethod.None)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        stream = stream.Compress(_compression);
 | 
					 | 
				
			||||||
                        compressed = true;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    sent = await SendAsync(opcode, Mask.Unmask, stream, compressed).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    if (!sent)
 | 
					 | 
				
			||||||
                        error("Sending a data has been interrupted.");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                catch (Exception ex)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    error("An exception has occurred while sending a data.", ex);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                finally
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (compressed)
 | 
					 | 
				
			||||||
                        stream.Dispose();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    src.Dispose();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return sent;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            finally
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _forSend.Release();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task<bool> SendAsync(Opcode opcode, Mask mask, Stream stream, bool compressed)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var len = stream.Length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Not fragmented */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (len == 0)
 | 
					 | 
				
			||||||
                return await SendAsync(Fin.Final, opcode, mask, new byte[0], compressed).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var quo = len / FragmentLength;
 | 
					 | 
				
			||||||
            var rem = (int)(len % FragmentLength);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            byte[] buff = null;
 | 
					 | 
				
			||||||
            if (quo == 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                buff = new byte[rem];
 | 
					 | 
				
			||||||
                return await stream.ReadAsync(buff, 0, rem).ConfigureAwait(false) == rem &&
 | 
					 | 
				
			||||||
                       await SendAsync(Fin.Final, opcode, mask, buff, compressed).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            buff = new byte[FragmentLength];
 | 
					 | 
				
			||||||
            if (quo == 1 && rem == 0)
 | 
					 | 
				
			||||||
                return await stream.ReadAsync(buff, 0, FragmentLength).ConfigureAwait(false) == FragmentLength &&
 | 
					 | 
				
			||||||
                       await SendAsync(Fin.Final, opcode, mask, buff, compressed).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Send fragmented */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Begin
 | 
					 | 
				
			||||||
            if (await stream.ReadAsync(buff, 0, FragmentLength).ConfigureAwait(false) != FragmentLength ||
 | 
					 | 
				
			||||||
                !await SendAsync(Fin.More, opcode, mask, buff, compressed).ConfigureAwait(false))
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var n = rem == 0 ? quo - 2 : quo - 1;
 | 
					 | 
				
			||||||
            for (long i = 0; i < n; i++)
 | 
					 | 
				
			||||||
                if (await stream.ReadAsync(buff, 0, FragmentLength).ConfigureAwait(false) != FragmentLength ||
 | 
					 | 
				
			||||||
                    !await SendAsync(Fin.More, Opcode.Cont, mask, buff, compressed).ConfigureAwait(false))
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // End
 | 
					 | 
				
			||||||
            if (rem == 0)
 | 
					 | 
				
			||||||
                rem = FragmentLength;
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                buff = new byte[rem];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return await stream.ReadAsync(buff, 0, rem).ConfigureAwait(false) == rem &&
 | 
					 | 
				
			||||||
                   await SendAsync(Fin.Final, Opcode.Cont, mask, buff, compressed).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private Task<bool> SendAsync(Fin fin, Opcode opcode, Mask mask, byte[] data, bool compressed)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            lock (_forConn)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (_readyState != WebSocketState.Open)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return Task.FromResult(false);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return WriteBytesAsync(
 | 
					 | 
				
			||||||
                  WebSocketFrame.CreateWebSocketFrame(fin, opcode, mask, data, compressed).ToByteArray());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // As server
 | 
					 | 
				
			||||||
        private Task<bool> SendHttpResponseAsync(HttpResponse response)
 | 
					 | 
				
			||||||
            => WriteBytesAsync(response.ToByteArray());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void startReceiving()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_messageEventQueue.Count > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _messageEventQueue.Clear();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _exitReceiving = new AutoResetEvent(false);
 | 
					 | 
				
			||||||
            _receivePong = new AutoResetEvent(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Action receive = null;
 | 
					 | 
				
			||||||
            receive = async () => await WebSocketFrame.ReadAsync(
 | 
					 | 
				
			||||||
                _stream,
 | 
					 | 
				
			||||||
                true,
 | 
					 | 
				
			||||||
                async frame =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (await ProcessWebSocketFrameAsync(frame).ConfigureAwait(false) && _readyState != WebSocketState.Closed)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        receive();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (!frame.IsData)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            return;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        await _forEvent.WaitAsync().ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        try
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            var e = dequeueFromMessageEventQueue();
 | 
					 | 
				
			||||||
                            if (e != null && _readyState == WebSocketState.Open)
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                OnMessage.Emit(this, e);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        catch (Exception ex)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            await ProcessExceptionAsync(ex, "An exception has occurred while OnMessage.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        finally
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            _forEvent.Release();
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if (_exitReceiving != null)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        _exitReceiving.Set();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                async ex => await ProcessExceptionAsync(ex, "An exception has occurred while receiving a message.")).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            receive();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task<bool> WriteBytesAsync(byte[] data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await _stream.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // As server
 | 
					 | 
				
			||||||
        internal async Task CloseAsync(HttpResponse response)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _readyState = WebSocketState.CloseSent;
 | 
					 | 
				
			||||||
            await SendHttpResponseAsync(response).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            closeServerResources();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _readyState = WebSocketState.Closed;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // As server
 | 
					 | 
				
			||||||
        internal Task CloseAsync(HttpStatusCode code)
 | 
					 | 
				
			||||||
            => CloseAsync(createHandshakeCloseResponse(code));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // As server
 | 
					 | 
				
			||||||
        public async Task ConnectAsServer()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _readyState = WebSocketState.Open;
 | 
					 | 
				
			||||||
                await OpenAsync().ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await ProcessExceptionAsync(ex, "An exception has occurred while connecting.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Closes the WebSocket connection, and releases all associated resources.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public Task CloseAsync()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var msg = _readyState.CheckIfClosable();
 | 
					 | 
				
			||||||
            if (msg != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                error(msg);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return Task.CompletedTask;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var send = _readyState == WebSocketState.Open;
 | 
					 | 
				
			||||||
            return CloseAsync(new PayloadData(), send, send);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Closes the WebSocket connection with the specified <see cref="CloseStatusCode"/>
 | 
					 | 
				
			||||||
        /// and <see cref="string"/>, and releases all associated resources.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <remarks>
 | 
					 | 
				
			||||||
        /// This method emits a <see cref="OnError"/> event if the size
 | 
					 | 
				
			||||||
        /// of <paramref name="reason"/> is greater than 123 bytes.
 | 
					 | 
				
			||||||
        /// </remarks>
 | 
					 | 
				
			||||||
        /// <param name="code">
 | 
					 | 
				
			||||||
        /// One of the <see cref="CloseStatusCode"/> enum values, represents the status code
 | 
					 | 
				
			||||||
        /// indicating the reason for the close.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        /// <param name="reason">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the reason for the close.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public async Task CloseAsync(CloseStatusCode code, string reason)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            byte[] data = null;
 | 
					 | 
				
			||||||
            var msg = _readyState.CheckIfClosable() ??
 | 
					 | 
				
			||||||
                      (data = await ((ushort)code).AppendAsync(reason).ConfigureAwait(false)).CheckIfValidControlData("reason");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (msg != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                error(msg);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var send = _readyState == WebSocketState.Open && !code.IsReserved();
 | 
					 | 
				
			||||||
            await CloseAsync(new PayloadData(data), send, send).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Sends a binary <paramref name="data"/> asynchronously using the WebSocket connection.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <remarks>
 | 
					 | 
				
			||||||
        /// This method doesn't wait for the send to be complete.
 | 
					 | 
				
			||||||
        /// </remarks>
 | 
					 | 
				
			||||||
        /// <param name="data">
 | 
					 | 
				
			||||||
        /// An array of <see cref="byte"/> that represents the binary data to send.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public Task SendAsync(byte[] data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (data == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                throw new ArgumentNullException(nameof(data));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var msg = _readyState.CheckIfOpen();
 | 
					 | 
				
			||||||
            if (msg != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                throw new Exception(msg);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return SendAsync(Opcode.Binary, new MemoryStream(data));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Sends a text <paramref name="data"/> asynchronously using the WebSocket connection.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <remarks>
 | 
					 | 
				
			||||||
        /// This method doesn't wait for the send to be complete.
 | 
					 | 
				
			||||||
        /// </remarks>
 | 
					 | 
				
			||||||
        /// <param name="data">
 | 
					 | 
				
			||||||
        /// A <see cref="string"/> that represents the text data to send.
 | 
					 | 
				
			||||||
        /// </param>
 | 
					 | 
				
			||||||
        public Task SendAsync(string data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (data == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                throw new ArgumentNullException(nameof(data));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var msg = _readyState.CheckIfOpen();
 | 
					 | 
				
			||||||
            if (msg != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                throw new Exception(msg);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return SendAsync(Opcode.Text, new MemoryStream(Encoding.UTF8.GetBytes(data)));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Explicit Interface Implementation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Closes the WebSocket connection, and releases all associated resources.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <remarks>
 | 
					 | 
				
			||||||
        /// This method closes the WebSocket connection with <see cref="CloseStatusCode.Away"/>.
 | 
					 | 
				
			||||||
        /// </remarks>
 | 
					 | 
				
			||||||
        void IDisposable.Dispose()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            CloseAsync(CloseStatusCode.Away, null).GetAwaiter().GetResult();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,61 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// The exception that is thrown when a <see cref="WebSocket"/> gets a fatal error.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public class WebSocketException : Exception
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Internal Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketException()
 | 
					 | 
				
			||||||
          : this(CloseStatusCode.Abnormal, null, null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketException(string message)
 | 
					 | 
				
			||||||
          : this(CloseStatusCode.Abnormal, message, null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketException(CloseStatusCode code)
 | 
					 | 
				
			||||||
          : this(code, null, null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketException(string message, Exception innerException)
 | 
					 | 
				
			||||||
          : this(CloseStatusCode.Abnormal, message, innerException)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketException(CloseStatusCode code, string message)
 | 
					 | 
				
			||||||
          : this(code, message, null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketException(CloseStatusCode code, string message, Exception innerException)
 | 
					 | 
				
			||||||
          : base(message ?? code.GetMessage(), innerException)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Code = code;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Gets the status code indicating the cause for the exception.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <value>
 | 
					 | 
				
			||||||
        /// One of the <see cref="CloseStatusCode"/> enum values, represents the status code indicating
 | 
					 | 
				
			||||||
        /// the cause for the exception.
 | 
					 | 
				
			||||||
        /// </value>
 | 
					 | 
				
			||||||
        public CloseStatusCode Code
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get; private set;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,432 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace SocketHttpListener
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    internal class WebSocketFrame : IEnumerable<byte>
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Private Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private byte[] _extPayloadLength;
 | 
					 | 
				
			||||||
        private Fin _fin;
 | 
					 | 
				
			||||||
        private Mask _mask;
 | 
					 | 
				
			||||||
        private byte[] _maskingKey;
 | 
					 | 
				
			||||||
        private Opcode _opcode;
 | 
					 | 
				
			||||||
        private PayloadData _payloadData;
 | 
					 | 
				
			||||||
        private byte _payloadLength;
 | 
					 | 
				
			||||||
        private Rsv _rsv1;
 | 
					 | 
				
			||||||
        private Rsv _rsv2;
 | 
					 | 
				
			||||||
        private Rsv _rsv3;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Fields
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static readonly byte[] EmptyUnmaskPingData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Static Constructor
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        static WebSocketFrame()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            EmptyUnmaskPingData = CreatePingFrame(Mask.Unmask).ToByteArray();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Private Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private WebSocketFrame()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Constructors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketFrame(Opcode opcode, PayloadData payload)
 | 
					 | 
				
			||||||
            : this(Fin.Final, opcode, Mask.Mask, payload, false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketFrame(Opcode opcode, Mask mask, PayloadData payload)
 | 
					 | 
				
			||||||
            : this(Fin.Final, opcode, mask, payload, false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketFrame(Fin fin, Opcode opcode, Mask mask, PayloadData payload)
 | 
					 | 
				
			||||||
            : this(fin, opcode, mask, payload, false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal WebSocketFrame(
 | 
					 | 
				
			||||||
          Fin fin, Opcode opcode, Mask mask, PayloadData payload, bool compressed)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _fin = fin;
 | 
					 | 
				
			||||||
            _rsv1 = isData(opcode) && compressed ? Rsv.On : Rsv.Off;
 | 
					 | 
				
			||||||
            _rsv2 = Rsv.Off;
 | 
					 | 
				
			||||||
            _rsv3 = Rsv.Off;
 | 
					 | 
				
			||||||
            _opcode = opcode;
 | 
					 | 
				
			||||||
            _mask = mask;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var len = payload.Length;
 | 
					 | 
				
			||||||
            if (len < 126)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _payloadLength = (byte)len;
 | 
					 | 
				
			||||||
                _extPayloadLength = new byte[0];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (len < 0x010000)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _payloadLength = (byte)126;
 | 
					 | 
				
			||||||
                _extPayloadLength = ((ushort)len).ToByteArrayInternally(ByteOrder.Big);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _payloadLength = (byte)127;
 | 
					 | 
				
			||||||
                _extPayloadLength = len.ToByteArrayInternally(ByteOrder.Big);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (mask == Mask.Mask)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _maskingKey = createMaskingKey();
 | 
					 | 
				
			||||||
                payload.Mask(_maskingKey);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _maskingKey = new byte[0];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _payloadData = payload;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public byte[] ExtendedPayloadLength => _extPayloadLength;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Fin Fin => _fin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsBinary => _opcode == Opcode.Binary;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsClose => _opcode == Opcode.Close;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsCompressed => _rsv1 == Rsv.On;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsContinuation => _opcode == Opcode.Cont;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsControl => _opcode == Opcode.Close || _opcode == Opcode.Ping || _opcode == Opcode.Pong;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsData => _opcode == Opcode.Binary || _opcode == Opcode.Text;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsFinal => _fin == Fin.Final;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsFragmented => _fin == Fin.More || _opcode == Opcode.Cont;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsMasked => _mask == Mask.Mask;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsPerMessageCompressed => (_opcode == Opcode.Binary || _opcode == Opcode.Text) && _rsv1 == Rsv.On;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsPing => _opcode == Opcode.Ping;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsPong => _opcode == Opcode.Pong;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool IsText => _opcode == Opcode.Text;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ulong Length => 2 + (ulong)(_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Mask Mask => _mask;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public byte[] MaskingKey => _maskingKey;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Opcode Opcode => _opcode;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public PayloadData PayloadData => _payloadData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public byte PayloadLength => _payloadLength;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Rsv Rsv1 => _rsv1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Rsv Rsv2 => _rsv2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Rsv Rsv3 => _rsv3;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Private Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private byte[] createMaskingKey()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var key = new byte[4];
 | 
					 | 
				
			||||||
            var rand = new Random();
 | 
					 | 
				
			||||||
            rand.NextBytes(key);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return key;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static bool isControl(Opcode opcode)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return opcode == Opcode.Close || opcode == Opcode.Ping || opcode == Opcode.Pong;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static bool isData(Opcode opcode)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return opcode == Opcode.Text || opcode == Opcode.Binary;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static async Task<WebSocketFrame> ReadAsync(byte[] header, Stream stream, bool unmask)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            /* Header */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // FIN
 | 
					 | 
				
			||||||
            var fin = (header[0] & 0x80) == 0x80 ? Fin.Final : Fin.More;
 | 
					 | 
				
			||||||
            // RSV1
 | 
					 | 
				
			||||||
            var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off;
 | 
					 | 
				
			||||||
            // RSV2
 | 
					 | 
				
			||||||
            var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off;
 | 
					 | 
				
			||||||
            // RSV3
 | 
					 | 
				
			||||||
            var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off;
 | 
					 | 
				
			||||||
            // Opcode
 | 
					 | 
				
			||||||
            var opcode = (Opcode)(header[0] & 0x0f);
 | 
					 | 
				
			||||||
            // MASK
 | 
					 | 
				
			||||||
            var mask = (header[1] & 0x80) == 0x80 ? Mask.Mask : Mask.Unmask;
 | 
					 | 
				
			||||||
            // Payload Length
 | 
					 | 
				
			||||||
            var payloadLen = (byte)(header[1] & 0x7f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Check if correct frame.
 | 
					 | 
				
			||||||
            var incorrect = isControl(opcode) && fin == Fin.More
 | 
					 | 
				
			||||||
                            ? "A control frame is fragmented."
 | 
					 | 
				
			||||||
                            : !isData(opcode) && rsv1 == Rsv.On
 | 
					 | 
				
			||||||
                              ? "A non data frame is compressed."
 | 
					 | 
				
			||||||
                              : null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (incorrect != null)
 | 
					 | 
				
			||||||
                throw new WebSocketException(CloseStatusCode.IncorrectData, incorrect);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Check if consistent frame.
 | 
					 | 
				
			||||||
            if (isControl(opcode) && payloadLen > 125)
 | 
					 | 
				
			||||||
                throw new WebSocketException(
 | 
					 | 
				
			||||||
                  CloseStatusCode.InconsistentData,
 | 
					 | 
				
			||||||
                  "The length of payload data of a control frame is greater than 125 bytes.");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var frame = new WebSocketFrame();
 | 
					 | 
				
			||||||
            frame._fin = fin;
 | 
					 | 
				
			||||||
            frame._rsv1 = rsv1;
 | 
					 | 
				
			||||||
            frame._rsv2 = rsv2;
 | 
					 | 
				
			||||||
            frame._rsv3 = rsv3;
 | 
					 | 
				
			||||||
            frame._opcode = opcode;
 | 
					 | 
				
			||||||
            frame._mask = mask;
 | 
					 | 
				
			||||||
            frame._payloadLength = payloadLen;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Extended Payload Length */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var size = payloadLen < 126
 | 
					 | 
				
			||||||
                       ? 0
 | 
					 | 
				
			||||||
                       : payloadLen == 126
 | 
					 | 
				
			||||||
                         ? 2
 | 
					 | 
				
			||||||
                         : 8;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var extPayloadLen = size > 0 ? await stream.ReadBytesAsync(size).ConfigureAwait(false) : Array.Empty<byte>();
 | 
					 | 
				
			||||||
            if (size > 0 && extPayloadLen.Length != size)
 | 
					 | 
				
			||||||
                throw new WebSocketException(
 | 
					 | 
				
			||||||
                  "The 'Extended Payload Length' of a frame cannot be read from the data source.");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            frame._extPayloadLength = extPayloadLen;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Masking Key */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var masked = mask == Mask.Mask;
 | 
					 | 
				
			||||||
            var maskingKey = masked ? await stream.ReadBytesAsync(4).ConfigureAwait(false) : Array.Empty<byte>();
 | 
					 | 
				
			||||||
            if (masked && maskingKey.Length != 4)
 | 
					 | 
				
			||||||
                throw new WebSocketException(
 | 
					 | 
				
			||||||
                  "The 'Masking Key' of a frame cannot be read from the data source.");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            frame._maskingKey = maskingKey;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Payload Data */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ulong len = payloadLen < 126
 | 
					 | 
				
			||||||
                        ? payloadLen
 | 
					 | 
				
			||||||
                        : payloadLen == 126
 | 
					 | 
				
			||||||
                          ? extPayloadLen.ToUInt16(ByteOrder.Big)
 | 
					 | 
				
			||||||
                          : extPayloadLen.ToUInt64(ByteOrder.Big);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            byte[] data = null;
 | 
					 | 
				
			||||||
            if (len > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // Check if allowable payload data length.
 | 
					 | 
				
			||||||
                if (payloadLen > 126 && len > PayloadData.MaxLength)
 | 
					 | 
				
			||||||
                    throw new WebSocketException(
 | 
					 | 
				
			||||||
                      CloseStatusCode.TooBig,
 | 
					 | 
				
			||||||
                      "The length of 'Payload Data' of a frame is greater than the allowable length.");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                data = payloadLen > 126
 | 
					 | 
				
			||||||
                       ? await stream.ReadBytesAsync((long)len, 1024).ConfigureAwait(false)
 | 
					 | 
				
			||||||
                       : await stream.ReadBytesAsync((int)len).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                //if (data.LongLength != (long)len)
 | 
					 | 
				
			||||||
                //    throw new WebSocketException(
 | 
					 | 
				
			||||||
                //      "The 'Payload Data' of a frame cannot be read from the data source.");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                data = Array.Empty<byte>();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var payload = new PayloadData(data, masked);
 | 
					 | 
				
			||||||
            if (masked && unmask)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                payload.Mask(maskingKey);
 | 
					 | 
				
			||||||
                frame._mask = Mask.Unmask;
 | 
					 | 
				
			||||||
                frame._maskingKey = Array.Empty<byte>();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            frame._payloadData = payload;
 | 
					 | 
				
			||||||
            return frame;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Internal Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static WebSocketFrame CreateCloseFrame(Mask mask, byte[] data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return new WebSocketFrame(Opcode.Close, mask, new PayloadData(data));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static WebSocketFrame CreateCloseFrame(Mask mask, PayloadData payload)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return new WebSocketFrame(Opcode.Close, mask, payload);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static async Task<WebSocketFrame> CreateCloseFrameAsync(Mask mask, CloseStatusCode code, string reason)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return new WebSocketFrame(
 | 
					 | 
				
			||||||
              Opcode.Close, mask, new PayloadData(await ((ushort)code).AppendAsync(reason).ConfigureAwait(false)));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static WebSocketFrame CreatePingFrame(Mask mask)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return new WebSocketFrame(Opcode.Ping, mask, new PayloadData());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static WebSocketFrame CreatePingFrame(Mask mask, byte[] data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return new WebSocketFrame(Opcode.Ping, mask, new PayloadData(data));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static WebSocketFrame CreatePongFrame(Mask mask, PayloadData payload)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return new WebSocketFrame(Opcode.Pong, mask, payload);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static WebSocketFrame CreateWebSocketFrame(
 | 
					 | 
				
			||||||
          Fin fin, Opcode opcode, Mask mask, byte[] data, bool compressed)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return new WebSocketFrame(fin, opcode, mask, new PayloadData(data), compressed);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static Task<WebSocketFrame> ReadAsync(Stream stream)
 | 
					 | 
				
			||||||
            => ReadAsync(stream, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static async Task<WebSocketFrame> ReadAsync(Stream stream, bool unmask)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var header = await stream.ReadBytesAsync(2).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            if (header.Length != 2)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                throw new WebSocketException(
 | 
					 | 
				
			||||||
                  "The header part of a frame cannot be read from the data source.");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return await ReadAsync(header, stream, unmask).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        internal static async Task ReadAsync(
 | 
					 | 
				
			||||||
          Stream stream, bool unmask, Action<WebSocketFrame> completed, Action<Exception> error)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var header = await stream.ReadBytesAsync(2).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                if (header.Length != 2)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    throw new WebSocketException(
 | 
					 | 
				
			||||||
                      "The header part of a frame cannot be read from the data source.");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var frame = await ReadAsync(header, stream, unmask).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                completed?.Invoke(frame);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                error.Invoke(ex);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Public Methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public IEnumerator<byte> GetEnumerator()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            foreach (var b in ToByteArray())
 | 
					 | 
				
			||||||
                yield return b;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void Print(bool dumped)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            //Console.WriteLine(dumped ? dump(this) : print(this));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public byte[] ToByteArray()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            using (var buff = new MemoryStream())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var header = (int)_fin;
 | 
					 | 
				
			||||||
                header = (header << 1) + (int)_rsv1;
 | 
					 | 
				
			||||||
                header = (header << 1) + (int)_rsv2;
 | 
					 | 
				
			||||||
                header = (header << 1) + (int)_rsv3;
 | 
					 | 
				
			||||||
                header = (header << 4) + (int)_opcode;
 | 
					 | 
				
			||||||
                header = (header << 1) + (int)_mask;
 | 
					 | 
				
			||||||
                header = (header << 7) + (int)_payloadLength;
 | 
					 | 
				
			||||||
                buff.Write(((ushort)header).ToByteArrayInternally(ByteOrder.Big), 0, 2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (_payloadLength > 125)
 | 
					 | 
				
			||||||
                    buff.Write(_extPayloadLength, 0, _extPayloadLength.Length);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (_mask == Mask.Mask)
 | 
					 | 
				
			||||||
                    buff.Write(_maskingKey, 0, _maskingKey.Length);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (_payloadLength > 0)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var payload = _payloadData.ToByteArray();
 | 
					 | 
				
			||||||
                    if (_payloadLength < 127)
 | 
					 | 
				
			||||||
                        buff.Write(payload, 0, payload.Length);
 | 
					 | 
				
			||||||
                    else
 | 
					 | 
				
			||||||
                        buff.WriteBytes(payload);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return buff.ToArray();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override string ToString()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return BitConverter.ToString(ToByteArray());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Explicitly Implemented Interface Members
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        IEnumerator IEnumerable.GetEnumerator()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GetEnumerator();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user