Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9029714f17 | |||
| fb0485faec |
-15
@@ -1,15 +0,0 @@
|
|||||||
## Ignore build results and temporary files.
|
|
||||||
|
|
||||||
Backup*
|
|
||||||
_UpgradeReport_Files
|
|
||||||
bin
|
|
||||||
obj
|
|
||||||
.idea
|
|
||||||
|
|
||||||
*.mdb
|
|
||||||
*.pdb
|
|
||||||
*.pidb
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userprefs
|
|
||||||
UpgradeLog*.*
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Net;
|
|
||||||
|
|
||||||
namespace Example
|
|
||||||
{
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
public static void Main (string[] args)
|
|
||||||
{
|
|
||||||
// Create a new instance of the WebSocket class.
|
|
||||||
//
|
|
||||||
// The WebSocket class inherits the System.IDisposable interface, so you can
|
|
||||||
// use the using statement. And the WebSocket connection will be closed with
|
|
||||||
// close status 1001 (going away) when the control leaves the using block.
|
|
||||||
//
|
|
||||||
// If you would like to connect to the server with the secure connection,
|
|
||||||
// you should create a new instance with a wss scheme WebSocket URL.
|
|
||||||
|
|
||||||
using (var ws = new WebSocket ("ws://localhost:4649/Echo"))
|
|
||||||
//using (var ws = new WebSocket ("wss://localhost:5963/Echo"))
|
|
||||||
//using (var ws = new WebSocket ("ws://localhost:4649/Chat"))
|
|
||||||
//using (var ws = new WebSocket ("wss://localhost:5963/Chat"))
|
|
||||||
//using (var ws = new WebSocket ("ws://localhost:4649/Chat?name=nobita"))
|
|
||||||
//using (var ws = new WebSocket ("wss://localhost:5963/Chat?name=nobita"))
|
|
||||||
{
|
|
||||||
// Set the WebSocket events.
|
|
||||||
|
|
||||||
ws.OnOpen += (sender, e) => ws.Send ("Hi, there!");
|
|
||||||
|
|
||||||
ws.OnMessage += (sender, e) => {
|
|
||||||
var fmt = "WebSocket Message: {0}";
|
|
||||||
var body = !e.IsPing ? e.Data : "Received a ping.";
|
|
||||||
var msg = String.Format (fmt, body);
|
|
||||||
|
|
||||||
Console.WriteLine (msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.OnError += (sender, e) => {
|
|
||||||
var fmt = "WebSocket Error: {0}";
|
|
||||||
var msg = String.Format (fmt, e.Message);
|
|
||||||
|
|
||||||
Console.WriteLine (msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.OnClose += (sender, e) => {
|
|
||||||
var fmt = "WebSocket Close ({0}): {1}";
|
|
||||||
var msg = String.Format (fmt, e.Code, e.Reason);
|
|
||||||
|
|
||||||
Console.WriteLine (msg);
|
|
||||||
};
|
|
||||||
#if DEBUG
|
|
||||||
// To change the logging level.
|
|
||||||
ws.Log.Level = LogLevel.Trace;
|
|
||||||
|
|
||||||
// To change the wait time for the response to the Ping or Close.
|
|
||||||
//ws.WaitTime = TimeSpan.FromSeconds (10);
|
|
||||||
|
|
||||||
// To emit a WebSocket.OnMessage event when receives a ping.
|
|
||||||
//ws.EmitOnPing = true;
|
|
||||||
#endif
|
|
||||||
// To enable the Per-message Compression extension.
|
|
||||||
//ws.Compression = CompressionMethod.Deflate;
|
|
||||||
|
|
||||||
// To validate the server certificate.
|
|
||||||
/*
|
|
||||||
ws.SslConfiguration.ServerCertificateValidationCallback =
|
|
||||||
(sender, certificate, chain, sslPolicyErrors) => {
|
|
||||||
ws.Log.Debug (
|
|
||||||
String.Format (
|
|
||||||
"Certificate:\n- Issuer: {0}\n- Subject: {1}",
|
|
||||||
certificate.Issuer,
|
|
||||||
certificate.Subject
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return true; // If the server certificate is valid.
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// To send the credentials for the HTTP Authentication (Basic/Digest).
|
|
||||||
//ws.SetCredentials ("nobita", "password", false);
|
|
||||||
|
|
||||||
// To send the Origin header.
|
|
||||||
//ws.Origin = "http://localhost:4649";
|
|
||||||
|
|
||||||
// To send the cookies.
|
|
||||||
//ws.SetCookie (new Cookie ("name", "nobita"));
|
|
||||||
//ws.SetCookie (new Cookie ("roles", "\"idiot, gunfighter\""));
|
|
||||||
|
|
||||||
// To connect through the HTTP Proxy server.
|
|
||||||
//ws.SetProxy ("http://localhost:3128", "nobita", "password");
|
|
||||||
|
|
||||||
// To enable the redirection.
|
|
||||||
//ws.EnableRedirection = true;
|
|
||||||
|
|
||||||
// Connect to the server.
|
|
||||||
ws.Connect ();
|
|
||||||
|
|
||||||
// Connect to the server asynchronously.
|
|
||||||
//ws.ConnectAsync ();
|
|
||||||
|
|
||||||
Console.WriteLine ("\nType 'exit' to exit.\n");
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
Thread.Sleep (1000);
|
|
||||||
Console.Write ("> ");
|
|
||||||
|
|
||||||
var msg = Console.ReadLine ();
|
|
||||||
|
|
||||||
if (msg == "exit")
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Send a text message.
|
|
||||||
ws.Send (msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<appSettings>
|
|
||||||
<add key="CertFilePassword" value="password"/>
|
|
||||||
<add key="ServerCertFile" value="/path/to/cert.pfx"/>
|
|
||||||
</appSettings>
|
|
||||||
</configuration>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
// Information about this assembly is defined by the following attributes.
|
|
||||||
// Change them to the values specific to your project.
|
|
||||||
|
|
||||||
[assembly: AssemblyTitle("Example2")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
|
||||||
[assembly: AssemblyProduct("")]
|
|
||||||
[assembly: AssemblyCopyright("sta.blockhead")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
|
||||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
|
||||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.*")]
|
|
||||||
|
|
||||||
// The following attributes are used to specify the signing key for the assembly,
|
|
||||||
// if desired. See the Mono documentation for more information about signing.
|
|
||||||
|
|
||||||
//[assembly: AssemblyDelaySign(false)]
|
|
||||||
//[assembly: AssemblyKeyFile("")]
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
|
|
||||||
namespace Example2
|
|
||||||
{
|
|
||||||
public class Chat : WebSocketBehavior
|
|
||||||
{
|
|
||||||
private string _name;
|
|
||||||
private static int _number = 0;
|
|
||||||
private string _prefix;
|
|
||||||
|
|
||||||
public Chat ()
|
|
||||||
{
|
|
||||||
_prefix = "anon#";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Prefix {
|
|
||||||
get {
|
|
||||||
return _prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_prefix = !value.IsNullOrEmpty () ? value : "anon#";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string getName ()
|
|
||||||
{
|
|
||||||
var name = QueryString["name"];
|
|
||||||
|
|
||||||
return !name.IsNullOrEmpty () ? name : _prefix + getNumber ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getNumber ()
|
|
||||||
{
|
|
||||||
return Interlocked.Increment (ref _number);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnClose (CloseEventArgs e)
|
|
||||||
{
|
|
||||||
if (_name == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var fmt = "{0} got logged off...";
|
|
||||||
var msg = String.Format (fmt, _name);
|
|
||||||
|
|
||||||
Sessions.Broadcast (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnMessage (MessageEventArgs e)
|
|
||||||
{
|
|
||||||
var fmt = "{0}: {1}";
|
|
||||||
var msg = String.Format (fmt, _name, e.Data);
|
|
||||||
|
|
||||||
Sessions.Broadcast (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnOpen ()
|
|
||||||
{
|
|
||||||
_name = getName ();
|
|
||||||
|
|
||||||
var fmt = "{0} has logged in!";
|
|
||||||
var msg = String.Format (fmt, _name);
|
|
||||||
|
|
||||||
Sessions.Broadcast (msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
|
|
||||||
namespace Example2
|
|
||||||
{
|
|
||||||
public class Echo : WebSocketBehavior
|
|
||||||
{
|
|
||||||
protected override void OnMessage (MessageEventArgs e)
|
|
||||||
{
|
|
||||||
Send (e.Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProductVersion>9.0.21022</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}</ProjectGuid>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<RootNamespace>Example2</RootNamespace>
|
|
||||||
<AssemblyName>example2</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Externalconsole>true</Externalconsole>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<DebugType>none</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Release</OutputPath>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Externalconsole>true</Externalconsole>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_Ubuntu|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug_Ubuntu</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Externalconsole>true</Externalconsole>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_Ubuntu|AnyCPU' ">
|
|
||||||
<DebugType>none</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Release_Ubuntu</OutputPath>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Externalconsole>true</Externalconsole>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Configuration" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="AssemblyInfo.cs" />
|
|
||||||
<Compile Include="Program.cs" />
|
|
||||||
<Compile Include="Echo.cs" />
|
|
||||||
<Compile Include="Chat.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\websocket-sharp\websocket-sharp.csproj">
|
|
||||||
<Project>{B357BAC7-529E-4D81-A0D2-71041B19C8DE}</Project>
|
|
||||||
<Name>websocket-sharp</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="App.config" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Configuration;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Net;
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
|
|
||||||
namespace Example2
|
|
||||||
{
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
public static void Main (string[] args)
|
|
||||||
{
|
|
||||||
// Create a new instance of the WebSocketServer class.
|
|
||||||
//
|
|
||||||
// If you would like to provide the secure connection, you should
|
|
||||||
// create a new instance with the 'secure' parameter set to true or
|
|
||||||
// with a wss scheme WebSocket URL.
|
|
||||||
|
|
||||||
var wssv = new WebSocketServer (4649);
|
|
||||||
//var wssv = new WebSocketServer (5963, true);
|
|
||||||
|
|
||||||
//var wssv = new WebSocketServer (System.Net.IPAddress.Any, 4649);
|
|
||||||
//var wssv = new WebSocketServer (System.Net.IPAddress.Any, 5963, true);
|
|
||||||
|
|
||||||
//var wssv = new WebSocketServer (System.Net.IPAddress.IPv6Any, 4649);
|
|
||||||
//var wssv = new WebSocketServer (System.Net.IPAddress.IPv6Any, 5963, true);
|
|
||||||
|
|
||||||
//var wssv = new WebSocketServer ("ws://0.0.0.0:4649");
|
|
||||||
//var wssv = new WebSocketServer ("wss://0.0.0.0:5963");
|
|
||||||
|
|
||||||
//var wssv = new WebSocketServer ("ws://[::0]:4649");
|
|
||||||
//var wssv = new WebSocketServer ("wss://[::0]:5963");
|
|
||||||
|
|
||||||
//var wssv = new WebSocketServer (System.Net.IPAddress.Loopback, 4649);
|
|
||||||
//var wssv = new WebSocketServer (System.Net.IPAddress.Loopback, 5963, true);
|
|
||||||
|
|
||||||
//var wssv = new WebSocketServer (System.Net.IPAddress.IPv6Loopback, 4649);
|
|
||||||
//var wssv = new WebSocketServer (System.Net.IPAddress.IPv6Loopback, 5963, true);
|
|
||||||
|
|
||||||
//var wssv = new WebSocketServer ("ws://localhost:4649");
|
|
||||||
//var wssv = new WebSocketServer ("wss://localhost:5963");
|
|
||||||
|
|
||||||
//var wssv = new WebSocketServer ("ws://127.0.0.1:4649");
|
|
||||||
//var wssv = new WebSocketServer ("wss://127.0.0.1:5963");
|
|
||||||
|
|
||||||
//var wssv = new WebSocketServer ("ws://[::1]:4649");
|
|
||||||
//var wssv = new WebSocketServer ("wss://[::1]:5963");
|
|
||||||
#if DEBUG
|
|
||||||
// To change the logging level.
|
|
||||||
wssv.Log.Level = LogLevel.Trace;
|
|
||||||
|
|
||||||
// To change the wait time for the response to the WebSocket Ping or Close.
|
|
||||||
//wssv.WaitTime = TimeSpan.FromSeconds (2);
|
|
||||||
|
|
||||||
// Not to remove the inactive sessions periodically.
|
|
||||||
//wssv.KeepClean = false;
|
|
||||||
#endif
|
|
||||||
// To provide the secure connection.
|
|
||||||
/*
|
|
||||||
var cert = ConfigurationManager.AppSettings["ServerCertFile"];
|
|
||||||
var passwd = ConfigurationManager.AppSettings["CertFilePassword"];
|
|
||||||
wssv.SslConfiguration.ServerCertificate = new X509Certificate2 (cert, passwd);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// To provide the HTTP Authentication (Basic/Digest).
|
|
||||||
/*
|
|
||||||
wssv.AuthenticationSchemes = AuthenticationSchemes.Basic;
|
|
||||||
wssv.Realm = "WebSocket Test";
|
|
||||||
wssv.UserCredentialsFinder = id => {
|
|
||||||
var name = id.Name;
|
|
||||||
|
|
||||||
// Return user name, password, and roles.
|
|
||||||
return name == "nobita"
|
|
||||||
? new NetworkCredential (name, "password", "gunfighter")
|
|
||||||
: null; // If the user credentials are not found.
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// To resolve to wait for socket in TIME_WAIT state.
|
|
||||||
//wssv.ReuseAddress = true;
|
|
||||||
|
|
||||||
// Add the WebSocket services.
|
|
||||||
wssv.AddWebSocketService<Echo> ("/Echo");
|
|
||||||
wssv.AddWebSocketService<Chat> ("/Chat");
|
|
||||||
|
|
||||||
// Add the WebSocket service with initializing.
|
|
||||||
/*
|
|
||||||
wssv.AddWebSocketService<Chat> (
|
|
||||||
"/Chat",
|
|
||||||
s => {
|
|
||||||
s.Prefix = "Anon#";
|
|
||||||
|
|
||||||
// To send the Sec-WebSocket-Protocol header that has a subprotocol name.
|
|
||||||
s.Protocol = "chat";
|
|
||||||
|
|
||||||
// To ignore the Sec-WebSocket-Extensions header.
|
|
||||||
s.IgnoreExtensions = true;
|
|
||||||
|
|
||||||
// To emit a WebSocket.OnMessage event when receives a ping.
|
|
||||||
s.EmitOnPing = true;
|
|
||||||
|
|
||||||
// To validate the Origin header.
|
|
||||||
s.OriginValidator = val => {
|
|
||||||
// Check the value of the Origin header, and return true if valid.
|
|
||||||
Uri origin;
|
|
||||||
|
|
||||||
return !val.IsNullOrEmpty ()
|
|
||||||
&& Uri.TryCreate (val, UriKind.Absolute, out origin)
|
|
||||||
&& origin.Host == "localhost";
|
|
||||||
};
|
|
||||||
|
|
||||||
// To validate the cookies.
|
|
||||||
s.CookiesValidator = (req, res) => {
|
|
||||||
// Check the cookies in 'req', and set the cookies to send to
|
|
||||||
// the client with 'res' if necessary.
|
|
||||||
foreach (var cookie in req) {
|
|
||||||
cookie.Expired = true;
|
|
||||||
res.Add (cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true; // If valid.
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
wssv.Start ();
|
|
||||||
|
|
||||||
if (wssv.IsListening) {
|
|
||||||
Console.WriteLine ("Listening on port {0}, and providing WebSocket services:", wssv.Port);
|
|
||||||
|
|
||||||
foreach (var path in wssv.WebSocketServices.Paths)
|
|
||||||
Console.WriteLine ("- {0}", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine ("\nPress Enter key to stop the server...");
|
|
||||||
Console.ReadLine ();
|
|
||||||
|
|
||||||
wssv.Stop ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<appSettings>
|
|
||||||
<add key="CertFilePassword" value="password"/>
|
|
||||||
<add key="DocumentRootPath" value="../../Public"/>
|
|
||||||
<add key="ServerCertFile" value="/path/to/cert.pfx"/>
|
|
||||||
</appSettings>
|
|
||||||
</configuration>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
// Information about this assembly is defined by the following attributes.
|
|
||||||
// Change them to the values specific to your project.
|
|
||||||
|
|
||||||
[assembly: AssemblyTitle("Example3")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
|
||||||
[assembly: AssemblyProduct("")]
|
|
||||||
[assembly: AssemblyCopyright("sta.blockhead")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
|
||||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
|
||||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.*")]
|
|
||||||
|
|
||||||
// The following attributes are used to specify the signing key for the assembly,
|
|
||||||
// if desired. See the Mono documentation for more information about signing.
|
|
||||||
|
|
||||||
//[assembly: AssemblyDelaySign(false)]
|
|
||||||
//[assembly: AssemblyKeyFile("")]
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
|
|
||||||
namespace Example3
|
|
||||||
{
|
|
||||||
public class Chat : WebSocketBehavior
|
|
||||||
{
|
|
||||||
private string _name;
|
|
||||||
private static int _number = 0;
|
|
||||||
private string _prefix;
|
|
||||||
|
|
||||||
public Chat ()
|
|
||||||
{
|
|
||||||
_prefix = "anon#";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Prefix {
|
|
||||||
get {
|
|
||||||
return _prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_prefix = !value.IsNullOrEmpty () ? value : "anon#";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string getName ()
|
|
||||||
{
|
|
||||||
var name = QueryString["name"];
|
|
||||||
|
|
||||||
return !name.IsNullOrEmpty () ? name : _prefix + getNumber ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getNumber ()
|
|
||||||
{
|
|
||||||
return Interlocked.Increment (ref _number);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnClose (CloseEventArgs e)
|
|
||||||
{
|
|
||||||
if (_name == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var fmt = "{0} got logged off...";
|
|
||||||
var msg = String.Format (fmt, _name);
|
|
||||||
|
|
||||||
Sessions.Broadcast (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnMessage (MessageEventArgs e)
|
|
||||||
{
|
|
||||||
var fmt = "{0}: {1}";
|
|
||||||
var msg = String.Format (fmt, _name, e.Data);
|
|
||||||
|
|
||||||
Sessions.Broadcast (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnOpen ()
|
|
||||||
{
|
|
||||||
_name = getName ();
|
|
||||||
|
|
||||||
var fmt = "{0} has logged in!";
|
|
||||||
var msg = String.Format (fmt, _name);
|
|
||||||
|
|
||||||
Sessions.Broadcast (msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
|
|
||||||
namespace Example3
|
|
||||||
{
|
|
||||||
public class Echo : WebSocketBehavior
|
|
||||||
{
|
|
||||||
protected override void OnMessage (MessageEventArgs e)
|
|
||||||
{
|
|
||||||
Send (e.Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProductVersion>9.0.21022</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}</ProjectGuid>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<RootNamespace>Example3</RootNamespace>
|
|
||||||
<AssemblyName>example3</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Externalconsole>true</Externalconsole>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<DebugType>none</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Release</OutputPath>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Externalconsole>true</Externalconsole>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_Ubuntu|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug_Ubuntu</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Externalconsole>true</Externalconsole>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_Ubuntu|AnyCPU' ">
|
|
||||||
<DebugType>none</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Release_Ubuntu</OutputPath>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Externalconsole>true</Externalconsole>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Configuration" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="AssemblyInfo.cs" />
|
|
||||||
<Compile Include="Program.cs" />
|
|
||||||
<Compile Include="Chat.cs" />
|
|
||||||
<Compile Include="Echo.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\websocket-sharp\websocket-sharp.csproj">
|
|
||||||
<Project>{B357BAC7-529E-4D81-A0D2-71041B19C8DE}</Project>
|
|
||||||
<Name>websocket-sharp</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="App.config" />
|
|
||||||
<None Include="Public\index.html" />
|
|
||||||
<None Include="Public\Js\echotest.js" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Public\" />
|
|
||||||
<Folder Include="Public\Js\" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Configuration;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Text;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Net;
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
|
|
||||||
namespace Example3
|
|
||||||
{
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
public static void Main (string[] args)
|
|
||||||
{
|
|
||||||
// Create a new instance of the HttpServer class.
|
|
||||||
//
|
|
||||||
// If you would like to provide the secure connection, you should
|
|
||||||
// create a new instance with the 'secure' parameter set to true or
|
|
||||||
// with an https scheme HTTP URL.
|
|
||||||
|
|
||||||
var httpsv = new HttpServer (4649);
|
|
||||||
//var httpsv = new HttpServer (5963, true);
|
|
||||||
|
|
||||||
//var httpsv = new HttpServer (System.Net.IPAddress.Any, 4649);
|
|
||||||
//var httpsv = new HttpServer (System.Net.IPAddress.Any, 5963, true);
|
|
||||||
|
|
||||||
//var httpsv = new HttpServer (System.Net.IPAddress.IPv6Any, 4649);
|
|
||||||
//var httpsv = new HttpServer (System.Net.IPAddress.IPv6Any, 5963, true);
|
|
||||||
|
|
||||||
//var httpsv = new HttpServer ("http://0.0.0.0:4649");
|
|
||||||
//var httpsv = new HttpServer ("https://0.0.0.0:5963");
|
|
||||||
|
|
||||||
//var httpsv = new HttpServer ("http://[::0]:4649");
|
|
||||||
//var httpsv = new HttpServer ("https://[::0]:5963");
|
|
||||||
|
|
||||||
//var httpsv = new HttpServer (System.Net.IPAddress.Loopback, 4649);
|
|
||||||
//var httpsv = new HttpServer (System.Net.IPAddress.Loopback, 5963, true);
|
|
||||||
|
|
||||||
//var httpsv = new HttpServer (System.Net.IPAddress.IPv6Loopback, 4649);
|
|
||||||
//var httpsv = new HttpServer (System.Net.IPAddress.IPv6Loopback, 5963, true);
|
|
||||||
|
|
||||||
//var httpsv = new HttpServer ("http://localhost:4649");
|
|
||||||
//var httpsv = new HttpServer ("https://localhost:5963");
|
|
||||||
|
|
||||||
//var httpsv = new HttpServer ("http://127.0.0.1:4649");
|
|
||||||
//var httpsv = new HttpServer ("https://127.0.0.1:5963");
|
|
||||||
|
|
||||||
//var httpsv = new HttpServer ("http://[::1]:4649");
|
|
||||||
//var httpsv = new HttpServer ("https://[::1]:5963");
|
|
||||||
#if DEBUG
|
|
||||||
// To change the logging level.
|
|
||||||
httpsv.Log.Level = LogLevel.Trace;
|
|
||||||
|
|
||||||
// To change the wait time for the response to the WebSocket Ping or Close.
|
|
||||||
//httpsv.WaitTime = TimeSpan.FromSeconds (2);
|
|
||||||
|
|
||||||
// Not to remove the inactive WebSocket sessions periodically.
|
|
||||||
//httpsv.KeepClean = false;
|
|
||||||
#endif
|
|
||||||
// To provide the secure connection.
|
|
||||||
/*
|
|
||||||
var cert = ConfigurationManager.AppSettings["ServerCertFile"];
|
|
||||||
var passwd = ConfigurationManager.AppSettings["CertFilePassword"];
|
|
||||||
httpsv.SslConfiguration.ServerCertificate = new X509Certificate2 (cert, passwd);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// To provide the HTTP Authentication (Basic/Digest).
|
|
||||||
/*
|
|
||||||
httpsv.AuthenticationSchemes = AuthenticationSchemes.Basic;
|
|
||||||
httpsv.Realm = "WebSocket Test";
|
|
||||||
httpsv.UserCredentialsFinder = id => {
|
|
||||||
var name = id.Name;
|
|
||||||
|
|
||||||
// Return user name, password, and roles.
|
|
||||||
return name == "nobita"
|
|
||||||
? new NetworkCredential (name, "password", "gunfighter")
|
|
||||||
: null; // If the user credentials are not found.
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// To resolve to wait for socket in TIME_WAIT state.
|
|
||||||
//httpsv.ReuseAddress = true;
|
|
||||||
|
|
||||||
// Set the document root path.
|
|
||||||
httpsv.DocumentRootPath = ConfigurationManager.AppSettings["DocumentRootPath"];
|
|
||||||
|
|
||||||
// Set the HTTP GET request event.
|
|
||||||
httpsv.OnGet += (sender, e) => {
|
|
||||||
var req = e.Request;
|
|
||||||
var res = e.Response;
|
|
||||||
|
|
||||||
var path = req.RawUrl;
|
|
||||||
|
|
||||||
if (path == "/")
|
|
||||||
path += "index.html";
|
|
||||||
|
|
||||||
byte[] contents;
|
|
||||||
|
|
||||||
if (!e.TryReadFile (path, out contents)) {
|
|
||||||
res.StatusCode = (int) HttpStatusCode.NotFound;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.EndsWith (".html")) {
|
|
||||||
res.ContentType = "text/html";
|
|
||||||
res.ContentEncoding = Encoding.UTF8;
|
|
||||||
}
|
|
||||||
else if (path.EndsWith (".js")) {
|
|
||||||
res.ContentType = "application/javascript";
|
|
||||||
res.ContentEncoding = Encoding.UTF8;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.ContentLength64 = contents.LongLength;
|
|
||||||
|
|
||||||
res.Close (contents, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the WebSocket services.
|
|
||||||
httpsv.AddWebSocketService<Echo> ("/Echo");
|
|
||||||
httpsv.AddWebSocketService<Chat> ("/Chat");
|
|
||||||
|
|
||||||
// Add the WebSocket service with initializing.
|
|
||||||
/*
|
|
||||||
httpsv.AddWebSocketService<Chat> (
|
|
||||||
"/Chat",
|
|
||||||
s => {
|
|
||||||
s.Prefix = "Anon#";
|
|
||||||
|
|
||||||
// To send the Sec-WebSocket-Protocol header that has a subprotocol name.
|
|
||||||
s.Protocol = "chat";
|
|
||||||
|
|
||||||
// To ignore the Sec-WebSocket-Extensions header.
|
|
||||||
s.IgnoreExtensions = true;
|
|
||||||
|
|
||||||
// To emit a WebSocket.OnMessage event when receives a ping.
|
|
||||||
s.EmitOnPing = true;
|
|
||||||
|
|
||||||
// To validate the Origin header.
|
|
||||||
s.OriginValidator = val => {
|
|
||||||
// Check the value of the Origin header, and return true if valid.
|
|
||||||
Uri origin;
|
|
||||||
|
|
||||||
return !val.IsNullOrEmpty ()
|
|
||||||
&& Uri.TryCreate (val, UriKind.Absolute, out origin)
|
|
||||||
&& origin.Host == "localhost";
|
|
||||||
};
|
|
||||||
|
|
||||||
// To validate the cookies.
|
|
||||||
s.CookiesValidator = (req, res) => {
|
|
||||||
// Check the cookies in 'req', and set the cookies to send to
|
|
||||||
// the client with 'res' if necessary.
|
|
||||||
foreach (var cookie in req) {
|
|
||||||
cookie.Expired = true;
|
|
||||||
res.Add (cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true; // If valid.
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
httpsv.Start ();
|
|
||||||
|
|
||||||
if (httpsv.IsListening) {
|
|
||||||
Console.WriteLine ("Listening on port {0}, and providing WebSocket services:", httpsv.Port);
|
|
||||||
|
|
||||||
foreach (var path in httpsv.WebSocketServices.Paths)
|
|
||||||
Console.WriteLine ("- {0}", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine ("\nPress Enter key to stop the server...");
|
|
||||||
Console.ReadLine ();
|
|
||||||
|
|
||||||
httpsv.Stop ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* echotest.js
|
|
||||||
*
|
|
||||||
* Derived from Echo Test of WebSocket.org (http://www.websocket.org/echo.html).
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012 Kaazing Corporation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var url = "ws://localhost:4649/Echo";
|
|
||||||
//var url = "wss://localhost:5963/Echo";
|
|
||||||
var output;
|
|
||||||
|
|
||||||
function init () {
|
|
||||||
output = document.getElementById ("output");
|
|
||||||
doWebSocket ();
|
|
||||||
}
|
|
||||||
|
|
||||||
function doWebSocket () {
|
|
||||||
websocket = new WebSocket (url);
|
|
||||||
|
|
||||||
websocket.onopen = function (e) {
|
|
||||||
onOpen (e);
|
|
||||||
};
|
|
||||||
|
|
||||||
websocket.onmessage = function (e) {
|
|
||||||
onMessage (e);
|
|
||||||
};
|
|
||||||
|
|
||||||
websocket.onerror = function (e) {
|
|
||||||
onError (e);
|
|
||||||
};
|
|
||||||
|
|
||||||
websocket.onclose = function (e) {
|
|
||||||
onClose (e);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpen (event) {
|
|
||||||
writeToScreen ("CONNECTED");
|
|
||||||
send ("WebSocket rocks");
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMessage (event) {
|
|
||||||
writeToScreen ('<span style="color: blue;">RESPONSE: ' + event.data + '</span>');
|
|
||||||
websocket.close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onError (event) {
|
|
||||||
writeToScreen ('<span style="color: red;">ERROR: ' + event.data + '</span>');
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClose (event) {
|
|
||||||
writeToScreen ("DISCONNECTED");
|
|
||||||
}
|
|
||||||
|
|
||||||
function send (message) {
|
|
||||||
writeToScreen ("SENT: " + message);
|
|
||||||
websocket.send (message);
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeToScreen (message) {
|
|
||||||
var pre = document.createElement ("p");
|
|
||||||
pre.style.wordWrap = "break-word";
|
|
||||||
pre.innerHTML = message;
|
|
||||||
output.appendChild (pre);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener ("load", init, false);
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>WebSocket Echo Test</title>
|
|
||||||
<script type="text/javascript" src="/Js/echotest.js">
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>WebSocket Echo Test</h2>
|
|
||||||
<div id="output"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2010-2022 sta.blockhead
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
@@ -1,708 +1,8 @@
|
|||||||

|
# websocket-sharp #
|
||||||
|
|
||||||
## Welcome to websocket-sharp! ##
|
A C# implementation of a WebSocket protocol client.
|
||||||
|
|
||||||
websocket-sharp supports:
|
|
||||||
|
|
||||||
- [RFC 6455](#supported-websocket-specifications)
|
|
||||||
- [WebSocket Client](#websocket-client) and [Server](#websocket-server)
|
|
||||||
- [Per-message Compression](#per-message-compression) extension
|
|
||||||
- [Secure Connection](#secure-connection)
|
|
||||||
- [HTTP Authentication](#http-authentication)
|
|
||||||
- [Query string, Origin header, and Cookies](#query-string-origin-header-and-cookies)
|
|
||||||
- [Connecting through the HTTP proxy server](#connecting-through-the-http-proxy-server)
|
|
||||||
- .NET Framework **3.5** or later (includes compatible environment such as [Mono])
|
|
||||||
|
|
||||||
## Branches ##
|
|
||||||
|
|
||||||
- [master] for production releases.
|
|
||||||
- [hybi-00] for older [draft-ietf-hybi-thewebsocketprotocol-00]. No longer maintained.
|
|
||||||
- [draft75] for even more old [draft-hixie-thewebsocketprotocol-75]. No longer maintained.
|
|
||||||
|
|
||||||
## Build ##
|
|
||||||
|
|
||||||
websocket-sharp is built as a single assembly, **websocket-sharp.dll**.
|
|
||||||
|
|
||||||
websocket-sharp is developed with [MonoDevelop]. So a simple way to build is to open **websocket-sharp.sln** and run build for **websocket-sharp project** with any of the build configurations (e.g. `Debug`) in MonoDevelop.
|
|
||||||
|
|
||||||
## Install ##
|
|
||||||
|
|
||||||
### Self Build ###
|
|
||||||
|
|
||||||
You should add your websocket-sharp.dll (e.g. `/path/to/websocket-sharp/bin/Debug/websocket-sharp.dll`) to the library references of your project.
|
|
||||||
|
|
||||||
If you would like to use that dll in your [Unity] project, you should add it to any folder of your project (e.g. `Assets/Plugins`) in the **Unity Editor**.
|
|
||||||
|
|
||||||
### NuGet Gallery ###
|
|
||||||
|
|
||||||
websocket-sharp is available on the [NuGet Gallery], as still a **prerelease** version.
|
|
||||||
|
|
||||||
- [NuGet Gallery: websocket-sharp]
|
|
||||||
|
|
||||||
You can add websocket-sharp to your project with the NuGet Package Manager, by using the following command in the Package Manager Console.
|
|
||||||
|
|
||||||
PM> Install-Package WebSocketSharp -Pre
|
|
||||||
|
|
||||||
### Unity Asset Store ###
|
|
||||||
|
|
||||||
websocket-sharp is available on the Unity Asset Store (Sorry, Not available now).
|
|
||||||
|
|
||||||
- [WebSocket-Sharp for Unity]
|
|
||||||
|
|
||||||
It works with **Unity Free**, but there are some limitations:
|
|
||||||
|
|
||||||
- [Security Sandbox of the Webplayer] (The server is not available in Web Player)
|
|
||||||
- [WebGL Networking] (Not available in WebGL)
|
|
||||||
- Incompatible platform (Not available for such UWP)
|
|
||||||
- Lack of dll for the System.IO.Compression (The compression extension is not available on Windows)
|
|
||||||
- .NET Socket Support for iOS/Android (iOS/Android Pro is required if your Unity is earlier than Unity 5)
|
|
||||||
- .NET API 2.0 compatibility level for iOS/Android
|
|
||||||
|
|
||||||
.NET API 2.0 compatibility level for iOS/Android may require to fix lack of some features for later than .NET Framework 2.0, such as the `System.Func<...>` delegates (so i have added them in the asset package).
|
|
||||||
|
|
||||||
And it is priced at **US$15**. I believe your $15 makes this project more better, **Thank you!**
|
|
||||||
|
|
||||||
## Usage ##
|
## Usage ##
|
||||||
|
|
||||||
### WebSocket Client ###
|
Please refer to wsclient/wsclient.cs.
|
||||||
|
|
||||||
```csharp
|
|
||||||
using System;
|
|
||||||
using WebSocketSharp;
|
|
||||||
|
|
||||||
namespace Example
|
|
||||||
{
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
public static void Main (string[] args)
|
|
||||||
{
|
|
||||||
using (var ws = new WebSocket ("ws://dragonsnest.far/Laputa")) {
|
|
||||||
ws.OnMessage += (sender, e) =>
|
|
||||||
Console.WriteLine ("Laputa says: " + e.Data);
|
|
||||||
|
|
||||||
ws.Connect ();
|
|
||||||
ws.Send ("BALUS");
|
|
||||||
Console.ReadKey (true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 1 ####
|
|
||||||
|
|
||||||
Required namespace.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using WebSocketSharp;
|
|
||||||
```
|
|
||||||
|
|
||||||
The `WebSocket` class exists in the `WebSocketSharp` namespace.
|
|
||||||
|
|
||||||
#### Step 2 ####
|
|
||||||
|
|
||||||
Creating a new instance of the `WebSocket` class with the WebSocket URL to connect.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var ws = new WebSocket ("ws://example.com");
|
|
||||||
```
|
|
||||||
|
|
||||||
The `WebSocket` class inherits the `System.IDisposable` interface, so you can create it with the `using` statement.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using (var ws = new WebSocket ("ws://example.com")) {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This will **close** the WebSocket connection with status code `1001` (going away) when the control leaves the `using` block.
|
|
||||||
|
|
||||||
#### Step 3 ####
|
|
||||||
|
|
||||||
Setting the `WebSocket` events.
|
|
||||||
|
|
||||||
##### WebSocket.OnOpen Event #####
|
|
||||||
|
|
||||||
This event occurs when the WebSocket connection has been established.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.OnOpen += (sender, e) => {
|
|
||||||
...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
`System.EventArgs.Empty` is passed as `e`, so you do not need to use it.
|
|
||||||
|
|
||||||
##### WebSocket.OnMessage Event #####
|
|
||||||
|
|
||||||
This event occurs when the `WebSocket` receives a message.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.OnMessage += (sender, e) => {
|
|
||||||
...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
A `WebSocketSharp.MessageEventArgs` instance is passed as `e`.
|
|
||||||
|
|
||||||
If you would like to get the message data, you should access `e.Data` or `e.RawData` property.
|
|
||||||
|
|
||||||
`e.Data` property returns a `string`, so it is mainly used to get the **text** message data.
|
|
||||||
|
|
||||||
`e.RawData` property returns a `byte[]`, so it is mainly used to get the **binary** message data.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
if (e.IsText) {
|
|
||||||
// Do something with e.Data.
|
|
||||||
...
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.IsBinary) {
|
|
||||||
// Do something with e.RawData.
|
|
||||||
...
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And if you would like to notify that a **ping** has been received, via this event, you should set the `WebSocket.EmitOnPing` property to `true`.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.EmitOnPing = true;
|
|
||||||
ws.OnMessage += (sender, e) => {
|
|
||||||
if (e.IsPing) {
|
|
||||||
// Do something to notify that a ping has been received.
|
|
||||||
...
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
##### WebSocket.OnError Event #####
|
|
||||||
|
|
||||||
This event occurs when the `WebSocket` gets an error.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.OnError += (sender, e) => {
|
|
||||||
...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
A `WebSocketSharp.ErrorEventArgs` instance is passed as `e`.
|
|
||||||
|
|
||||||
If you would like to get the error message, you should access `e.Message` property.
|
|
||||||
|
|
||||||
`e.Message` property returns a `string` that represents the error message.
|
|
||||||
|
|
||||||
And `e.Exception` property returns a `System.Exception` instance that represents the cause of the error if it is due to an exception.
|
|
||||||
|
|
||||||
##### WebSocket.OnClose Event #####
|
|
||||||
|
|
||||||
This event occurs when the WebSocket connection has been closed.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.OnClose += (sender, e) => {
|
|
||||||
...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
A `WebSocketSharp.CloseEventArgs` instance is passed as `e`.
|
|
||||||
|
|
||||||
If you would like to get the reason for the close, you should access `e.Code` or `e.Reason` property.
|
|
||||||
|
|
||||||
`e.Code` property returns a `ushort` that represents the status code for the close.
|
|
||||||
|
|
||||||
`e.Reason` property returns a `string` that represents the reason for the close.
|
|
||||||
|
|
||||||
#### Step 4 ####
|
|
||||||
|
|
||||||
Connecting to the WebSocket server.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.Connect ();
|
|
||||||
```
|
|
||||||
|
|
||||||
If you would like to connect to the server asynchronously, you should use the `WebSocket.ConnectAsync ()` method.
|
|
||||||
|
|
||||||
#### Step 5 ####
|
|
||||||
|
|
||||||
Sending data to the WebSocket server.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.Send (data);
|
|
||||||
```
|
|
||||||
|
|
||||||
The `WebSocket.Send` method is overloaded.
|
|
||||||
|
|
||||||
You can use the `WebSocket.Send (string)`, `WebSocket.Send (byte[])`, or `WebSocket.Send (System.IO.FileInfo)` method to send the data.
|
|
||||||
|
|
||||||
If you would like to send the data asynchronously, you should use the `WebSocket.SendAsync` method.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.SendAsync (data, completed);
|
|
||||||
```
|
|
||||||
|
|
||||||
And also if you would like to do something when the send is complete, you should set `completed` to any `Action<bool>` delegate.
|
|
||||||
|
|
||||||
#### Step 6 ####
|
|
||||||
|
|
||||||
Closing the WebSocket connection.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.Close (code, reason);
|
|
||||||
```
|
|
||||||
|
|
||||||
If you would like to close the connection explicitly, you should use the `WebSocket.Close` method.
|
|
||||||
|
|
||||||
The `WebSocket.Close` method is overloaded.
|
|
||||||
|
|
||||||
You can use the `WebSocket.Close ()`, `WebSocket.Close (ushort)`, `WebSocket.Close (WebSocketSharp.CloseStatusCode)`, `WebSocket.Close (ushort, string)`, or `WebSocket.Close (WebSocketSharp.CloseStatusCode, string)` method to close the connection.
|
|
||||||
|
|
||||||
If you would like to close the connection asynchronously, you should use the `WebSocket.CloseAsync` method.
|
|
||||||
|
|
||||||
### WebSocket Server ###
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using System;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
|
|
||||||
namespace Example
|
|
||||||
{
|
|
||||||
public class Laputa : WebSocketBehavior
|
|
||||||
{
|
|
||||||
protected override void OnMessage (MessageEventArgs e)
|
|
||||||
{
|
|
||||||
var msg = e.Data == "BALUS"
|
|
||||||
? "Are you kidding?"
|
|
||||||
: "I'm not available now.";
|
|
||||||
|
|
||||||
Send (msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
public static void Main (string[] args)
|
|
||||||
{
|
|
||||||
var wssv = new WebSocketServer ("ws://dragonsnest.far");
|
|
||||||
|
|
||||||
wssv.AddWebSocketService<Laputa> ("/Laputa");
|
|
||||||
wssv.Start ();
|
|
||||||
Console.ReadKey (true);
|
|
||||||
wssv.Stop ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 1 ####
|
|
||||||
|
|
||||||
Required namespace.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
```
|
|
||||||
|
|
||||||
The `WebSocketBehavior` and `WebSocketServer` classes exist in the `WebSocketSharp.Server` namespace.
|
|
||||||
|
|
||||||
#### Step 2 ####
|
|
||||||
|
|
||||||
Creating the class that inherits the `WebSocketBehavior` class.
|
|
||||||
|
|
||||||
For example, if you would like to provide an echo service,
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using System;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
|
|
||||||
public class Echo : WebSocketBehavior
|
|
||||||
{
|
|
||||||
protected override void OnMessage (MessageEventArgs e)
|
|
||||||
{
|
|
||||||
Send (e.Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And if you would like to provide a chat service,
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using System;
|
|
||||||
using WebSocketSharp;
|
|
||||||
using WebSocketSharp.Server;
|
|
||||||
|
|
||||||
public class Chat : WebSocketBehavior
|
|
||||||
{
|
|
||||||
private string _suffix;
|
|
||||||
|
|
||||||
public Chat ()
|
|
||||||
{
|
|
||||||
_suffix = String.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Suffix {
|
|
||||||
get {
|
|
||||||
return _suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_suffix = value ?? String.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnMessage (MessageEventArgs e)
|
|
||||||
{
|
|
||||||
Sessions.Broadcast (e.Data + _suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can define the behavior of any WebSocket service by creating the class that inherits the `WebSocketBehavior` class.
|
|
||||||
|
|
||||||
If you override the `WebSocketBehavior.OnMessage (MessageEventArgs)` method, it will be called when the `WebSocket` used in a session in the service receives a message.
|
|
||||||
|
|
||||||
And if you override the `WebSocketBehavior.OnOpen ()`, `WebSocketBehavior.OnError (ErrorEventArgs)`, and `WebSocketBehavior.OnClose (CloseEventArgs)` methods, each of them will be called when each of the `WebSocket` events (`OnOpen`, `OnError`, and `OnClose`) occurs.
|
|
||||||
|
|
||||||
The `WebSocketBehavior.Send` method can send data to the client on a session in the service.
|
|
||||||
|
|
||||||
If you would like to get the sessions in the service, you should access the `WebSocketBehavior.Sessions` property (returns a `WebSocketSharp.Server.WebSocketSessionManager`).
|
|
||||||
|
|
||||||
The `WebSocketBehavior.Sessions.Broadcast` method can send data to every client in the service.
|
|
||||||
|
|
||||||
#### Step 3 ####
|
|
||||||
|
|
||||||
Creating a new instance of the `WebSocketServer` class.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var wssv = new WebSocketServer (4649);
|
|
||||||
|
|
||||||
wssv.AddWebSocketService<Echo> ("/Echo");
|
|
||||||
wssv.AddWebSocketService<Chat> ("/Chat");
|
|
||||||
wssv.AddWebSocketService<Chat> ("/ChatWithNyan", s => s.Suffix = " Nyan!");
|
|
||||||
```
|
|
||||||
|
|
||||||
You can add any WebSocket service to your `WebSocketServer` with the specified behavior and absolute path to the service, by using the `WebSocketServer.AddWebSocketService<TBehavior> (string)` or `WebSocketServer.AddWebSocketService<TBehavior> (string, Action<TBehavior>)` method.
|
|
||||||
|
|
||||||
The type of `TBehavior` must inherit the `WebSocketBehavior` class, and must have a public parameterless constructor.
|
|
||||||
|
|
||||||
So you can use a class in the above Step 2 to add the service.
|
|
||||||
|
|
||||||
If you create a new instance of the `WebSocketServer` class without a port number, it sets the port number to **80**. So it is necessary to run with root permission.
|
|
||||||
|
|
||||||
$ sudo mono example2.exe
|
|
||||||
|
|
||||||
#### Step 4 ####
|
|
||||||
|
|
||||||
Starting the WebSocket server.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
wssv.Start ();
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 5 ####
|
|
||||||
|
|
||||||
Stopping the WebSocket server.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
wssv.Stop ();
|
|
||||||
```
|
|
||||||
|
|
||||||
### HTTP Server with the WebSocket ###
|
|
||||||
|
|
||||||
I have modified the `System.Net.HttpListener`, `System.Net.HttpListenerContext`, and some other classes from **[Mono]** to create an HTTP server that allows to accept the WebSocket handshake requests.
|
|
||||||
|
|
||||||
So websocket-sharp provides the `WebSocketSharp.Server.HttpServer` class.
|
|
||||||
|
|
||||||
You can add any WebSocket service to your `HttpServer` with the specified behavior and path to the service, by using the `HttpServer.AddWebSocketService<TBehavior> (string)` or `HttpServer.AddWebSocketService<TBehavior> (string, Action<TBehavior>)` method.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var httpsv = new HttpServer (4649);
|
|
||||||
|
|
||||||
httpsv.AddWebSocketService<Echo> ("/Echo");
|
|
||||||
httpsv.AddWebSocketService<Chat> ("/Chat");
|
|
||||||
httpsv.AddWebSocketService<Chat> ("/ChatWithNyan", s => s.Suffix = " Nyan!");
|
|
||||||
```
|
|
||||||
|
|
||||||
For more information, would you see **[Example3]**?
|
|
||||||
|
|
||||||
### WebSocket Extensions ###
|
|
||||||
|
|
||||||
#### Per-message Compression ####
|
|
||||||
|
|
||||||
websocket-sharp supports the [Per-message Compression][compression] extension (but does not support it with the [context take over]).
|
|
||||||
|
|
||||||
As a WebSocket client, if you would like to enable this extension, you should set the `WebSocket.Compression` property to a compression method before calling the connect method.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.Compression = CompressionMethod.Deflate;
|
|
||||||
```
|
|
||||||
|
|
||||||
And then the client will send the following header in the handshake request to the server.
|
|
||||||
|
|
||||||
Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover
|
|
||||||
|
|
||||||
If the server supports this extension, it will return the same header which has the corresponding value.
|
|
||||||
|
|
||||||
So eventually this extension will be available when the client receives the header in the handshake response.
|
|
||||||
|
|
||||||
#### Ignoring the extensions ####
|
|
||||||
|
|
||||||
As a WebSocket server, if you would like to ignore the extensions requested from a client, you should set the `WebSocketBehavior.IgnoreExtensions` property to `true` in your `WebSocketBehavior` constructor or initializing it, such as the following.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
wssv.AddWebSocketService<Chat> (
|
|
||||||
"/Chat",
|
|
||||||
s => s.IgnoreExtensions = true // To ignore the extensions requested from a client.
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
If it is set to `true`, the service will not return the Sec-WebSocket-Extensions header in its handshake response.
|
|
||||||
|
|
||||||
I think this is useful when you get something error in connecting the server and exclude the extensions as a cause of the error.
|
|
||||||
|
|
||||||
### Secure Connection ###
|
|
||||||
|
|
||||||
websocket-sharp supports the secure connection with **SSL/TLS**.
|
|
||||||
|
|
||||||
As a WebSocket client, you should create a new instance of the `WebSocket` class with a **wss** scheme WebSocket URL.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var ws = new WebSocket ("wss://example.com");
|
|
||||||
```
|
|
||||||
|
|
||||||
If you would like to set a custom validation for the server certificate, you should set the `WebSocket.SslConfiguration.ServerCertificateValidationCallback` property to a callback for it.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.SslConfiguration.ServerCertificateValidationCallback =
|
|
||||||
(sender, certificate, chain, sslPolicyErrors) => {
|
|
||||||
// Do something to validate the server certificate.
|
|
||||||
...
|
|
||||||
|
|
||||||
return true; // If the server certificate is valid.
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The default callback always returns `true`.
|
|
||||||
|
|
||||||
As a WebSocket server, you should create a new instance of the `WebSocketServer` or `HttpServer` class with some settings for the secure connection, such as the following.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var wssv = new WebSocketServer (5963, true);
|
|
||||||
wssv.SslConfiguration.ServerCertificate = new X509Certificate2 (
|
|
||||||
"/path/to/cert.pfx", "password for cert.pfx"
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### HTTP Authentication ###
|
|
||||||
|
|
||||||
websocket-sharp supports the [HTTP Authentication (Basic/Digest)][rfc2617].
|
|
||||||
|
|
||||||
As a WebSocket client, you should set a pair of user name and password for the HTTP authentication, by using the `WebSocket.SetCredentials (string, string, bool)` method before calling the connect method.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.SetCredentials ("nobita", "password", preAuth);
|
|
||||||
```
|
|
||||||
|
|
||||||
If `preAuth` is `true`, the client will send the credentials for the Basic authentication in the first handshake request to the server.
|
|
||||||
|
|
||||||
Otherwise, it will send the credentials for either the Basic or Digest (determined by the unauthorized response to the first handshake request) authentication in the second handshake request to the server.
|
|
||||||
|
|
||||||
As a WebSocket server, you should set an HTTP authentication scheme, a realm, and any function to find the user credentials before calling the start method, such as the following.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
wssv.AuthenticationSchemes = AuthenticationSchemes.Basic;
|
|
||||||
wssv.Realm = "WebSocket Test";
|
|
||||||
wssv.UserCredentialsFinder = id => {
|
|
||||||
var name = id.Name;
|
|
||||||
|
|
||||||
// Return user name, password, and roles.
|
|
||||||
return name == "nobita"
|
|
||||||
? new NetworkCredential (name, "password", "gunfighter")
|
|
||||||
: null; // If the user credentials are not found.
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
If you would like to provide the Digest authentication, you should set such as the following.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
wssv.AuthenticationSchemes = AuthenticationSchemes.Digest;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Query string, Origin header, and Cookies ###
|
|
||||||
|
|
||||||
As a WebSocket client, if you would like to send the query string in the handshake request, you should create a new instance of the `WebSocket` class with a WebSocket URL that includes the [Query] string parameters.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var ws = new WebSocket ("ws://example.com/?name=nobita");
|
|
||||||
```
|
|
||||||
|
|
||||||
If you would like to send the Origin header in the handshake request, you should set the `WebSocket.Origin` property to an allowable value as the [Origin] header before calling the connect method.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.Origin = "http://example.com";
|
|
||||||
```
|
|
||||||
|
|
||||||
And if you would like to send the cookies in the handshake request, you should set any cookie by using the `WebSocket.SetCookie (WebSocketSharp.Net.Cookie)` method before calling the connect method.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.SetCookie (new Cookie ("name", "nobita"));
|
|
||||||
```
|
|
||||||
|
|
||||||
As a WebSocket server, if you would like to get the query string included in a handshake request, you should access the `WebSocketBehavior.QueryString` property, such as the following.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public class Chat : WebSocketBehavior
|
|
||||||
{
|
|
||||||
private string _name;
|
|
||||||
...
|
|
||||||
|
|
||||||
protected override void OnOpen ()
|
|
||||||
{
|
|
||||||
_name = QueryString["name"];
|
|
||||||
}
|
|
||||||
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you would like to get the value of the Origin header included in a handshake request, you should access the `WebSocketBehavior.Context.Origin` property.
|
|
||||||
|
|
||||||
If you would like to get the cookies included in a handshake request, you should access the `WebSocketBehavior.Context.CookieCollection` property.
|
|
||||||
|
|
||||||
And if you would like to validate the Origin header, cookies, or both, you should set each validation for it with your `WebSocketBehavior`, for example, by using the `WebSocketServer.AddWebSocketService<TBehavior> (string, Action<TBehavior>)` method with initializing, such as the following.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
wssv.AddWebSocketService<Chat> (
|
|
||||||
"/Chat",
|
|
||||||
s => {
|
|
||||||
s.OriginValidator = val => {
|
|
||||||
// Check the value of the Origin header, and return true if valid.
|
|
||||||
Uri origin;
|
|
||||||
|
|
||||||
return !val.IsNullOrEmpty ()
|
|
||||||
&& Uri.TryCreate (val, UriKind.Absolute, out origin)
|
|
||||||
&& origin.Host == "example.com";
|
|
||||||
};
|
|
||||||
|
|
||||||
s.CookiesValidator = (req, res) => {
|
|
||||||
// Check the cookies in 'req', and set the cookies to send to
|
|
||||||
// the client with 'res' if necessary.
|
|
||||||
foreach (var cookie in req) {
|
|
||||||
cookie.Expired = true;
|
|
||||||
res.Add (cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true; // If valid.
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Connecting through the HTTP proxy server ###
|
|
||||||
|
|
||||||
websocket-sharp supports to connect through the HTTP proxy server.
|
|
||||||
|
|
||||||
If you would like to connect to a WebSocket server through the HTTP proxy server, you should set the proxy server URL, and if necessary, a pair of user name and password for the proxy server authentication (Basic/Digest), by using the `WebSocket.SetProxy (string, string, string)` method before calling the connect method.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var ws = new WebSocket ("ws://example.com");
|
|
||||||
ws.SetProxy ("http://localhost:3128", "nobita", "password");
|
|
||||||
```
|
|
||||||
|
|
||||||
I have tested this with **[Squid]**. It is necessary to disable the following option in **squid.conf** (e.g. `/etc/squid/squid.conf`).
|
|
||||||
|
|
||||||
```
|
|
||||||
# Deny CONNECT to other than SSL ports
|
|
||||||
#http_access deny CONNECT !SSL_ports
|
|
||||||
```
|
|
||||||
|
|
||||||
### Logging ###
|
|
||||||
|
|
||||||
The `WebSocket` class has the own logging function.
|
|
||||||
|
|
||||||
You can use it with the `WebSocket.Log` property (returns a `WebSocketSharp.Logger`).
|
|
||||||
|
|
||||||
So if you would like to change the current logging level (`WebSocketSharp.LogLevel.Error` as the default), you should set the `WebSocket.Log.Level` property to any of the `LogLevel` enum values.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.Log.Level = LogLevel.Debug;
|
|
||||||
```
|
|
||||||
|
|
||||||
The above means a log with lower than `LogLevel.Debug` cannot be outputted.
|
|
||||||
|
|
||||||
And if you would like to output a log, you should use any of the output methods. The following outputs a log with `LogLevel.Debug`.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
ws.Log.Debug ("This is a debug message.");
|
|
||||||
```
|
|
||||||
|
|
||||||
The `WebSocketServer` and `HttpServer` classes have the same logging function.
|
|
||||||
|
|
||||||
## Examples ##
|
|
||||||
|
|
||||||
Examples using websocket-sharp.
|
|
||||||
|
|
||||||
### Example ###
|
|
||||||
|
|
||||||
[Example] connects to the server executed by [Example2] or [Example3].
|
|
||||||
|
|
||||||
### Example2 ###
|
|
||||||
|
|
||||||
[Example2] starts a WebSocket server.
|
|
||||||
|
|
||||||
### Example3 ###
|
|
||||||
|
|
||||||
[Example3] starts an HTTP server that allows to accept the WebSocket handshake requests.
|
|
||||||
|
|
||||||
Would you access to [http://localhost:4649](http://localhost:4649) to do **WebSocket Echo Test** with your web browser while Example3 is running?
|
|
||||||
|
|
||||||
## Supported WebSocket Specifications ##
|
|
||||||
|
|
||||||
websocket-sharp supports **RFC 6455**, and it is based on the following references:
|
|
||||||
|
|
||||||
- [The WebSocket Protocol][rfc6455]
|
|
||||||
- [The WebSocket API][api]
|
|
||||||
- [Compression Extensions for WebSocket][compression]
|
|
||||||
|
|
||||||
Thanks for translating to japanese.
|
|
||||||
|
|
||||||
- [The WebSocket Protocol 日本語訳][rfc6455_ja]
|
|
||||||
- [The WebSocket API 日本語訳][api_ja]
|
|
||||||
|
|
||||||
## License ##
|
|
||||||
|
|
||||||
websocket-sharp is provided under [The MIT License].
|
|
||||||
|
|
||||||
|
|
||||||
[Example]: https://github.com/sta/websocket-sharp/tree/master/Example
|
|
||||||
[Example2]: https://github.com/sta/websocket-sharp/tree/master/Example2
|
|
||||||
[Example3]: https://github.com/sta/websocket-sharp/tree/master/Example3
|
|
||||||
[Mono]: http://www.mono-project.com
|
|
||||||
[MonoDevelop]: http://monodevelop.com
|
|
||||||
[NuGet Gallery]: http://www.nuget.org
|
|
||||||
[NuGet Gallery: websocket-sharp]: http://www.nuget.org/packages/WebSocketSharp
|
|
||||||
[Origin]: http://tools.ietf.org/html/rfc6454#section-7
|
|
||||||
[Query]: http://tools.ietf.org/html/rfc3986#section-3.4
|
|
||||||
[Security Sandbox of the Webplayer]: http://docs.unity3d.com/Manual/SecuritySandbox.html
|
|
||||||
[Squid]: http://www.squid-cache.org
|
|
||||||
[The MIT License]: https://raw.github.com/sta/websocket-sharp/master/LICENSE.txt
|
|
||||||
[Unity]: http://unity3d.com
|
|
||||||
[WebGL Networking]: http://docs.unity3d.com/Manual/webgl-networking.html
|
|
||||||
[WebSocket-Sharp for Unity]: http://u3d.as/content/sta-blockhead/websocket-sharp-for-unity
|
|
||||||
[api]: http://www.w3.org/TR/websockets
|
|
||||||
[api_ja]: http://www.hcn.zaq.ne.jp/___/WEB/WebSocket-ja.html
|
|
||||||
[compression]: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-19
|
|
||||||
[context take over]: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-19#section-8.1.1
|
|
||||||
[draft-hixie-thewebsocketprotocol-75]: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
|
|
||||||
[draft-ietf-hybi-thewebsocketprotocol-00]: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
|
|
||||||
[draft75]: https://github.com/sta/websocket-sharp/tree/draft75
|
|
||||||
[hybi-00]: https://github.com/sta/websocket-sharp/tree/hybi-00
|
|
||||||
[master]: https://github.com/sta/websocket-sharp/tree/master
|
|
||||||
[rfc2617]: http://tools.ietf.org/html/rfc2617
|
|
||||||
[rfc6455]: http://tools.ietf.org/html/rfc6455
|
|
||||||
[rfc6455_ja]: http://www.hcn.zaq.ne.jp/___/WEB/RFC6455-ja.html
|
|
||||||
|
|||||||
+1
-21
@@ -3,11 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 10.00
|
|||||||
# Visual Studio 2008
|
# Visual Studio 2008
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "websocket-sharp", "websocket-sharp\websocket-sharp.csproj", "{B357BAC7-529E-4D81-A0D2-71041B19C8DE}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "websocket-sharp", "websocket-sharp\websocket-sharp.csproj", "{B357BAC7-529E-4D81-A0D2-71041B19C8DE}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{52805AEC-EFB1-4F42-BB8E-3ED4E692C568}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "wsclient", "wsclient\wsclient.csproj", "{52805AEC-EFB1-4F42-BB8E-3ED4E692C568}"
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2", "Example2\Example2.csproj", "{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example3", "Example3\Example3.csproj", "{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -33,22 +29,6 @@ Global
|
|||||||
{B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU
|
{B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU
|
||||||
{B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.Build.0 = Release|Any CPU
|
{B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU
|
|
||||||
{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU
|
|
||||||
{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU
|
|
||||||
{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU
|
|
||||||
{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU
|
|
||||||
{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU
|
|
||||||
{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU
|
|
||||||
{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU
|
|
||||||
{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(MonoDevelopProperties) = preSolution
|
GlobalSection(MonoDevelopProperties) = preSolution
|
||||||
StartupItem = websocket-sharp\websocket-sharp.csproj
|
StartupItem = websocket-sharp\websocket-sharp.csproj
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<Properties>
|
||||||
|
<MonoDevelop.Ide.Workspace ActiveConfiguration="Debug_Ubuntu" ctype="Workspace" />
|
||||||
|
<MonoDevelop.Ide.Workbench ActiveDocument="websocket-sharp/WebSocket.cs" ctype="Workbench">
|
||||||
|
<Files>
|
||||||
|
<File FileName="websocket-sharp/WebSocket.cs" Line="247" Column="31" />
|
||||||
|
</Files>
|
||||||
|
</MonoDevelop.Ide.Workbench>
|
||||||
|
<MonoDevelop.Ide.DebuggingService.Breakpoints>
|
||||||
|
<BreakpointStore />
|
||||||
|
</MonoDevelop.Ide.DebuggingService.Breakpoints>
|
||||||
|
<MonoDevelop.Ide.DebuggingService.PinnedWatches ctype="PinnedWatchStore" />
|
||||||
|
</Properties>
|
||||||
@@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
|
|||||||
// Change them to the values specific to your project.
|
// Change them to the values specific to your project.
|
||||||
|
|
||||||
[assembly: AssemblyTitle("websocket-sharp")]
|
[assembly: AssemblyTitle("websocket-sharp")]
|
||||||
[assembly: AssemblyDescription("A C# implementation of the WebSocket protocol client and server")]
|
[assembly: AssemblyDescription("A C# implementation of a WebSocket protocol client")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("websocket-sharp.dll")]
|
[assembly: AssemblyProduct("websocket-sharp.dll")]
|
||||||
@@ -17,10 +17,11 @@ using System.Runtime.CompilerServices;
|
|||||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.2.*")]
|
[assembly: AssemblyVersion("0.9.9.*")]
|
||||||
|
|
||||||
// The following attributes are used to specify the signing key for the assembly,
|
// The following attributes are used to specify the signing key for the assembly,
|
||||||
// if desired. See the Mono documentation for more information about signing.
|
// if desired. See the Mono documentation for more information about signing.
|
||||||
|
|
||||||
//[assembly: AssemblyDelaySign(false)]
|
//[assembly: AssemblyDelaySign(false)]
|
||||||
//[assembly: AssemblyKeyFile("")]
|
//[assembly: AssemblyKeyFile("")]
|
||||||
|
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* CloseEventArgs.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2019 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the event data for the <see cref="WebSocket.OnClose"/> event.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// That event occurs when the WebSocket connection has been closed.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If you would like to get the reason for the connection close, you should
|
|
||||||
/// access the <see cref="Code"/> or <see cref="Reason"/> property.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public class CloseEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private bool _clean;
|
|
||||||
private PayloadData _payloadData;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal CloseEventArgs (PayloadData payloadData, bool clean)
|
|
||||||
{
|
|
||||||
_payloadData = payloadData;
|
|
||||||
_clean = clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal CloseEventArgs (ushort code, string reason, bool clean)
|
|
||||||
{
|
|
||||||
_payloadData = new PayloadData (code, reason);
|
|
||||||
_clean = clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the status code for the connection close.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="ushort"/> that represents the status code for
|
|
||||||
/// the connection close if present.
|
|
||||||
/// </value>
|
|
||||||
public ushort Code {
|
|
||||||
get {
|
|
||||||
return _payloadData.Code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the reason for the connection close.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the reason for
|
|
||||||
/// the connection close if present.
|
|
||||||
/// </value>
|
|
||||||
public string Reason {
|
|
||||||
get {
|
|
||||||
return _payloadData.Reason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the connection has been closed cleanly.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the connection has been closed cleanly; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool WasClean {
|
|
||||||
get {
|
|
||||||
return _clean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* CloseStatusCode.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2016 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the status code for the WebSocket connection close.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// The values of this enumeration 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" cannot be sent as a status code in
|
|
||||||
/// closing handshake by an endpoint.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public enum CloseStatusCode : ushort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to close status 1000. Indicates 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 a type of
|
|
||||||
/// data that it cannot accept.
|
|
||||||
/// </summary>
|
|
||||||
UnsupportedData = 1003,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to close status 1004. Still undefined. A Reserved value.
|
|
||||||
/// </summary>
|
|
||||||
Undefined = 1004,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to close status 1005. Indicates that no status code was
|
|
||||||
/// actually present. A Reserved value.
|
|
||||||
/// </summary>
|
|
||||||
NoStatus = 1005,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to close status 1006. Indicates that the connection was
|
|
||||||
/// closed abnormally. A 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 data that is not consistent with the type of the message.
|
|
||||||
/// </summary>
|
|
||||||
InvalidData = 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 a client is
|
|
||||||
/// terminating the connection because it has expected the server to
|
|
||||||
/// negotiate one or more extension, but the server did not return
|
|
||||||
/// them in the handshake response.
|
|
||||||
/// </summary>
|
|
||||||
MandatoryExtension = 1010,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to close status 1011. Indicates that a 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. A Reserved value.
|
|
||||||
/// </summary>
|
|
||||||
TlsHandshakeFailure = 1015
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* CompressionMethod.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2017 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the method for compression.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The methods are defined in
|
|
||||||
/// <see href="https://tools.ietf.org/html/rfc7692">
|
|
||||||
/// Compression Extensions for WebSocket</see>.
|
|
||||||
/// </remarks>
|
|
||||||
public enum CompressionMethod : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies no compression.
|
|
||||||
/// </summary>
|
|
||||||
None,
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies DEFLATE.
|
|
||||||
/// </summary>
|
|
||||||
Deflate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* ErrorEventArgs.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2016 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - Frank Razenberg <frank@zzattack.org>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the event data for the <see cref="WebSocket.OnError"/> event.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// That event occurs when the <see cref="WebSocket"/> gets an error.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If you would like to get the error message, you should access
|
|
||||||
/// the <see cref="ErrorEventArgs.Message"/> property.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// And if the error is due to an exception, you can get it by accessing
|
|
||||||
/// the <see cref="ErrorEventArgs.Exception"/> property.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public class ErrorEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private Exception _exception;
|
|
||||||
private string _message;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal ErrorEventArgs (string message)
|
|
||||||
: this (message, null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ErrorEventArgs (string message, Exception exception)
|
|
||||||
{
|
|
||||||
_message = message;
|
|
||||||
_exception = exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the exception that caused the error.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// An <see cref="System.Exception"/> instance that represents the cause of
|
|
||||||
/// the error if it is due to an exception; otherwise, <see langword="null"/>.
|
|
||||||
/// </value>
|
|
||||||
public Exception Exception {
|
|
||||||
get {
|
|
||||||
return _exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the error message.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the error message.
|
|
||||||
/// </value>
|
|
||||||
public string Message {
|
|
||||||
get {
|
|
||||||
return _message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+10
-1864
File diff suppressed because it is too large
Load Diff
@@ -1,51 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* Fin.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether a WebSocket frame is the final frame of a message.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The values of this enumeration are defined in
|
|
||||||
/// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2</see> of RFC 6455.
|
|
||||||
/// </remarks>
|
|
||||||
internal enum Fin : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 0. Indicates more frames of a message follow.
|
|
||||||
/// </summary>
|
|
||||||
More = 0x0,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 1. Indicates the final frame of a message.
|
|
||||||
/// </summary>
|
|
||||||
Final = 0x1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpBase.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2014 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using WebSocketSharp.Net;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
internal abstract class HttpBase
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private NameValueCollection _headers;
|
|
||||||
private const int _headersMaxLength = 8192;
|
|
||||||
private Version _version;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Fields
|
|
||||||
|
|
||||||
internal byte[] EntityBodyData;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Fields
|
|
||||||
|
|
||||||
protected const string CrLf = "\r\n";
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Constructors
|
|
||||||
|
|
||||||
protected HttpBase (Version version, NameValueCollection headers)
|
|
||||||
{
|
|
||||||
_version = version;
|
|
||||||
_headers = headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public string EntityBody {
|
|
||||||
get {
|
|
||||||
if (EntityBodyData == null || EntityBodyData.LongLength == 0)
|
|
||||||
return String.Empty;
|
|
||||||
|
|
||||||
Encoding enc = null;
|
|
||||||
|
|
||||||
var contentType = _headers["Content-Type"];
|
|
||||||
if (contentType != null && contentType.Length > 0)
|
|
||||||
enc = HttpUtility.GetEncoding (contentType);
|
|
||||||
|
|
||||||
return (enc ?? Encoding.UTF8).GetString (EntityBodyData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public NameValueCollection Headers {
|
|
||||||
get {
|
|
||||||
return _headers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Version ProtocolVersion {
|
|
||||||
get {
|
|
||||||
return _version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static byte[] readEntityBody (Stream stream, string length)
|
|
||||||
{
|
|
||||||
long len;
|
|
||||||
if (!Int64.TryParse (length, out len))
|
|
||||||
throw new ArgumentException ("Cannot be parsed.", "length");
|
|
||||||
|
|
||||||
if (len < 0)
|
|
||||||
throw new ArgumentOutOfRangeException ("length", "Less than zero.");
|
|
||||||
|
|
||||||
return len > 1024
|
|
||||||
? stream.ReadBytes (len, 1024)
|
|
||||||
: len > 0
|
|
||||||
? stream.ReadBytes ((int) len)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string[] readHeaders (Stream stream, int maxLength)
|
|
||||||
{
|
|
||||||
var buff = new List<byte> ();
|
|
||||||
var cnt = 0;
|
|
||||||
Action<int> add = i => {
|
|
||||||
if (i == -1)
|
|
||||||
throw new EndOfStreamException ("The header cannot be read from the data source.");
|
|
||||||
|
|
||||||
buff.Add ((byte) i);
|
|
||||||
cnt++;
|
|
||||||
};
|
|
||||||
|
|
||||||
var read = false;
|
|
||||||
while (cnt < maxLength) {
|
|
||||||
if (stream.ReadByte ().EqualsWith ('\r', add) &&
|
|
||||||
stream.ReadByte ().EqualsWith ('\n', add) &&
|
|
||||||
stream.ReadByte ().EqualsWith ('\r', add) &&
|
|
||||||
stream.ReadByte ().EqualsWith ('\n', add)) {
|
|
||||||
read = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!read)
|
|
||||||
throw new WebSocketException ("The length of header part is greater than the max length.");
|
|
||||||
|
|
||||||
return Encoding.UTF8.GetString (buff.ToArray ())
|
|
||||||
.Replace (CrLf + " ", " ")
|
|
||||||
.Replace (CrLf + "\t", " ")
|
|
||||||
.Split (new[] { CrLf }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Methods
|
|
||||||
|
|
||||||
protected static T Read<T> (Stream stream, Func<string[], T> parser, int millisecondsTimeout)
|
|
||||||
where T : HttpBase
|
|
||||||
{
|
|
||||||
var timeout = false;
|
|
||||||
var timer = new Timer (
|
|
||||||
state => {
|
|
||||||
timeout = true;
|
|
||||||
stream.Close ();
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
millisecondsTimeout,
|
|
||||||
-1);
|
|
||||||
|
|
||||||
T http = null;
|
|
||||||
Exception exception = null;
|
|
||||||
try {
|
|
||||||
http = parser (readHeaders (stream, _headersMaxLength));
|
|
||||||
var contentLen = http.Headers["Content-Length"];
|
|
||||||
if (contentLen != null && contentLen.Length > 0)
|
|
||||||
http.EntityBodyData = readEntityBody (stream, contentLen);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
exception = ex;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
timer.Change (-1, -1);
|
|
||||||
timer.Dispose ();
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg = timeout
|
|
||||||
? "A timeout has occurred while reading an HTTP request/response."
|
|
||||||
: exception != null
|
|
||||||
? "An exception has occurred while reading an HTTP request/response."
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (msg != null)
|
|
||||||
throw new WebSocketException (msg, exception);
|
|
||||||
|
|
||||||
return http;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public byte[] ToByteArray ()
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetBytes (ToString ());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpRequest.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - David Burhans
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using WebSocketSharp.Net;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
internal class HttpRequest : HttpBase
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private CookieCollection _cookies;
|
|
||||||
private string _method;
|
|
||||||
private string _uri;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Constructors
|
|
||||||
|
|
||||||
private HttpRequest (string method, string uri, Version version, NameValueCollection headers)
|
|
||||||
: base (version, headers)
|
|
||||||
{
|
|
||||||
_method = method;
|
|
||||||
_uri = uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpRequest (string method, string uri)
|
|
||||||
: this (method, uri, HttpVersion.Version11, new NameValueCollection ())
|
|
||||||
{
|
|
||||||
Headers["User-Agent"] = "websocket-sharp/1.0";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public AuthenticationResponse AuthenticationResponse {
|
|
||||||
get {
|
|
||||||
var res = Headers["Authorization"];
|
|
||||||
return res != null && res.Length > 0
|
|
||||||
? AuthenticationResponse.Parse (res)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CookieCollection Cookies {
|
|
||||||
get {
|
|
||||||
if (_cookies == null)
|
|
||||||
_cookies = Headers.GetCookies (false);
|
|
||||||
|
|
||||||
return _cookies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string HttpMethod {
|
|
||||||
get {
|
|
||||||
return _method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsWebSocketRequest {
|
|
||||||
get {
|
|
||||||
return _method == "GET"
|
|
||||||
&& ProtocolVersion > HttpVersion.Version10
|
|
||||||
&& Headers.Upgrades ("websocket");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string RequestUri {
|
|
||||||
get {
|
|
||||||
return _uri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal static HttpRequest CreateConnectRequest (Uri uri)
|
|
||||||
{
|
|
||||||
var host = uri.DnsSafeHost;
|
|
||||||
var port = uri.Port;
|
|
||||||
var authority = String.Format ("{0}:{1}", host, port);
|
|
||||||
var req = new HttpRequest ("CONNECT", authority);
|
|
||||||
req.Headers["Host"] = port == 80 ? host : authority;
|
|
||||||
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static HttpRequest CreateWebSocketRequest (Uri uri)
|
|
||||||
{
|
|
||||||
var req = new HttpRequest ("GET", uri.PathAndQuery);
|
|
||||||
var headers = req.Headers;
|
|
||||||
|
|
||||||
// Only includes a port number in the Host header value if it's non-default.
|
|
||||||
// See: https://tools.ietf.org/html/rfc6455#page-17
|
|
||||||
var port = uri.Port;
|
|
||||||
var schm = uri.Scheme;
|
|
||||||
headers["Host"] = (port == 80 && schm == "ws") || (port == 443 && schm == "wss")
|
|
||||||
? uri.DnsSafeHost
|
|
||||||
: uri.Authority;
|
|
||||||
|
|
||||||
headers["Upgrade"] = "websocket";
|
|
||||||
headers["Connection"] = "Upgrade";
|
|
||||||
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal HttpResponse GetResponse (Stream stream, int millisecondsTimeout)
|
|
||||||
{
|
|
||||||
var buff = ToByteArray ();
|
|
||||||
stream.Write (buff, 0, buff.Length);
|
|
||||||
|
|
||||||
return Read<HttpResponse> (stream, HttpResponse.Parse, millisecondsTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static HttpRequest Parse (string[] headerParts)
|
|
||||||
{
|
|
||||||
var requestLine = headerParts[0].Split (new[] { ' ' }, 3);
|
|
||||||
if (requestLine.Length != 3)
|
|
||||||
throw new ArgumentException ("Invalid request line: " + headerParts[0]);
|
|
||||||
|
|
||||||
var headers = new WebHeaderCollection ();
|
|
||||||
for (int i = 1; i < headerParts.Length; i++)
|
|
||||||
headers.InternalSet (headerParts[i], false);
|
|
||||||
|
|
||||||
return new HttpRequest (
|
|
||||||
requestLine[0], requestLine[1], new Version (requestLine[2].Substring (5)), headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static HttpRequest Read (Stream stream, int millisecondsTimeout)
|
|
||||||
{
|
|
||||||
return Read<HttpRequest> (stream, Parse, millisecondsTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public void SetCookies (CookieCollection cookies)
|
|
||||||
{
|
|
||||||
if (cookies == null || cookies.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var buff = new StringBuilder (64);
|
|
||||||
foreach (var cookie in cookies.Sorted)
|
|
||||||
if (!cookie.Expired)
|
|
||||||
buff.AppendFormat ("{0}; ", cookie.ToString ());
|
|
||||||
|
|
||||||
var len = buff.Length;
|
|
||||||
if (len > 2) {
|
|
||||||
buff.Length = len - 2;
|
|
||||||
Headers["Cookie"] = buff.ToString ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
var output = new StringBuilder (64);
|
|
||||||
output.AppendFormat ("{0} {1} HTTP/{2}{3}", _method, _uri, ProtocolVersion, CrLf);
|
|
||||||
|
|
||||||
var headers = Headers;
|
|
||||||
foreach (var key in headers.AllKeys)
|
|
||||||
output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf);
|
|
||||||
|
|
||||||
output.Append (CrLf);
|
|
||||||
|
|
||||||
var entity = EntityBody;
|
|
||||||
if (entity.Length > 0)
|
|
||||||
output.Append (entity);
|
|
||||||
|
|
||||||
return output.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpResponse.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2014 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using WebSocketSharp.Net;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
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, NameValueCollection 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 NameValueCollection ())
|
|
||||||
{
|
|
||||||
Headers["Server"] = "websocket-sharp/1.0";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public CookieCollection Cookies {
|
|
||||||
get {
|
|
||||||
return Headers.GetCookies (true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasConnectionClose {
|
|
||||||
get {
|
|
||||||
var comparison = StringComparison.OrdinalIgnoreCase;
|
|
||||||
return Headers.Contains ("Connection", "close", comparison);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsProxyAuthenticationRequired {
|
|
||||||
get {
|
|
||||||
return _code == "407";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsRedirect {
|
|
||||||
get {
|
|
||||||
return _code == "301" || _code == "302";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsUnauthorized {
|
|
||||||
get {
|
|
||||||
return _code == "401";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsWebSocketResponse {
|
|
||||||
get {
|
|
||||||
return ProtocolVersion > HttpVersion.Version10
|
|
||||||
&& _code == "101"
|
|
||||||
&& Headers.Upgrades ("websocket");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Reason {
|
|
||||||
get {
|
|
||||||
return _reason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string StatusCode {
|
|
||||||
get {
|
|
||||||
return _code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal static HttpResponse CreateCloseResponse (HttpStatusCode code)
|
|
||||||
{
|
|
||||||
var res = new HttpResponse (code);
|
|
||||||
res.Headers["Connection"] = "close";
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static HttpResponse CreateUnauthorizedResponse (string challenge)
|
|
||||||
{
|
|
||||||
var res = new HttpResponse (HttpStatusCode.Unauthorized);
|
|
||||||
res.Headers["WWW-Authenticate"] = challenge;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static HttpResponse CreateWebSocketResponse ()
|
|
||||||
{
|
|
||||||
var res = new HttpResponse (HttpStatusCode.SwitchingProtocols);
|
|
||||||
|
|
||||||
var headers = res.Headers;
|
|
||||||
headers["Upgrade"] = "websocket";
|
|
||||||
headers["Connection"] = "Upgrade";
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static HttpResponse Parse (string[] headerParts)
|
|
||||||
{
|
|
||||||
var statusLine = headerParts[0].Split (new[] { ' ' }, 3);
|
|
||||||
if (statusLine.Length != 3)
|
|
||||||
throw new ArgumentException ("Invalid status line: " + headerParts[0]);
|
|
||||||
|
|
||||||
var headers = new WebHeaderCollection ();
|
|
||||||
for (int i = 1; i < headerParts.Length; i++)
|
|
||||||
headers.InternalSet (headerParts[i], true);
|
|
||||||
|
|
||||||
return new HttpResponse (
|
|
||||||
statusLine[1], statusLine[2], new Version (statusLine[0].Substring (5)), headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static HttpResponse Read (Stream stream, int millisecondsTimeout)
|
|
||||||
{
|
|
||||||
return Read<HttpResponse> (stream, Parse, millisecondsTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public void SetCookies (CookieCollection cookies)
|
|
||||||
{
|
|
||||||
if (cookies == null || cookies.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var headers = Headers;
|
|
||||||
foreach (var cookie in cookies.Sorted)
|
|
||||||
headers.Add ("Set-Cookie", cookie.ToResponseString ());
|
|
||||||
}
|
|
||||||
|
|
||||||
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.AllKeys)
|
|
||||||
output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf);
|
|
||||||
|
|
||||||
output.Append (CrLf);
|
|
||||||
|
|
||||||
var entity = EntityBody;
|
|
||||||
if (entity.Length > 0)
|
|
||||||
output.Append (entity);
|
|
||||||
|
|
||||||
return output.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
#region License
|
#region MIT License
|
||||||
/*
|
/**
|
||||||
* ByteOrder.cs
|
* IWsStream.cs
|
||||||
*
|
*
|
||||||
* The MIT License
|
* The MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012-2015 sta.blockhead
|
* Copyright (c) 2010 sta.blockhead
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -30,18 +30,12 @@ using System;
|
|||||||
|
|
||||||
namespace WebSocketSharp
|
namespace WebSocketSharp
|
||||||
{
|
{
|
||||||
/// <summary>
|
public interface IWsStream : IDisposable
|
||||||
/// Specifies the byte order.
|
|
||||||
/// </summary>
|
|
||||||
public enum ByteOrder
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
void Close();
|
||||||
/// Specifies Little-endian.
|
int Read(byte[] buffer, int offset, int size);
|
||||||
/// </summary>
|
int ReadByte();
|
||||||
Little,
|
void Write(byte[] buffer, int offset, int count);
|
||||||
/// <summary>
|
void WriteByte(byte value);
|
||||||
/// Specifies Big-endian.
|
|
||||||
/// </summary>
|
|
||||||
Big
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* LogData.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a log data used by the <see cref="Logger"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public class LogData
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private StackFrame _caller;
|
|
||||||
private DateTime _date;
|
|
||||||
private LogLevel _level;
|
|
||||||
private string _message;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal LogData (LogLevel level, StackFrame caller, string message)
|
|
||||||
{
|
|
||||||
_level = level;
|
|
||||||
_caller = caller;
|
|
||||||
_message = message ?? String.Empty;
|
|
||||||
_date = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the information of the logging method caller.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="StackFrame"/> that provides the information of the logging method caller.
|
|
||||||
/// </value>
|
|
||||||
public StackFrame Caller {
|
|
||||||
get {
|
|
||||||
return _caller;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the date and time when the log data was created.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="DateTime"/> that represents the date and time when the log data was created.
|
|
||||||
/// </value>
|
|
||||||
public DateTime Date {
|
|
||||||
get {
|
|
||||||
return _date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the logging level of the log data.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// One of the <see cref="LogLevel"/> enum values, indicates the logging level of the log data.
|
|
||||||
/// </value>
|
|
||||||
public LogLevel Level {
|
|
||||||
get {
|
|
||||||
return _level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the message of the log data.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the message of the log data.
|
|
||||||
/// </value>
|
|
||||||
public string Message {
|
|
||||||
get {
|
|
||||||
return _message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a <see cref="string"/> that represents the current <see cref="LogData"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="string"/> that represents the current <see cref="LogData"/>.
|
|
||||||
/// </returns>
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
var header = String.Format ("{0}|{1,-5}|", _date, _level);
|
|
||||||
var method = _caller.GetMethod ();
|
|
||||||
var type = method.DeclaringType;
|
|
||||||
#if DEBUG
|
|
||||||
var lineNum = _caller.GetFileLineNumber ();
|
|
||||||
var headerAndCaller =
|
|
||||||
String.Format ("{0}{1}.{2}:{3}|", header, type.Name, method.Name, lineNum);
|
|
||||||
#else
|
|
||||||
var headerAndCaller = String.Format ("{0}{1}.{2}|", header, type.Name, method.Name);
|
|
||||||
#endif
|
|
||||||
var msgs = _message.Replace ("\r\n", "\n").TrimEnd ('\n').Split ('\n');
|
|
||||||
if (msgs.Length <= 1)
|
|
||||||
return String.Format ("{0}{1}", headerAndCaller, _message);
|
|
||||||
|
|
||||||
var buff = new StringBuilder (String.Format ("{0}{1}\n", headerAndCaller, msgs[0]), 64);
|
|
||||||
|
|
||||||
var fmt = String.Format ("{{0,{0}}}{{1}}\n", header.Length);
|
|
||||||
for (var i = 1; i < msgs.Length; i++)
|
|
||||||
buff.AppendFormat (fmt, "", msgs[i]);
|
|
||||||
|
|
||||||
buff.Length--;
|
|
||||||
return buff.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* LogLevel.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the logging level.
|
|
||||||
/// </summary>
|
|
||||||
public enum LogLevel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the bottom logging level.
|
|
||||||
/// </summary>
|
|
||||||
Trace,
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the 2nd logging level from the bottom.
|
|
||||||
/// </summary>
|
|
||||||
Debug,
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the 3rd logging level from the bottom.
|
|
||||||
/// </summary>
|
|
||||||
Info,
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the 3rd logging level from the top.
|
|
||||||
/// </summary>
|
|
||||||
Warn,
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the 2nd logging level from the top.
|
|
||||||
/// </summary>
|
|
||||||
Error,
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the top logging level.
|
|
||||||
/// </summary>
|
|
||||||
Fatal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,330 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* Logger.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a set of methods and properties for logging.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// If you output a log with lower than the value of the <see cref="Logger.Level"/> property,
|
|
||||||
/// it cannot be outputted.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default output action writes a log to the standard output stream and the log file
|
|
||||||
/// if the <see cref="Logger.File"/> property has a valid path to it.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If you would like to use the custom output action, you should set
|
|
||||||
/// the <see cref="Logger.Output"/> property to any <c>Action<LogData, string></c>
|
|
||||||
/// delegate.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public class Logger
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private volatile string _file;
|
|
||||||
private volatile LogLevel _level;
|
|
||||||
private Action<LogData, string> _output;
|
|
||||||
private object _sync;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Logger"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This constructor initializes the current logging level with <see cref="LogLevel.Error"/>.
|
|
||||||
/// </remarks>
|
|
||||||
public Logger ()
|
|
||||||
: this (LogLevel.Error, null, null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Logger"/> class with
|
|
||||||
/// the specified logging <paramref name="level"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="level">
|
|
||||||
/// One of the <see cref="LogLevel"/> enum values.
|
|
||||||
/// </param>
|
|
||||||
public Logger (LogLevel level)
|
|
||||||
: this (level, null, null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Logger"/> class with
|
|
||||||
/// the specified logging <paramref name="level"/>, path to the log <paramref name="file"/>,
|
|
||||||
/// and <paramref name="output"/> action.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="level">
|
|
||||||
/// One of the <see cref="LogLevel"/> enum values.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="file">
|
|
||||||
/// A <see cref="string"/> that represents the path to the log file.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="output">
|
|
||||||
/// An <c>Action<LogData, string></c> delegate that references the method(s) used to
|
|
||||||
/// output a log. A <see cref="string"/> parameter passed to this delegate is
|
|
||||||
/// <paramref name="file"/>.
|
|
||||||
/// </param>
|
|
||||||
public Logger (LogLevel level, string file, Action<LogData, string> output)
|
|
||||||
{
|
|
||||||
_level = level;
|
|
||||||
_file = file;
|
|
||||||
_output = output ?? defaultOutput;
|
|
||||||
_sync = new object ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the current path to the log file.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the current path to the log file if any.
|
|
||||||
/// </value>
|
|
||||||
public string File {
|
|
||||||
get {
|
|
||||||
return _file;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
lock (_sync) {
|
|
||||||
_file = value;
|
|
||||||
Warn (
|
|
||||||
String.Format ("The current path to the log file has been changed to {0}.", _file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the current logging level.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// A log with lower than the value of this property cannot be outputted.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// One of the <see cref="LogLevel"/> enum values, specifies the current logging level.
|
|
||||||
/// </value>
|
|
||||||
public LogLevel Level {
|
|
||||||
get {
|
|
||||||
return _level;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
lock (_sync) {
|
|
||||||
_level = value;
|
|
||||||
Warn (String.Format ("The current logging level has been changed to {0}.", _level));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the current output action used to output a log.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// An <c>Action<LogData, string></c> delegate that references the method(s) used to
|
|
||||||
/// output a log. A <see cref="string"/> parameter passed to this delegate is the value of
|
|
||||||
/// the <see cref="Logger.File"/> property.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If the value to set is <see langword="null"/>, the current output action is changed to
|
|
||||||
/// the default output action.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public Action<LogData, string> Output {
|
|
||||||
get {
|
|
||||||
return _output;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
lock (_sync) {
|
|
||||||
_output = value ?? defaultOutput;
|
|
||||||
Warn ("The current output action has been changed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static void defaultOutput (LogData data, string path)
|
|
||||||
{
|
|
||||||
var log = data.ToString ();
|
|
||||||
Console.WriteLine (log);
|
|
||||||
if (path != null && path.Length > 0)
|
|
||||||
writeToFile (log, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void output (string message, LogLevel level)
|
|
||||||
{
|
|
||||||
lock (_sync) {
|
|
||||||
if (_level > level)
|
|
||||||
return;
|
|
||||||
|
|
||||||
LogData data = null;
|
|
||||||
try {
|
|
||||||
data = new LogData (level, new StackFrame (2, true), message);
|
|
||||||
_output (data, _file);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
data = new LogData (LogLevel.Fatal, new StackFrame (0, true), ex.Message);
|
|
||||||
Console.WriteLine (data.ToString ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeToFile (string value, string path)
|
|
||||||
{
|
|
||||||
using (var writer = new StreamWriter (path, true))
|
|
||||||
using (var syncWriter = TextWriter.Synchronized (writer))
|
|
||||||
syncWriter.WriteLine (value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Debug"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the current logging level is higher than <see cref="LogLevel.Debug"/>,
|
|
||||||
/// this method doesn't output <paramref name="message"/> as a log.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="message">
|
|
||||||
/// A <see cref="string"/> that represents the message to output as a log.
|
|
||||||
/// </param>
|
|
||||||
public void Debug (string message)
|
|
||||||
{
|
|
||||||
if (_level > LogLevel.Debug)
|
|
||||||
return;
|
|
||||||
|
|
||||||
output (message, LogLevel.Debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Error"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the current logging level is higher than <see cref="LogLevel.Error"/>,
|
|
||||||
/// this method doesn't output <paramref name="message"/> as a log.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="message">
|
|
||||||
/// A <see cref="string"/> that represents the message to output as a log.
|
|
||||||
/// </param>
|
|
||||||
public void Error (string message)
|
|
||||||
{
|
|
||||||
if (_level > LogLevel.Error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
output (message, LogLevel.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Fatal"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">
|
|
||||||
/// A <see cref="string"/> that represents the message to output as a log.
|
|
||||||
/// </param>
|
|
||||||
public void Fatal (string message)
|
|
||||||
{
|
|
||||||
output (message, LogLevel.Fatal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Info"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the current logging level is higher than <see cref="LogLevel.Info"/>,
|
|
||||||
/// this method doesn't output <paramref name="message"/> as a log.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="message">
|
|
||||||
/// A <see cref="string"/> that represents the message to output as a log.
|
|
||||||
/// </param>
|
|
||||||
public void Info (string message)
|
|
||||||
{
|
|
||||||
if (_level > LogLevel.Info)
|
|
||||||
return;
|
|
||||||
|
|
||||||
output (message, LogLevel.Info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Trace"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the current logging level is higher than <see cref="LogLevel.Trace"/>,
|
|
||||||
/// this method doesn't output <paramref name="message"/> as a log.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="message">
|
|
||||||
/// A <see cref="string"/> that represents the message to output as a log.
|
|
||||||
/// </param>
|
|
||||||
public void Trace (string message)
|
|
||||||
{
|
|
||||||
if (_level > LogLevel.Trace)
|
|
||||||
return;
|
|
||||||
|
|
||||||
output (message, LogLevel.Trace);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Outputs <paramref name="message"/> as a log with <see cref="LogLevel.Warn"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the current logging level is higher than <see cref="LogLevel.Warn"/>,
|
|
||||||
/// this method doesn't output <paramref name="message"/> as a log.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="message">
|
|
||||||
/// A <see cref="string"/> that represents the message to output as a log.
|
|
||||||
/// </param>
|
|
||||||
public void Warn (string message)
|
|
||||||
{
|
|
||||||
if (_level > LogLevel.Warn)
|
|
||||||
return;
|
|
||||||
|
|
||||||
output (message, LogLevel.Warn);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* Mask.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether the payload data of a WebSocket frame is masked.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The values of this enumeration are defined in
|
|
||||||
/// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2</see> of RFC 6455.
|
|
||||||
/// </remarks>
|
|
||||||
internal enum Mask : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 0. Indicates not masked.
|
|
||||||
/// </summary>
|
|
||||||
Off = 0x0,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 1. Indicates masked.
|
|
||||||
/// </summary>
|
|
||||||
On = 0x1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* MessageEventArgs.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2016 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the event data for the <see cref="WebSocket.OnMessage"/> event.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// That event occurs when the <see cref="WebSocket"/> receives
|
|
||||||
/// a message or a ping if the <see cref="WebSocket.EmitOnPing"/>
|
|
||||||
/// property is set to <c>true</c>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If you would like to get the message data, you should access
|
|
||||||
/// the <see cref="Data"/> or <see cref="RawData"/> property.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public class MessageEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private string _data;
|
|
||||||
private bool _dataSet;
|
|
||||||
private Opcode _opcode;
|
|
||||||
private byte[] _rawData;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal MessageEventArgs (WebSocketFrame frame)
|
|
||||||
{
|
|
||||||
_opcode = frame.Opcode;
|
|
||||||
_rawData = frame.PayloadData.ApplicationData;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal MessageEventArgs (Opcode opcode, byte[] rawData)
|
|
||||||
{
|
|
||||||
if ((ulong) rawData.LongLength > PayloadData.MaxLength)
|
|
||||||
throw new WebSocketException (CloseStatusCode.TooBig);
|
|
||||||
|
|
||||||
_opcode = opcode;
|
|
||||||
_rawData = rawData;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the opcode for the message.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <see cref="Opcode.Text"/>, <see cref="Opcode.Binary"/>,
|
|
||||||
/// or <see cref="Opcode.Ping"/>.
|
|
||||||
/// </value>
|
|
||||||
internal Opcode Opcode {
|
|
||||||
get {
|
|
||||||
return _opcode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the message data as a <see cref="string"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the message data if its type is
|
|
||||||
/// text or ping and if decoding it to a string has successfully done;
|
|
||||||
/// otherwise, <see langword="null"/>.
|
|
||||||
/// </value>
|
|
||||||
public string Data {
|
|
||||||
get {
|
|
||||||
setData ();
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the message type is binary.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the message type is binary; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsBinary {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Binary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the message type is ping.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the message type is ping; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsPing {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Ping;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the message type is text.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the message type is text; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsText {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the message data as an array of <see cref="byte"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// An array of <see cref="byte"/> that represents the message data.
|
|
||||||
/// </value>
|
|
||||||
public byte[] RawData {
|
|
||||||
get {
|
|
||||||
setData ();
|
|
||||||
return _rawData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private void setData ()
|
|
||||||
{
|
|
||||||
if (_dataSet)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_opcode == Opcode.Binary) {
|
|
||||||
_dataSet = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string data;
|
|
||||||
if (_rawData.TryGetUTF8DecodedString (out data))
|
|
||||||
_data = data;
|
|
||||||
|
|
||||||
_dataSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* AuthenticationBase.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal abstract class AuthenticationBase
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private AuthenticationSchemes _scheme;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Fields
|
|
||||||
|
|
||||||
internal NameValueCollection Parameters;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Constructors
|
|
||||||
|
|
||||||
protected AuthenticationBase (AuthenticationSchemes scheme, NameValueCollection parameters)
|
|
||||||
{
|
|
||||||
_scheme = scheme;
|
|
||||||
Parameters = parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public string Algorithm {
|
|
||||||
get {
|
|
||||||
return Parameters["algorithm"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Nonce {
|
|
||||||
get {
|
|
||||||
return Parameters["nonce"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Opaque {
|
|
||||||
get {
|
|
||||||
return Parameters["opaque"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Qop {
|
|
||||||
get {
|
|
||||||
return Parameters["qop"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Realm {
|
|
||||||
get {
|
|
||||||
return Parameters["realm"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AuthenticationSchemes Scheme {
|
|
||||||
get {
|
|
||||||
return _scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal static string CreateNonceValue ()
|
|
||||||
{
|
|
||||||
var src = new byte[16];
|
|
||||||
var rand = new Random ();
|
|
||||||
rand.NextBytes (src);
|
|
||||||
|
|
||||||
var res = new StringBuilder (32);
|
|
||||||
foreach (var b in src)
|
|
||||||
res.Append (b.ToString ("x2"));
|
|
||||||
|
|
||||||
return res.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static NameValueCollection ParseParameters (string value)
|
|
||||||
{
|
|
||||||
var res = new NameValueCollection ();
|
|
||||||
foreach (var param in value.SplitHeaderValue (',')) {
|
|
||||||
var i = param.IndexOf ('=');
|
|
||||||
var name = i > 0 ? param.Substring (0, i).Trim () : null;
|
|
||||||
var val = i < 0
|
|
||||||
? param.Trim ().Trim ('"')
|
|
||||||
: i < param.Length - 1
|
|
||||||
? param.Substring (i + 1).Trim ().Trim ('"')
|
|
||||||
: String.Empty;
|
|
||||||
|
|
||||||
res.Add (name, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal abstract string ToBasicString ();
|
|
||||||
|
|
||||||
internal abstract string ToDigestString ();
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
return _scheme == AuthenticationSchemes.Basic
|
|
||||||
? ToBasicString ()
|
|
||||||
: _scheme == AuthenticationSchemes.Digest
|
|
||||||
? ToDigestString ()
|
|
||||||
: String.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* AuthenticationChallenge.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2014 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class AuthenticationChallenge : AuthenticationBase
|
|
||||||
{
|
|
||||||
#region Private Constructors
|
|
||||||
|
|
||||||
private AuthenticationChallenge (AuthenticationSchemes scheme, NameValueCollection parameters)
|
|
||||||
: base (scheme, parameters)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal AuthenticationChallenge (AuthenticationSchemes scheme, string realm)
|
|
||||||
: base (scheme, new NameValueCollection ())
|
|
||||||
{
|
|
||||||
Parameters["realm"] = realm;
|
|
||||||
if (scheme == AuthenticationSchemes.Digest) {
|
|
||||||
Parameters["nonce"] = CreateNonceValue ();
|
|
||||||
Parameters["algorithm"] = "MD5";
|
|
||||||
Parameters["qop"] = "auth";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public string Domain {
|
|
||||||
get {
|
|
||||||
return Parameters["domain"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Stale {
|
|
||||||
get {
|
|
||||||
return Parameters["stale"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal static AuthenticationChallenge CreateBasicChallenge (string realm)
|
|
||||||
{
|
|
||||||
return new AuthenticationChallenge (AuthenticationSchemes.Basic, realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static AuthenticationChallenge CreateDigestChallenge (string realm)
|
|
||||||
{
|
|
||||||
return new AuthenticationChallenge (AuthenticationSchemes.Digest, realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static AuthenticationChallenge Parse (string value)
|
|
||||||
{
|
|
||||||
var chal = value.Split (new[] { ' ' }, 2);
|
|
||||||
if (chal.Length != 2)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var schm = chal[0].ToLower ();
|
|
||||||
return schm == "basic"
|
|
||||||
? new AuthenticationChallenge (
|
|
||||||
AuthenticationSchemes.Basic, ParseParameters (chal[1]))
|
|
||||||
: schm == "digest"
|
|
||||||
? new AuthenticationChallenge (
|
|
||||||
AuthenticationSchemes.Digest, ParseParameters (chal[1]))
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override string ToBasicString ()
|
|
||||||
{
|
|
||||||
return String.Format ("Basic realm=\"{0}\"", Parameters["realm"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override string ToDigestString ()
|
|
||||||
{
|
|
||||||
var output = new StringBuilder (128);
|
|
||||||
|
|
||||||
var domain = Parameters["domain"];
|
|
||||||
if (domain != null)
|
|
||||||
output.AppendFormat (
|
|
||||||
"Digest realm=\"{0}\", domain=\"{1}\", nonce=\"{2}\"",
|
|
||||||
Parameters["realm"],
|
|
||||||
domain,
|
|
||||||
Parameters["nonce"]);
|
|
||||||
else
|
|
||||||
output.AppendFormat (
|
|
||||||
"Digest realm=\"{0}\", nonce=\"{1}\"", Parameters["realm"], Parameters["nonce"]);
|
|
||||||
|
|
||||||
var opaque = Parameters["opaque"];
|
|
||||||
if (opaque != null)
|
|
||||||
output.AppendFormat (", opaque=\"{0}\"", opaque);
|
|
||||||
|
|
||||||
var stale = Parameters["stale"];
|
|
||||||
if (stale != null)
|
|
||||||
output.AppendFormat (", stale={0}", stale);
|
|
||||||
|
|
||||||
var algo = Parameters["algorithm"];
|
|
||||||
if (algo != null)
|
|
||||||
output.AppendFormat (", algorithm={0}", algo);
|
|
||||||
|
|
||||||
var qop = Parameters["qop"];
|
|
||||||
if (qop != null)
|
|
||||||
output.AppendFormat (", qop=\"{0}\"", qop);
|
|
||||||
|
|
||||||
return output.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,323 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* AuthenticationResponse.cs
|
|
||||||
*
|
|
||||||
* ParseBasicCredentials is derived from System.Net.HttpListenerContext.cs of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2013-2014 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class AuthenticationResponse : AuthenticationBase
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private uint _nonceCount;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Constructors
|
|
||||||
|
|
||||||
private AuthenticationResponse (AuthenticationSchemes scheme, NameValueCollection parameters)
|
|
||||||
: base (scheme, parameters)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal AuthenticationResponse (NetworkCredential credentials)
|
|
||||||
: this (AuthenticationSchemes.Basic, new NameValueCollection (), credentials, 0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal AuthenticationResponse (
|
|
||||||
AuthenticationChallenge challenge, NetworkCredential credentials, uint nonceCount)
|
|
||||||
: this (challenge.Scheme, challenge.Parameters, credentials, nonceCount)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal AuthenticationResponse (
|
|
||||||
AuthenticationSchemes scheme,
|
|
||||||
NameValueCollection parameters,
|
|
||||||
NetworkCredential credentials,
|
|
||||||
uint nonceCount)
|
|
||||||
: base (scheme, parameters)
|
|
||||||
{
|
|
||||||
Parameters["username"] = credentials.Username;
|
|
||||||
Parameters["password"] = credentials.Password;
|
|
||||||
Parameters["uri"] = credentials.Domain;
|
|
||||||
_nonceCount = nonceCount;
|
|
||||||
if (scheme == AuthenticationSchemes.Digest)
|
|
||||||
initAsDigest ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal uint NonceCount {
|
|
||||||
get {
|
|
||||||
return _nonceCount < UInt32.MaxValue
|
|
||||||
? _nonceCount
|
|
||||||
: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public string Cnonce {
|
|
||||||
get {
|
|
||||||
return Parameters["cnonce"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Nc {
|
|
||||||
get {
|
|
||||||
return Parameters["nc"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Password {
|
|
||||||
get {
|
|
||||||
return Parameters["password"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Response {
|
|
||||||
get {
|
|
||||||
return Parameters["response"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Uri {
|
|
||||||
get {
|
|
||||||
return Parameters["uri"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string UserName {
|
|
||||||
get {
|
|
||||||
return Parameters["username"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static string createA1 (string username, string password, string realm)
|
|
||||||
{
|
|
||||||
return String.Format ("{0}:{1}:{2}", username, realm, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string createA1 (
|
|
||||||
string username, string password, string realm, string nonce, string cnonce)
|
|
||||||
{
|
|
||||||
return String.Format (
|
|
||||||
"{0}:{1}:{2}", hash (createA1 (username, password, realm)), nonce, cnonce);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string createA2 (string method, string uri)
|
|
||||||
{
|
|
||||||
return String.Format ("{0}:{1}", method, uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string createA2 (string method, string uri, string entity)
|
|
||||||
{
|
|
||||||
return String.Format ("{0}:{1}:{2}", method, uri, hash (entity));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string hash (string value)
|
|
||||||
{
|
|
||||||
var src = Encoding.UTF8.GetBytes (value);
|
|
||||||
var md5 = MD5.Create ();
|
|
||||||
var hashed = md5.ComputeHash (src);
|
|
||||||
|
|
||||||
var res = new StringBuilder (64);
|
|
||||||
foreach (var b in hashed)
|
|
||||||
res.Append (b.ToString ("x2"));
|
|
||||||
|
|
||||||
return res.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initAsDigest ()
|
|
||||||
{
|
|
||||||
var qops = Parameters["qop"];
|
|
||||||
if (qops != null) {
|
|
||||||
if (qops.Split (',').Contains (qop => qop.Trim ().ToLower () == "auth")) {
|
|
||||||
Parameters["qop"] = "auth";
|
|
||||||
Parameters["cnonce"] = CreateNonceValue ();
|
|
||||||
Parameters["nc"] = String.Format ("{0:x8}", ++_nonceCount);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Parameters["qop"] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Parameters["method"] = "GET";
|
|
||||||
Parameters["response"] = CreateRequestDigest (Parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal static string CreateRequestDigest (NameValueCollection parameters)
|
|
||||||
{
|
|
||||||
var user = parameters["username"];
|
|
||||||
var pass = parameters["password"];
|
|
||||||
var realm = parameters["realm"];
|
|
||||||
var nonce = parameters["nonce"];
|
|
||||||
var uri = parameters["uri"];
|
|
||||||
var algo = parameters["algorithm"];
|
|
||||||
var qop = parameters["qop"];
|
|
||||||
var cnonce = parameters["cnonce"];
|
|
||||||
var nc = parameters["nc"];
|
|
||||||
var method = parameters["method"];
|
|
||||||
|
|
||||||
var a1 = algo != null && algo.ToLower () == "md5-sess"
|
|
||||||
? createA1 (user, pass, realm, nonce, cnonce)
|
|
||||||
: createA1 (user, pass, realm);
|
|
||||||
|
|
||||||
var a2 = qop != null && qop.ToLower () == "auth-int"
|
|
||||||
? createA2 (method, uri, parameters["entity"])
|
|
||||||
: createA2 (method, uri);
|
|
||||||
|
|
||||||
var secret = hash (a1);
|
|
||||||
var data = qop != null
|
|
||||||
? String.Format ("{0}:{1}:{2}:{3}:{4}", nonce, nc, cnonce, qop, hash (a2))
|
|
||||||
: String.Format ("{0}:{1}", nonce, hash (a2));
|
|
||||||
|
|
||||||
return hash (String.Format ("{0}:{1}", secret, data));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static AuthenticationResponse Parse (string value)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
var cred = value.Split (new[] { ' ' }, 2);
|
|
||||||
if (cred.Length != 2)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var schm = cred[0].ToLower ();
|
|
||||||
return schm == "basic"
|
|
||||||
? new AuthenticationResponse (
|
|
||||||
AuthenticationSchemes.Basic, ParseBasicCredentials (cred[1]))
|
|
||||||
: schm == "digest"
|
|
||||||
? new AuthenticationResponse (
|
|
||||||
AuthenticationSchemes.Digest, ParseParameters (cred[1]))
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static NameValueCollection ParseBasicCredentials (string value)
|
|
||||||
{
|
|
||||||
// Decode the basic-credentials (a Base64 encoded string).
|
|
||||||
var userPass = Encoding.Default.GetString (Convert.FromBase64String (value));
|
|
||||||
|
|
||||||
// The format is [<domain>\]<username>:<password>.
|
|
||||||
var i = userPass.IndexOf (':');
|
|
||||||
var user = userPass.Substring (0, i);
|
|
||||||
var pass = i < userPass.Length - 1 ? userPass.Substring (i + 1) : String.Empty;
|
|
||||||
|
|
||||||
// Check if 'domain' exists.
|
|
||||||
i = user.IndexOf ('\\');
|
|
||||||
if (i > -1)
|
|
||||||
user = user.Substring (i + 1);
|
|
||||||
|
|
||||||
var res = new NameValueCollection ();
|
|
||||||
res["username"] = user;
|
|
||||||
res["password"] = pass;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override string ToBasicString ()
|
|
||||||
{
|
|
||||||
var userPass = String.Format ("{0}:{1}", Parameters["username"], Parameters["password"]);
|
|
||||||
var cred = Convert.ToBase64String (Encoding.UTF8.GetBytes (userPass));
|
|
||||||
|
|
||||||
return "Basic " + cred;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override string ToDigestString ()
|
|
||||||
{
|
|
||||||
var output = new StringBuilder (256);
|
|
||||||
output.AppendFormat (
|
|
||||||
"Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"",
|
|
||||||
Parameters["username"],
|
|
||||||
Parameters["realm"],
|
|
||||||
Parameters["nonce"],
|
|
||||||
Parameters["uri"],
|
|
||||||
Parameters["response"]);
|
|
||||||
|
|
||||||
var opaque = Parameters["opaque"];
|
|
||||||
if (opaque != null)
|
|
||||||
output.AppendFormat (", opaque=\"{0}\"", opaque);
|
|
||||||
|
|
||||||
var algo = Parameters["algorithm"];
|
|
||||||
if (algo != null)
|
|
||||||
output.AppendFormat (", algorithm={0}", algo);
|
|
||||||
|
|
||||||
var qop = Parameters["qop"];
|
|
||||||
if (qop != null)
|
|
||||||
output.AppendFormat (
|
|
||||||
", qop={0}, cnonce=\"{1}\", nc={2}", qop, Parameters["cnonce"], Parameters["nc"]);
|
|
||||||
|
|
||||||
return output.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public IIdentity ToIdentity ()
|
|
||||||
{
|
|
||||||
var schm = Scheme;
|
|
||||||
return schm == AuthenticationSchemes.Basic
|
|
||||||
? new HttpBasicIdentity (Parameters["username"], Parameters["password"]) as IIdentity
|
|
||||||
: schm == AuthenticationSchemes.Digest
|
|
||||||
? new HttpDigestIdentity (Parameters)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* AuthenticationSchemes.cs
|
|
||||||
*
|
|
||||||
* This code is derived from AuthenticationSchemes.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2016 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Atsushi Enomoto <atsushi@ximian.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the scheme for authentication.
|
|
||||||
/// </summary>
|
|
||||||
public enum AuthenticationSchemes
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// No authentication is allowed.
|
|
||||||
/// </summary>
|
|
||||||
None,
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies digest authentication.
|
|
||||||
/// </summary>
|
|
||||||
Digest = 1,
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies basic authentication.
|
|
||||||
/// </summary>
|
|
||||||
Basic = 8,
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies anonymous authentication.
|
|
||||||
/// </summary>
|
|
||||||
Anonymous = 0x8000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* Chunk.cs
|
|
||||||
*
|
|
||||||
* This code is derived from ChunkStream.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2003 Ximian, Inc (http://www.ximian.com)
|
|
||||||
* Copyright (c) 2014-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class Chunk
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private byte[] _data;
|
|
||||||
private int _offset;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
public Chunk (byte[] data)
|
|
||||||
{
|
|
||||||
_data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public int ReadLeft {
|
|
||||||
get {
|
|
||||||
return _data.Length - _offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public int Read (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
var left = _data.Length - _offset;
|
|
||||||
|
|
||||||
if (left == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (count > left)
|
|
||||||
count = left;
|
|
||||||
|
|
||||||
Buffer.BlockCopy (_data, _offset, buffer, offset, count);
|
|
||||||
|
|
||||||
_offset += count;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,383 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* ChunkStream.cs
|
|
||||||
*
|
|
||||||
* This code is derived from ChunkStream.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2003 Ximian, Inc (http://www.ximian.com)
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class ChunkStream
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private int _chunkRead;
|
|
||||||
private int _chunkSize;
|
|
||||||
private List<Chunk> _chunks;
|
|
||||||
private bool _gotIt;
|
|
||||||
private WebHeaderCollection _headers;
|
|
||||||
private StringBuilder _saved;
|
|
||||||
private bool _sawCr;
|
|
||||||
private InputChunkState _state;
|
|
||||||
private int _trailerState;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
public ChunkStream (WebHeaderCollection headers)
|
|
||||||
{
|
|
||||||
_headers = headers;
|
|
||||||
|
|
||||||
_chunkSize = -1;
|
|
||||||
_chunks = new List<Chunk> ();
|
|
||||||
_saved = new StringBuilder ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public WebHeaderCollection Headers {
|
|
||||||
get {
|
|
||||||
return _headers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool WantsMore {
|
|
||||||
get {
|
|
||||||
return _state < InputChunkState.End;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private int read (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
var nread = 0;
|
|
||||||
var cnt = _chunks.Count;
|
|
||||||
|
|
||||||
for (var i = 0; i < cnt; i++) {
|
|
||||||
var chunk = _chunks[i];
|
|
||||||
|
|
||||||
if (chunk == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (chunk.ReadLeft == 0) {
|
|
||||||
_chunks[i] = null;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
nread += chunk.Read (buffer, offset + nread, count - nread);
|
|
||||||
|
|
||||||
if (nread == count)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputChunkState seekCrLf (byte[] buffer, ref int offset, int length)
|
|
||||||
{
|
|
||||||
if (!_sawCr) {
|
|
||||||
if (buffer[offset++] != 13)
|
|
||||||
throwProtocolViolation ("CR is expected.");
|
|
||||||
|
|
||||||
_sawCr = true;
|
|
||||||
|
|
||||||
if (offset == length)
|
|
||||||
return InputChunkState.DataEnded;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer[offset++] != 10)
|
|
||||||
throwProtocolViolation ("LF is expected.");
|
|
||||||
|
|
||||||
return InputChunkState.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputChunkState setChunkSize (
|
|
||||||
byte[] buffer, ref int offset, int length
|
|
||||||
)
|
|
||||||
{
|
|
||||||
byte b = 0;
|
|
||||||
|
|
||||||
while (offset < length) {
|
|
||||||
b = buffer[offset++];
|
|
||||||
|
|
||||||
if (_sawCr) {
|
|
||||||
if (b != 10)
|
|
||||||
throwProtocolViolation ("LF is expected.");
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b == 13) {
|
|
||||||
_sawCr = true;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b == 10)
|
|
||||||
throwProtocolViolation ("LF is unexpected.");
|
|
||||||
|
|
||||||
if (_gotIt)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (b == 32 || b == 59) { // SP or ';'
|
|
||||||
_gotIt = true;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_saved.Append ((char) b);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_saved.Length > 20)
|
|
||||||
throwProtocolViolation ("The chunk size is too big.");
|
|
||||||
|
|
||||||
if (b != 10)
|
|
||||||
return InputChunkState.None;
|
|
||||||
|
|
||||||
var s = _saved.ToString ();
|
|
||||||
|
|
||||||
try {
|
|
||||||
_chunkSize = Int32.Parse (s, NumberStyles.HexNumber);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
throwProtocolViolation ("The chunk size cannot be parsed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_chunkRead = 0;
|
|
||||||
|
|
||||||
if (_chunkSize == 0) {
|
|
||||||
_trailerState = 2;
|
|
||||||
|
|
||||||
return InputChunkState.Trailer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return InputChunkState.Data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputChunkState setTrailer (
|
|
||||||
byte[] buffer, ref int offset, int length
|
|
||||||
)
|
|
||||||
{
|
|
||||||
while (offset < length) {
|
|
||||||
if (_trailerState == 4) // CR LF CR LF
|
|
||||||
break;
|
|
||||||
|
|
||||||
var b = buffer[offset++];
|
|
||||||
_saved.Append ((char) b);
|
|
||||||
|
|
||||||
if (_trailerState == 1 || _trailerState == 3) { // CR or CR LF CR
|
|
||||||
if (b != 10)
|
|
||||||
throwProtocolViolation ("LF is expected.");
|
|
||||||
|
|
||||||
_trailerState++;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b == 13) {
|
|
||||||
_trailerState++;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b == 10)
|
|
||||||
throwProtocolViolation ("LF is unexpected.");
|
|
||||||
|
|
||||||
_trailerState = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var len = _saved.Length;
|
|
||||||
|
|
||||||
if (len > 4196)
|
|
||||||
throwProtocolViolation ("The trailer is too long.");
|
|
||||||
|
|
||||||
if (_trailerState < 4)
|
|
||||||
return InputChunkState.Trailer;
|
|
||||||
|
|
||||||
if (len == 2)
|
|
||||||
return InputChunkState.End;
|
|
||||||
|
|
||||||
_saved.Length = len - 2;
|
|
||||||
var val = _saved.ToString ();
|
|
||||||
var reader = new StringReader (val);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
var line = reader.ReadLine ();
|
|
||||||
|
|
||||||
if (line == null || line.Length == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
_headers.Add (line);
|
|
||||||
}
|
|
||||||
|
|
||||||
return InputChunkState.End;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void throwProtocolViolation (string message)
|
|
||||||
{
|
|
||||||
throw new WebException (
|
|
||||||
message, null, WebExceptionStatus.ServerProtocolViolation, null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void write (byte[] buffer, int offset, int length)
|
|
||||||
{
|
|
||||||
if (_state == InputChunkState.End)
|
|
||||||
throwProtocolViolation ("The chunks were ended.");
|
|
||||||
|
|
||||||
if (_state == InputChunkState.None) {
|
|
||||||
_state = setChunkSize (buffer, ref offset, length);
|
|
||||||
|
|
||||||
if (_state == InputChunkState.None)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_saved.Length = 0;
|
|
||||||
_sawCr = false;
|
|
||||||
_gotIt = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_state == InputChunkState.Data) {
|
|
||||||
if (offset >= length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_state = writeData (buffer, ref offset, length);
|
|
||||||
|
|
||||||
if (_state == InputChunkState.Data)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_state == InputChunkState.DataEnded) {
|
|
||||||
if (offset >= length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_state = seekCrLf (buffer, ref offset, length);
|
|
||||||
|
|
||||||
if (_state == InputChunkState.DataEnded)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_sawCr = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_state == InputChunkState.Trailer) {
|
|
||||||
if (offset >= length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_state = setTrailer (buffer, ref offset, length);
|
|
||||||
|
|
||||||
if (_state == InputChunkState.Trailer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_saved.Length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset >= length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
write (buffer, offset, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputChunkState writeData (
|
|
||||||
byte[] buffer, ref int offset, int length
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var cnt = length - offset;
|
|
||||||
var left = _chunkSize - _chunkRead;
|
|
||||||
|
|
||||||
if (cnt > left)
|
|
||||||
cnt = left;
|
|
||||||
|
|
||||||
var data = new byte[cnt];
|
|
||||||
Buffer.BlockCopy (buffer, offset, data, 0, cnt);
|
|
||||||
|
|
||||||
var chunk = new Chunk (data);
|
|
||||||
_chunks.Add (chunk);
|
|
||||||
|
|
||||||
offset += cnt;
|
|
||||||
_chunkRead += cnt;
|
|
||||||
|
|
||||||
return _chunkRead == _chunkSize
|
|
||||||
? InputChunkState.DataEnded
|
|
||||||
: InputChunkState.Data;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal void ResetBuffer ()
|
|
||||||
{
|
|
||||||
_chunkRead = 0;
|
|
||||||
_chunkSize = -1;
|
|
||||||
|
|
||||||
_chunks.Clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public int Read (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (count <= 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return read (buffer, offset, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (count <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
write (buffer, offset, offset + count);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* ChunkedRequestStream.cs
|
|
||||||
*
|
|
||||||
* This code is derived from ChunkedInputStream.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class ChunkedRequestStream : RequestStream
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private static readonly int _bufferLength;
|
|
||||||
private HttpListenerContext _context;
|
|
||||||
private ChunkStream _decoder;
|
|
||||||
private bool _disposed;
|
|
||||||
private bool _noMoreData;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Static Constructor
|
|
||||||
|
|
||||||
static ChunkedRequestStream ()
|
|
||||||
{
|
|
||||||
_bufferLength = 8192;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal ChunkedRequestStream (
|
|
||||||
Stream stream,
|
|
||||||
byte[] buffer,
|
|
||||||
int offset,
|
|
||||||
int count,
|
|
||||||
HttpListenerContext context
|
|
||||||
)
|
|
||||||
: base (stream, buffer, offset, count, -1)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
|
|
||||||
_decoder = new ChunkStream (
|
|
||||||
(WebHeaderCollection) context.Request.Headers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private void onRead (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
var rstate = (ReadBufferState) asyncResult.AsyncState;
|
|
||||||
var ares = rstate.AsyncResult;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var nread = base.EndRead (asyncResult);
|
|
||||||
|
|
||||||
_decoder.Write (ares.Buffer, ares.Offset, nread);
|
|
||||||
nread = _decoder.Read (rstate.Buffer, rstate.Offset, rstate.Count);
|
|
||||||
|
|
||||||
rstate.Offset += nread;
|
|
||||||
rstate.Count -= nread;
|
|
||||||
|
|
||||||
if (rstate.Count == 0 || !_decoder.WantsMore || nread == 0) {
|
|
||||||
_noMoreData = !_decoder.WantsMore && nread == 0;
|
|
||||||
|
|
||||||
ares.Count = rstate.InitialCount - rstate.Count;
|
|
||||||
ares.Complete ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
_context.ErrorMessage = "I/O operation aborted";
|
|
||||||
_context.SendError ();
|
|
||||||
|
|
||||||
ares.Complete (ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public override IAsyncResult BeginRead (
|
|
||||||
byte[] buffer, int offset, int count, AsyncCallback callback, object state
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (_disposed) {
|
|
||||||
var name = GetType ().ToString ();
|
|
||||||
|
|
||||||
throw new ObjectDisposedException (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException ("buffer");
|
|
||||||
|
|
||||||
if (offset < 0) {
|
|
||||||
var msg = "A negative value.";
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException ("offset", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count < 0) {
|
|
||||||
var msg = "A negative value.";
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException ("count", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var len = buffer.Length;
|
|
||||||
|
|
||||||
if (offset + count > len) {
|
|
||||||
var msg = "The sum of 'offset' and 'count' is greater than the length of 'buffer'.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ares = new HttpStreamAsyncResult (callback, state);
|
|
||||||
|
|
||||||
if (_noMoreData) {
|
|
||||||
ares.Complete ();
|
|
||||||
|
|
||||||
return ares;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nread = _decoder.Read (buffer, offset, count);
|
|
||||||
|
|
||||||
offset += nread;
|
|
||||||
count -= nread;
|
|
||||||
|
|
||||||
if (count == 0) {
|
|
||||||
ares.Count = nread;
|
|
||||||
ares.Complete ();
|
|
||||||
|
|
||||||
return ares;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_decoder.WantsMore) {
|
|
||||||
_noMoreData = nread == 0;
|
|
||||||
|
|
||||||
ares.Count = nread;
|
|
||||||
ares.Complete ();
|
|
||||||
|
|
||||||
return ares;
|
|
||||||
}
|
|
||||||
|
|
||||||
ares.Buffer = new byte[_bufferLength];
|
|
||||||
ares.Offset = 0;
|
|
||||||
ares.Count = _bufferLength;
|
|
||||||
|
|
||||||
var rstate = new ReadBufferState (buffer, offset, count, ares);
|
|
||||||
rstate.InitialCount += nread;
|
|
||||||
|
|
||||||
base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate);
|
|
||||||
|
|
||||||
return ares;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Close ()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
|
|
||||||
base.Close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int EndRead (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
if (_disposed) {
|
|
||||||
var name = GetType ().ToString ();
|
|
||||||
|
|
||||||
throw new ObjectDisposedException (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asyncResult == null)
|
|
||||||
throw new ArgumentNullException ("asyncResult");
|
|
||||||
|
|
||||||
var ares = asyncResult as HttpStreamAsyncResult;
|
|
||||||
|
|
||||||
if (ares == null) {
|
|
||||||
var msg = "A wrong IAsyncResult instance.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "asyncResult");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ares.IsCompleted)
|
|
||||||
ares.AsyncWaitHandle.WaitOne ();
|
|
||||||
|
|
||||||
if (ares.HasException) {
|
|
||||||
var msg = "The I/O operation has been aborted.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (995, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ares.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
var ares = BeginRead (buffer, offset, count, null, null);
|
|
||||||
|
|
||||||
return EndRead (ares);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,303 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* ClientSslConfiguration.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 liryna
|
|
||||||
* Copyright (c) 2014-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Liryna <liryna.stark@gmail.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Net.Security;
|
|
||||||
using System.Security.Authentication;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stores the parameters for the <see cref="SslStream"/> used by clients.
|
|
||||||
/// </summary>
|
|
||||||
public class ClientSslConfiguration
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private bool _checkCertRevocation;
|
|
||||||
private LocalCertificateSelectionCallback _clientCertSelectionCallback;
|
|
||||||
private X509CertificateCollection _clientCerts;
|
|
||||||
private SslProtocols _enabledSslProtocols;
|
|
||||||
private RemoteCertificateValidationCallback _serverCertValidationCallback;
|
|
||||||
private string _targetHost;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ClientSslConfiguration"/>
|
|
||||||
/// class with the specified target host server name.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="targetHost">
|
|
||||||
/// A <see cref="string"/> that specifies the target host server name.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="targetHost"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <paramref name="targetHost"/> is an empty string.
|
|
||||||
/// </exception>
|
|
||||||
public ClientSslConfiguration (string targetHost)
|
|
||||||
{
|
|
||||||
if (targetHost == null)
|
|
||||||
throw new ArgumentNullException ("targetHost");
|
|
||||||
|
|
||||||
if (targetHost.Length == 0)
|
|
||||||
throw new ArgumentException ("An empty string.", "targetHost");
|
|
||||||
|
|
||||||
_targetHost = targetHost;
|
|
||||||
|
|
||||||
_enabledSslProtocols = SslProtocols.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ClientSslConfiguration"/>
|
|
||||||
/// class that stores the parameters copied from the specified configuration.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configuration">
|
|
||||||
/// A <see cref="ClientSslConfiguration"/> from which to copy.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="configuration"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
public ClientSslConfiguration (ClientSslConfiguration configuration)
|
|
||||||
{
|
|
||||||
if (configuration == null)
|
|
||||||
throw new ArgumentNullException ("configuration");
|
|
||||||
|
|
||||||
_checkCertRevocation = configuration._checkCertRevocation;
|
|
||||||
_clientCertSelectionCallback = configuration._clientCertSelectionCallback;
|
|
||||||
_clientCerts = configuration._clientCerts;
|
|
||||||
_enabledSslProtocols = configuration._enabledSslProtocols;
|
|
||||||
_serverCertValidationCallback = configuration._serverCertValidationCallback;
|
|
||||||
_targetHost = configuration._targetHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the certificate revocation
|
|
||||||
/// list is checked during authentication.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// <c>true</c> if the certificate revocation list is checked during
|
|
||||||
/// authentication; otherwise, <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public bool CheckCertificateRevocation {
|
|
||||||
get {
|
|
||||||
return _checkCertRevocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_checkCertRevocation = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the collection of client certificates from which to select
|
|
||||||
/// one to supply to the server.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="X509CertificateCollection"/> or <see langword="null"/>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The collection contains client certificates from which to select.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <see langword="null"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public X509CertificateCollection ClientCertificates {
|
|
||||||
get {
|
|
||||||
return _clientCerts;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_clientCerts = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the callback used to select the certificate to supply to
|
|
||||||
/// the server.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// No certificate is supplied if the callback returns <see langword="null"/>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="LocalCertificateSelectionCallback"/> delegate that
|
|
||||||
/// invokes the method called for selecting the certificate.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is a delegate that invokes a method that only
|
|
||||||
/// returns <see langword="null"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public LocalCertificateSelectionCallback ClientCertificateSelectionCallback {
|
|
||||||
get {
|
|
||||||
if (_clientCertSelectionCallback == null)
|
|
||||||
_clientCertSelectionCallback = defaultSelectClientCertificate;
|
|
||||||
|
|
||||||
return _clientCertSelectionCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_clientCertSelectionCallback = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the protocols used for authentication.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// Any of the <see cref="SslProtocols"/> enum values.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It represents the protocols used for authentication.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <see cref="SslProtocols.None"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public SslProtocols EnabledSslProtocols {
|
|
||||||
get {
|
|
||||||
return _enabledSslProtocols;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_enabledSslProtocols = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the callback used to validate the certificate supplied by
|
|
||||||
/// the server.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The certificate is valid if the callback returns <c>true</c>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="RemoteCertificateValidationCallback"/> delegate that
|
|
||||||
/// invokes the method called for validating the certificate.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is a delegate that invokes a method that only
|
|
||||||
/// returns <c>true</c>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
|
|
||||||
get {
|
|
||||||
if (_serverCertValidationCallback == null)
|
|
||||||
_serverCertValidationCallback = defaultValidateServerCertificate;
|
|
||||||
|
|
||||||
return _serverCertValidationCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_serverCertValidationCallback = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the target host server name.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the name of the server that
|
|
||||||
/// will share a secure connection with a client.
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// The value specified for a set operation is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// The value specified for a set operation is an empty string.
|
|
||||||
/// </exception>
|
|
||||||
public string TargetHost {
|
|
||||||
get {
|
|
||||||
return _targetHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
if (value == null)
|
|
||||||
throw new ArgumentNullException ("value");
|
|
||||||
|
|
||||||
if (value.Length == 0)
|
|
||||||
throw new ArgumentException ("An empty string.", "value");
|
|
||||||
|
|
||||||
_targetHost = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static X509Certificate defaultSelectClientCertificate (
|
|
||||||
object sender,
|
|
||||||
string targetHost,
|
|
||||||
X509CertificateCollection clientCertificates,
|
|
||||||
X509Certificate serverCertificate,
|
|
||||||
string[] acceptableIssuers
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool defaultValidateServerCertificate (
|
|
||||||
object sender,
|
|
||||||
X509Certificate certificate,
|
|
||||||
X509Chain chain,
|
|
||||||
SslPolicyErrors sslPolicyErrors
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,821 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* CookieCollection.cs
|
|
||||||
*
|
|
||||||
* This code is derived from CookieCollection.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2004,2009 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2019 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Lawrence Pit <loz@cable.a2000.nl>
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
|
|
||||||
* - Sebastien Pouliot <sebastien@ximian.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a collection of instances of the <see cref="Cookie"/> class.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class CookieCollection : ICollection<Cookie>
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private List<Cookie> _list;
|
|
||||||
private bool _readOnly;
|
|
||||||
private object _sync;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="CookieCollection"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public CookieCollection ()
|
|
||||||
{
|
|
||||||
_list = new List<Cookie> ();
|
|
||||||
_sync = ((ICollection) _list).SyncRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal IList<Cookie> List {
|
|
||||||
get {
|
|
||||||
return _list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IEnumerable<Cookie> Sorted {
|
|
||||||
get {
|
|
||||||
var list = new List<Cookie> (_list);
|
|
||||||
if (list.Count > 1)
|
|
||||||
list.Sort (compareForSorted);
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of cookies in the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// An <see cref="int"/> that represents the number of cookies in
|
|
||||||
/// the collection.
|
|
||||||
/// </value>
|
|
||||||
public int Count {
|
|
||||||
get {
|
|
||||||
return _list.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the collection is read-only.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// <c>true</c> if the collection is read-only; otherwise, <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public bool IsReadOnly {
|
|
||||||
get {
|
|
||||||
return _readOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal set {
|
|
||||||
_readOnly = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the access to the collection is
|
|
||||||
/// thread safe.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// <c>true</c> if the access to the collection is thread safe;
|
|
||||||
/// otherwise, <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public bool IsSynchronized {
|
|
||||||
get {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the cookie at the specified index from the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="Cookie"/> at the specified index in the collection.
|
|
||||||
/// </value>
|
|
||||||
/// <param name="index">
|
|
||||||
/// An <see cref="int"/> that specifies the zero-based index of the cookie
|
|
||||||
/// to find.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||||
/// <paramref name="index"/> is out of allowable range for the collection.
|
|
||||||
/// </exception>
|
|
||||||
public Cookie this[int index] {
|
|
||||||
get {
|
|
||||||
if (index < 0 || index >= _list.Count)
|
|
||||||
throw new ArgumentOutOfRangeException ("index");
|
|
||||||
|
|
||||||
return _list[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the cookie with the specified name from the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="Cookie"/> with the specified name in the collection.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if not found.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
/// <param name="name">
|
|
||||||
/// A <see cref="string"/> that specifies the name of the cookie to find.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="name"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
public Cookie this[string name] {
|
|
||||||
get {
|
|
||||||
if (name == null)
|
|
||||||
throw new ArgumentNullException ("name");
|
|
||||||
|
|
||||||
var caseInsensitive = StringComparison.InvariantCultureIgnoreCase;
|
|
||||||
|
|
||||||
foreach (var cookie in Sorted) {
|
|
||||||
if (cookie.Name.Equals (name, caseInsensitive))
|
|
||||||
return cookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets an object used to synchronize access to the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// An <see cref="object"/> used to synchronize access to the collection.
|
|
||||||
/// </value>
|
|
||||||
public object SyncRoot {
|
|
||||||
get {
|
|
||||||
return _sync;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private void add (Cookie cookie)
|
|
||||||
{
|
|
||||||
var idx = search (cookie);
|
|
||||||
if (idx == -1) {
|
|
||||||
_list.Add (cookie);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_list[idx] = cookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int compareForSort (Cookie x, Cookie y)
|
|
||||||
{
|
|
||||||
return (x.Name.Length + x.Value.Length)
|
|
||||||
- (y.Name.Length + y.Value.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int compareForSorted (Cookie x, Cookie y)
|
|
||||||
{
|
|
||||||
var ret = x.Version - y.Version;
|
|
||||||
return ret != 0
|
|
||||||
? ret
|
|
||||||
: (ret = x.Name.CompareTo (y.Name)) != 0
|
|
||||||
? ret
|
|
||||||
: y.Path.Length - x.Path.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CookieCollection parseRequest (string value)
|
|
||||||
{
|
|
||||||
var ret = new CookieCollection ();
|
|
||||||
|
|
||||||
Cookie cookie = null;
|
|
||||||
var ver = 0;
|
|
||||||
|
|
||||||
var caseInsensitive = StringComparison.InvariantCultureIgnoreCase;
|
|
||||||
var pairs = value.SplitHeaderValue (',', ';').ToList ();
|
|
||||||
|
|
||||||
for (var i = 0; i < pairs.Count; i++) {
|
|
||||||
var pair = pairs[i].Trim ();
|
|
||||||
if (pair.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var idx = pair.IndexOf ('=');
|
|
||||||
if (idx == -1) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (pair.Equals ("$port", caseInsensitive)) {
|
|
||||||
cookie.Port = "\"\"";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx == 0) {
|
|
||||||
if (cookie != null) {
|
|
||||||
ret.add (cookie);
|
|
||||||
cookie = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = pair.Substring (0, idx).TrimEnd (' ');
|
|
||||||
var val = idx < pair.Length - 1
|
|
||||||
? pair.Substring (idx + 1).TrimStart (' ')
|
|
||||||
: String.Empty;
|
|
||||||
|
|
||||||
if (name.Equals ("$version", caseInsensitive)) {
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int num;
|
|
||||||
if (!Int32.TryParse (val.Unquote (), out num))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ver = num;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("$path", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.Path = val;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("$domain", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.Domain = val;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("$port", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.Port = val;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookie != null)
|
|
||||||
ret.add (cookie);
|
|
||||||
|
|
||||||
if (!Cookie.TryCreate (name, val, out cookie))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (ver != 0)
|
|
||||||
cookie.Version = ver;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookie != null)
|
|
||||||
ret.add (cookie);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CookieCollection parseResponse (string value)
|
|
||||||
{
|
|
||||||
var ret = new CookieCollection ();
|
|
||||||
|
|
||||||
Cookie cookie = null;
|
|
||||||
|
|
||||||
var caseInsensitive = StringComparison.InvariantCultureIgnoreCase;
|
|
||||||
var pairs = value.SplitHeaderValue (',', ';').ToList ();
|
|
||||||
|
|
||||||
for (var i = 0; i < pairs.Count; i++) {
|
|
||||||
var pair = pairs[i].Trim ();
|
|
||||||
if (pair.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var idx = pair.IndexOf ('=');
|
|
||||||
if (idx == -1) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (pair.Equals ("port", caseInsensitive)) {
|
|
||||||
cookie.Port = "\"\"";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pair.Equals ("discard", caseInsensitive)) {
|
|
||||||
cookie.Discard = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pair.Equals ("secure", caseInsensitive)) {
|
|
||||||
cookie.Secure = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pair.Equals ("httponly", caseInsensitive)) {
|
|
||||||
cookie.HttpOnly = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx == 0) {
|
|
||||||
if (cookie != null) {
|
|
||||||
ret.add (cookie);
|
|
||||||
cookie = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = pair.Substring (0, idx).TrimEnd (' ');
|
|
||||||
var val = idx < pair.Length - 1
|
|
||||||
? pair.Substring (idx + 1).TrimStart (' ')
|
|
||||||
: String.Empty;
|
|
||||||
|
|
||||||
if (name.Equals ("version", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int num;
|
|
||||||
if (!Int32.TryParse (val.Unquote (), out num))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.Version = num;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("expires", caseInsensitive)) {
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (i == pairs.Count - 1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
i++;
|
|
||||||
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (cookie.Expires != DateTime.MinValue)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var buff = new StringBuilder (val, 32);
|
|
||||||
buff.AppendFormat (", {0}", pairs[i].Trim ());
|
|
||||||
|
|
||||||
DateTime expires;
|
|
||||||
if (
|
|
||||||
!DateTime.TryParseExact (
|
|
||||||
buff.ToString (),
|
|
||||||
new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" },
|
|
||||||
CultureInfo.CreateSpecificCulture ("en-US"),
|
|
||||||
DateTimeStyles.AdjustToUniversal
|
|
||||||
| DateTimeStyles.AssumeUniversal,
|
|
||||||
out expires
|
|
||||||
)
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.Expires = expires.ToLocalTime ();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("max-age", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int num;
|
|
||||||
if (!Int32.TryParse (val.Unquote (), out num))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.MaxAge = num;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("path", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.Path = val;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("domain", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.Domain = val;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("port", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.Port = val;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("comment", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.Comment = urlDecode (val, Encoding.UTF8);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("commenturl", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.CommentUri = val.Unquote ().ToUri ();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Equals ("samesite", caseInsensitive)) {
|
|
||||||
if (cookie == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cookie.SameSite = val.Unquote ();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookie != null)
|
|
||||||
ret.add (cookie);
|
|
||||||
|
|
||||||
Cookie.TryCreate (name, val, out cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookie != null)
|
|
||||||
ret.add (cookie);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int search (Cookie cookie)
|
|
||||||
{
|
|
||||||
for (var i = _list.Count - 1; i >= 0; i--) {
|
|
||||||
if (_list[i].EqualsWithoutValue (cookie))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string urlDecode (string s, Encoding encoding)
|
|
||||||
{
|
|
||||||
if (s.IndexOfAny (new[] { '%', '+' }) == -1)
|
|
||||||
return s;
|
|
||||||
|
|
||||||
try {
|
|
||||||
return HttpUtility.UrlDecode (s, encoding);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal static CookieCollection Parse (string value, bool response)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return response
|
|
||||||
? parseResponse (value)
|
|
||||||
: parseRequest (value);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new CookieException ("It could not be parsed.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetOrRemove (Cookie cookie)
|
|
||||||
{
|
|
||||||
var idx = search (cookie);
|
|
||||||
if (idx == -1) {
|
|
||||||
if (cookie.Expired)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_list.Add (cookie);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookie.Expired) {
|
|
||||||
_list.RemoveAt (idx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_list[idx] = cookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetOrRemove (CookieCollection cookies)
|
|
||||||
{
|
|
||||||
foreach (var cookie in cookies._list)
|
|
||||||
SetOrRemove (cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Sort ()
|
|
||||||
{
|
|
||||||
if (_list.Count > 1)
|
|
||||||
_list.Sort (compareForSort);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds the specified cookie to the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cookie">
|
|
||||||
/// A <see cref="Cookie"/> to add.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// The collection is read-only.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="cookie"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
public void Add (Cookie cookie)
|
|
||||||
{
|
|
||||||
if (_readOnly) {
|
|
||||||
var msg = "The collection is read-only.";
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookie == null)
|
|
||||||
throw new ArgumentNullException ("cookie");
|
|
||||||
|
|
||||||
add (cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds the specified cookies to the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cookies">
|
|
||||||
/// A <see cref="CookieCollection"/> that contains the cookies to add.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// The collection is read-only.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="cookies"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
public void Add (CookieCollection cookies)
|
|
||||||
{
|
|
||||||
if (_readOnly) {
|
|
||||||
var msg = "The collection is read-only.";
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookies == null)
|
|
||||||
throw new ArgumentNullException ("cookies");
|
|
||||||
|
|
||||||
foreach (var cookie in cookies._list)
|
|
||||||
add (cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all cookies from the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// The collection is read-only.
|
|
||||||
/// </exception>
|
|
||||||
public void Clear ()
|
|
||||||
{
|
|
||||||
if (_readOnly) {
|
|
||||||
var msg = "The collection is read-only.";
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
_list.Clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the collection contains the specified cookie.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the cookie is found in the collection; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="cookie">
|
|
||||||
/// A <see cref="Cookie"/> to find.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="cookie"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
public bool Contains (Cookie cookie)
|
|
||||||
{
|
|
||||||
if (cookie == null)
|
|
||||||
throw new ArgumentNullException ("cookie");
|
|
||||||
|
|
||||||
return search (cookie) > -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Copies the elements of the collection to the specified array,
|
|
||||||
/// starting at the specified index.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="array">
|
|
||||||
/// An array of <see cref="Cookie"/> that specifies the destination of
|
|
||||||
/// the elements copied from the collection.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="index">
|
|
||||||
/// An <see cref="int"/> that specifies the zero-based index in
|
|
||||||
/// the array at which copying starts.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="array"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||||
/// <paramref name="index"/> is less than zero.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// The space from <paramref name="index"/> to the end of
|
|
||||||
/// <paramref name="array"/> is not enough to copy to.
|
|
||||||
/// </exception>
|
|
||||||
public void CopyTo (Cookie[] array, int index)
|
|
||||||
{
|
|
||||||
if (array == null)
|
|
||||||
throw new ArgumentNullException ("array");
|
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
throw new ArgumentOutOfRangeException ("index", "Less than zero.");
|
|
||||||
|
|
||||||
if (array.Length - index < _list.Count) {
|
|
||||||
var msg = "The available space of the array is not enough to copy to.";
|
|
||||||
throw new ArgumentException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
_list.CopyTo (array, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the enumerator that iterates through the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// An <see cref="T:System.Collections.Generic.IEnumerator{Cookie}"/>
|
|
||||||
/// instance that can be used to iterate through the collection.
|
|
||||||
/// </returns>
|
|
||||||
public IEnumerator<Cookie> GetEnumerator ()
|
|
||||||
{
|
|
||||||
return _list.GetEnumerator ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the specified cookie from the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// <para>
|
|
||||||
/// <c>true</c> if the cookie is successfully removed; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <c>false</c> if the cookie is not found in the collection.
|
|
||||||
/// </para>
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="cookie">
|
|
||||||
/// A <see cref="Cookie"/> to remove.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// The collection is read-only.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="cookie"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
public bool Remove (Cookie cookie)
|
|
||||||
{
|
|
||||||
if (_readOnly) {
|
|
||||||
var msg = "The collection is read-only.";
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookie == null)
|
|
||||||
throw new ArgumentNullException ("cookie");
|
|
||||||
|
|
||||||
var idx = search (cookie);
|
|
||||||
if (idx == -1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_list.RemoveAt (idx);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Explicit Interface Implementations
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the enumerator that iterates through the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// An <see cref="IEnumerator"/> instance that can be used to iterate
|
|
||||||
/// through the collection.
|
|
||||||
/// </returns>
|
|
||||||
IEnumerator IEnumerable.GetEnumerator ()
|
|
||||||
{
|
|
||||||
return _list.GetEnumerator ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* CookieException.cs
|
|
||||||
*
|
|
||||||
* This code is derived from CookieException.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2019 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Lawrence Pit <loz@cable.a2000.nl>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Security.Permissions;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The exception that is thrown when a <see cref="Cookie"/> gets an error.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class CookieException : FormatException, ISerializable
|
|
||||||
{
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal CookieException (string message)
|
|
||||||
: base (message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal CookieException (string message, Exception innerException)
|
|
||||||
: base (message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="CookieException"/> class
|
|
||||||
/// with the serialized data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serializationInfo">
|
|
||||||
/// A <see cref="SerializationInfo"/> that holds the serialized object data.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="streamingContext">
|
|
||||||
/// A <see cref="StreamingContext"/> that specifies the source for
|
|
||||||
/// the deserialization.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="serializationInfo"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
protected CookieException (
|
|
||||||
SerializationInfo serializationInfo, StreamingContext streamingContext
|
|
||||||
)
|
|
||||||
: base (serializationInfo, streamingContext)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="CookieException"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public CookieException ()
|
|
||||||
: base ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Populates the specified <see cref="SerializationInfo"/> instance with
|
|
||||||
/// the data needed to serialize the current instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serializationInfo">
|
|
||||||
/// A <see cref="SerializationInfo"/> that holds the serialized object data.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="streamingContext">
|
|
||||||
/// A <see cref="StreamingContext"/> that specifies the destination for
|
|
||||||
/// the serialization.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="serializationInfo"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
[
|
|
||||||
SecurityPermission (
|
|
||||||
SecurityAction.LinkDemand,
|
|
||||||
Flags = SecurityPermissionFlag.SerializationFormatter
|
|
||||||
)
|
|
||||||
]
|
|
||||||
public override void GetObjectData (
|
|
||||||
SerializationInfo serializationInfo, StreamingContext streamingContext
|
|
||||||
)
|
|
||||||
{
|
|
||||||
base.GetObjectData (serializationInfo, streamingContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Explicit Interface Implementation
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Populates the specified <see cref="SerializationInfo"/> instance with
|
|
||||||
/// the data needed to serialize the current instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serializationInfo">
|
|
||||||
/// A <see cref="SerializationInfo"/> that holds the serialized object data.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="streamingContext">
|
|
||||||
/// A <see cref="StreamingContext"/> that specifies the destination for
|
|
||||||
/// the serialization.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="serializationInfo"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
[
|
|
||||||
SecurityPermission (
|
|
||||||
SecurityAction.LinkDemand,
|
|
||||||
Flags = SecurityPermissionFlag.SerializationFormatter,
|
|
||||||
SerializationFormatter = true
|
|
||||||
)
|
|
||||||
]
|
|
||||||
void ISerializable.GetObjectData (
|
|
||||||
SerializationInfo serializationInfo, StreamingContext streamingContext
|
|
||||||
)
|
|
||||||
{
|
|
||||||
base.GetObjectData (serializationInfo, streamingContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,582 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* EndPointListener.cs
|
|
||||||
*
|
|
||||||
* This code is derived from EndPointListener.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - Liryna <liryna.stark@gmail.com>
|
|
||||||
* - Nicholas Devenish
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal sealed class EndPointListener
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private List<HttpListenerPrefix> _all; // host == '+'
|
|
||||||
private Dictionary<HttpConnection, HttpConnection> _connections;
|
|
||||||
private object _connectionsSync;
|
|
||||||
private static readonly string _defaultCertFolderPath;
|
|
||||||
private IPEndPoint _endpoint;
|
|
||||||
private List<HttpListenerPrefix> _prefixes;
|
|
||||||
private bool _secure;
|
|
||||||
private Socket _socket;
|
|
||||||
private ServerSslConfiguration _sslConfig;
|
|
||||||
private List<HttpListenerPrefix> _unhandled; // host == '*'
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Static Constructor
|
|
||||||
|
|
||||||
static EndPointListener ()
|
|
||||||
{
|
|
||||||
_defaultCertFolderPath = Environment.GetFolderPath (
|
|
||||||
Environment.SpecialFolder.ApplicationData
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal EndPointListener (
|
|
||||||
IPEndPoint endpoint,
|
|
||||||
bool secure,
|
|
||||||
string certificateFolderPath,
|
|
||||||
ServerSslConfiguration sslConfig,
|
|
||||||
bool reuseAddress
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_endpoint = endpoint;
|
|
||||||
|
|
||||||
if (secure) {
|
|
||||||
var cert = getCertificate (
|
|
||||||
endpoint.Port,
|
|
||||||
certificateFolderPath,
|
|
||||||
sslConfig.ServerCertificate
|
|
||||||
);
|
|
||||||
|
|
||||||
if (cert == null) {
|
|
||||||
var msg = "No server certificate could be found.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
_secure = true;
|
|
||||||
_sslConfig = new ServerSslConfiguration (sslConfig);
|
|
||||||
_sslConfig.ServerCertificate = cert;
|
|
||||||
}
|
|
||||||
|
|
||||||
_prefixes = new List<HttpListenerPrefix> ();
|
|
||||||
_connections = new Dictionary<HttpConnection, HttpConnection> ();
|
|
||||||
_connectionsSync = ((ICollection) _connections).SyncRoot;
|
|
||||||
|
|
||||||
_socket = new Socket (
|
|
||||||
endpoint.Address.AddressFamily,
|
|
||||||
SocketType.Stream,
|
|
||||||
ProtocolType.Tcp
|
|
||||||
);
|
|
||||||
|
|
||||||
if (reuseAddress) {
|
|
||||||
_socket.SetSocketOption (
|
|
||||||
SocketOptionLevel.Socket,
|
|
||||||
SocketOptionName.ReuseAddress,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_socket.Bind (endpoint);
|
|
||||||
_socket.Listen (500);
|
|
||||||
_socket.BeginAccept (onAccept, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public IPAddress Address {
|
|
||||||
get {
|
|
||||||
return _endpoint.Address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSecure {
|
|
||||||
get {
|
|
||||||
return _secure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Port {
|
|
||||||
get {
|
|
||||||
return _endpoint.Port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServerSslConfiguration SslConfiguration {
|
|
||||||
get {
|
|
||||||
return _sslConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static void addSpecial (
|
|
||||||
List<HttpListenerPrefix> prefixes, HttpListenerPrefix prefix
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var path = prefix.Path;
|
|
||||||
|
|
||||||
foreach (var pref in prefixes) {
|
|
||||||
if (pref.Path == path) {
|
|
||||||
var msg = "The prefix is already in use.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (87, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prefixes.Add (prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearConnections ()
|
|
||||||
{
|
|
||||||
HttpConnection[] conns = null;
|
|
||||||
|
|
||||||
lock (_connectionsSync) {
|
|
||||||
var cnt = _connections.Count;
|
|
||||||
|
|
||||||
if (cnt == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
conns = new HttpConnection[cnt];
|
|
||||||
|
|
||||||
var vals = _connections.Values;
|
|
||||||
vals.CopyTo (conns, 0);
|
|
||||||
|
|
||||||
_connections.Clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var conn in conns)
|
|
||||||
conn.Close (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RSACryptoServiceProvider createRSAFromFile (string path)
|
|
||||||
{
|
|
||||||
var rsa = new RSACryptoServiceProvider ();
|
|
||||||
|
|
||||||
var key = File.ReadAllBytes (path);
|
|
||||||
rsa.ImportCspBlob (key);
|
|
||||||
|
|
||||||
return rsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static X509Certificate2 getCertificate (
|
|
||||||
int port, string folderPath, X509Certificate2 defaultCertificate
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (folderPath == null || folderPath.Length == 0)
|
|
||||||
folderPath = _defaultCertFolderPath;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var cer = Path.Combine (folderPath, String.Format ("{0}.cer", port));
|
|
||||||
var key = Path.Combine (folderPath, String.Format ("{0}.key", port));
|
|
||||||
|
|
||||||
if (File.Exists (cer) && File.Exists (key)) {
|
|
||||||
var cert = new X509Certificate2 (cer);
|
|
||||||
cert.PrivateKey = createRSAFromFile (key);
|
|
||||||
|
|
||||||
return cert;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultCertificate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void leaveIfNoPrefix ()
|
|
||||||
{
|
|
||||||
if (_prefixes.Count > 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var prefs = _unhandled;
|
|
||||||
|
|
||||||
if (prefs != null && prefs.Count > 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
prefs = _all;
|
|
||||||
|
|
||||||
if (prefs != null && prefs.Count > 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void onAccept (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
var lsnr = (EndPointListener) asyncResult.AsyncState;
|
|
||||||
|
|
||||||
Socket sock = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
sock = lsnr._socket.EndAccept (asyncResult);
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Exception) {
|
|
||||||
// TODO: Logging.
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
lsnr._socket.BeginAccept (onAccept, lsnr);
|
|
||||||
}
|
|
||||||
catch (Exception) {
|
|
||||||
// TODO: Logging.
|
|
||||||
|
|
||||||
if (sock != null)
|
|
||||||
sock.Close ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sock == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
processAccepted (sock, lsnr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void processAccepted (
|
|
||||||
Socket socket, EndPointListener listener
|
|
||||||
)
|
|
||||||
{
|
|
||||||
HttpConnection conn = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
conn = new HttpConnection (socket, listener);
|
|
||||||
}
|
|
||||||
catch (Exception) {
|
|
||||||
// TODO: Logging.
|
|
||||||
|
|
||||||
socket.Close ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (listener._connectionsSync)
|
|
||||||
listener._connections.Add (conn, conn);
|
|
||||||
|
|
||||||
conn.BeginReadRequest ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool removeSpecial (
|
|
||||||
List<HttpListenerPrefix> prefixes, HttpListenerPrefix prefix
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var path = prefix.Path;
|
|
||||||
var cnt = prefixes.Count;
|
|
||||||
|
|
||||||
for (var i = 0; i < cnt; i++) {
|
|
||||||
if (prefixes[i].Path == path) {
|
|
||||||
prefixes.RemoveAt (i);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HttpListener searchHttpListenerFromSpecial (
|
|
||||||
string path, List<HttpListenerPrefix> prefixes
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (prefixes == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
HttpListener ret = null;
|
|
||||||
|
|
||||||
var bestLen = -1;
|
|
||||||
|
|
||||||
foreach (var pref in prefixes) {
|
|
||||||
var prefPath = pref.Path;
|
|
||||||
var len = prefPath.Length;
|
|
||||||
|
|
||||||
if (len < bestLen)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (path.StartsWith (prefPath, StringComparison.Ordinal)) {
|
|
||||||
bestLen = len;
|
|
||||||
ret = pref.Listener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal static bool CertificateExists (int port, string folderPath)
|
|
||||||
{
|
|
||||||
if (folderPath == null || folderPath.Length == 0)
|
|
||||||
folderPath = _defaultCertFolderPath;
|
|
||||||
|
|
||||||
var cer = Path.Combine (folderPath, String.Format ("{0}.cer", port));
|
|
||||||
var key = Path.Combine (folderPath, String.Format ("{0}.key", port));
|
|
||||||
|
|
||||||
return File.Exists (cer) && File.Exists (key);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RemoveConnection (HttpConnection connection)
|
|
||||||
{
|
|
||||||
lock (_connectionsSync)
|
|
||||||
_connections.Remove (connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool TrySearchHttpListener (Uri uri, out HttpListener listener)
|
|
||||||
{
|
|
||||||
listener = null;
|
|
||||||
|
|
||||||
if (uri == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var host = uri.Host;
|
|
||||||
var dns = Uri.CheckHostName (host) == UriHostNameType.Dns;
|
|
||||||
var port = uri.Port.ToString ();
|
|
||||||
var path = HttpUtility.UrlDecode (uri.AbsolutePath);
|
|
||||||
|
|
||||||
if (path[path.Length - 1] != '/')
|
|
||||||
path += "/";
|
|
||||||
|
|
||||||
if (host != null && host.Length > 0) {
|
|
||||||
var prefs = _prefixes;
|
|
||||||
var bestLen = -1;
|
|
||||||
|
|
||||||
foreach (var pref in prefs) {
|
|
||||||
if (dns) {
|
|
||||||
var prefHost = pref.Host;
|
|
||||||
var prefDns = Uri.CheckHostName (prefHost) == UriHostNameType.Dns;
|
|
||||||
|
|
||||||
if (prefDns) {
|
|
||||||
if (prefHost != host)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pref.Port != port)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var prefPath = pref.Path;
|
|
||||||
var len = prefPath.Length;
|
|
||||||
|
|
||||||
if (len < bestLen)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (path.StartsWith (prefPath, StringComparison.Ordinal)) {
|
|
||||||
bestLen = len;
|
|
||||||
listener = pref.Listener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestLen != -1)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
listener = searchHttpListenerFromSpecial (path, _unhandled);
|
|
||||||
|
|
||||||
if (listener != null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
listener = searchHttpListenerFromSpecial (path, _all);
|
|
||||||
|
|
||||||
return listener != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public void AddPrefix (HttpListenerPrefix prefix)
|
|
||||||
{
|
|
||||||
List<HttpListenerPrefix> current, future;
|
|
||||||
|
|
||||||
if (prefix.Host == "*") {
|
|
||||||
do {
|
|
||||||
current = _unhandled;
|
|
||||||
future = current != null
|
|
||||||
? new List<HttpListenerPrefix> (current)
|
|
||||||
: new List<HttpListenerPrefix> ();
|
|
||||||
|
|
||||||
addSpecial (future, prefix);
|
|
||||||
}
|
|
||||||
while (
|
|
||||||
Interlocked.CompareExchange (ref _unhandled, future, current) != current
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefix.Host == "+") {
|
|
||||||
do {
|
|
||||||
current = _all;
|
|
||||||
future = current != null
|
|
||||||
? new List<HttpListenerPrefix> (current)
|
|
||||||
: new List<HttpListenerPrefix> ();
|
|
||||||
|
|
||||||
addSpecial (future, prefix);
|
|
||||||
}
|
|
||||||
while (
|
|
||||||
Interlocked.CompareExchange (ref _all, future, current) != current
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
current = _prefixes;
|
|
||||||
var idx = current.IndexOf (prefix);
|
|
||||||
|
|
||||||
if (idx > -1) {
|
|
||||||
if (current[idx].Listener != prefix.Listener) {
|
|
||||||
var msg = String.Format (
|
|
||||||
"There is another listener for {0}.", prefix
|
|
||||||
);
|
|
||||||
|
|
||||||
throw new HttpListenerException (87, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
future = new List<HttpListenerPrefix> (current);
|
|
||||||
future.Add (prefix);
|
|
||||||
}
|
|
||||||
while (
|
|
||||||
Interlocked.CompareExchange (ref _prefixes, future, current) != current
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Close ()
|
|
||||||
{
|
|
||||||
_socket.Close ();
|
|
||||||
|
|
||||||
clearConnections ();
|
|
||||||
EndPointManager.RemoveEndPoint (_endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemovePrefix (HttpListenerPrefix prefix)
|
|
||||||
{
|
|
||||||
List<HttpListenerPrefix> current, future;
|
|
||||||
|
|
||||||
if (prefix.Host == "*") {
|
|
||||||
do {
|
|
||||||
current = _unhandled;
|
|
||||||
|
|
||||||
if (current == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
future = new List<HttpListenerPrefix> (current);
|
|
||||||
|
|
||||||
if (!removeSpecial (future, prefix))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
while (
|
|
||||||
Interlocked.CompareExchange (ref _unhandled, future, current) != current
|
|
||||||
);
|
|
||||||
|
|
||||||
leaveIfNoPrefix ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefix.Host == "+") {
|
|
||||||
do {
|
|
||||||
current = _all;
|
|
||||||
|
|
||||||
if (current == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
future = new List<HttpListenerPrefix> (current);
|
|
||||||
|
|
||||||
if (!removeSpecial (future, prefix))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
while (
|
|
||||||
Interlocked.CompareExchange (ref _all, future, current) != current
|
|
||||||
);
|
|
||||||
|
|
||||||
leaveIfNoPrefix ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
current = _prefixes;
|
|
||||||
|
|
||||||
if (!current.Contains (prefix))
|
|
||||||
break;
|
|
||||||
|
|
||||||
future = new List<HttpListenerPrefix> (current);
|
|
||||||
future.Remove (prefix);
|
|
||||||
}
|
|
||||||
while (
|
|
||||||
Interlocked.CompareExchange (ref _prefixes, future, current) != current
|
|
||||||
);
|
|
||||||
|
|
||||||
leaveIfNoPrefix ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* EndPointManager.cs
|
|
||||||
*
|
|
||||||
* This code is derived from EndPointManager.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - Liryna <liryna.stark@gmail.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal sealed class EndPointManager
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private static readonly Dictionary<IPEndPoint, EndPointListener> _endpoints;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Static Constructor
|
|
||||||
|
|
||||||
static EndPointManager ()
|
|
||||||
{
|
|
||||||
_endpoints = new Dictionary<IPEndPoint, EndPointListener> ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Constructors
|
|
||||||
|
|
||||||
private EndPointManager ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static void addPrefix (string uriPrefix, HttpListener listener)
|
|
||||||
{
|
|
||||||
var pref = new HttpListenerPrefix (uriPrefix, listener);
|
|
||||||
|
|
||||||
var addr = convertToIPAddress (pref.Host);
|
|
||||||
|
|
||||||
if (addr == null) {
|
|
||||||
var msg = "The URI prefix includes an invalid host.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (87, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!addr.IsLocal ()) {
|
|
||||||
var msg = "The URI prefix includes an invalid host.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (87, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
int port;
|
|
||||||
|
|
||||||
if (!Int32.TryParse (pref.Port, out port)) {
|
|
||||||
var msg = "The URI prefix includes an invalid port.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (87, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!port.IsPortNumber ()) {
|
|
||||||
var msg = "The URI prefix includes an invalid port.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (87, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var path = pref.Path;
|
|
||||||
|
|
||||||
if (path.IndexOf ('%') != -1) {
|
|
||||||
var msg = "The URI prefix includes an invalid path.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (87, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.IndexOf ("//", StringComparison.Ordinal) != -1) {
|
|
||||||
var msg = "The URI prefix includes an invalid path.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (87, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var endpoint = new IPEndPoint (addr, port);
|
|
||||||
|
|
||||||
EndPointListener lsnr;
|
|
||||||
|
|
||||||
if (_endpoints.TryGetValue (endpoint, out lsnr)) {
|
|
||||||
if (lsnr.IsSecure ^ pref.IsSecure) {
|
|
||||||
var msg = "The URI prefix includes an invalid scheme.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (87, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lsnr = new EndPointListener (
|
|
||||||
endpoint,
|
|
||||||
pref.IsSecure,
|
|
||||||
listener.CertificateFolderPath,
|
|
||||||
listener.SslConfiguration,
|
|
||||||
listener.ReuseAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
_endpoints.Add (endpoint, lsnr);
|
|
||||||
}
|
|
||||||
|
|
||||||
lsnr.AddPrefix (pref);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IPAddress convertToIPAddress (string hostname)
|
|
||||||
{
|
|
||||||
if (hostname == "*")
|
|
||||||
return IPAddress.Any;
|
|
||||||
|
|
||||||
if (hostname == "+")
|
|
||||||
return IPAddress.Any;
|
|
||||||
|
|
||||||
return hostname.ToIPAddress ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void removePrefix (string uriPrefix, HttpListener listener)
|
|
||||||
{
|
|
||||||
var pref = new HttpListenerPrefix (uriPrefix, listener);
|
|
||||||
|
|
||||||
var addr = convertToIPAddress (pref.Host);
|
|
||||||
|
|
||||||
if (addr == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!addr.IsLocal ())
|
|
||||||
return;
|
|
||||||
|
|
||||||
int port;
|
|
||||||
|
|
||||||
if (!Int32.TryParse (pref.Port, out port))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!port.IsPortNumber ())
|
|
||||||
return;
|
|
||||||
|
|
||||||
var path = pref.Path;
|
|
||||||
|
|
||||||
if (path.IndexOf ('%') != -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (path.IndexOf ("//", StringComparison.Ordinal) != -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var endpoint = new IPEndPoint (addr, port);
|
|
||||||
|
|
||||||
EndPointListener lsnr;
|
|
||||||
|
|
||||||
if (!_endpoints.TryGetValue (endpoint, out lsnr))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (lsnr.IsSecure ^ pref.IsSecure)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lsnr.RemovePrefix (pref);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal static bool RemoveEndPoint (IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
lock (((ICollection) _endpoints).SyncRoot)
|
|
||||||
return _endpoints.Remove (endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public static void AddListener (HttpListener listener)
|
|
||||||
{
|
|
||||||
var added = new List<string> ();
|
|
||||||
|
|
||||||
lock (((ICollection) _endpoints).SyncRoot) {
|
|
||||||
try {
|
|
||||||
foreach (var pref in listener.Prefixes) {
|
|
||||||
addPrefix (pref, listener);
|
|
||||||
added.Add (pref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
foreach (var pref in added)
|
|
||||||
removePrefix (pref, listener);
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddPrefix (string uriPrefix, HttpListener listener)
|
|
||||||
{
|
|
||||||
lock (((ICollection) _endpoints).SyncRoot)
|
|
||||||
addPrefix (uriPrefix, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveListener (HttpListener listener)
|
|
||||||
{
|
|
||||||
lock (((ICollection) _endpoints).SyncRoot) {
|
|
||||||
foreach (var pref in listener.Prefixes)
|
|
||||||
removePrefix (pref, listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemovePrefix (string uriPrefix, HttpListener listener)
|
|
||||||
{
|
|
||||||
lock (((ICollection) _endpoints).SyncRoot)
|
|
||||||
removePrefix (uriPrefix, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpBasicIdentity.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpListenerBasicIdentity.cs (System.Net) of
|
|
||||||
* Mono (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2014-2017 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Security.Principal;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Holds the username and password from an HTTP Basic authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
public class HttpBasicIdentity : GenericIdentity
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private string _password;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpBasicIdentity (string username, string password)
|
|
||||||
: base (username, "Basic")
|
|
||||||
{
|
|
||||||
_password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the password from a basic authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the password.
|
|
||||||
/// </value>
|
|
||||||
public virtual string Password {
|
|
||||||
get {
|
|
||||||
return _password;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,571 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpConnection.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpConnection.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - Liryna <liryna.stark@gmail.com>
|
|
||||||
* - Rohan Singh <rohan-singh@hotmail.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Security;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal sealed class HttpConnection
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private int _attempts;
|
|
||||||
private byte[] _buffer;
|
|
||||||
private static readonly int _bufferLength;
|
|
||||||
private HttpListenerContext _context;
|
|
||||||
private StringBuilder _currentLine;
|
|
||||||
private InputState _inputState;
|
|
||||||
private RequestStream _inputStream;
|
|
||||||
private LineState _lineState;
|
|
||||||
private EndPointListener _listener;
|
|
||||||
private EndPoint _localEndPoint;
|
|
||||||
private static readonly int _maxInputLength;
|
|
||||||
private ResponseStream _outputStream;
|
|
||||||
private int _position;
|
|
||||||
private EndPoint _remoteEndPoint;
|
|
||||||
private MemoryStream _requestBuffer;
|
|
||||||
private int _reuses;
|
|
||||||
private bool _secure;
|
|
||||||
private Socket _socket;
|
|
||||||
private Stream _stream;
|
|
||||||
private object _sync;
|
|
||||||
private int _timeout;
|
|
||||||
private Dictionary<int, bool> _timeoutCanceled;
|
|
||||||
private Timer _timer;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Static Constructor
|
|
||||||
|
|
||||||
static HttpConnection ()
|
|
||||||
{
|
|
||||||
_bufferLength = 8192;
|
|
||||||
_maxInputLength = 32768;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpConnection (Socket socket, EndPointListener listener)
|
|
||||||
{
|
|
||||||
_socket = socket;
|
|
||||||
_listener = listener;
|
|
||||||
|
|
||||||
var netStream = new NetworkStream (socket, false);
|
|
||||||
|
|
||||||
if (listener.IsSecure) {
|
|
||||||
var sslConf = listener.SslConfiguration;
|
|
||||||
var sslStream = new SslStream (
|
|
||||||
netStream,
|
|
||||||
false,
|
|
||||||
sslConf.ClientCertificateValidationCallback
|
|
||||||
);
|
|
||||||
|
|
||||||
sslStream.AuthenticateAsServer (
|
|
||||||
sslConf.ServerCertificate,
|
|
||||||
sslConf.ClientCertificateRequired,
|
|
||||||
sslConf.EnabledSslProtocols,
|
|
||||||
sslConf.CheckCertificateRevocation
|
|
||||||
);
|
|
||||||
|
|
||||||
_secure = true;
|
|
||||||
_stream = sslStream;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_stream = netStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer = new byte[_bufferLength];
|
|
||||||
_localEndPoint = socket.LocalEndPoint;
|
|
||||||
_remoteEndPoint = socket.RemoteEndPoint;
|
|
||||||
_sync = new object ();
|
|
||||||
_timeoutCanceled = new Dictionary<int, bool> ();
|
|
||||||
_timer = new Timer (onTimeout, this, Timeout.Infinite, Timeout.Infinite);
|
|
||||||
|
|
||||||
init (90000); // 90k ms for first request, 15k ms from then on.
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public bool IsClosed {
|
|
||||||
get {
|
|
||||||
return _socket == null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsLocal {
|
|
||||||
get {
|
|
||||||
return ((IPEndPoint) _remoteEndPoint).Address.IsLocal ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSecure {
|
|
||||||
get {
|
|
||||||
return _secure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IPEndPoint LocalEndPoint {
|
|
||||||
get {
|
|
||||||
return (IPEndPoint) _localEndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IPEndPoint RemoteEndPoint {
|
|
||||||
get {
|
|
||||||
return (IPEndPoint) _remoteEndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Reuses {
|
|
||||||
get {
|
|
||||||
return _reuses;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream Stream {
|
|
||||||
get {
|
|
||||||
return _stream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private void close ()
|
|
||||||
{
|
|
||||||
lock (_sync) {
|
|
||||||
if (_socket == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
disposeTimer ();
|
|
||||||
disposeRequestBuffer ();
|
|
||||||
disposeStream ();
|
|
||||||
closeSocket ();
|
|
||||||
}
|
|
||||||
|
|
||||||
_context.Unregister ();
|
|
||||||
_listener.RemoveConnection (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void closeSocket ()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
_socket.Shutdown (SocketShutdown.Both);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
}
|
|
||||||
|
|
||||||
_socket.Close ();
|
|
||||||
|
|
||||||
_socket = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void disposeRequestBuffer ()
|
|
||||||
{
|
|
||||||
if (_requestBuffer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_requestBuffer.Dispose ();
|
|
||||||
|
|
||||||
_requestBuffer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void disposeStream ()
|
|
||||||
{
|
|
||||||
if (_stream == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_stream.Dispose ();
|
|
||||||
|
|
||||||
_stream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void disposeTimer ()
|
|
||||||
{
|
|
||||||
if (_timer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
_timer.Change (Timeout.Infinite, Timeout.Infinite);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
}
|
|
||||||
|
|
||||||
_timer.Dispose ();
|
|
||||||
|
|
||||||
_timer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init (int timeout)
|
|
||||||
{
|
|
||||||
_timeout = timeout;
|
|
||||||
|
|
||||||
_context = new HttpListenerContext (this);
|
|
||||||
_currentLine = new StringBuilder (64);
|
|
||||||
_inputState = InputState.RequestLine;
|
|
||||||
_inputStream = null;
|
|
||||||
_lineState = LineState.None;
|
|
||||||
_outputStream = null;
|
|
||||||
_position = 0;
|
|
||||||
_requestBuffer = new MemoryStream ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void onRead (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
var conn = (HttpConnection) asyncResult.AsyncState;
|
|
||||||
var current = conn._attempts;
|
|
||||||
|
|
||||||
if (conn._socket == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (conn._sync) {
|
|
||||||
if (conn._socket == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
conn._timer.Change (Timeout.Infinite, Timeout.Infinite);
|
|
||||||
conn._timeoutCanceled[current] = true;
|
|
||||||
|
|
||||||
var nread = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
nread = conn._stream.EndRead (asyncResult);
|
|
||||||
}
|
|
||||||
catch (Exception) {
|
|
||||||
// TODO: Logging.
|
|
||||||
|
|
||||||
conn.close ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nread <= 0) {
|
|
||||||
conn.close ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn._requestBuffer.Write (conn._buffer, 0, nread);
|
|
||||||
var len = (int) conn._requestBuffer.Length;
|
|
||||||
|
|
||||||
if (conn.processInput (conn._requestBuffer.GetBuffer (), len)) {
|
|
||||||
if (!conn._context.HasErrorMessage)
|
|
||||||
conn._context.Request.FinishInitialization ();
|
|
||||||
|
|
||||||
if (conn._context.HasErrorMessage) {
|
|
||||||
conn._context.SendError ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = conn._context.Request.Url;
|
|
||||||
HttpListener lsnr;
|
|
||||||
|
|
||||||
if (conn._listener.TrySearchHttpListener (url, out lsnr)) {
|
|
||||||
if (!lsnr.AuthenticateContext (conn._context))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!lsnr.RegisterContext (conn._context)) {
|
|
||||||
conn._context.ErrorStatusCode = 503;
|
|
||||||
conn._context.SendError ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn._context.ErrorStatusCode = 404;
|
|
||||||
conn._context.SendError ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.BeginReadRequest ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void onTimeout (object state)
|
|
||||||
{
|
|
||||||
var conn = (HttpConnection) state;
|
|
||||||
var current = conn._attempts;
|
|
||||||
|
|
||||||
if (conn._socket == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (conn._sync) {
|
|
||||||
if (conn._socket == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (conn._timeoutCanceled[current])
|
|
||||||
return;
|
|
||||||
|
|
||||||
conn._context.ErrorStatusCode = 408;
|
|
||||||
conn._context.SendError ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool processInput (byte[] data, int length)
|
|
||||||
{
|
|
||||||
// This method returns a bool:
|
|
||||||
// - true Done processing
|
|
||||||
// - false Need more input
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (true) {
|
|
||||||
int nread;
|
|
||||||
var line = readLineFrom (data, _position, length, out nread);
|
|
||||||
|
|
||||||
_position += nread;
|
|
||||||
|
|
||||||
if (line == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (line.Length == 0) {
|
|
||||||
if (_inputState == InputState.RequestLine)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (_position > _maxInputLength)
|
|
||||||
_context.ErrorMessage = "Headers too long";
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_inputState == InputState.RequestLine) {
|
|
||||||
_context.Request.SetRequestLine (line);
|
|
||||||
_inputState = InputState.Headers;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_context.Request.AddHeader (line);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_context.HasErrorMessage)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
_context.ErrorMessage = ex.Message;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_position >= _maxInputLength) {
|
|
||||||
_context.ErrorMessage = "Headers too long";
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string readLineFrom (
|
|
||||||
byte[] buffer, int offset, int length, out int nread
|
|
||||||
)
|
|
||||||
{
|
|
||||||
nread = 0;
|
|
||||||
|
|
||||||
for (var i = offset; i < length; i++) {
|
|
||||||
nread++;
|
|
||||||
|
|
||||||
var b = buffer[i];
|
|
||||||
|
|
||||||
if (b == 13) {
|
|
||||||
_lineState = LineState.Cr;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b == 10) {
|
|
||||||
_lineState = LineState.Lf;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentLine.Append ((char) b);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_lineState != LineState.Lf)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var ret = _currentLine.ToString ();
|
|
||||||
|
|
||||||
_currentLine.Length = 0;
|
|
||||||
_lineState = LineState.None;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal void BeginReadRequest ()
|
|
||||||
{
|
|
||||||
_attempts++;
|
|
||||||
|
|
||||||
_timeoutCanceled.Add (_attempts, false);
|
|
||||||
_timer.Change (_timeout, Timeout.Infinite);
|
|
||||||
|
|
||||||
try {
|
|
||||||
_stream.BeginRead (_buffer, 0, _bufferLength, onRead, this);
|
|
||||||
}
|
|
||||||
catch (Exception) {
|
|
||||||
// TODO: Logging.
|
|
||||||
|
|
||||||
close ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Close (bool force)
|
|
||||||
{
|
|
||||||
if (_socket == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (_sync) {
|
|
||||||
if (_socket == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (force) {
|
|
||||||
if (_outputStream != null)
|
|
||||||
_outputStream.Close (true);
|
|
||||||
|
|
||||||
close ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetResponseStream ().Close (false);
|
|
||||||
|
|
||||||
if (_context.Response.CloseConnection) {
|
|
||||||
close ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_context.Request.FlushInput ()) {
|
|
||||||
close ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
disposeRequestBuffer ();
|
|
||||||
_context.Unregister ();
|
|
||||||
|
|
||||||
_reuses++;
|
|
||||||
|
|
||||||
init (15000);
|
|
||||||
BeginReadRequest ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public void Close ()
|
|
||||||
{
|
|
||||||
Close (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RequestStream GetRequestStream (long contentLength, bool chunked)
|
|
||||||
{
|
|
||||||
lock (_sync) {
|
|
||||||
if (_socket == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (_inputStream != null)
|
|
||||||
return _inputStream;
|
|
||||||
|
|
||||||
var buff = _requestBuffer.GetBuffer ();
|
|
||||||
var len = (int) _requestBuffer.Length;
|
|
||||||
var cnt = len - _position;
|
|
||||||
|
|
||||||
disposeRequestBuffer ();
|
|
||||||
|
|
||||||
_inputStream = chunked
|
|
||||||
? new ChunkedRequestStream (
|
|
||||||
_stream, buff, _position, cnt, _context
|
|
||||||
)
|
|
||||||
: new RequestStream (
|
|
||||||
_stream, buff, _position, cnt, contentLength
|
|
||||||
);
|
|
||||||
|
|
||||||
return _inputStream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResponseStream GetResponseStream ()
|
|
||||||
{
|
|
||||||
lock (_sync) {
|
|
||||||
if (_socket == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (_outputStream != null)
|
|
||||||
return _outputStream;
|
|
||||||
|
|
||||||
var lsnr = _context.Listener;
|
|
||||||
var ignore = lsnr != null ? lsnr.IgnoreWriteExceptions : true;
|
|
||||||
_outputStream = new ResponseStream (_stream, _context.Response, ignore);
|
|
||||||
|
|
||||||
return _outputStream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpDigestIdentity.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014-2017 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Security.Principal;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Holds the username and other parameters from
|
|
||||||
/// an HTTP Digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
public class HttpDigestIdentity : GenericIdentity
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private NameValueCollection _parameters;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpDigestIdentity (NameValueCollection parameters)
|
|
||||||
: base (parameters["username"], "Digest")
|
|
||||||
{
|
|
||||||
_parameters = parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the algorithm parameter from a digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the algorithm parameter.
|
|
||||||
/// </value>
|
|
||||||
public string Algorithm {
|
|
||||||
get {
|
|
||||||
return _parameters["algorithm"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the cnonce parameter from a digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the cnonce parameter.
|
|
||||||
/// </value>
|
|
||||||
public string Cnonce {
|
|
||||||
get {
|
|
||||||
return _parameters["cnonce"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the nc parameter from a digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the nc parameter.
|
|
||||||
/// </value>
|
|
||||||
public string Nc {
|
|
||||||
get {
|
|
||||||
return _parameters["nc"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the nonce parameter from a digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the nonce parameter.
|
|
||||||
/// </value>
|
|
||||||
public string Nonce {
|
|
||||||
get {
|
|
||||||
return _parameters["nonce"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the opaque parameter from a digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the opaque parameter.
|
|
||||||
/// </value>
|
|
||||||
public string Opaque {
|
|
||||||
get {
|
|
||||||
return _parameters["opaque"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the qop parameter from a digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the qop parameter.
|
|
||||||
/// </value>
|
|
||||||
public string Qop {
|
|
||||||
get {
|
|
||||||
return _parameters["qop"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the realm parameter from a digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the realm parameter.
|
|
||||||
/// </value>
|
|
||||||
public string Realm {
|
|
||||||
get {
|
|
||||||
return _parameters["realm"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the response parameter from a digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the response parameter.
|
|
||||||
/// </value>
|
|
||||||
public string Response {
|
|
||||||
get {
|
|
||||||
return _parameters["response"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the uri parameter from a digest authentication attempt.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the uri parameter.
|
|
||||||
/// </value>
|
|
||||||
public string Uri {
|
|
||||||
get {
|
|
||||||
return _parameters["uri"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal bool IsValid (
|
|
||||||
string password, string realm, string method, string entity
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var copied = new NameValueCollection (_parameters);
|
|
||||||
copied["password"] = password;
|
|
||||||
copied["realm"] = realm;
|
|
||||||
copied["method"] = method;
|
|
||||||
copied["entity"] = entity;
|
|
||||||
|
|
||||||
var expected = AuthenticationResponse.CreateRequestDigest (copied);
|
|
||||||
return _parameters["response"] == expected;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpHeaderInfo.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class HttpHeaderInfo
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private string _headerName;
|
|
||||||
private HttpHeaderType _headerType;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpHeaderInfo (string headerName, HttpHeaderType headerType)
|
|
||||||
{
|
|
||||||
_headerName = headerName;
|
|
||||||
_headerType = headerType;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal bool IsMultiValueInRequest {
|
|
||||||
get {
|
|
||||||
var headerType = _headerType & HttpHeaderType.MultiValueInRequest;
|
|
||||||
|
|
||||||
return headerType == HttpHeaderType.MultiValueInRequest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool IsMultiValueInResponse {
|
|
||||||
get {
|
|
||||||
var headerType = _headerType & HttpHeaderType.MultiValueInResponse;
|
|
||||||
|
|
||||||
return headerType == HttpHeaderType.MultiValueInResponse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public string HeaderName {
|
|
||||||
get {
|
|
||||||
return _headerName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpHeaderType HeaderType {
|
|
||||||
get {
|
|
||||||
return _headerType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsRequest {
|
|
||||||
get {
|
|
||||||
var headerType = _headerType & HttpHeaderType.Request;
|
|
||||||
|
|
||||||
return headerType == HttpHeaderType.Request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsResponse {
|
|
||||||
get {
|
|
||||||
var headerType = _headerType & HttpHeaderType.Response;
|
|
||||||
|
|
||||||
return headerType == HttpHeaderType.Response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public bool IsMultiValue (bool response)
|
|
||||||
{
|
|
||||||
var headerType = _headerType & HttpHeaderType.MultiValue;
|
|
||||||
|
|
||||||
if (headerType != HttpHeaderType.MultiValue)
|
|
||||||
return response ? IsMultiValueInResponse : IsMultiValueInRequest;
|
|
||||||
|
|
||||||
return response ? IsResponse : IsRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsRestricted (bool response)
|
|
||||||
{
|
|
||||||
var headerType = _headerType & HttpHeaderType.Restricted;
|
|
||||||
|
|
||||||
if (headerType != HttpHeaderType.Restricted)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return response ? IsResponse : IsRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpHeaderType.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2014 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
internal enum HttpHeaderType
|
|
||||||
{
|
|
||||||
Unspecified = 0,
|
|
||||||
Request = 1,
|
|
||||||
Response = 1 << 1,
|
|
||||||
Restricted = 1 << 2,
|
|
||||||
MultiValue = 1 << 3,
|
|
||||||
MultiValueInRequest = 1 << 4,
|
|
||||||
MultiValueInResponse = 1 << 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,973 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpListener.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpListener.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - Liryna <liryna.stark@gmail.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
// TODO: Logging.
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a simple, programmatically controlled HTTP listener.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class HttpListener : IDisposable
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private AuthenticationSchemes _authSchemes;
|
|
||||||
private Func<HttpListenerRequest, AuthenticationSchemes> _authSchemeSelector;
|
|
||||||
private string _certFolderPath;
|
|
||||||
private Queue<HttpListenerContext> _contextQueue;
|
|
||||||
private LinkedList<HttpListenerContext> _contextRegistry;
|
|
||||||
private object _contextRegistrySync;
|
|
||||||
private static readonly string _defaultRealm;
|
|
||||||
private bool _disposed;
|
|
||||||
private bool _ignoreWriteExceptions;
|
|
||||||
private volatile bool _listening;
|
|
||||||
private Logger _log;
|
|
||||||
private string _objectName;
|
|
||||||
private HttpListenerPrefixCollection _prefixes;
|
|
||||||
private string _realm;
|
|
||||||
private bool _reuseAddress;
|
|
||||||
private ServerSslConfiguration _sslConfig;
|
|
||||||
private Func<IIdentity, NetworkCredential> _userCredFinder;
|
|
||||||
private Queue<HttpListenerAsyncResult> _waitQueue;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Static Constructor
|
|
||||||
|
|
||||||
static HttpListener ()
|
|
||||||
{
|
|
||||||
_defaultRealm = "SECRET AREA";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="HttpListener"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public HttpListener ()
|
|
||||||
{
|
|
||||||
_authSchemes = AuthenticationSchemes.Anonymous;
|
|
||||||
_contextQueue = new Queue<HttpListenerContext> ();
|
|
||||||
|
|
||||||
_contextRegistry = new LinkedList<HttpListenerContext> ();
|
|
||||||
_contextRegistrySync = ((ICollection) _contextRegistry).SyncRoot;
|
|
||||||
|
|
||||||
_log = new Logger ();
|
|
||||||
_objectName = GetType ().ToString ();
|
|
||||||
_prefixes = new HttpListenerPrefixCollection (this);
|
|
||||||
_waitQueue = new Queue<HttpListenerAsyncResult> ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal bool ReuseAddress {
|
|
||||||
get {
|
|
||||||
return _reuseAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_reuseAddress = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the scheme used to authenticate the clients.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// One of the <see cref="WebSocketSharp.Net.AuthenticationSchemes"/>
|
|
||||||
/// enum values.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It represents the scheme used to authenticate the clients.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is
|
|
||||||
/// <see cref="WebSocketSharp.Net.AuthenticationSchemes.Anonymous"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public AuthenticationSchemes AuthenticationSchemes {
|
|
||||||
get {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
return _authSchemes;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
_authSchemes = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the delegate called to select the scheme used to
|
|
||||||
/// authenticate the clients.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// If this property is set, the listener uses the authentication
|
|
||||||
/// scheme selected by the delegate for each request.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Or if this property is not set, the listener uses the value of
|
|
||||||
/// the <see cref="HttpListener.AuthenticationSchemes"/> property
|
|
||||||
/// as the authentication scheme for all requests.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <c>Func<<see cref="HttpListenerRequest"/>,
|
|
||||||
/// <see cref="AuthenticationSchemes"/>></c> delegate or
|
|
||||||
/// <see langword="null"/> if not needed.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The delegate references the method used to select
|
|
||||||
/// an authentication scheme.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <see langword="null"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public Func<HttpListenerRequest, AuthenticationSchemes> AuthenticationSchemeSelector {
|
|
||||||
get {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
return _authSchemeSelector;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
_authSchemeSelector = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the path to the folder in which stores the certificate
|
|
||||||
/// files used to authenticate the server on the secure connection.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// This property represents the path to the folder in which stores
|
|
||||||
/// the certificate files associated with each port number of added
|
|
||||||
/// URI prefixes.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// A set of the certificate files is a pair of <port number>.cer
|
|
||||||
/// (DER) and <port number>.key (DER, RSA Private Key).
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If this property is <see langword="null"/> or an empty string,
|
|
||||||
/// the result of <c>System.Environment.GetFolderPath (<see
|
|
||||||
/// cref="Environment.SpecialFolder.ApplicationData"/>)</c>
|
|
||||||
/// is used as the default path.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the path to the folder
|
|
||||||
/// in which stores the certificate files.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <see langword="null"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public string CertificateFolderPath {
|
|
||||||
get {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
return _certFolderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
_certFolderPath = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the listener returns
|
|
||||||
/// exceptions that occur when sending the response to the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// <c>true</c> if the listener should not return those exceptions;
|
|
||||||
/// otherwise, <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public bool IgnoreWriteExceptions {
|
|
||||||
get {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
return _ignoreWriteExceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
_ignoreWriteExceptions = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the listener has been started.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the listener has been started; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsListening {
|
|
||||||
get {
|
|
||||||
return _listening;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the listener can be used with
|
|
||||||
/// the current operating system.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c>.
|
|
||||||
/// </value>
|
|
||||||
public static bool IsSupported {
|
|
||||||
get {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the logging functions.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// The default logging level is <see cref="LogLevel.Error"/>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If you would like to change it, you should set the <c>Log.Level</c>
|
|
||||||
/// property to any of the <see cref="LogLevel"/> enum values.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="Logger"/> that provides the logging functions.
|
|
||||||
/// </value>
|
|
||||||
public Logger Log {
|
|
||||||
get {
|
|
||||||
return _log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the URI prefixes handled by the listener.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="HttpListenerPrefixCollection"/> that contains the URI
|
|
||||||
/// prefixes.
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public HttpListenerPrefixCollection Prefixes {
|
|
||||||
get {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
return _prefixes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name of the realm associated with the listener.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If this property is <see langword="null"/> or an empty string,
|
|
||||||
/// "SECRET AREA" will be used as the name of the realm.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the name of the realm.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <see langword="null"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public string Realm {
|
|
||||||
get {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
return _realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
_realm = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the SSL configuration used to authenticate the server and
|
|
||||||
/// optionally the client for secure connection.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="ServerSslConfiguration"/> that represents the SSL
|
|
||||||
/// configuration for secure connection.
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public ServerSslConfiguration SslConfiguration {
|
|
||||||
get {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
if (_sslConfig == null)
|
|
||||||
_sslConfig = new ServerSslConfiguration ();
|
|
||||||
|
|
||||||
return _sslConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether, when NTLM authentication is used,
|
|
||||||
/// the authentication information of first request is used to authenticate
|
|
||||||
/// additional requests on the same connection.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This property is not currently supported and always throws
|
|
||||||
/// a <see cref="NotSupportedException"/>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the authentication information of first request is used;
|
|
||||||
/// otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="NotSupportedException">
|
|
||||||
/// Any use of this property.
|
|
||||||
/// </exception>
|
|
||||||
public bool UnsafeConnectionNtlmAuthentication {
|
|
||||||
get {
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the delegate called to find the credentials for
|
|
||||||
/// an identity used to authenticate a client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <c>Func<<see cref="IIdentity"/>,
|
|
||||||
/// <see cref="NetworkCredential"/>></c> delegate or
|
|
||||||
/// <see langword="null"/> if not needed.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It references the method used to find the credentials.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <see langword="null"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public Func<IIdentity, NetworkCredential> UserCredentialsFinder {
|
|
||||||
get {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
return _userCredFinder;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
_userCredFinder = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private HttpListenerAsyncResult beginGetContext (
|
|
||||||
AsyncCallback callback, object state
|
|
||||||
)
|
|
||||||
{
|
|
||||||
lock (_contextRegistrySync) {
|
|
||||||
if (!_listening) {
|
|
||||||
var msg = _disposed
|
|
||||||
? "The listener is closed."
|
|
||||||
: "The listener is stopped.";
|
|
||||||
|
|
||||||
throw new HttpListenerException (995, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ares = new HttpListenerAsyncResult (callback, state);
|
|
||||||
|
|
||||||
if (_contextQueue.Count == 0) {
|
|
||||||
_waitQueue.Enqueue (ares);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var ctx = _contextQueue.Dequeue ();
|
|
||||||
ares.Complete (ctx, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ares;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cleanupContextQueue (bool force)
|
|
||||||
{
|
|
||||||
if (_contextQueue.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (force) {
|
|
||||||
_contextQueue.Clear ();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ctxs = _contextQueue.ToArray ();
|
|
||||||
|
|
||||||
_contextQueue.Clear ();
|
|
||||||
|
|
||||||
foreach (var ctx in ctxs) {
|
|
||||||
ctx.ErrorStatusCode = 503;
|
|
||||||
ctx.SendError ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cleanupContextRegistry ()
|
|
||||||
{
|
|
||||||
var cnt = _contextRegistry.Count;
|
|
||||||
|
|
||||||
if (cnt == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ctxs = new HttpListenerContext[cnt];
|
|
||||||
_contextRegistry.CopyTo (ctxs, 0);
|
|
||||||
|
|
||||||
_contextRegistry.Clear ();
|
|
||||||
|
|
||||||
foreach (var ctx in ctxs)
|
|
||||||
ctx.Connection.Close (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cleanupWaitQueue (string message)
|
|
||||||
{
|
|
||||||
if (_waitQueue.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var aress = _waitQueue.ToArray ();
|
|
||||||
|
|
||||||
_waitQueue.Clear ();
|
|
||||||
|
|
||||||
foreach (var ares in aress) {
|
|
||||||
var ex = new HttpListenerException (995, message);
|
|
||||||
ares.Complete (ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close (bool force)
|
|
||||||
{
|
|
||||||
if (!_listening) {
|
|
||||||
_disposed = true;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_listening = false;
|
|
||||||
|
|
||||||
cleanupContextQueue (force);
|
|
||||||
cleanupContextRegistry ();
|
|
||||||
|
|
||||||
var msg = "The listener is closed.";
|
|
||||||
cleanupWaitQueue (msg);
|
|
||||||
|
|
||||||
EndPointManager.RemoveListener (this);
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string getRealm ()
|
|
||||||
{
|
|
||||||
var realm = _realm;
|
|
||||||
|
|
||||||
return realm != null && realm.Length > 0 ? realm : _defaultRealm;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthenticationSchemes selectAuthenticationScheme (
|
|
||||||
HttpListenerRequest request
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var selector = _authSchemeSelector;
|
|
||||||
|
|
||||||
if (selector == null)
|
|
||||||
return _authSchemes;
|
|
||||||
|
|
||||||
try {
|
|
||||||
return selector (request);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return AuthenticationSchemes.None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal bool AuthenticateContext (HttpListenerContext context)
|
|
||||||
{
|
|
||||||
var req = context.Request;
|
|
||||||
var schm = selectAuthenticationScheme (req);
|
|
||||||
|
|
||||||
if (schm == AuthenticationSchemes.Anonymous)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (schm == AuthenticationSchemes.None) {
|
|
||||||
context.ErrorStatusCode = 403;
|
|
||||||
context.ErrorMessage = "Authentication not allowed";
|
|
||||||
|
|
||||||
context.SendError ();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var realm = getRealm ();
|
|
||||||
var user = HttpUtility.CreateUser (
|
|
||||||
req.Headers["Authorization"],
|
|
||||||
schm,
|
|
||||||
realm,
|
|
||||||
req.HttpMethod,
|
|
||||||
_userCredFinder
|
|
||||||
);
|
|
||||||
|
|
||||||
var authenticated = user != null && user.Identity.IsAuthenticated;
|
|
||||||
|
|
||||||
if (!authenticated) {
|
|
||||||
context.SendAuthenticationChallenge (schm, realm);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.User = user;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void CheckDisposed ()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool RegisterContext (HttpListenerContext context)
|
|
||||||
{
|
|
||||||
if (!_listening)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
lock (_contextRegistrySync) {
|
|
||||||
if (!_listening)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
context.Listener = this;
|
|
||||||
|
|
||||||
_contextRegistry.AddLast (context);
|
|
||||||
|
|
||||||
if (_waitQueue.Count == 0) {
|
|
||||||
_contextQueue.Enqueue (context);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var ares = _waitQueue.Dequeue ();
|
|
||||||
ares.Complete (context, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UnregisterContext (HttpListenerContext context)
|
|
||||||
{
|
|
||||||
lock (_contextRegistrySync)
|
|
||||||
_contextRegistry.Remove (context);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shuts down the listener immediately.
|
|
||||||
/// </summary>
|
|
||||||
public void Abort ()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (_contextRegistrySync) {
|
|
||||||
if (_disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
close (true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Begins getting an incoming request asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// This asynchronous operation must be completed by calling
|
|
||||||
/// the EndGetContext method.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Typically, the EndGetContext method is called by
|
|
||||||
/// <paramref name="callback"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>
|
|
||||||
/// An <see cref="IAsyncResult"/> that represents the status of
|
|
||||||
/// the asynchronous operation.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="callback">
|
|
||||||
/// An <see cref="AsyncCallback"/> delegate that references the method to
|
|
||||||
/// invoke when the asynchronous operation completes.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="state">
|
|
||||||
/// An <see cref="object"/> that represents a user defined object to
|
|
||||||
/// pass to <paramref name="callback"/>.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// <para>
|
|
||||||
/// This listener has no URI prefix on which listens.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// This listener has not been started or is currently stopped.
|
|
||||||
/// </para>
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="HttpListenerException">
|
|
||||||
/// This method is canceled.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public IAsyncResult BeginGetContext (AsyncCallback callback, object state)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
if (_prefixes.Count == 0) {
|
|
||||||
var msg = "The listener has no URI prefix on which listens.";
|
|
||||||
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_listening) {
|
|
||||||
var msg = "The listener has not been started.";
|
|
||||||
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return beginGetContext (callback, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shuts down the listener.
|
|
||||||
/// </summary>
|
|
||||||
public void Close ()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (_contextRegistrySync) {
|
|
||||||
if (_disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
close (false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ends an asynchronous operation to get an incoming request.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method completes an asynchronous operation started by calling
|
|
||||||
/// the BeginGetContext method.
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="HttpListenerContext"/> that represents a request.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="asyncResult">
|
|
||||||
/// An <see cref="IAsyncResult"/> instance obtained by calling
|
|
||||||
/// the BeginGetContext method.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="asyncResult"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <paramref name="asyncResult"/> was not obtained by calling
|
|
||||||
/// the BeginGetContext method.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// This method was already called for <paramref name="asyncResult"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="HttpListenerException">
|
|
||||||
/// This method is canceled.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
if (asyncResult == null)
|
|
||||||
throw new ArgumentNullException ("asyncResult");
|
|
||||||
|
|
||||||
var ares = asyncResult as HttpListenerAsyncResult;
|
|
||||||
|
|
||||||
if (ares == null) {
|
|
||||||
var msg = "A wrong IAsyncResult instance.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "asyncResult");
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (ares.SyncRoot) {
|
|
||||||
if (ares.EndCalled) {
|
|
||||||
var msg = "This IAsyncResult instance cannot be reused.";
|
|
||||||
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
ares.EndCalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ares.IsCompleted)
|
|
||||||
ares.AsyncWaitHandle.WaitOne ();
|
|
||||||
|
|
||||||
return ares.Context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets an incoming request.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method waits for an incoming request and returns when a request is
|
|
||||||
/// received.
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="HttpListenerContext"/> that represents a request.
|
|
||||||
/// </returns>
|
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// <para>
|
|
||||||
/// This listener has no URI prefix on which listens.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// This listener has not been started or is currently stopped.
|
|
||||||
/// </para>
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="HttpListenerException">
|
|
||||||
/// This method is canceled.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public HttpListenerContext GetContext ()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
if (_prefixes.Count == 0) {
|
|
||||||
var msg = "The listener has no URI prefix on which listens.";
|
|
||||||
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_listening) {
|
|
||||||
var msg = "The listener has not been started.";
|
|
||||||
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ares = beginGetContext (null, null);
|
|
||||||
ares.EndCalled = true;
|
|
||||||
|
|
||||||
if (!ares.IsCompleted)
|
|
||||||
ares.AsyncWaitHandle.WaitOne ();
|
|
||||||
|
|
||||||
return ares.Context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Starts receiving incoming requests.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public void Start ()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
lock (_contextRegistrySync) {
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
if (_listening)
|
|
||||||
return;
|
|
||||||
|
|
||||||
EndPointManager.AddListener (this);
|
|
||||||
|
|
||||||
_listening = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops receiving incoming requests.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// This listener has been closed.
|
|
||||||
/// </exception>
|
|
||||||
public void Stop ()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException (_objectName);
|
|
||||||
|
|
||||||
lock (_contextRegistrySync) {
|
|
||||||
if (!_listening)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_listening = false;
|
|
||||||
|
|
||||||
cleanupContextQueue (false);
|
|
||||||
cleanupContextRegistry ();
|
|
||||||
|
|
||||||
var msg = "The listener is stopped.";
|
|
||||||
cleanupWaitQueue (msg);
|
|
||||||
|
|
||||||
EndPointManager.RemoveListener (this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Explicit Interface Implementations
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases all resources used by the listener.
|
|
||||||
/// </summary>
|
|
||||||
void IDisposable.Dispose ()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (_contextRegistrySync) {
|
|
||||||
if (_disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
close (true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpListenerAsyncResult.cs
|
|
||||||
*
|
|
||||||
* This code is derived from ListenerAsyncResult.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Ximian, Inc. (http://www.ximian.com)
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - Nicholas Devenish
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class HttpListenerAsyncResult : IAsyncResult
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private AsyncCallback _callback;
|
|
||||||
private bool _completed;
|
|
||||||
private bool _completedSynchronously;
|
|
||||||
private HttpListenerContext _context;
|
|
||||||
private bool _endCalled;
|
|
||||||
private Exception _exception;
|
|
||||||
private object _state;
|
|
||||||
private object _sync;
|
|
||||||
private ManualResetEvent _waitHandle;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpListenerAsyncResult (AsyncCallback callback, object state)
|
|
||||||
{
|
|
||||||
_callback = callback;
|
|
||||||
_state = state;
|
|
||||||
|
|
||||||
_sync = new object ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal HttpListenerContext Context
|
|
||||||
{
|
|
||||||
get {
|
|
||||||
if (_exception != null)
|
|
||||||
throw _exception;
|
|
||||||
|
|
||||||
return _context;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool EndCalled {
|
|
||||||
get {
|
|
||||||
return _endCalled;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_endCalled = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal object SyncRoot {
|
|
||||||
get {
|
|
||||||
return _sync;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public object AsyncState {
|
|
||||||
get {
|
|
||||||
return _state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public WaitHandle AsyncWaitHandle {
|
|
||||||
get {
|
|
||||||
lock (_sync) {
|
|
||||||
if (_waitHandle == null)
|
|
||||||
_waitHandle = new ManualResetEvent (_completed);
|
|
||||||
|
|
||||||
return _waitHandle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CompletedSynchronously {
|
|
||||||
get {
|
|
||||||
return _completedSynchronously;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCompleted {
|
|
||||||
get {
|
|
||||||
lock (_sync)
|
|
||||||
return _completed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private void complete ()
|
|
||||||
{
|
|
||||||
lock (_sync) {
|
|
||||||
_completed = true;
|
|
||||||
|
|
||||||
if (_waitHandle != null)
|
|
||||||
_waitHandle.Set ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_callback == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem (
|
|
||||||
state => {
|
|
||||||
try {
|
|
||||||
_callback (this);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
}
|
|
||||||
},
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal void Complete (Exception exception)
|
|
||||||
{
|
|
||||||
_exception = exception;
|
|
||||||
|
|
||||||
complete ();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Complete (
|
|
||||||
HttpListenerContext context, bool completedSynchronously
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_completedSynchronously = completedSynchronously;
|
|
||||||
|
|
||||||
complete ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,311 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpListenerContext.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpListenerContext.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Text;
|
|
||||||
using WebSocketSharp.Net.WebSockets;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides the access to the HTTP request and response objects used by
|
|
||||||
/// the <see cref="HttpListener"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This class cannot be inherited.
|
|
||||||
/// </remarks>
|
|
||||||
public sealed class HttpListenerContext
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private HttpConnection _connection;
|
|
||||||
private string _errorMessage;
|
|
||||||
private int _errorStatusCode;
|
|
||||||
private HttpListener _listener;
|
|
||||||
private HttpListenerRequest _request;
|
|
||||||
private HttpListenerResponse _response;
|
|
||||||
private IPrincipal _user;
|
|
||||||
private HttpListenerWebSocketContext _websocketContext;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpListenerContext (HttpConnection connection)
|
|
||||||
{
|
|
||||||
_connection = connection;
|
|
||||||
|
|
||||||
_errorStatusCode = 400;
|
|
||||||
_request = new HttpListenerRequest (this);
|
|
||||||
_response = new HttpListenerResponse (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal HttpConnection Connection {
|
|
||||||
get {
|
|
||||||
return _connection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal string ErrorMessage {
|
|
||||||
get {
|
|
||||||
return _errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_errorMessage = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int ErrorStatusCode {
|
|
||||||
get {
|
|
||||||
return _errorStatusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_errorStatusCode = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool HasErrorMessage {
|
|
||||||
get {
|
|
||||||
return _errorMessage != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal HttpListener Listener {
|
|
||||||
get {
|
|
||||||
return _listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_listener = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP request object that represents a client request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="HttpListenerRequest"/> that represents the client request.
|
|
||||||
/// </value>
|
|
||||||
public HttpListenerRequest Request {
|
|
||||||
get {
|
|
||||||
return _request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP response object used to send a response to the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="HttpListenerResponse"/> that represents a response to
|
|
||||||
/// the client request.
|
|
||||||
/// </value>
|
|
||||||
public HttpListenerResponse Response {
|
|
||||||
get {
|
|
||||||
return _response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the client information (identity, authentication, and security
|
|
||||||
/// roles).
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="IPrincipal"/> instance or <see langword="null"/>
|
|
||||||
/// if not authenticated.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The instance describes the client.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public IPrincipal User {
|
|
||||||
get {
|
|
||||||
return _user;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal set {
|
|
||||||
_user = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static string createErrorContent (
|
|
||||||
int statusCode, string statusDescription, string message
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return message != null && message.Length > 0
|
|
||||||
? String.Format (
|
|
||||||
"<html><body><h1>{0} {1} ({2})</h1></body></html>",
|
|
||||||
statusCode,
|
|
||||||
statusDescription,
|
|
||||||
message
|
|
||||||
)
|
|
||||||
: String.Format (
|
|
||||||
"<html><body><h1>{0} {1}</h1></body></html>",
|
|
||||||
statusCode,
|
|
||||||
statusDescription
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal HttpListenerWebSocketContext GetWebSocketContext (string protocol)
|
|
||||||
{
|
|
||||||
_websocketContext = new HttpListenerWebSocketContext (this, protocol);
|
|
||||||
|
|
||||||
return _websocketContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SendAuthenticationChallenge (
|
|
||||||
AuthenticationSchemes scheme, string realm
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var chal = new AuthenticationChallenge (scheme, realm).ToString ();
|
|
||||||
|
|
||||||
_response.StatusCode = 401;
|
|
||||||
_response.Headers.InternalSet ("WWW-Authenticate", chal, true);
|
|
||||||
|
|
||||||
_response.Close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SendError ()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
_response.StatusCode = _errorStatusCode;
|
|
||||||
_response.ContentType = "text/html";
|
|
||||||
|
|
||||||
var content = createErrorContent (
|
|
||||||
_errorStatusCode,
|
|
||||||
_response.StatusDescription,
|
|
||||||
_errorMessage
|
|
||||||
);
|
|
||||||
|
|
||||||
var enc = Encoding.UTF8;
|
|
||||||
var entity = enc.GetBytes (content);
|
|
||||||
_response.ContentEncoding = enc;
|
|
||||||
_response.ContentLength64 = entity.LongLength;
|
|
||||||
|
|
||||||
_response.Close (entity, true);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
_connection.Close (true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Unregister ()
|
|
||||||
{
|
|
||||||
if (_listener == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_listener.UnregisterContext (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Accepts a WebSocket handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="HttpListenerWebSocketContext"/> that represents
|
|
||||||
/// the WebSocket handshake request.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="protocol">
|
|
||||||
/// A <see cref="string"/> that specifies the subprotocol supported on
|
|
||||||
/// the WebSocket connection.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="protocol"/> is empty.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="protocol"/> contains an invalid character.
|
|
||||||
/// </para>
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// This method has already been called.
|
|
||||||
/// </exception>
|
|
||||||
public HttpListenerWebSocketContext AcceptWebSocket (string protocol)
|
|
||||||
{
|
|
||||||
if (_websocketContext != null) {
|
|
||||||
var msg = "The accepting is already in progress.";
|
|
||||||
|
|
||||||
throw new InvalidOperationException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (protocol != null) {
|
|
||||||
if (protocol.Length == 0) {
|
|
||||||
var msg = "An empty string.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "protocol");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!protocol.IsToken ()) {
|
|
||||||
var msg = "It contains an invalid character.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "protocol");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetWebSocketContext (protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpListenerException.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpListenerException.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The exception that is thrown when an error occurs processing
|
|
||||||
/// an HTTP request.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class HttpListenerException : Win32Exception
|
|
||||||
{
|
|
||||||
#region Protected Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="HttpListenerException"/>
|
|
||||||
/// class from the specified instances of the <see cref="SerializationInfo"/>
|
|
||||||
/// and <see cref="StreamingContext"/> classes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serializationInfo">
|
|
||||||
/// A <see cref="SerializationInfo"/> that contains the serialized
|
|
||||||
/// object data.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="streamingContext">
|
|
||||||
/// A <see cref="StreamingContext"/> that specifies the source for
|
|
||||||
/// the deserialization.
|
|
||||||
/// </param>
|
|
||||||
protected HttpListenerException (
|
|
||||||
SerializationInfo serializationInfo, StreamingContext streamingContext
|
|
||||||
)
|
|
||||||
: base (serializationInfo, streamingContext)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="HttpListenerException"/>
|
|
||||||
/// class.
|
|
||||||
/// </summary>
|
|
||||||
public HttpListenerException ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="HttpListenerException"/>
|
|
||||||
/// class with the specified error code.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="errorCode">
|
|
||||||
/// An <see cref="int"/> that specifies the error code.
|
|
||||||
/// </param>
|
|
||||||
public HttpListenerException (int errorCode)
|
|
||||||
: base (errorCode)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="HttpListenerException"/>
|
|
||||||
/// class with the specified error code and message.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="errorCode">
|
|
||||||
/// An <see cref="int"/> that specifies the error code.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="message">
|
|
||||||
/// A <see cref="string"/> that specifies the message.
|
|
||||||
/// </param>
|
|
||||||
public HttpListenerException (int errorCode, string message)
|
|
||||||
: base (errorCode, message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the error code that identifies the error that occurred.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// An <see cref="int"/> that represents the error code.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It is any of the Win32 error codes.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override int ErrorCode {
|
|
||||||
get {
|
|
||||||
return NativeErrorCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpListenerPrefix.cs
|
|
||||||
*
|
|
||||||
* This code is derived from ListenerPrefix.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
* - Oleg Mihailik <mihailik@gmail.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal sealed class HttpListenerPrefix
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private string _host;
|
|
||||||
private HttpListener _listener;
|
|
||||||
private string _original;
|
|
||||||
private string _path;
|
|
||||||
private string _port;
|
|
||||||
private string _prefix;
|
|
||||||
private bool _secure;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="HttpListenerPrefix"/> class
|
|
||||||
/// with the specified URI prefix and HTTP listener.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This constructor must be called after calling the CheckPrefix method.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="uriPrefix">
|
|
||||||
/// A <see cref="string"/> that specifies the URI prefix.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="listener">
|
|
||||||
/// A <see cref="HttpListener"/> that specifies the HTTP listener.
|
|
||||||
/// </param>
|
|
||||||
internal HttpListenerPrefix (string uriPrefix, HttpListener listener)
|
|
||||||
{
|
|
||||||
_original = uriPrefix;
|
|
||||||
_listener = listener;
|
|
||||||
|
|
||||||
parse (uriPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public string Host {
|
|
||||||
get {
|
|
||||||
return _host;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSecure {
|
|
||||||
get {
|
|
||||||
return _secure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpListener Listener {
|
|
||||||
get {
|
|
||||||
return _listener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Original {
|
|
||||||
get {
|
|
||||||
return _original;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Path {
|
|
||||||
get {
|
|
||||||
return _path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Port {
|
|
||||||
get {
|
|
||||||
return _port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private void parse (string uriPrefix)
|
|
||||||
{
|
|
||||||
if (uriPrefix.StartsWith ("https"))
|
|
||||||
_secure = true;
|
|
||||||
|
|
||||||
var len = uriPrefix.Length;
|
|
||||||
var host = uriPrefix.IndexOf (':') + 3;
|
|
||||||
var root = uriPrefix.IndexOf ('/', host + 1, len - host - 1);
|
|
||||||
|
|
||||||
var colon = uriPrefix.LastIndexOf (':', root - 1, root - host - 1);
|
|
||||||
|
|
||||||
if (uriPrefix[root - 1] != ']' && colon > host) {
|
|
||||||
_host = uriPrefix.Substring (host, colon - host);
|
|
||||||
_port = uriPrefix.Substring (colon + 1, root - colon - 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_host = uriPrefix.Substring (host, root - host);
|
|
||||||
_port = _secure ? "443" : "80";
|
|
||||||
}
|
|
||||||
|
|
||||||
_path = uriPrefix.Substring (root);
|
|
||||||
|
|
||||||
_prefix = String.Format (
|
|
||||||
"{0}://{1}:{2}{3}",
|
|
||||||
_secure ? "https" : "http",
|
|
||||||
_host,
|
|
||||||
_port,
|
|
||||||
_path
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public static void CheckPrefix (string uriPrefix)
|
|
||||||
{
|
|
||||||
if (uriPrefix == null)
|
|
||||||
throw new ArgumentNullException ("uriPrefix");
|
|
||||||
|
|
||||||
var len = uriPrefix.Length;
|
|
||||||
|
|
||||||
if (len == 0) {
|
|
||||||
var msg = "An empty string.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "uriPrefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
var schm = uriPrefix.StartsWith ("http://")
|
|
||||||
|| uriPrefix.StartsWith ("https://");
|
|
||||||
|
|
||||||
if (!schm) {
|
|
||||||
var msg = "The scheme is not 'http' or 'https'.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "uriPrefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
var end = len - 1;
|
|
||||||
|
|
||||||
if (uriPrefix[end] != '/') {
|
|
||||||
var msg = "It ends without '/'.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "uriPrefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
var host = uriPrefix.IndexOf (':') + 3;
|
|
||||||
|
|
||||||
if (host >= end) {
|
|
||||||
var msg = "No host is specified.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "uriPrefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uriPrefix[host] == ':') {
|
|
||||||
var msg = "No host is specified.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "uriPrefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
var root = uriPrefix.IndexOf ('/', host, len - host);
|
|
||||||
|
|
||||||
if (root == host) {
|
|
||||||
var msg = "No host is specified.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "uriPrefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uriPrefix[root - 1] == ':') {
|
|
||||||
var msg = "No port is specified.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "uriPrefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root == end - 1) {
|
|
||||||
var msg = "No path is specified.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "uriPrefix");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the current instance is equal to the specified
|
|
||||||
/// <see cref="object"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method will be required to detect duplicates in any collection.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// <para>
|
|
||||||
/// An <see cref="object"/> instance to compare to the current instance.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// An reference to a <see cref="HttpListenerPrefix"/> instance.
|
|
||||||
/// </para>
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the current instance and <paramref name="obj"/> have
|
|
||||||
/// the same URI prefix; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals (object obj)
|
|
||||||
{
|
|
||||||
var pref = obj as HttpListenerPrefix;
|
|
||||||
|
|
||||||
return pref != null && _prefix.Equals (pref._prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the hash code for the current instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method will be required to detect duplicates in any collection.
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>
|
|
||||||
/// An <see cref="int"/> that represents the hash code.
|
|
||||||
/// </returns>
|
|
||||||
public override int GetHashCode ()
|
|
||||||
{
|
|
||||||
return _prefix.GetHashCode ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
return _prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpListenerPrefixCollection.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpListenerPrefixCollection.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a collection used to store the URI prefixes for a instance of
|
|
||||||
/// the <see cref="HttpListener"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The <see cref="HttpListener"/> instance responds to the request which has
|
|
||||||
/// a requested URI that the prefixes most closely match.
|
|
||||||
/// </remarks>
|
|
||||||
public class HttpListenerPrefixCollection : ICollection<string>
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private HttpListener _listener;
|
|
||||||
private List<string> _prefixes;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpListenerPrefixCollection (HttpListener listener)
|
|
||||||
{
|
|
||||||
_listener = listener;
|
|
||||||
_prefixes = new List<string> ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of prefixes in the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// An <see cref="int"/> that represents the number of prefixes.
|
|
||||||
/// </value>
|
|
||||||
public int Count {
|
|
||||||
get {
|
|
||||||
return _prefixes.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the access to the collection is
|
|
||||||
/// read-only.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// Always returns <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsReadOnly {
|
|
||||||
get {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the access to the collection is
|
|
||||||
/// synchronized.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// Always returns <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsSynchronized {
|
|
||||||
get {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds the specified URI prefix to the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uriPrefix">
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that specifies the URI prefix to add.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It must be a well-formed URI prefix with http or https scheme,
|
|
||||||
/// and must end with a '/'.
|
|
||||||
/// </para>
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="uriPrefix"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <paramref name="uriPrefix"/> is invalid.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// The <see cref="HttpListener"/> instance associated with this
|
|
||||||
/// collection is closed.
|
|
||||||
/// </exception>
|
|
||||||
public void Add (string uriPrefix)
|
|
||||||
{
|
|
||||||
_listener.CheckDisposed ();
|
|
||||||
|
|
||||||
HttpListenerPrefix.CheckPrefix (uriPrefix);
|
|
||||||
|
|
||||||
if (_prefixes.Contains (uriPrefix))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_listener.IsListening)
|
|
||||||
EndPointManager.AddPrefix (uriPrefix, _listener);
|
|
||||||
|
|
||||||
_prefixes.Add (uriPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all URI prefixes from the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// The <see cref="HttpListener"/> instance associated with this
|
|
||||||
/// collection is closed.
|
|
||||||
/// </exception>
|
|
||||||
public void Clear ()
|
|
||||||
{
|
|
||||||
_listener.CheckDisposed ();
|
|
||||||
|
|
||||||
if (_listener.IsListening)
|
|
||||||
EndPointManager.RemoveListener (_listener);
|
|
||||||
|
|
||||||
_prefixes.Clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a value indicating whether the collection contains the
|
|
||||||
/// specified URI prefix.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the collection contains the URI prefix; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="uriPrefix">
|
|
||||||
/// A <see cref="string"/> that specifies the URI prefix to test.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="uriPrefix"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// The <see cref="HttpListener"/> instance associated with this
|
|
||||||
/// collection is closed.
|
|
||||||
/// </exception>
|
|
||||||
public bool Contains (string uriPrefix)
|
|
||||||
{
|
|
||||||
_listener.CheckDisposed ();
|
|
||||||
|
|
||||||
if (uriPrefix == null)
|
|
||||||
throw new ArgumentNullException ("uriPrefix");
|
|
||||||
|
|
||||||
return _prefixes.Contains (uriPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Copies the contents of the collection to the specified array of string.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="array">
|
|
||||||
/// An array of <see cref="string"/> that specifies the destination of
|
|
||||||
/// the URI prefix strings copied from the collection.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="offset">
|
|
||||||
/// An <see cref="int"/> that specifies the zero-based index in
|
|
||||||
/// the array at which copying begins.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="array"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||||
/// <paramref name="offset"/> is less than zero.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// The space from <paramref name="offset"/> to the end of
|
|
||||||
/// <paramref name="array"/> is not enough to copy to.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// The <see cref="HttpListener"/> instance associated with this
|
|
||||||
/// collection is closed.
|
|
||||||
/// </exception>
|
|
||||||
public void CopyTo (string[] array, int offset)
|
|
||||||
{
|
|
||||||
_listener.CheckDisposed ();
|
|
||||||
|
|
||||||
_prefixes.CopyTo (array, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the enumerator that iterates through the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// An <see cref="T:System.Collections.Generic.IEnumerator{string}"/>
|
|
||||||
/// instance that can be used to iterate through the collection.
|
|
||||||
/// </returns>
|
|
||||||
public IEnumerator<string> GetEnumerator ()
|
|
||||||
{
|
|
||||||
return _prefixes.GetEnumerator ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the specified URI prefix from the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the URI prefix is successfully removed; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="uriPrefix">
|
|
||||||
/// A <see cref="string"/> that specifies the URI prefix to remove.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="uriPrefix"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ObjectDisposedException">
|
|
||||||
/// The <see cref="HttpListener"/> instance associated with this
|
|
||||||
/// collection is closed.
|
|
||||||
/// </exception>
|
|
||||||
public bool Remove (string uriPrefix)
|
|
||||||
{
|
|
||||||
_listener.CheckDisposed ();
|
|
||||||
|
|
||||||
if (uriPrefix == null)
|
|
||||||
throw new ArgumentNullException ("uriPrefix");
|
|
||||||
|
|
||||||
if (!_prefixes.Contains (uriPrefix))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (_listener.IsListening)
|
|
||||||
EndPointManager.RemovePrefix (uriPrefix, _listener);
|
|
||||||
|
|
||||||
return _prefixes.Remove (uriPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Explicit Interface Implementations
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the enumerator that iterates through the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// An <see cref="IEnumerator"/> instance that can be used to iterate
|
|
||||||
/// through the collection.
|
|
||||||
/// </returns>
|
|
||||||
IEnumerator IEnumerable.GetEnumerator ()
|
|
||||||
{
|
|
||||||
return _prefixes.GetEnumerator ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,928 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpListenerRequest.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpListenerRequest.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents an incoming HTTP request to a <see cref="HttpListener"/>
|
|
||||||
/// instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This class cannot be inherited.
|
|
||||||
/// </remarks>
|
|
||||||
public sealed class HttpListenerRequest
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private static readonly byte[] _100continue;
|
|
||||||
private string[] _acceptTypes;
|
|
||||||
private bool _chunked;
|
|
||||||
private HttpConnection _connection;
|
|
||||||
private Encoding _contentEncoding;
|
|
||||||
private long _contentLength;
|
|
||||||
private HttpListenerContext _context;
|
|
||||||
private CookieCollection _cookies;
|
|
||||||
private WebHeaderCollection _headers;
|
|
||||||
private string _httpMethod;
|
|
||||||
private Stream _inputStream;
|
|
||||||
private Version _protocolVersion;
|
|
||||||
private NameValueCollection _queryString;
|
|
||||||
private string _rawUrl;
|
|
||||||
private Guid _requestTraceIdentifier;
|
|
||||||
private Uri _url;
|
|
||||||
private Uri _urlReferrer;
|
|
||||||
private bool _urlSet;
|
|
||||||
private string _userHostName;
|
|
||||||
private string[] _userLanguages;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Static Constructor
|
|
||||||
|
|
||||||
static HttpListenerRequest ()
|
|
||||||
{
|
|
||||||
_100continue = Encoding.ASCII.GetBytes ("HTTP/1.1 100 Continue\r\n\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpListenerRequest (HttpListenerContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
|
|
||||||
_connection = context.Connection;
|
|
||||||
_contentLength = -1;
|
|
||||||
_headers = new WebHeaderCollection ();
|
|
||||||
_requestTraceIdentifier = Guid.NewGuid ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the media types that are acceptable for the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// An array of <see cref="string"/> that contains the names of the media
|
|
||||||
/// types specified in the value of the Accept header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public string[] AcceptTypes {
|
|
||||||
get {
|
|
||||||
var val = _headers["Accept"];
|
|
||||||
|
|
||||||
if (val == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (_acceptTypes == null) {
|
|
||||||
_acceptTypes = val
|
|
||||||
.SplitHeaderValue (',')
|
|
||||||
.TrimEach ()
|
|
||||||
.ToList ()
|
|
||||||
.ToArray ();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _acceptTypes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets an error code that identifies a problem with the certificate
|
|
||||||
/// provided by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// An <see cref="int"/> that represents an error code.
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="NotSupportedException">
|
|
||||||
/// This property is not supported.
|
|
||||||
/// </exception>
|
|
||||||
public int ClientCertificateError {
|
|
||||||
get {
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the encoding for the entity body data included in the request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="Encoding"/> converted from the charset value of the
|
|
||||||
/// Content-Type header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see cref="Encoding.UTF8"/> if the charset value is not available.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public Encoding ContentEncoding {
|
|
||||||
get {
|
|
||||||
if (_contentEncoding == null)
|
|
||||||
_contentEncoding = getContentEncoding ();
|
|
||||||
|
|
||||||
return _contentEncoding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the length in bytes of the entity body data included in the
|
|
||||||
/// request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="long"/> converted from the value of the Content-Length
|
|
||||||
/// header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -1 if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public long ContentLength64 {
|
|
||||||
get {
|
|
||||||
return _contentLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the media type of the entity body data included in the request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the value of the Content-Type
|
|
||||||
/// header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public string ContentType {
|
|
||||||
get {
|
|
||||||
return _headers["Content-Type"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the cookies included in the request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="CookieCollection"/> that contains the cookies.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// An empty collection if not included.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public CookieCollection Cookies {
|
|
||||||
get {
|
|
||||||
if (_cookies == null)
|
|
||||||
_cookies = _headers.GetCookies (false);
|
|
||||||
|
|
||||||
return _cookies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the request has the entity body data.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the request has the entity body data; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool HasEntityBody {
|
|
||||||
get {
|
|
||||||
return _contentLength > 0 || _chunked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the headers included in the request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="NameValueCollection"/> that contains the headers.
|
|
||||||
/// </value>
|
|
||||||
public NameValueCollection Headers {
|
|
||||||
get {
|
|
||||||
return _headers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP method specified by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the HTTP method specified in
|
|
||||||
/// the request line.
|
|
||||||
/// </value>
|
|
||||||
public string HttpMethod {
|
|
||||||
get {
|
|
||||||
return _httpMethod;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a stream that contains the entity body data included in
|
|
||||||
/// the request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="Stream"/> that contains the entity body data.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see cref="Stream.Null"/> if the entity body data is not available.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public Stream InputStream {
|
|
||||||
get {
|
|
||||||
if (_inputStream == null) {
|
|
||||||
_inputStream = _contentLength > 0 || _chunked
|
|
||||||
? _connection
|
|
||||||
.GetRequestStream (_contentLength, _chunked)
|
|
||||||
: Stream.Null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _inputStream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the client is authenticated.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the client is authenticated; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsAuthenticated {
|
|
||||||
get {
|
|
||||||
return _context.User != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the request is sent from the local
|
|
||||||
/// computer.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the request is sent from the same computer as the server;
|
|
||||||
/// otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsLocal {
|
|
||||||
get {
|
|
||||||
return _connection.IsLocal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether a secure connection is used to send
|
|
||||||
/// the request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the connection is secure; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsSecureConnection {
|
|
||||||
get {
|
|
||||||
return _connection.IsSecure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the request is a WebSocket handshake
|
|
||||||
/// request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the request is a WebSocket handshake request; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsWebSocketRequest {
|
|
||||||
get {
|
|
||||||
return _httpMethod == "GET" && _headers.Upgrades ("websocket");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether a persistent connection is requested.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the request specifies that the connection is kept open;
|
|
||||||
/// otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool KeepAlive {
|
|
||||||
get {
|
|
||||||
return _headers.KeepsAlive (_protocolVersion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the endpoint to which the request is sent.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server IP
|
|
||||||
/// address and port number.
|
|
||||||
/// </value>
|
|
||||||
public System.Net.IPEndPoint LocalEndPoint {
|
|
||||||
get {
|
|
||||||
return _connection.LocalEndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP version specified by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="Version"/> that represents the HTTP version specified in
|
|
||||||
/// the request line.
|
|
||||||
/// </value>
|
|
||||||
public Version ProtocolVersion {
|
|
||||||
get {
|
|
||||||
return _protocolVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the query string included in the request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="NameValueCollection"/> that contains the query
|
|
||||||
/// parameters.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// An empty collection if not included.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public NameValueCollection QueryString {
|
|
||||||
get {
|
|
||||||
if (_queryString == null) {
|
|
||||||
var url = Url;
|
|
||||||
|
|
||||||
_queryString = QueryStringCollection
|
|
||||||
.Parse (
|
|
||||||
url != null ? url.Query : null,
|
|
||||||
Encoding.UTF8
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _queryString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the raw URL specified by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the request target specified in
|
|
||||||
/// the request line.
|
|
||||||
/// </value>
|
|
||||||
public string RawUrl {
|
|
||||||
get {
|
|
||||||
return _rawUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the endpoint from which the request is sent.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client IP
|
|
||||||
/// address and port number.
|
|
||||||
/// </value>
|
|
||||||
public System.Net.IPEndPoint RemoteEndPoint {
|
|
||||||
get {
|
|
||||||
return _connection.RemoteEndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the trace identifier of the request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="Guid"/> that represents the trace identifier.
|
|
||||||
/// </value>
|
|
||||||
public Guid RequestTraceIdentifier {
|
|
||||||
get {
|
|
||||||
return _requestTraceIdentifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the URL requested by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="Uri"/> that represents the URL parsed from the request.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the URL cannot be parsed.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public Uri Url {
|
|
||||||
get {
|
|
||||||
if (!_urlSet) {
|
|
||||||
_url = HttpUtility
|
|
||||||
.CreateRequestUrl (
|
|
||||||
_rawUrl,
|
|
||||||
_userHostName ?? UserHostAddress,
|
|
||||||
IsWebSocketRequest,
|
|
||||||
IsSecureConnection
|
|
||||||
);
|
|
||||||
|
|
||||||
_urlSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the URI of the resource from which the requested URL was obtained.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="Uri"/> converted from the value of the Referer header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header value is not available.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public Uri UrlReferrer {
|
|
||||||
get {
|
|
||||||
var val = _headers["Referer"];
|
|
||||||
|
|
||||||
if (val == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (_urlReferrer == null)
|
|
||||||
_urlReferrer = val.ToUri ();
|
|
||||||
|
|
||||||
return _urlReferrer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the user agent from which the request is originated.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the value of the User-Agent
|
|
||||||
/// header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public string UserAgent {
|
|
||||||
get {
|
|
||||||
return _headers["User-Agent"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the IP address and port number to which the request is sent.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the server IP address and port
|
|
||||||
/// number.
|
|
||||||
/// </value>
|
|
||||||
public string UserHostAddress {
|
|
||||||
get {
|
|
||||||
return _connection.LocalEndPoint.ToString ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the server host name requested by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the value of the Host header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It includes the port number if provided.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public string UserHostName {
|
|
||||||
get {
|
|
||||||
return _userHostName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the natural languages that are acceptable for the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// An array of <see cref="string"/> that contains the names of the
|
|
||||||
/// natural languages specified in the value of the Accept-Language
|
|
||||||
/// header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public string[] UserLanguages {
|
|
||||||
get {
|
|
||||||
var val = _headers["Accept-Language"];
|
|
||||||
|
|
||||||
if (val == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (_userLanguages == null)
|
|
||||||
_userLanguages = val.Split (',').TrimEach ().ToList ().ToArray ();
|
|
||||||
|
|
||||||
return _userLanguages;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private Encoding getContentEncoding ()
|
|
||||||
{
|
|
||||||
var val = _headers["Content-Type"];
|
|
||||||
|
|
||||||
if (val == null)
|
|
||||||
return Encoding.UTF8;
|
|
||||||
|
|
||||||
Encoding ret;
|
|
||||||
|
|
||||||
return HttpUtility.TryGetEncoding (val, out ret)
|
|
||||||
? ret
|
|
||||||
: Encoding.UTF8;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal void AddHeader (string headerField)
|
|
||||||
{
|
|
||||||
var start = headerField[0];
|
|
||||||
|
|
||||||
if (start == ' ' || start == '\t') {
|
|
||||||
_context.ErrorMessage = "Invalid header field";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var colon = headerField.IndexOf (':');
|
|
||||||
|
|
||||||
if (colon < 1) {
|
|
||||||
_context.ErrorMessage = "Invalid header field";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = headerField.Substring (0, colon).Trim ();
|
|
||||||
|
|
||||||
if (name.Length == 0 || !name.IsToken ()) {
|
|
||||||
_context.ErrorMessage = "Invalid header name";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = colon < headerField.Length - 1
|
|
||||||
? headerField.Substring (colon + 1).Trim ()
|
|
||||||
: String.Empty;
|
|
||||||
|
|
||||||
_headers.InternalSet (name, val, false);
|
|
||||||
|
|
||||||
var lower = name.ToLower (CultureInfo.InvariantCulture);
|
|
||||||
|
|
||||||
if (lower == "host") {
|
|
||||||
if (_userHostName != null) {
|
|
||||||
_context.ErrorMessage = "Invalid Host header";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.Length == 0) {
|
|
||||||
_context.ErrorMessage = "Invalid Host header";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_userHostName = val;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lower == "content-length") {
|
|
||||||
if (_contentLength > -1) {
|
|
||||||
_context.ErrorMessage = "Invalid Content-Length header";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long len;
|
|
||||||
|
|
||||||
if (!Int64.TryParse (val, out len)) {
|
|
||||||
_context.ErrorMessage = "Invalid Content-Length header";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < 0) {
|
|
||||||
_context.ErrorMessage = "Invalid Content-Length header";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_contentLength = len;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void FinishInitialization ()
|
|
||||||
{
|
|
||||||
if (_userHostName == null) {
|
|
||||||
_context.ErrorMessage = "Host header required";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var transferEnc = _headers["Transfer-Encoding"];
|
|
||||||
|
|
||||||
if (transferEnc != null) {
|
|
||||||
var comparison = StringComparison.OrdinalIgnoreCase;
|
|
||||||
|
|
||||||
if (!transferEnc.Equals ("chunked", comparison)) {
|
|
||||||
_context.ErrorMessage = "Invalid Transfer-Encoding header";
|
|
||||||
_context.ErrorStatusCode = 501;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_chunked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_httpMethod == "POST" || _httpMethod == "PUT") {
|
|
||||||
if (_contentLength <= 0 && !_chunked) {
|
|
||||||
_context.ErrorMessage = String.Empty;
|
|
||||||
_context.ErrorStatusCode = 411;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var expect = _headers["Expect"];
|
|
||||||
|
|
||||||
if (expect != null) {
|
|
||||||
var comparison = StringComparison.OrdinalIgnoreCase;
|
|
||||||
|
|
||||||
if (!expect.Equals ("100-continue", comparison)) {
|
|
||||||
_context.ErrorMessage = "Invalid Expect header";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var output = _connection.GetResponseStream ();
|
|
||||||
output.InternalWrite (_100continue, 0, _100continue.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool FlushInput ()
|
|
||||||
{
|
|
||||||
var input = InputStream;
|
|
||||||
|
|
||||||
if (input == Stream.Null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
var len = 2048;
|
|
||||||
|
|
||||||
if (_contentLength > 0 && _contentLength < len)
|
|
||||||
len = (int) _contentLength;
|
|
||||||
|
|
||||||
var buff = new byte[len];
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
var ares = input.BeginRead (buff, 0, len, null, null);
|
|
||||||
|
|
||||||
if (!ares.IsCompleted) {
|
|
||||||
var timeout = 100;
|
|
||||||
|
|
||||||
if (!ares.AsyncWaitHandle.WaitOne (timeout))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.EndRead (ares) <= 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool IsUpgradeRequest (string protocol)
|
|
||||||
{
|
|
||||||
return _headers.Upgrades (protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetRequestLine (string requestLine)
|
|
||||||
{
|
|
||||||
var parts = requestLine.Split (new[] { ' ' }, 3);
|
|
||||||
|
|
||||||
if (parts.Length < 3) {
|
|
||||||
_context.ErrorMessage = "Invalid request line (parts)";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var method = parts[0];
|
|
||||||
|
|
||||||
if (method.Length == 0) {
|
|
||||||
_context.ErrorMessage = "Invalid request line (method)";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var target = parts[1];
|
|
||||||
|
|
||||||
if (target.Length == 0) {
|
|
||||||
_context.ErrorMessage = "Invalid request line (target)";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawVer = parts[2];
|
|
||||||
|
|
||||||
if (rawVer.Length != 8) {
|
|
||||||
_context.ErrorMessage = "Invalid request line (version)";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rawVer.StartsWith ("HTTP/", StringComparison.Ordinal)) {
|
|
||||||
_context.ErrorMessage = "Invalid request line (version)";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Version ver;
|
|
||||||
|
|
||||||
if (!rawVer.Substring (5).TryCreateVersion (out ver)) {
|
|
||||||
_context.ErrorMessage = "Invalid request line (version)";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ver != HttpVersion.Version11) {
|
|
||||||
_context.ErrorMessage = "Invalid request line (version)";
|
|
||||||
_context.ErrorStatusCode = 505;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!method.IsHttpMethod (ver)) {
|
|
||||||
_context.ErrorMessage = "Invalid request line (method)";
|
|
||||||
_context.ErrorStatusCode = 501;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_httpMethod = method;
|
|
||||||
_rawUrl = target;
|
|
||||||
_protocolVersion = ver;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Begins getting the certificate provided by the client asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// An <see cref="IAsyncResult"/> instance that indicates the status of the
|
|
||||||
/// operation.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="requestCallback">
|
|
||||||
/// An <see cref="AsyncCallback"/> delegate that invokes the method called
|
|
||||||
/// when the operation is complete.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="state">
|
|
||||||
/// An <see cref="object"/> that represents a user defined object to pass to
|
|
||||||
/// the callback delegate.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="NotSupportedException">
|
|
||||||
/// This method is not supported.
|
|
||||||
/// </exception>
|
|
||||||
public IAsyncResult BeginGetClientCertificate (
|
|
||||||
AsyncCallback requestCallback, object state
|
|
||||||
)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ends an asynchronous operation to get the certificate provided by the
|
|
||||||
/// client.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="X509Certificate2"/> that represents an X.509 certificate
|
|
||||||
/// provided by the client.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="asyncResult">
|
|
||||||
/// An <see cref="IAsyncResult"/> instance returned when the operation
|
|
||||||
/// started.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="NotSupportedException">
|
|
||||||
/// This method is not supported.
|
|
||||||
/// </exception>
|
|
||||||
public X509Certificate2 EndGetClientCertificate (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the certificate provided by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="X509Certificate2"/> that represents an X.509 certificate
|
|
||||||
/// provided by the client.
|
|
||||||
/// </returns>
|
|
||||||
/// <exception cref="NotSupportedException">
|
|
||||||
/// This method is not supported.
|
|
||||||
/// </exception>
|
|
||||||
public X509Certificate2 GetClientCertificate ()
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a string that represents the current instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="string"/> that contains the request line and headers
|
|
||||||
/// included in the request.
|
|
||||||
/// </returns>
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
var buff = new StringBuilder (64);
|
|
||||||
|
|
||||||
buff
|
|
||||||
.AppendFormat (
|
|
||||||
"{0} {1} HTTP/{2}\r\n", _httpMethod, _rawUrl, _protocolVersion
|
|
||||||
)
|
|
||||||
.Append (_headers.ToString ());
|
|
||||||
|
|
||||||
return buff.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,233 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpRequestHeader.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpRequestHeader.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2014-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the HTTP header that may be specified in a client request.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The headers of this enumeration are defined in
|
|
||||||
/// <see href="http://tools.ietf.org/html/rfc2616#section-14">RFC 2616</see> or
|
|
||||||
/// <see href="http://tools.ietf.org/html/rfc6455#section-11.3">RFC 6455</see>.
|
|
||||||
/// </remarks>
|
|
||||||
public enum HttpRequestHeader
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Cache-Control header.
|
|
||||||
/// </summary>
|
|
||||||
CacheControl,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Connection header.
|
|
||||||
/// </summary>
|
|
||||||
Connection,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Date header.
|
|
||||||
/// </summary>
|
|
||||||
Date,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Keep-Alive header.
|
|
||||||
/// </summary>
|
|
||||||
KeepAlive,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Pragma header.
|
|
||||||
/// </summary>
|
|
||||||
Pragma,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Trailer header.
|
|
||||||
/// </summary>
|
|
||||||
Trailer,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Transfer-Encoding header.
|
|
||||||
/// </summary>
|
|
||||||
TransferEncoding,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Upgrade header.
|
|
||||||
/// </summary>
|
|
||||||
Upgrade,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Via header.
|
|
||||||
/// </summary>
|
|
||||||
Via,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Warning header.
|
|
||||||
/// </summary>
|
|
||||||
Warning,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Allow header.
|
|
||||||
/// </summary>
|
|
||||||
Allow,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Length header.
|
|
||||||
/// </summary>
|
|
||||||
ContentLength,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Type header.
|
|
||||||
/// </summary>
|
|
||||||
ContentType,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Encoding header.
|
|
||||||
/// </summary>
|
|
||||||
ContentEncoding,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Language header.
|
|
||||||
/// </summary>
|
|
||||||
ContentLanguage,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Location header.
|
|
||||||
/// </summary>
|
|
||||||
ContentLocation,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-MD5 header.
|
|
||||||
/// </summary>
|
|
||||||
ContentMd5,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Range header.
|
|
||||||
/// </summary>
|
|
||||||
ContentRange,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Expires header.
|
|
||||||
/// </summary>
|
|
||||||
Expires,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Last-Modified header.
|
|
||||||
/// </summary>
|
|
||||||
LastModified,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Accept header.
|
|
||||||
/// </summary>
|
|
||||||
Accept,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Accept-Charset header.
|
|
||||||
/// </summary>
|
|
||||||
AcceptCharset,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Accept-Encoding header.
|
|
||||||
/// </summary>
|
|
||||||
AcceptEncoding,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Accept-Language header.
|
|
||||||
/// </summary>
|
|
||||||
AcceptLanguage,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Authorization header.
|
|
||||||
/// </summary>
|
|
||||||
Authorization,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Cookie header.
|
|
||||||
/// </summary>
|
|
||||||
Cookie,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Expect header.
|
|
||||||
/// </summary>
|
|
||||||
Expect,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the From header.
|
|
||||||
/// </summary>
|
|
||||||
From,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Host header.
|
|
||||||
/// </summary>
|
|
||||||
Host,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the If-Match header.
|
|
||||||
/// </summary>
|
|
||||||
IfMatch,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the If-Modified-Since header.
|
|
||||||
/// </summary>
|
|
||||||
IfModifiedSince,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the If-None-Match header.
|
|
||||||
/// </summary>
|
|
||||||
IfNoneMatch,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the If-Range header.
|
|
||||||
/// </summary>
|
|
||||||
IfRange,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the If-Unmodified-Since header.
|
|
||||||
/// </summary>
|
|
||||||
IfUnmodifiedSince,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Max-Forwards header.
|
|
||||||
/// </summary>
|
|
||||||
MaxForwards,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Proxy-Authorization header.
|
|
||||||
/// </summary>
|
|
||||||
ProxyAuthorization,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Referer header.
|
|
||||||
/// </summary>
|
|
||||||
Referer,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Range header.
|
|
||||||
/// </summary>
|
|
||||||
Range,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the TE header.
|
|
||||||
/// </summary>
|
|
||||||
Te,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Translate header.
|
|
||||||
/// </summary>
|
|
||||||
Translate,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the User-Agent header.
|
|
||||||
/// </summary>
|
|
||||||
UserAgent,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Sec-WebSocket-Key header.
|
|
||||||
/// </summary>
|
|
||||||
SecWebSocketKey,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Sec-WebSocket-Extensions header.
|
|
||||||
/// </summary>
|
|
||||||
SecWebSocketExtensions,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Sec-WebSocket-Protocol header.
|
|
||||||
/// </summary>
|
|
||||||
SecWebSocketProtocol,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Sec-WebSocket-Version header.
|
|
||||||
/// </summary>
|
|
||||||
SecWebSocketVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpResponseHeader.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpResponseHeader.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2014-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the HTTP header that can be specified in a server response.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The headers of this enumeration are defined in
|
|
||||||
/// <see href="http://tools.ietf.org/html/rfc2616#section-14">RFC 2616</see> or
|
|
||||||
/// <see href="http://tools.ietf.org/html/rfc6455#section-11.3">RFC 6455</see>.
|
|
||||||
/// </remarks>
|
|
||||||
public enum HttpResponseHeader
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Cache-Control header.
|
|
||||||
/// </summary>
|
|
||||||
CacheControl,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Connection header.
|
|
||||||
/// </summary>
|
|
||||||
Connection,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Date header.
|
|
||||||
/// </summary>
|
|
||||||
Date,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Keep-Alive header.
|
|
||||||
/// </summary>
|
|
||||||
KeepAlive,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Pragma header.
|
|
||||||
/// </summary>
|
|
||||||
Pragma,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Trailer header.
|
|
||||||
/// </summary>
|
|
||||||
Trailer,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Transfer-Encoding header.
|
|
||||||
/// </summary>
|
|
||||||
TransferEncoding,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Upgrade header.
|
|
||||||
/// </summary>
|
|
||||||
Upgrade,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Via header.
|
|
||||||
/// </summary>
|
|
||||||
Via,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Warning header.
|
|
||||||
/// </summary>
|
|
||||||
Warning,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Allow header.
|
|
||||||
/// </summary>
|
|
||||||
Allow,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Length header.
|
|
||||||
/// </summary>
|
|
||||||
ContentLength,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Type header.
|
|
||||||
/// </summary>
|
|
||||||
ContentType,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Encoding header.
|
|
||||||
/// </summary>
|
|
||||||
ContentEncoding,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Language header.
|
|
||||||
/// </summary>
|
|
||||||
ContentLanguage,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Location header.
|
|
||||||
/// </summary>
|
|
||||||
ContentLocation,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-MD5 header.
|
|
||||||
/// </summary>
|
|
||||||
ContentMd5,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Content-Range header.
|
|
||||||
/// </summary>
|
|
||||||
ContentRange,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Expires header.
|
|
||||||
/// </summary>
|
|
||||||
Expires,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Last-Modified header.
|
|
||||||
/// </summary>
|
|
||||||
LastModified,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Accept-Ranges header.
|
|
||||||
/// </summary>
|
|
||||||
AcceptRanges,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Age header.
|
|
||||||
/// </summary>
|
|
||||||
Age,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the ETag header.
|
|
||||||
/// </summary>
|
|
||||||
ETag,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Location header.
|
|
||||||
/// </summary>
|
|
||||||
Location,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Proxy-Authenticate header.
|
|
||||||
/// </summary>
|
|
||||||
ProxyAuthenticate,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Retry-After header.
|
|
||||||
/// </summary>
|
|
||||||
RetryAfter,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Server header.
|
|
||||||
/// </summary>
|
|
||||||
Server,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Set-Cookie header.
|
|
||||||
/// </summary>
|
|
||||||
SetCookie,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Vary header.
|
|
||||||
/// </summary>
|
|
||||||
Vary,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the WWW-Authenticate header.
|
|
||||||
/// </summary>
|
|
||||||
WwwAuthenticate,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Sec-WebSocket-Extensions header.
|
|
||||||
/// </summary>
|
|
||||||
SecWebSocketExtensions,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Sec-WebSocket-Accept header.
|
|
||||||
/// </summary>
|
|
||||||
SecWebSocketAccept,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Sec-WebSocket-Protocol header.
|
|
||||||
/// </summary>
|
|
||||||
SecWebSocketProtocol,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the Sec-WebSocket-Version header.
|
|
||||||
/// </summary>
|
|
||||||
SecWebSocketVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,346 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpStatusCode.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpStatusCode.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* It was automatically generated from ECMA CLI XML Library Specification.
|
|
||||||
* Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)]
|
|
||||||
* Created: Wed, 5 Sep 2001 06:32:05 UTC
|
|
||||||
* Source file: AllTypes.xml
|
|
||||||
* URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2001 Ximian, Inc. (http://www.ximian.com)
|
|
||||||
* Copyright (c) 2012-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the HTTP status code that can be specified in a server response.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The values of this enumeration are defined in
|
|
||||||
/// <see href="http://tools.ietf.org/html/rfc2616#section-10">RFC 2616</see>.
|
|
||||||
/// </remarks>
|
|
||||||
public enum HttpStatusCode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 100. Indicates that the client should continue
|
|
||||||
/// with its request.
|
|
||||||
/// </summary>
|
|
||||||
Continue = 100,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 101. Indicates that the server is switching
|
|
||||||
/// the HTTP version or protocol on the connection.
|
|
||||||
/// </summary>
|
|
||||||
SwitchingProtocols = 101,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 200. Indicates that the client's request has
|
|
||||||
/// succeeded.
|
|
||||||
/// </summary>
|
|
||||||
OK = 200,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 201. Indicates that the client's request has
|
|
||||||
/// been fulfilled and resulted in a new resource being created.
|
|
||||||
/// </summary>
|
|
||||||
Created = 201,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 202. Indicates that the client's request has
|
|
||||||
/// been accepted for processing, but the processing has not been completed.
|
|
||||||
/// </summary>
|
|
||||||
Accepted = 202,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 203. Indicates that the returned metainformation
|
|
||||||
/// is from a local or a third-party copy instead of the origin server.
|
|
||||||
/// </summary>
|
|
||||||
NonAuthoritativeInformation = 203,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 204. Indicates that the server has fulfilled
|
|
||||||
/// the client's request but does not need to return an entity-body.
|
|
||||||
/// </summary>
|
|
||||||
NoContent = 204,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 205. Indicates that the server has fulfilled
|
|
||||||
/// the client's request, and the user agent should reset the document view
|
|
||||||
/// which caused the request to be sent.
|
|
||||||
/// </summary>
|
|
||||||
ResetContent = 205,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 206. Indicates that the server has fulfilled
|
|
||||||
/// the partial GET request for the resource.
|
|
||||||
/// </summary>
|
|
||||||
PartialContent = 206,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 300. Indicates that the requested resource
|
|
||||||
/// corresponds to any of multiple representations.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// MultipleChoices is a synonym for Ambiguous.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
MultipleChoices = 300,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 300. Indicates that the requested resource
|
|
||||||
/// corresponds to any of multiple representations.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Ambiguous is a synonym for MultipleChoices.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
Ambiguous = 300,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 301. Indicates that the requested resource
|
|
||||||
/// has been assigned a new permanent URI and any future references to
|
|
||||||
/// this resource should use one of the returned URIs.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// MovedPermanently is a synonym for Moved.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
MovedPermanently = 301,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 301. Indicates that the requested resource
|
|
||||||
/// has been assigned a new permanent URI and any future references to
|
|
||||||
/// this resource should use one of the returned URIs.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Moved is a synonym for MovedPermanently.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
Moved = 301,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 302. Indicates that the requested resource
|
|
||||||
/// is located temporarily under a different URI.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Found is a synonym for Redirect.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
Found = 302,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 302. Indicates that the requested resource
|
|
||||||
/// is located temporarily under a different URI.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Redirect is a synonym for Found.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
Redirect = 302,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 303. Indicates that the response to
|
|
||||||
/// the request can be found under a different URI and should be
|
|
||||||
/// retrieved using a GET method on that resource.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// SeeOther is a synonym for RedirectMethod.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
SeeOther = 303,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 303. Indicates that the response to
|
|
||||||
/// the request can be found under a different URI and should be
|
|
||||||
/// retrieved using a GET method on that resource.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// RedirectMethod is a synonym for SeeOther.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
RedirectMethod = 303,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 304. Indicates that the client has performed
|
|
||||||
/// a conditional GET request and access is allowed, but the document has
|
|
||||||
/// not been modified.
|
|
||||||
/// </summary>
|
|
||||||
NotModified = 304,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 305. Indicates that the requested resource
|
|
||||||
/// must be accessed through the proxy given by the Location field.
|
|
||||||
/// </summary>
|
|
||||||
UseProxy = 305,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 306. This status code was used in a previous
|
|
||||||
/// version of the specification, is no longer used, and is reserved for
|
|
||||||
/// future use.
|
|
||||||
/// </summary>
|
|
||||||
Unused = 306,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 307. Indicates that the requested resource
|
|
||||||
/// is located temporarily under a different URI.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// TemporaryRedirect is a synonym for RedirectKeepVerb.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
TemporaryRedirect = 307,
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Equivalent to status code 307. Indicates that the requested resource
|
|
||||||
/// is located temporarily under a different URI.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// RedirectKeepVerb is a synonym for TemporaryRedirect.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
RedirectKeepVerb = 307,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 400. Indicates that the client's request could
|
|
||||||
/// not be understood by the server due to malformed syntax.
|
|
||||||
/// </summary>
|
|
||||||
BadRequest = 400,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 401. Indicates that the client's request
|
|
||||||
/// requires user authentication.
|
|
||||||
/// </summary>
|
|
||||||
Unauthorized = 401,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 402. This status code is reserved for future
|
|
||||||
/// use.
|
|
||||||
/// </summary>
|
|
||||||
PaymentRequired = 402,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 403. Indicates that the server understood
|
|
||||||
/// the client's request but is refusing to fulfill it.
|
|
||||||
/// </summary>
|
|
||||||
Forbidden = 403,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 404. Indicates that the server has not found
|
|
||||||
/// anything matching the request URI.
|
|
||||||
/// </summary>
|
|
||||||
NotFound = 404,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 405. Indicates that the method specified
|
|
||||||
/// in the request line is not allowed for the resource identified by
|
|
||||||
/// the request URI.
|
|
||||||
/// </summary>
|
|
||||||
MethodNotAllowed = 405,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 406. Indicates that the server does not
|
|
||||||
/// have the appropriate resource to respond to the Accept headers in
|
|
||||||
/// the client's request.
|
|
||||||
/// </summary>
|
|
||||||
NotAcceptable = 406,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 407. Indicates that the client must first
|
|
||||||
/// authenticate itself with the proxy.
|
|
||||||
/// </summary>
|
|
||||||
ProxyAuthenticationRequired = 407,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 408. Indicates that the client did not produce
|
|
||||||
/// a request within the time that the server was prepared to wait.
|
|
||||||
/// </summary>
|
|
||||||
RequestTimeout = 408,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 409. Indicates that the client's request could
|
|
||||||
/// not be completed due to a conflict on the server.
|
|
||||||
/// </summary>
|
|
||||||
Conflict = 409,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 410. Indicates that the requested resource is
|
|
||||||
/// no longer available at the server and no forwarding address is known.
|
|
||||||
/// </summary>
|
|
||||||
Gone = 410,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 411. Indicates that the server refuses to
|
|
||||||
/// accept the client's request without a defined Content-Length.
|
|
||||||
/// </summary>
|
|
||||||
LengthRequired = 411,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 412. Indicates that the precondition given in
|
|
||||||
/// one or more of the request headers evaluated to false when it was tested
|
|
||||||
/// on the server.
|
|
||||||
/// </summary>
|
|
||||||
PreconditionFailed = 412,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 413. Indicates that the entity of the client's
|
|
||||||
/// request is larger than the server is willing or able to process.
|
|
||||||
/// </summary>
|
|
||||||
RequestEntityTooLarge = 413,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 414. Indicates that the request URI is longer
|
|
||||||
/// than the server is willing to interpret.
|
|
||||||
/// </summary>
|
|
||||||
RequestUriTooLong = 414,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 415. Indicates that the entity of the client's
|
|
||||||
/// request is in a format not supported by the requested resource for the
|
|
||||||
/// requested method.
|
|
||||||
/// </summary>
|
|
||||||
UnsupportedMediaType = 415,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 416. Indicates that none of the range
|
|
||||||
/// specifier values in a Range request header overlap the current
|
|
||||||
/// extent of the selected resource.
|
|
||||||
/// </summary>
|
|
||||||
RequestedRangeNotSatisfiable = 416,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 417. Indicates that the expectation given in
|
|
||||||
/// an Expect request header could not be met by the server.
|
|
||||||
/// </summary>
|
|
||||||
ExpectationFailed = 417,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 500. Indicates that the server encountered
|
|
||||||
/// an unexpected condition which prevented it from fulfilling the client's
|
|
||||||
/// request.
|
|
||||||
/// </summary>
|
|
||||||
InternalServerError = 500,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 501. Indicates that the server does not
|
|
||||||
/// support the functionality required to fulfill the client's request.
|
|
||||||
/// </summary>
|
|
||||||
NotImplemented = 501,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 502. Indicates that a gateway or proxy server
|
|
||||||
/// received an invalid response from the upstream server.
|
|
||||||
/// </summary>
|
|
||||||
BadGateway = 502,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 503. Indicates that the server is currently
|
|
||||||
/// unable to handle the client's request due to a temporary overloading
|
|
||||||
/// or maintenance of the server.
|
|
||||||
/// </summary>
|
|
||||||
ServiceUnavailable = 503,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 504. Indicates that a gateway or proxy server
|
|
||||||
/// did not receive a timely response from the upstream server or some other
|
|
||||||
/// auxiliary server.
|
|
||||||
/// </summary>
|
|
||||||
GatewayTimeout = 504,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to status code 505. Indicates that the server does not
|
|
||||||
/// support the HTTP version used in the client's request.
|
|
||||||
/// </summary>
|
|
||||||
HttpVersionNotSupported = 505,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpStreamAsyncResult.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpStreamAsyncResult.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class HttpStreamAsyncResult : IAsyncResult
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private byte[] _buffer;
|
|
||||||
private AsyncCallback _callback;
|
|
||||||
private bool _completed;
|
|
||||||
private int _count;
|
|
||||||
private Exception _exception;
|
|
||||||
private int _offset;
|
|
||||||
private object _state;
|
|
||||||
private object _sync;
|
|
||||||
private int _syncRead;
|
|
||||||
private ManualResetEvent _waitHandle;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpStreamAsyncResult (AsyncCallback callback, object state)
|
|
||||||
{
|
|
||||||
_callback = callback;
|
|
||||||
_state = state;
|
|
||||||
|
|
||||||
_sync = new object ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal byte[] Buffer {
|
|
||||||
get {
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_buffer = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int Count {
|
|
||||||
get {
|
|
||||||
return _count;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_count = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Exception Exception {
|
|
||||||
get {
|
|
||||||
return _exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool HasException {
|
|
||||||
get {
|
|
||||||
return _exception != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int Offset {
|
|
||||||
get {
|
|
||||||
return _offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_offset = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int SyncRead {
|
|
||||||
get {
|
|
||||||
return _syncRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_syncRead = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public object AsyncState {
|
|
||||||
get {
|
|
||||||
return _state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public WaitHandle AsyncWaitHandle {
|
|
||||||
get {
|
|
||||||
lock (_sync) {
|
|
||||||
if (_waitHandle == null)
|
|
||||||
_waitHandle = new ManualResetEvent (_completed);
|
|
||||||
|
|
||||||
return _waitHandle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CompletedSynchronously {
|
|
||||||
get {
|
|
||||||
return _syncRead == _count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCompleted {
|
|
||||||
get {
|
|
||||||
lock (_sync)
|
|
||||||
return _completed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal void Complete ()
|
|
||||||
{
|
|
||||||
lock (_sync) {
|
|
||||||
if (_completed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_completed = true;
|
|
||||||
|
|
||||||
if (_waitHandle != null)
|
|
||||||
_waitHandle.Set ();
|
|
||||||
|
|
||||||
if (_callback != null)
|
|
||||||
_callback.BeginInvoke (this, ar => _callback.EndInvoke (ar), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Complete (Exception exception)
|
|
||||||
{
|
|
||||||
lock (_sync) {
|
|
||||||
if (_completed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_completed = true;
|
|
||||||
_exception = exception;
|
|
||||||
|
|
||||||
if (_waitHandle != null)
|
|
||||||
_waitHandle.Set ();
|
|
||||||
|
|
||||||
if (_callback != null)
|
|
||||||
_callback.BeginInvoke (this, ar => _callback.EndInvoke (ar), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,73 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpVersion.cs
|
|
||||||
*
|
|
||||||
* This code is derived from System.Net.HttpVersion.cs of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2014 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Lawrence Pit <loz@cable.a2000.nl>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides the HTTP version numbers.
|
|
||||||
/// </summary>
|
|
||||||
public class HttpVersion
|
|
||||||
{
|
|
||||||
#region Public Fields
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a <see cref="Version"/> instance for the HTTP/1.0.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly Version Version10 = new Version (1, 0);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a <see cref="Version"/> instance for the HTTP/1.1.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly Version Version11 = new Version (1, 1);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="HttpVersion"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public HttpVersion ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* InputChunkState.cs
|
|
||||||
*
|
|
||||||
* This code is derived from ChunkStream.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2003 Ximian, Inc (http://www.ximian.com)
|
|
||||||
* Copyright (c) 2014-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal enum InputChunkState
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Data,
|
|
||||||
DataEnded,
|
|
||||||
Trailer,
|
|
||||||
End
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* InputState.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpConnection.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2014-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal enum InputState
|
|
||||||
{
|
|
||||||
RequestLine,
|
|
||||||
Headers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* LineState.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpConnection.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2014-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal enum LineState
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Cr,
|
|
||||||
Lf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* NetworkCredential.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014-2017 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides the credentials for the password-based authentication.
|
|
||||||
/// </summary>
|
|
||||||
public class NetworkCredential
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private string _domain;
|
|
||||||
private static readonly string[] _noRoles;
|
|
||||||
private string _password;
|
|
||||||
private string[] _roles;
|
|
||||||
private string _username;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Static Constructor
|
|
||||||
|
|
||||||
static NetworkCredential ()
|
|
||||||
{
|
|
||||||
_noRoles = new string[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="NetworkCredential"/> class with
|
|
||||||
/// the specified <paramref name="username"/> and <paramref name="password"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="username">
|
|
||||||
/// A <see cref="string"/> that represents the username associated with
|
|
||||||
/// the credentials.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="password">
|
|
||||||
/// A <see cref="string"/> that represents the password for the username
|
|
||||||
/// associated with the credentials.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="username"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <paramref name="username"/> is empty.
|
|
||||||
/// </exception>
|
|
||||||
public NetworkCredential (string username, string password)
|
|
||||||
: this (username, password, null, null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="NetworkCredential"/> class with
|
|
||||||
/// the specified <paramref name="username"/>, <paramref name="password"/>,
|
|
||||||
/// <paramref name="domain"/> and <paramref name="roles"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="username">
|
|
||||||
/// A <see cref="string"/> that represents the username associated with
|
|
||||||
/// the credentials.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="password">
|
|
||||||
/// A <see cref="string"/> that represents the password for the username
|
|
||||||
/// associated with the credentials.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="domain">
|
|
||||||
/// A <see cref="string"/> that represents the domain associated with
|
|
||||||
/// the credentials.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="roles">
|
|
||||||
/// An array of <see cref="string"/> that represents the roles
|
|
||||||
/// associated with the credentials if any.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="username"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <paramref name="username"/> is empty.
|
|
||||||
/// </exception>
|
|
||||||
public NetworkCredential (
|
|
||||||
string username, string password, string domain, params string[] roles
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (username == null)
|
|
||||||
throw new ArgumentNullException ("username");
|
|
||||||
|
|
||||||
if (username.Length == 0)
|
|
||||||
throw new ArgumentException ("An empty string.", "username");
|
|
||||||
|
|
||||||
_username = username;
|
|
||||||
_password = password;
|
|
||||||
_domain = domain;
|
|
||||||
_roles = roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the domain associated with the credentials.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This property returns an empty string if the domain was
|
|
||||||
/// initialized with <see langword="null"/>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the domain name
|
|
||||||
/// to which the username belongs.
|
|
||||||
/// </value>
|
|
||||||
public string Domain {
|
|
||||||
get {
|
|
||||||
return _domain ?? String.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal set {
|
|
||||||
_domain = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the password for the username associated with the credentials.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This property returns an empty string if the password was
|
|
||||||
/// initialized with <see langword="null"/>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the password.
|
|
||||||
/// </value>
|
|
||||||
public string Password {
|
|
||||||
get {
|
|
||||||
return _password ?? String.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal set {
|
|
||||||
_password = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the roles associated with the credentials.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This property returns an empty array if the roles were
|
|
||||||
/// initialized with <see langword="null"/>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// An array of <see cref="string"/> that represents the role names
|
|
||||||
/// to which the username belongs.
|
|
||||||
/// </value>
|
|
||||||
public string[] Roles {
|
|
||||||
get {
|
|
||||||
return _roles ?? _noRoles;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal set {
|
|
||||||
_roles = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the username associated with the credentials.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the username.
|
|
||||||
/// </value>
|
|
||||||
public string Username {
|
|
||||||
get {
|
|
||||||
return _username;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal set {
|
|
||||||
_username = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* QueryStringCollection.cs
|
|
||||||
*
|
|
||||||
* This code is derived from HttpUtility.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2018 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Patrik Torstensson <Patrik.Torstensson@labs2.com>
|
|
||||||
* - Wictor Wilén (decode/encode functions) <wictor@ibizkit.se>
|
|
||||||
* - Tim Coleman <tim@timcoleman.com>
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal sealed class QueryStringCollection : NameValueCollection
|
|
||||||
{
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
public QueryStringCollection ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueryStringCollection (int capacity)
|
|
||||||
: base (capacity)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static string urlDecode (string s, Encoding encoding)
|
|
||||||
{
|
|
||||||
return s.IndexOfAny (new[] { '%', '+' }) > -1
|
|
||||||
? HttpUtility.UrlDecode (s, encoding)
|
|
||||||
: s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public static QueryStringCollection Parse (string query)
|
|
||||||
{
|
|
||||||
return Parse (query, Encoding.UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static QueryStringCollection Parse (string query, Encoding encoding)
|
|
||||||
{
|
|
||||||
if (query == null)
|
|
||||||
return new QueryStringCollection (1);
|
|
||||||
|
|
||||||
var len = query.Length;
|
|
||||||
if (len == 0)
|
|
||||||
return new QueryStringCollection (1);
|
|
||||||
|
|
||||||
if (query == "?")
|
|
||||||
return new QueryStringCollection (1);
|
|
||||||
|
|
||||||
if (query[0] == '?')
|
|
||||||
query = query.Substring (1);
|
|
||||||
|
|
||||||
if (encoding == null)
|
|
||||||
encoding = Encoding.UTF8;
|
|
||||||
|
|
||||||
var ret = new QueryStringCollection ();
|
|
||||||
|
|
||||||
var components = query.Split ('&');
|
|
||||||
foreach (var component in components) {
|
|
||||||
len = component.Length;
|
|
||||||
if (len == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (component == "=")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var i = component.IndexOf ('=');
|
|
||||||
if (i < 0) {
|
|
||||||
ret.Add (null, urlDecode (component, encoding));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
ret.Add (null, urlDecode (component.Substring (1), encoding));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = urlDecode (component.Substring (0, i), encoding);
|
|
||||||
|
|
||||||
var start = i + 1;
|
|
||||||
var val = start < len
|
|
||||||
? urlDecode (component.Substring (start), encoding)
|
|
||||||
: String.Empty;
|
|
||||||
|
|
||||||
ret.Add (name, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
var buff = new StringBuilder ();
|
|
||||||
|
|
||||||
foreach (var key in AllKeys)
|
|
||||||
buff.AppendFormat ("{0}={1}&", key, this[key]);
|
|
||||||
|
|
||||||
if (buff.Length > 0)
|
|
||||||
buff.Length--;
|
|
||||||
|
|
||||||
return buff.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* ReadBufferState.cs
|
|
||||||
*
|
|
||||||
* This code is derived from ChunkedInputStream.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2014-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class ReadBufferState
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private HttpStreamAsyncResult _asyncResult;
|
|
||||||
private byte[] _buffer;
|
|
||||||
private int _count;
|
|
||||||
private int _initialCount;
|
|
||||||
private int _offset;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
public ReadBufferState (
|
|
||||||
byte[] buffer, int offset, int count, HttpStreamAsyncResult asyncResult
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_buffer = buffer;
|
|
||||||
_offset = offset;
|
|
||||||
_count = count;
|
|
||||||
_asyncResult = asyncResult;
|
|
||||||
|
|
||||||
_initialCount = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public HttpStreamAsyncResult AsyncResult {
|
|
||||||
get {
|
|
||||||
return _asyncResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_asyncResult = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] Buffer {
|
|
||||||
get {
|
|
||||||
return _buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_buffer = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count {
|
|
||||||
get {
|
|
||||||
return _count;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_count = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int InitialCount {
|
|
||||||
get {
|
|
||||||
return _initialCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_initialCount = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Offset {
|
|
||||||
get {
|
|
||||||
return _offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_offset = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* RequestStream.cs
|
|
||||||
*
|
|
||||||
* This code is derived from RequestStream.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class RequestStream : Stream
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private long _bodyLeft;
|
|
||||||
private byte[] _buffer;
|
|
||||||
private int _count;
|
|
||||||
private bool _disposed;
|
|
||||||
private int _offset;
|
|
||||||
private Stream _stream;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal RequestStream (
|
|
||||||
Stream stream, byte[] buffer, int offset, int count, long contentLength
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_stream = stream;
|
|
||||||
_buffer = buffer;
|
|
||||||
_offset = offset;
|
|
||||||
_count = count;
|
|
||||||
_bodyLeft = contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public override bool CanRead {
|
|
||||||
get {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanSeek {
|
|
||||||
get {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanWrite {
|
|
||||||
get {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Length {
|
|
||||||
get {
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Position {
|
|
||||||
get {
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private int fillFromBuffer (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
// This method returns a int:
|
|
||||||
// - > 0 The number of bytes read from the internal buffer
|
|
||||||
// - 0 No more bytes read from the internal buffer
|
|
||||||
// - -1 No more content data
|
|
||||||
|
|
||||||
if (_bodyLeft == 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (_count == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (count > _count)
|
|
||||||
count = _count;
|
|
||||||
|
|
||||||
if (_bodyLeft > 0 && _bodyLeft < count)
|
|
||||||
count = (int) _bodyLeft;
|
|
||||||
|
|
||||||
Buffer.BlockCopy (_buffer, _offset, buffer, offset, count);
|
|
||||||
|
|
||||||
_offset += count;
|
|
||||||
_count -= count;
|
|
||||||
|
|
||||||
if (_bodyLeft > 0)
|
|
||||||
_bodyLeft -= count;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public override IAsyncResult BeginRead (
|
|
||||||
byte[] buffer, int offset, int count, AsyncCallback callback, object state
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (_disposed) {
|
|
||||||
var name = GetType ().ToString ();
|
|
||||||
|
|
||||||
throw new ObjectDisposedException (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException ("buffer");
|
|
||||||
|
|
||||||
if (offset < 0) {
|
|
||||||
var msg = "A negative value.";
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException ("offset", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count < 0) {
|
|
||||||
var msg = "A negative value.";
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException ("count", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var len = buffer.Length;
|
|
||||||
|
|
||||||
if (offset + count > len) {
|
|
||||||
var msg = "The sum of 'offset' and 'count' is greater than the length of 'buffer'.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
return _stream.BeginRead (buffer, offset, 0, callback, state);
|
|
||||||
|
|
||||||
var nread = fillFromBuffer (buffer, offset, count);
|
|
||||||
|
|
||||||
if (nread != 0) {
|
|
||||||
var ares = new HttpStreamAsyncResult (callback, state);
|
|
||||||
ares.Buffer = buffer;
|
|
||||||
ares.Offset = offset;
|
|
||||||
ares.Count = count;
|
|
||||||
ares.SyncRead = nread > 0 ? nread : 0;
|
|
||||||
ares.Complete ();
|
|
||||||
|
|
||||||
return ares;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_bodyLeft >= 0 && _bodyLeft < count)
|
|
||||||
count = (int) _bodyLeft;
|
|
||||||
|
|
||||||
return _stream.BeginRead (buffer, offset, count, callback, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IAsyncResult BeginWrite (
|
|
||||||
byte[] buffer, int offset, int count, AsyncCallback callback, object state
|
|
||||||
)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Close ()
|
|
||||||
{
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int EndRead (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
if (_disposed) {
|
|
||||||
var name = GetType ().ToString ();
|
|
||||||
|
|
||||||
throw new ObjectDisposedException (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asyncResult == null)
|
|
||||||
throw new ArgumentNullException ("asyncResult");
|
|
||||||
|
|
||||||
if (asyncResult is HttpStreamAsyncResult) {
|
|
||||||
var ares = (HttpStreamAsyncResult) asyncResult;
|
|
||||||
|
|
||||||
if (!ares.IsCompleted)
|
|
||||||
ares.AsyncWaitHandle.WaitOne ();
|
|
||||||
|
|
||||||
return ares.SyncRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nread = _stream.EndRead (asyncResult);
|
|
||||||
|
|
||||||
if (nread > 0 && _bodyLeft > 0)
|
|
||||||
_bodyLeft -= nread;
|
|
||||||
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void EndWrite (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (_disposed) {
|
|
||||||
var name = GetType ().ToString ();
|
|
||||||
|
|
||||||
throw new ObjectDisposedException (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException ("buffer");
|
|
||||||
|
|
||||||
if (offset < 0) {
|
|
||||||
var msg = "A negative value.";
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException ("offset", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count < 0) {
|
|
||||||
var msg = "A negative value.";
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException ("count", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var len = buffer.Length;
|
|
||||||
|
|
||||||
if (offset + count > len) {
|
|
||||||
var msg = "The sum of 'offset' and 'count' is greater than the length of 'buffer'.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var nread = fillFromBuffer (buffer, offset, count);
|
|
||||||
|
|
||||||
if (nread == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (nread > 0)
|
|
||||||
return nread;
|
|
||||||
|
|
||||||
nread = _stream.Read (buffer, offset, count);
|
|
||||||
|
|
||||||
if (nread > 0 && _bodyLeft > 0)
|
|
||||||
_bodyLeft -= nread;
|
|
||||||
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek (long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength (long value)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,403 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* ResponseStream.cs
|
|
||||||
*
|
|
||||||
* This code is derived from ResponseStream.cs (System.Net) of Mono
|
|
||||||
* (http://www.mono-project.com).
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
|
||||||
* Copyright (c) 2012-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Gonzalo Paniagua Javier <gonzalo@novell.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
internal class ResponseStream : Stream
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private MemoryStream _bodyBuffer;
|
|
||||||
private static readonly byte[] _crlf;
|
|
||||||
private bool _disposed;
|
|
||||||
private Stream _innerStream;
|
|
||||||
private static readonly byte[] _lastChunk;
|
|
||||||
private static readonly int _maxHeadersLength;
|
|
||||||
private HttpListenerResponse _response;
|
|
||||||
private bool _sendChunked;
|
|
||||||
private Action<byte[], int, int> _write;
|
|
||||||
private Action<byte[], int, int> _writeBody;
|
|
||||||
private Action<byte[], int, int> _writeChunked;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Static Constructor
|
|
||||||
|
|
||||||
static ResponseStream ()
|
|
||||||
{
|
|
||||||
_crlf = new byte[] { 13, 10 }; // "\r\n"
|
|
||||||
_lastChunk = new byte[] { 48, 13, 10, 13, 10 }; // "0\r\n\r\n"
|
|
||||||
_maxHeadersLength = 32768;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal ResponseStream (
|
|
||||||
Stream innerStream,
|
|
||||||
HttpListenerResponse response,
|
|
||||||
bool ignoreWriteExceptions
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_innerStream = innerStream;
|
|
||||||
_response = response;
|
|
||||||
|
|
||||||
if (ignoreWriteExceptions) {
|
|
||||||
_write = writeWithoutThrowingException;
|
|
||||||
_writeChunked = writeChunkedWithoutThrowingException;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_write = innerStream.Write;
|
|
||||||
_writeChunked = writeChunked;
|
|
||||||
}
|
|
||||||
|
|
||||||
_bodyBuffer = new MemoryStream ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public override bool CanRead {
|
|
||||||
get {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanSeek {
|
|
||||||
get {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanWrite {
|
|
||||||
get {
|
|
||||||
return !_disposed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Length {
|
|
||||||
get {
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Position {
|
|
||||||
get {
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private bool flush (bool closing)
|
|
||||||
{
|
|
||||||
if (!_response.HeadersSent) {
|
|
||||||
if (!flushHeaders ())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_response.HeadersSent = true;
|
|
||||||
|
|
||||||
_sendChunked = _response.SendChunked;
|
|
||||||
_writeBody = _sendChunked ? _writeChunked : _write;
|
|
||||||
}
|
|
||||||
|
|
||||||
flushBody (closing);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void flushBody (bool closing)
|
|
||||||
{
|
|
||||||
using (_bodyBuffer) {
|
|
||||||
var len = _bodyBuffer.Length;
|
|
||||||
|
|
||||||
if (len > Int32.MaxValue) {
|
|
||||||
_bodyBuffer.Position = 0;
|
|
||||||
|
|
||||||
var buffLen = 1024;
|
|
||||||
var buff = new byte[buffLen];
|
|
||||||
var nread = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
nread = _bodyBuffer.Read (buff, 0, buffLen);
|
|
||||||
|
|
||||||
if (nread <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
_writeBody (buff, 0, nread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (len > 0) {
|
|
||||||
_writeBody (_bodyBuffer.GetBuffer (), 0, (int) len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!closing) {
|
|
||||||
_bodyBuffer = new MemoryStream ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sendChunked)
|
|
||||||
_write (_lastChunk, 0, 5);
|
|
||||||
|
|
||||||
_bodyBuffer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool flushHeaders ()
|
|
||||||
{
|
|
||||||
if (!_response.SendChunked) {
|
|
||||||
if (_response.ContentLength64 != _bodyBuffer.Length)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var statusLine = _response.StatusLine;
|
|
||||||
var headers = _response.FullHeaders;
|
|
||||||
|
|
||||||
var buff = new MemoryStream ();
|
|
||||||
var enc = Encoding.UTF8;
|
|
||||||
|
|
||||||
using (var writer = new StreamWriter (buff, enc, 256)) {
|
|
||||||
writer.Write (statusLine);
|
|
||||||
writer.Write (headers.ToStringMultiValue (true));
|
|
||||||
writer.Flush ();
|
|
||||||
|
|
||||||
var start = enc.GetPreamble ().Length;
|
|
||||||
var len = buff.Length - start;
|
|
||||||
|
|
||||||
if (len > _maxHeadersLength)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_write (buff.GetBuffer (), start, (int) len);
|
|
||||||
}
|
|
||||||
|
|
||||||
_response.CloseConnection = headers["Connection"] == "close";
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getChunkSizeBytes (int size)
|
|
||||||
{
|
|
||||||
var chunkSize = String.Format ("{0:x}\r\n", size);
|
|
||||||
|
|
||||||
return Encoding.ASCII.GetBytes (chunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeChunked (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
var size = getChunkSizeBytes (count);
|
|
||||||
|
|
||||||
_innerStream.Write (size, 0, size.Length);
|
|
||||||
_innerStream.Write (buffer, offset, count);
|
|
||||||
_innerStream.Write (_crlf, 0, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeChunkedWithoutThrowingException (
|
|
||||||
byte[] buffer, int offset, int count
|
|
||||||
)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
writeChunked (buffer, offset, count);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeWithoutThrowingException (
|
|
||||||
byte[] buffer, int offset, int count
|
|
||||||
)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
_innerStream.Write (buffer, offset, count);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal void Close (bool force)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
|
|
||||||
if (!force) {
|
|
||||||
if (flush (true)) {
|
|
||||||
_response.Close ();
|
|
||||||
|
|
||||||
_response = null;
|
|
||||||
_innerStream = null;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_response.CloseConnection = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sendChunked)
|
|
||||||
_write (_lastChunk, 0, 5);
|
|
||||||
|
|
||||||
_bodyBuffer.Dispose ();
|
|
||||||
_response.Abort ();
|
|
||||||
|
|
||||||
_bodyBuffer = null;
|
|
||||||
_response = null;
|
|
||||||
_innerStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void InternalWrite (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
_write (buffer, offset, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public override IAsyncResult BeginRead (
|
|
||||||
byte[] buffer,
|
|
||||||
int offset,
|
|
||||||
int count,
|
|
||||||
AsyncCallback callback,
|
|
||||||
object state
|
|
||||||
)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IAsyncResult BeginWrite (
|
|
||||||
byte[] buffer,
|
|
||||||
int offset,
|
|
||||||
int count,
|
|
||||||
AsyncCallback callback,
|
|
||||||
object state
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (_disposed) {
|
|
||||||
var name = GetType ().ToString ();
|
|
||||||
|
|
||||||
throw new ObjectDisposedException (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _bodyBuffer.BeginWrite (buffer, offset, count, callback, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Close ()
|
|
||||||
{
|
|
||||||
Close (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose (bool disposing)
|
|
||||||
{
|
|
||||||
Close (!disposing);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int EndRead (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void EndWrite (IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
if (_disposed) {
|
|
||||||
var name = GetType ().ToString ();
|
|
||||||
|
|
||||||
throw new ObjectDisposedException (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
_bodyBuffer.EndWrite (asyncResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush ()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var sendChunked = _sendChunked || _response.SendChunked;
|
|
||||||
|
|
||||||
if (!sendChunked)
|
|
||||||
return;
|
|
||||||
|
|
||||||
flush (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek (long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength (long value)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write (byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (_disposed) {
|
|
||||||
var name = GetType ().ToString ();
|
|
||||||
|
|
||||||
throw new ObjectDisposedException (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
_bodyBuffer.Write (buffer, offset, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,236 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* ServerSslConfiguration.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 liryna
|
|
||||||
* Copyright (c) 2014-2020 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Authors
|
|
||||||
/*
|
|
||||||
* Authors:
|
|
||||||
* - Liryna <liryna.stark@gmail.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Net.Security;
|
|
||||||
using System.Security.Authentication;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stores the parameters for the <see cref="SslStream"/> used by servers.
|
|
||||||
/// </summary>
|
|
||||||
public class ServerSslConfiguration
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private bool _checkCertRevocation;
|
|
||||||
private bool _clientCertRequired;
|
|
||||||
private RemoteCertificateValidationCallback _clientCertValidationCallback;
|
|
||||||
private SslProtocols _enabledSslProtocols;
|
|
||||||
private X509Certificate2 _serverCert;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ServerSslConfiguration"/>
|
|
||||||
/// class.
|
|
||||||
/// </summary>
|
|
||||||
public ServerSslConfiguration ()
|
|
||||||
{
|
|
||||||
_enabledSslProtocols = SslProtocols.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ServerSslConfiguration"/>
|
|
||||||
/// class that stores the parameters copied from the specified configuration.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configuration">
|
|
||||||
/// A <see cref="ServerSslConfiguration"/> from which to copy.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="configuration"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
public ServerSslConfiguration (ServerSslConfiguration configuration)
|
|
||||||
{
|
|
||||||
if (configuration == null)
|
|
||||||
throw new ArgumentNullException ("configuration");
|
|
||||||
|
|
||||||
_checkCertRevocation = configuration._checkCertRevocation;
|
|
||||||
_clientCertRequired = configuration._clientCertRequired;
|
|
||||||
_clientCertValidationCallback = configuration._clientCertValidationCallback;
|
|
||||||
_enabledSslProtocols = configuration._enabledSslProtocols;
|
|
||||||
_serverCert = configuration._serverCert;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the certificate revocation
|
|
||||||
/// list is checked during authentication.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// <c>true</c> if the certificate revocation list is checked during
|
|
||||||
/// authentication; otherwise, <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public bool CheckCertificateRevocation {
|
|
||||||
get {
|
|
||||||
return _checkCertRevocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_checkCertRevocation = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the client is asked for
|
|
||||||
/// a certificate for authentication.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// <c>true</c> if the client is asked for a certificate for
|
|
||||||
/// authentication; otherwise, <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public bool ClientCertificateRequired {
|
|
||||||
get {
|
|
||||||
return _clientCertRequired;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_clientCertRequired = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the callback used to validate the certificate supplied by
|
|
||||||
/// the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The certificate is valid if the callback returns <c>true</c>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="RemoteCertificateValidationCallback"/> delegate that
|
|
||||||
/// invokes the method called for validating the certificate.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is a delegate that invokes a method that only
|
|
||||||
/// returns <c>true</c>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public RemoteCertificateValidationCallback ClientCertificateValidationCallback {
|
|
||||||
get {
|
|
||||||
if (_clientCertValidationCallback == null)
|
|
||||||
_clientCertValidationCallback = defaultValidateClientCertificate;
|
|
||||||
|
|
||||||
return _clientCertValidationCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_clientCertValidationCallback = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the protocols used for authentication.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// Any of the <see cref="SslProtocols"/> enum values.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It represents the protocols used for authentication.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <see cref="SslProtocols.None"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public SslProtocols EnabledSslProtocols {
|
|
||||||
get {
|
|
||||||
return _enabledSslProtocols;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_enabledSslProtocols = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the certificate used to authenticate the server.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="X509Certificate2"/> or <see langword="null"/>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The certificate represents an X.509 certificate.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <see langword="null"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public X509Certificate2 ServerCertificate {
|
|
||||||
get {
|
|
||||||
return _serverCert;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_serverCert = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static bool defaultValidateClientCertificate (
|
|
||||||
object sender,
|
|
||||||
X509Certificate certificate,
|
|
||||||
X509Chain chain,
|
|
||||||
SslPolicyErrors sslPolicyErrors
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,395 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpListenerWebSocketContext.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2018 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.IO;
|
|
||||||
using System.Security.Principal;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net.WebSockets
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides the access to the information in a WebSocket handshake request to
|
|
||||||
/// a <see cref="HttpListener"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public class HttpListenerWebSocketContext : WebSocketContext
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private HttpListenerContext _context;
|
|
||||||
private WebSocket _websocket;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpListenerWebSocketContext (
|
|
||||||
HttpListenerContext context, string protocol
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_websocket = new WebSocket (this, protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal Logger Log {
|
|
||||||
get {
|
|
||||||
return _context.Listener.Log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Stream Stream {
|
|
||||||
get {
|
|
||||||
return _context.Connection.Stream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP cookies included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains
|
|
||||||
/// the cookies.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// An empty collection if not included.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override CookieCollection CookieCollection {
|
|
||||||
get {
|
|
||||||
return _context.Request.Cookies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP headers included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="NameValueCollection"/> that contains the headers.
|
|
||||||
/// </value>
|
|
||||||
public override NameValueCollection Headers {
|
|
||||||
get {
|
|
||||||
return _context.Request.Headers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Host header included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the server host name requested
|
|
||||||
/// by the client.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It includes the port number if provided.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override string Host {
|
|
||||||
get {
|
|
||||||
return _context.Request.UserHostName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the client is authenticated.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the client is authenticated; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public override bool IsAuthenticated {
|
|
||||||
get {
|
|
||||||
return _context.Request.IsAuthenticated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the handshake request is sent from
|
|
||||||
/// the local computer.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the handshake request is sent from the same computer
|
|
||||||
/// as the server; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public override bool IsLocal {
|
|
||||||
get {
|
|
||||||
return _context.Request.IsLocal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether a secure connection is used to send
|
|
||||||
/// the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the connection is secure; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public override bool IsSecureConnection {
|
|
||||||
get {
|
|
||||||
return _context.Request.IsSecureConnection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the request is a WebSocket handshake
|
|
||||||
/// request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the request is a WebSocket handshake request; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public override bool IsWebSocketRequest {
|
|
||||||
get {
|
|
||||||
return _context.Request.IsWebSocketRequest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Origin header included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the value of the Origin header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override string Origin {
|
|
||||||
get {
|
|
||||||
return _context.Request.Headers["Origin"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the query string included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="NameValueCollection"/> that contains the query
|
|
||||||
/// parameters.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// An empty collection if not included.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override NameValueCollection QueryString {
|
|
||||||
get {
|
|
||||||
return _context.Request.QueryString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the URI requested by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="Uri"/> that represents the URI parsed from the request.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the URI cannot be parsed.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override Uri RequestUri {
|
|
||||||
get {
|
|
||||||
return _context.Request.Url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Sec-WebSocket-Key header included in
|
|
||||||
/// the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the value of
|
|
||||||
/// the Sec-WebSocket-Key header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The value is used to prove that the server received
|
|
||||||
/// a valid WebSocket handshake request.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override string SecWebSocketKey {
|
|
||||||
get {
|
|
||||||
return _context.Request.Headers["Sec-WebSocket-Key"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the names of the subprotocols from the Sec-WebSocket-Protocol
|
|
||||||
/// header included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// An <see cref="T:System.Collections.Generic.IEnumerable{string}"/>
|
|
||||||
/// instance.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It provides an enumerator which supports the iteration over
|
|
||||||
/// the collection of the names of the subprotocols.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override IEnumerable<string> SecWebSocketProtocols {
|
|
||||||
get {
|
|
||||||
var val = _context.Request.Headers["Sec-WebSocket-Protocol"];
|
|
||||||
if (val == null || val.Length == 0)
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
foreach (var elm in val.Split (',')) {
|
|
||||||
var protocol = elm.Trim ();
|
|
||||||
if (protocol.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
yield return protocol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Sec-WebSocket-Version header included in
|
|
||||||
/// the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the WebSocket protocol
|
|
||||||
/// version specified by the client.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override string SecWebSocketVersion {
|
|
||||||
get {
|
|
||||||
return _context.Request.Headers["Sec-WebSocket-Version"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the endpoint to which the handshake request is sent.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server IP
|
|
||||||
/// address and port number.
|
|
||||||
/// </value>
|
|
||||||
public override System.Net.IPEndPoint ServerEndPoint {
|
|
||||||
get {
|
|
||||||
return _context.Request.LocalEndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the client information.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="IPrincipal"/> instance that represents identity,
|
|
||||||
/// authentication, and security roles for the client.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the client is not authenticated.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override IPrincipal User {
|
|
||||||
get {
|
|
||||||
return _context.User;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the endpoint from which the handshake request is sent.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client IP
|
|
||||||
/// address and port number.
|
|
||||||
/// </value>
|
|
||||||
public override System.Net.IPEndPoint UserEndPoint {
|
|
||||||
get {
|
|
||||||
return _context.Request.RemoteEndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the WebSocket instance used for two-way communication between
|
|
||||||
/// the client and server.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="WebSocketSharp.WebSocket"/>.
|
|
||||||
/// </value>
|
|
||||||
public override WebSocket WebSocket {
|
|
||||||
get {
|
|
||||||
return _websocket;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal void Close ()
|
|
||||||
{
|
|
||||||
_context.Connection.Close (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Close (HttpStatusCode code)
|
|
||||||
{
|
|
||||||
_context.Response.StatusCode = (int) code;
|
|
||||||
_context.Response.Close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a string that represents the current instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="string"/> that contains the request line and headers
|
|
||||||
/// included in the handshake request.
|
|
||||||
/// </returns>
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
return _context.Request.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,518 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* TcpListenerWebSocketContext.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2018 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - Liryna <liryna.stark@gmail.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Security;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net.WebSockets
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides the access to the information in a WebSocket handshake request to
|
|
||||||
/// a <see cref="TcpListener"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
internal class TcpListenerWebSocketContext : WebSocketContext
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private Logger _log;
|
|
||||||
private NameValueCollection _queryString;
|
|
||||||
private HttpRequest _request;
|
|
||||||
private Uri _requestUri;
|
|
||||||
private bool _secure;
|
|
||||||
private System.Net.EndPoint _serverEndPoint;
|
|
||||||
private Stream _stream;
|
|
||||||
private TcpClient _tcpClient;
|
|
||||||
private IPrincipal _user;
|
|
||||||
private System.Net.EndPoint _userEndPoint;
|
|
||||||
private WebSocket _websocket;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal TcpListenerWebSocketContext (
|
|
||||||
TcpClient tcpClient,
|
|
||||||
string protocol,
|
|
||||||
bool secure,
|
|
||||||
ServerSslConfiguration sslConfig,
|
|
||||||
Logger log
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_tcpClient = tcpClient;
|
|
||||||
_secure = secure;
|
|
||||||
_log = log;
|
|
||||||
|
|
||||||
var netStream = tcpClient.GetStream ();
|
|
||||||
if (secure) {
|
|
||||||
var sslStream = new SslStream (
|
|
||||||
netStream,
|
|
||||||
false,
|
|
||||||
sslConfig.ClientCertificateValidationCallback
|
|
||||||
);
|
|
||||||
|
|
||||||
sslStream.AuthenticateAsServer (
|
|
||||||
sslConfig.ServerCertificate,
|
|
||||||
sslConfig.ClientCertificateRequired,
|
|
||||||
sslConfig.EnabledSslProtocols,
|
|
||||||
sslConfig.CheckCertificateRevocation
|
|
||||||
);
|
|
||||||
|
|
||||||
_stream = sslStream;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_stream = netStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
var sock = tcpClient.Client;
|
|
||||||
_serverEndPoint = sock.LocalEndPoint;
|
|
||||||
_userEndPoint = sock.RemoteEndPoint;
|
|
||||||
|
|
||||||
_request = HttpRequest.Read (_stream, 90000);
|
|
||||||
_websocket = new WebSocket (this, protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal Logger Log {
|
|
||||||
get {
|
|
||||||
return _log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Stream Stream {
|
|
||||||
get {
|
|
||||||
return _stream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP cookies included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains
|
|
||||||
/// the cookies.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// An empty collection if not included.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override CookieCollection CookieCollection {
|
|
||||||
get {
|
|
||||||
return _request.Cookies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP headers included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="NameValueCollection"/> that contains the headers.
|
|
||||||
/// </value>
|
|
||||||
public override NameValueCollection Headers {
|
|
||||||
get {
|
|
||||||
return _request.Headers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Host header included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the server host name requested
|
|
||||||
/// by the client.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It includes the port number if provided.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override string Host {
|
|
||||||
get {
|
|
||||||
return _request.Headers["Host"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the client is authenticated.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the client is authenticated; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public override bool IsAuthenticated {
|
|
||||||
get {
|
|
||||||
return _user != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the handshake request is sent from
|
|
||||||
/// the local computer.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the handshake request is sent from the same computer
|
|
||||||
/// as the server; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public override bool IsLocal {
|
|
||||||
get {
|
|
||||||
return UserEndPoint.Address.IsLocal ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether a secure connection is used to send
|
|
||||||
/// the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the connection is secure; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public override bool IsSecureConnection {
|
|
||||||
get {
|
|
||||||
return _secure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the request is a WebSocket handshake
|
|
||||||
/// request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the request is a WebSocket handshake request; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public override bool IsWebSocketRequest {
|
|
||||||
get {
|
|
||||||
return _request.IsWebSocketRequest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Origin header included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the value of the Origin header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override string Origin {
|
|
||||||
get {
|
|
||||||
return _request.Headers["Origin"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the query string included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="NameValueCollection"/> that contains the query
|
|
||||||
/// parameters.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// An empty collection if not included.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override NameValueCollection QueryString {
|
|
||||||
get {
|
|
||||||
if (_queryString == null) {
|
|
||||||
var uri = RequestUri;
|
|
||||||
_queryString = QueryStringCollection.Parse (
|
|
||||||
uri != null ? uri.Query : null,
|
|
||||||
Encoding.UTF8
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _queryString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the URI requested by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="Uri"/> that represents the URI parsed from the request.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the URI cannot be parsed.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override Uri RequestUri {
|
|
||||||
get {
|
|
||||||
if (_requestUri == null) {
|
|
||||||
_requestUri = HttpUtility.CreateRequestUrl (
|
|
||||||
_request.RequestUri,
|
|
||||||
_request.Headers["Host"],
|
|
||||||
_request.IsWebSocketRequest,
|
|
||||||
_secure
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _requestUri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Sec-WebSocket-Key header included in
|
|
||||||
/// the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the value of
|
|
||||||
/// the Sec-WebSocket-Key header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The value is used to prove that the server received
|
|
||||||
/// a valid WebSocket handshake request.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override string SecWebSocketKey {
|
|
||||||
get {
|
|
||||||
return _request.Headers["Sec-WebSocket-Key"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the names of the subprotocols from the Sec-WebSocket-Protocol
|
|
||||||
/// header included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// An <see cref="T:System.Collections.Generic.IEnumerable{string}"/>
|
|
||||||
/// instance.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It provides an enumerator which supports the iteration over
|
|
||||||
/// the collection of the names of the subprotocols.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override IEnumerable<string> SecWebSocketProtocols {
|
|
||||||
get {
|
|
||||||
var val = _request.Headers["Sec-WebSocket-Protocol"];
|
|
||||||
if (val == null || val.Length == 0)
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
foreach (var elm in val.Split (',')) {
|
|
||||||
var protocol = elm.Trim ();
|
|
||||||
if (protocol.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
yield return protocol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Sec-WebSocket-Version header included in
|
|
||||||
/// the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the WebSocket protocol
|
|
||||||
/// version specified by the client.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the header is not present.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override string SecWebSocketVersion {
|
|
||||||
get {
|
|
||||||
return _request.Headers["Sec-WebSocket-Version"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the endpoint to which the handshake request is sent.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server IP
|
|
||||||
/// address and port number.
|
|
||||||
/// </value>
|
|
||||||
public override System.Net.IPEndPoint ServerEndPoint {
|
|
||||||
get {
|
|
||||||
return (System.Net.IPEndPoint) _serverEndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the client information.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="IPrincipal"/> instance that represents identity,
|
|
||||||
/// authentication, and security roles for the client.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <see langword="null"/> if the client is not authenticated.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public override IPrincipal User {
|
|
||||||
get {
|
|
||||||
return _user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the endpoint from which the handshake request is sent.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client IP
|
|
||||||
/// address and port number.
|
|
||||||
/// </value>
|
|
||||||
public override System.Net.IPEndPoint UserEndPoint {
|
|
||||||
get {
|
|
||||||
return (System.Net.IPEndPoint) _userEndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the WebSocket instance used for two-way communication between
|
|
||||||
/// the client and server.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="WebSocketSharp.WebSocket"/>.
|
|
||||||
/// </value>
|
|
||||||
public override WebSocket WebSocket {
|
|
||||||
get {
|
|
||||||
return _websocket;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private HttpRequest sendAuthenticationChallenge (string challenge)
|
|
||||||
{
|
|
||||||
var res = HttpResponse.CreateUnauthorizedResponse (challenge);
|
|
||||||
var bytes = res.ToByteArray ();
|
|
||||||
_stream.Write (bytes, 0, bytes.Length);
|
|
||||||
|
|
||||||
return HttpRequest.Read (_stream, 15000);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal bool Authenticate (
|
|
||||||
AuthenticationSchemes scheme,
|
|
||||||
string realm,
|
|
||||||
Func<IIdentity, NetworkCredential> credentialsFinder
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var chal = new AuthenticationChallenge (scheme, realm).ToString ();
|
|
||||||
|
|
||||||
var retry = -1;
|
|
||||||
Func<bool> auth = null;
|
|
||||||
auth =
|
|
||||||
() => {
|
|
||||||
retry++;
|
|
||||||
if (retry > 99)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var user = HttpUtility.CreateUser (
|
|
||||||
_request.Headers["Authorization"],
|
|
||||||
scheme,
|
|
||||||
realm,
|
|
||||||
_request.HttpMethod,
|
|
||||||
credentialsFinder
|
|
||||||
);
|
|
||||||
|
|
||||||
if (user != null && user.Identity.IsAuthenticated) {
|
|
||||||
_user = user;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_request = sendAuthenticationChallenge (chal);
|
|
||||||
return auth ();
|
|
||||||
};
|
|
||||||
|
|
||||||
return auth ();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Close ()
|
|
||||||
{
|
|
||||||
_stream.Close ();
|
|
||||||
_tcpClient.Close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Close (HttpStatusCode code)
|
|
||||||
{
|
|
||||||
var res = HttpResponse.CreateCloseResponse (code);
|
|
||||||
var bytes = res.ToByteArray ();
|
|
||||||
_stream.Write (bytes, 0, bytes.Length);
|
|
||||||
|
|
||||||
_stream.Close ();
|
|
||||||
_tcpClient.Close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a string that represents the current instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="string"/> that contains the request line and headers
|
|
||||||
/// included in the handshake request.
|
|
||||||
/// </returns>
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
return _request.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* WebSocketContext.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2018 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Security.Principal;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Net.WebSockets
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Exposes the access to the information in a WebSocket handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This class is an abstract class.
|
|
||||||
/// </remarks>
|
|
||||||
public abstract class WebSocketContext
|
|
||||||
{
|
|
||||||
#region Protected Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="WebSocketContext"/> class.
|
|
||||||
/// </summary>
|
|
||||||
protected WebSocketContext ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP cookies included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains
|
|
||||||
/// the cookies.
|
|
||||||
/// </value>
|
|
||||||
public abstract CookieCollection CookieCollection { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HTTP headers included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="NameValueCollection"/> that contains the headers.
|
|
||||||
/// </value>
|
|
||||||
public abstract NameValueCollection Headers { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Host header included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the server host name requested
|
|
||||||
/// by the client.
|
|
||||||
/// </value>
|
|
||||||
public abstract string Host { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the client is authenticated.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the client is authenticated; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public abstract bool IsAuthenticated { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the handshake request is sent from
|
|
||||||
/// the local computer.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the handshake request is sent from the same computer
|
|
||||||
/// as the server; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public abstract bool IsLocal { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether a secure connection is used to send
|
|
||||||
/// the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the connection is secure; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public abstract bool IsSecureConnection { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the request is a WebSocket handshake
|
|
||||||
/// request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the request is a WebSocket handshake request; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public abstract bool IsWebSocketRequest { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Origin header included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the value of the Origin header.
|
|
||||||
/// </value>
|
|
||||||
public abstract string Origin { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the query string included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="NameValueCollection"/> that contains the query parameters.
|
|
||||||
/// </value>
|
|
||||||
public abstract NameValueCollection QueryString { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the URI requested by the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="Uri"/> that represents the URI parsed from the request.
|
|
||||||
/// </value>
|
|
||||||
public abstract Uri RequestUri { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Sec-WebSocket-Key header included in
|
|
||||||
/// the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that represents the value of
|
|
||||||
/// the Sec-WebSocket-Key header.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The value is used to prove that the server received
|
|
||||||
/// a valid WebSocket handshake request.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public abstract string SecWebSocketKey { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the names of the subprotocols from the Sec-WebSocket-Protocol
|
|
||||||
/// header included in the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// An <see cref="T:System.Collections.Generic.IEnumerable{string}"/>
|
|
||||||
/// instance.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It provides an enumerator which supports the iteration over
|
|
||||||
/// the collection of the names of the subprotocols.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public abstract IEnumerable<string> SecWebSocketProtocols { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the Sec-WebSocket-Version header included in
|
|
||||||
/// the handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the WebSocket protocol
|
|
||||||
/// version specified by the client.
|
|
||||||
/// </value>
|
|
||||||
public abstract string SecWebSocketVersion { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the endpoint to which the handshake request is sent.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server IP
|
|
||||||
/// address and port number.
|
|
||||||
/// </value>
|
|
||||||
public abstract System.Net.IPEndPoint ServerEndPoint { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the client information.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="IPrincipal"/> instance that represents identity,
|
|
||||||
/// authentication, and security roles for the client.
|
|
||||||
/// </value>
|
|
||||||
public abstract IPrincipal User { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the endpoint from which the handshake request is sent.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client IP
|
|
||||||
/// address and port number.
|
|
||||||
/// </value>
|
|
||||||
public abstract System.Net.IPEndPoint UserEndPoint { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the WebSocket instance used for two-way communication between
|
|
||||||
/// the client and server.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="WebSocketSharp.WebSocket"/>.
|
|
||||||
/// </value>
|
|
||||||
public abstract WebSocket WebSocket { get; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* Opcode.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2016 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the WebSocket frame type.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The values of this enumeration are defined in
|
|
||||||
/// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">
|
|
||||||
/// Section 5.2</see> of RFC 6455.
|
|
||||||
/// </remarks>
|
|
||||||
internal enum Opcode : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 0. Indicates continuation frame.
|
|
||||||
/// </summary>
|
|
||||||
Cont = 0x0,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 1. Indicates text frame.
|
|
||||||
/// </summary>
|
|
||||||
Text = 0x1,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 2. Indicates binary frame.
|
|
||||||
/// </summary>
|
|
||||||
Binary = 0x2,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 8. Indicates connection close frame.
|
|
||||||
/// </summary>
|
|
||||||
Close = 0x8,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 9. Indicates ping frame.
|
|
||||||
/// </summary>
|
|
||||||
Ping = 0x9,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 10. Indicates pong frame.
|
|
||||||
/// </summary>
|
|
||||||
Pong = 0xa
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
public class OpenEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public string LocalIP { get; private set; }
|
|
||||||
public string RemoteIP { get; private set; }
|
|
||||||
public OpenEventArgs(string localIp, string remoteIp)
|
|
||||||
{
|
|
||||||
LocalIP = localIp;
|
|
||||||
RemoteIP = remoteIp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* PayloadData.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2019 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
internal class PayloadData : IEnumerable<byte>
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private byte[] _data;
|
|
||||||
private long _extDataLength;
|
|
||||||
private long _length;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Fields
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the empty payload data.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly PayloadData Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the allowable max length of payload data.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="WebSocketException"/> will occur when the length of
|
|
||||||
/// incoming payload data is greater than the value of this field.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If you would like to change the value of this field, it must be
|
|
||||||
/// a number between <see cref="WebSocket.FragmentLength"/> and
|
|
||||||
/// <see cref="Int64.MaxValue"/> inclusive.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public static readonly ulong MaxLength;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Static Constructor
|
|
||||||
|
|
||||||
static PayloadData ()
|
|
||||||
{
|
|
||||||
Empty = new PayloadData (WebSocket.EmptyBytes, 0);
|
|
||||||
MaxLength = Int64.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal PayloadData (byte[] data)
|
|
||||||
: this (data, data.LongLength)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal PayloadData (byte[] data, long length)
|
|
||||||
{
|
|
||||||
_data = data;
|
|
||||||
_length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal PayloadData (ushort code, string reason)
|
|
||||||
{
|
|
||||||
_data = code.Append (reason);
|
|
||||||
_length = _data.LongLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal ushort Code {
|
|
||||||
get {
|
|
||||||
return _length >= 2
|
|
||||||
? _data.SubArray (0, 2).ToUInt16 (ByteOrder.Big)
|
|
||||||
: (ushort) 1005;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal long ExtensionDataLength {
|
|
||||||
get {
|
|
||||||
return _extDataLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_extDataLength = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool HasReservedCode {
|
|
||||||
get {
|
|
||||||
return _length >= 2 && Code.IsReserved ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal string Reason {
|
|
||||||
get {
|
|
||||||
if (_length <= 2)
|
|
||||||
return String.Empty;
|
|
||||||
|
|
||||||
var raw = _data.SubArray (2, _length - 2);
|
|
||||||
|
|
||||||
string reason;
|
|
||||||
return raw.TryGetUTF8DecodedString (out reason)
|
|
||||||
? reason
|
|
||||||
: String.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public byte[] ApplicationData {
|
|
||||||
get {
|
|
||||||
return _extDataLength > 0
|
|
||||||
? _data.SubArray (_extDataLength, _length - _extDataLength)
|
|
||||||
: _data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] ExtensionData {
|
|
||||||
get {
|
|
||||||
return _extDataLength > 0
|
|
||||||
? _data.SubArray (0, _extDataLength)
|
|
||||||
: WebSocket.EmptyBytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong Length {
|
|
||||||
get {
|
|
||||||
return (ulong) _length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal void Mask (byte[] key)
|
|
||||||
{
|
|
||||||
for (long i = 0; i < _length; i++)
|
|
||||||
_data[i] = (byte) (_data[i] ^ key[i % 4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public IEnumerator<byte> GetEnumerator ()
|
|
||||||
{
|
|
||||||
foreach (var b in _data)
|
|
||||||
yield return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] ToArray ()
|
|
||||||
{
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
return BitConverter.ToString (_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Explicit Interface Implementations
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator ()
|
|
||||||
{
|
|
||||||
return GetEnumerator ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* Rsv.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2015 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether each RSV (RSV1, RSV2, and RSV3) of a WebSocket frame is non-zero.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The values of this enumeration are defined in
|
|
||||||
/// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2</see> of RFC 6455.
|
|
||||||
/// </remarks>
|
|
||||||
internal enum Rsv : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 0. Indicates zero.
|
|
||||||
/// </summary>
|
|
||||||
Off = 0x0,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 1. Indicates non-zero.
|
|
||||||
/// </summary>
|
|
||||||
On = 0x1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* HttpRequestEventArgs.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Text;
|
|
||||||
using WebSocketSharp.Net;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Server
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the event data for the HTTP request events of the
|
|
||||||
/// <see cref="HttpServer"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// An HTTP request event occurs when the <see cref="HttpServer"/>
|
|
||||||
/// instance receives an HTTP request.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// You should access the <see cref="Request"/> property if you would
|
|
||||||
/// like to get the request data sent from a client.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// And you should access the <see cref="Response"/> property if you
|
|
||||||
/// would like to get the response data to return to the client.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public class HttpRequestEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private HttpListenerContext _context;
|
|
||||||
private string _docRootPath;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal HttpRequestEventArgs (
|
|
||||||
HttpListenerContext context, string documentRootPath
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_docRootPath = documentRootPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the request data sent from a client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="HttpListenerRequest"/> that provides the methods and
|
|
||||||
/// properties for the request data.
|
|
||||||
/// </value>
|
|
||||||
public HttpListenerRequest Request {
|
|
||||||
get {
|
|
||||||
return _context.Request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the response data to return to the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="HttpListenerResponse"/> that provides the methods and
|
|
||||||
/// properties for the response data.
|
|
||||||
/// </value>
|
|
||||||
public HttpListenerResponse Response {
|
|
||||||
get {
|
|
||||||
return _context.Response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the information for the client.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="IPrincipal"/> instance or <see langword="null"/>
|
|
||||||
/// if not authenticated.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// That instance describes the identity, authentication scheme,
|
|
||||||
/// and security roles for the client.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public IPrincipal User {
|
|
||||||
get {
|
|
||||||
return _context.User;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private string createFilePath (string childPath)
|
|
||||||
{
|
|
||||||
childPath = childPath.TrimStart ('/', '\\');
|
|
||||||
|
|
||||||
return new StringBuilder (_docRootPath, 32)
|
|
||||||
.AppendFormat ("/{0}", childPath)
|
|
||||||
.ToString ()
|
|
||||||
.Replace ('\\', '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool tryReadFile (string path, out byte[] contents)
|
|
||||||
{
|
|
||||||
contents = null;
|
|
||||||
|
|
||||||
if (!File.Exists (path))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
contents = File.ReadAllBytes (path);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads the specified file from the document folder of the
|
|
||||||
/// <see cref="HttpServer"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// <para>
|
|
||||||
/// An array of <see cref="byte"/> or <see langword="null"/>
|
|
||||||
/// if it fails.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// That array receives the contents of the file.
|
|
||||||
/// </para>
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="path">
|
|
||||||
/// A <see cref="string"/> that specifies a virtual path to
|
|
||||||
/// find the file from the document folder.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="path"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is an empty string.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> contains "..".
|
|
||||||
/// </para>
|
|
||||||
/// </exception>
|
|
||||||
public byte[] ReadFile (string path)
|
|
||||||
{
|
|
||||||
if (path == null)
|
|
||||||
throw new ArgumentNullException ("path");
|
|
||||||
|
|
||||||
if (path.Length == 0)
|
|
||||||
throw new ArgumentException ("An empty string.", "path");
|
|
||||||
|
|
||||||
if (path.IndexOf ("..") > -1)
|
|
||||||
throw new ArgumentException ("It contains '..'.", "path");
|
|
||||||
|
|
||||||
path = createFilePath (path);
|
|
||||||
byte[] contents;
|
|
||||||
|
|
||||||
tryReadFile (path, out contents);
|
|
||||||
|
|
||||||
return contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to read the specified file from the document folder of
|
|
||||||
/// the <see cref="HttpServer"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if it succeeds to read; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="path">
|
|
||||||
/// A <see cref="string"/> that specifies a virtual path to find
|
|
||||||
/// the file from the document folder.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="contents">
|
|
||||||
/// <para>
|
|
||||||
/// When this method returns, an array of <see cref="byte"/> or
|
|
||||||
/// <see langword="null"/> if it fails.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// That array receives the contents of the file.
|
|
||||||
/// </para>
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="path"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is an empty string.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> contains "..".
|
|
||||||
/// </para>
|
|
||||||
/// </exception>
|
|
||||||
public bool TryReadFile (string path, out byte[] contents)
|
|
||||||
{
|
|
||||||
if (path == null)
|
|
||||||
throw new ArgumentNullException ("path");
|
|
||||||
|
|
||||||
if (path.Length == 0)
|
|
||||||
throw new ArgumentException ("An empty string.", "path");
|
|
||||||
|
|
||||||
if (path.IndexOf ("..") > -1)
|
|
||||||
throw new ArgumentException ("It contains '..'.", "path");
|
|
||||||
|
|
||||||
path = createFilePath (path);
|
|
||||||
|
|
||||||
return tryReadFile (path, out contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,91 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* IWebSocketSession.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2018 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using WebSocketSharp.Net.WebSockets;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Server
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Exposes the access to the information in a WebSocket session.
|
|
||||||
/// </summary>
|
|
||||||
public interface IWebSocketSession
|
|
||||||
{
|
|
||||||
#region Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current state of the WebSocket connection for the session.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// One of the <see cref="WebSocketState"/> enum values.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It indicates the current state of the connection.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
WebSocketState ConnectionState { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the information in the WebSocket handshake request.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="WebSocketContext"/> instance that provides the access to
|
|
||||||
/// the information in the handshake request.
|
|
||||||
/// </value>
|
|
||||||
WebSocketContext Context { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the unique ID of the session.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the unique ID of the session.
|
|
||||||
/// </value>
|
|
||||||
string ID { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the name of the WebSocket subprotocol for the session.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the name of the subprotocol
|
|
||||||
/// if present.
|
|
||||||
/// </value>
|
|
||||||
string Protocol { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the time that the session has started.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="DateTime"/> that represents the time that the session
|
|
||||||
/// has started.
|
|
||||||
/// </value>
|
|
||||||
DateTime StartTime { get; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,226 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* WebSocketServiceHost.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - Juan Manuel Lallana <juan.manuel.lallana@gmail.com>
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using WebSocketSharp.Net.WebSockets;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Server
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Exposes the methods and properties used to access the information in
|
|
||||||
/// a WebSocket service provided by the <see cref="WebSocketServer"/> or
|
|
||||||
/// <see cref="HttpServer"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This class is an abstract class.
|
|
||||||
/// </remarks>
|
|
||||||
public abstract class WebSocketServiceHost
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private Logger _log;
|
|
||||||
private string _path;
|
|
||||||
private WebSocketSessionManager _sessions;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="WebSocketServiceHost"/>
|
|
||||||
/// class with the specified path and logging function.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">
|
|
||||||
/// A <see cref="string"/> that specifies the absolute path to
|
|
||||||
/// the service.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="log">
|
|
||||||
/// A <see cref="Logger"/> that specifies the logging function for
|
|
||||||
/// the service.
|
|
||||||
/// </param>
|
|
||||||
protected WebSocketServiceHost (string path, Logger log)
|
|
||||||
{
|
|
||||||
_path = path;
|
|
||||||
_log = log;
|
|
||||||
|
|
||||||
_sessions = new WebSocketSessionManager (log);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal ServerState State {
|
|
||||||
get {
|
|
||||||
return _sessions.State;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the logging function for the service.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="Logger"/> that provides the logging function.
|
|
||||||
/// </value>
|
|
||||||
protected Logger Log {
|
|
||||||
get {
|
|
||||||
return _log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the service cleans up the
|
|
||||||
/// inactive sessions periodically.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The set operation does nothing if the service has already started or
|
|
||||||
/// it is shutting down.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if the service cleans up the inactive sessions every
|
|
||||||
/// 60 seconds; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool KeepClean {
|
|
||||||
get {
|
|
||||||
return _sessions.KeepClean;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_sessions.KeepClean = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the path to the service.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="string"/> that represents the absolute path to
|
|
||||||
/// the service.
|
|
||||||
/// </value>
|
|
||||||
public string Path {
|
|
||||||
get {
|
|
||||||
return _path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the management function for the sessions in the service.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="WebSocketSessionManager"/> that manages the sessions in
|
|
||||||
/// the service.
|
|
||||||
/// </value>
|
|
||||||
public WebSocketSessionManager Sessions {
|
|
||||||
get {
|
|
||||||
return _sessions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the type of the behavior of the service.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="Type"/> that represents the type of the behavior of
|
|
||||||
/// the service.
|
|
||||||
/// </value>
|
|
||||||
public abstract Type BehaviorType { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the time to wait for the response to the WebSocket Ping
|
|
||||||
/// or Close.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The set operation does nothing if the service has already started or
|
|
||||||
/// it is shutting down.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// A <see cref="TimeSpan"/> to wait for the response.
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||||
/// The value specified for a set operation is zero or less.
|
|
||||||
/// </exception>
|
|
||||||
public TimeSpan WaitTime {
|
|
||||||
get {
|
|
||||||
return _sessions.WaitTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
_sessions.WaitTime = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal void Start ()
|
|
||||||
{
|
|
||||||
_sessions.Start ();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void StartSession (WebSocketContext context)
|
|
||||||
{
|
|
||||||
CreateSession ().Start (context, _sessions);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Stop (ushort code, string reason)
|
|
||||||
{
|
|
||||||
_sessions.Stop (code, reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new session for the service.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A <see cref="WebSocketBehavior"/> instance that represents
|
|
||||||
/// the new session.
|
|
||||||
/// </returns>
|
|
||||||
protected abstract WebSocketBehavior CreateSession ();
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* WebSocketServiceHost`1.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Server
|
|
||||||
{
|
|
||||||
internal class WebSocketServiceHost<TBehavior> : WebSocketServiceHost
|
|
||||||
where TBehavior : WebSocketBehavior, new ()
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private Func<TBehavior> _creator;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal WebSocketServiceHost (
|
|
||||||
string path, Action<TBehavior> initializer, Logger log
|
|
||||||
)
|
|
||||||
: base (path, log)
|
|
||||||
{
|
|
||||||
_creator = createSessionCreator (initializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public override Type BehaviorType {
|
|
||||||
get {
|
|
||||||
return typeof (TBehavior);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static Func<TBehavior> createSessionCreator (
|
|
||||||
Action<TBehavior> initializer
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (initializer == null)
|
|
||||||
return () => new TBehavior ();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
var ret = new TBehavior ();
|
|
||||||
|
|
||||||
initializer (ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Methods
|
|
||||||
|
|
||||||
protected override WebSocketBehavior CreateSession ()
|
|
||||||
{
|
|
||||||
return _creator ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,609 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* WebSocketServiceManager.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using WebSocketSharp.Net;
|
|
||||||
|
|
||||||
namespace WebSocketSharp.Server
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides the management function for the WebSocket services.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This class manages the WebSocket services provided by
|
|
||||||
/// the <see cref="WebSocketServer"/> or <see cref="HttpServer"/> class.
|
|
||||||
/// </remarks>
|
|
||||||
public class WebSocketServiceManager
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private Dictionary<string, WebSocketServiceHost> _hosts;
|
|
||||||
private volatile bool _keepClean;
|
|
||||||
private Logger _log;
|
|
||||||
private volatile ServerState _state;
|
|
||||||
private object _sync;
|
|
||||||
private TimeSpan _waitTime;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal WebSocketServiceManager (Logger log)
|
|
||||||
{
|
|
||||||
_log = log;
|
|
||||||
|
|
||||||
_hosts = new Dictionary<string, WebSocketServiceHost> ();
|
|
||||||
_keepClean = true;
|
|
||||||
_state = ServerState.Ready;
|
|
||||||
_sync = ((ICollection) _hosts).SyncRoot;
|
|
||||||
_waitTime = TimeSpan.FromSeconds (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of the WebSocket services.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// An <see cref="int"/> that represents the number of the services.
|
|
||||||
/// </value>
|
|
||||||
public int Count {
|
|
||||||
get {
|
|
||||||
lock (_sync)
|
|
||||||
return _hosts.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the service host instances for the WebSocket services.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// An <c>IEnumerable<WebSocketServiceHost></c> instance.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It provides an enumerator which supports the iteration over
|
|
||||||
/// the collection of the service host instances.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public IEnumerable<WebSocketServiceHost> Hosts {
|
|
||||||
get {
|
|
||||||
lock (_sync)
|
|
||||||
return _hosts.Values.ToList ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the service host instance for a WebSocket service with
|
|
||||||
/// the specified path.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="WebSocketServiceHost"/> instance or
|
|
||||||
/// <see langword="null"/> if not found.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The service host instance provides the function to access
|
|
||||||
/// the information in the service.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
/// <param name="path">
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that specifies an absolute path to
|
|
||||||
/// the service to find.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// / is trimmed from the end of the string if present.
|
|
||||||
/// </para>
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="path"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is an empty string.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is not an absolute path.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> includes either or both
|
|
||||||
/// query and fragment components.
|
|
||||||
/// </para>
|
|
||||||
/// </exception>
|
|
||||||
public WebSocketServiceHost this[string path] {
|
|
||||||
get {
|
|
||||||
if (path == null)
|
|
||||||
throw new ArgumentNullException ("path");
|
|
||||||
|
|
||||||
if (path.Length == 0)
|
|
||||||
throw new ArgumentException ("An empty string.", "path");
|
|
||||||
|
|
||||||
if (path[0] != '/') {
|
|
||||||
var msg = "It is not an absolute path.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "path");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.IndexOfAny (new[] { '?', '#' }) > -1) {
|
|
||||||
var msg = "It includes either or both query and fragment components.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "path");
|
|
||||||
}
|
|
||||||
|
|
||||||
WebSocketServiceHost host;
|
|
||||||
|
|
||||||
InternalTryGetServiceHost (path, out host);
|
|
||||||
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the inactive sessions in
|
|
||||||
/// the WebSocket services are cleaned up periodically.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The set operation does nothing if the server has already started or
|
|
||||||
/// it is shutting down.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// <c>true</c> if the inactive sessions are cleaned up every 60
|
|
||||||
/// seconds; otherwise, <c>false</c>.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is <c>true</c>.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public bool KeepClean {
|
|
||||||
get {
|
|
||||||
return _keepClean;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
lock (_sync) {
|
|
||||||
if (!canSet ())
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var host in _hosts.Values)
|
|
||||||
host.KeepClean = value;
|
|
||||||
|
|
||||||
_keepClean = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the paths for the WebSocket services.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// An <c>IEnumerable<string></c> instance.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It provides an enumerator which supports the iteration over
|
|
||||||
/// the collection of the paths.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
public IEnumerable<string> Paths {
|
|
||||||
get {
|
|
||||||
lock (_sync)
|
|
||||||
return _hosts.Keys.ToList ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the time to wait for the response to the WebSocket Ping
|
|
||||||
/// or Close.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The set operation does nothing if the server has already started or
|
|
||||||
/// it is shutting down.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="TimeSpan"/> to wait for the response.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The default value is the same as 1 second.
|
|
||||||
/// </para>
|
|
||||||
/// </value>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||||
/// The value specified for a set operation is zero or less.
|
|
||||||
/// </exception>
|
|
||||||
public TimeSpan WaitTime {
|
|
||||||
get {
|
|
||||||
return _waitTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
if (value <= TimeSpan.Zero) {
|
|
||||||
var msg = "It is zero or less.";
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException ("value", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_sync) {
|
|
||||||
if (!canSet ())
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var host in _hosts.Values)
|
|
||||||
host.WaitTime = value;
|
|
||||||
|
|
||||||
_waitTime = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private bool canSet ()
|
|
||||||
{
|
|
||||||
return _state == ServerState.Ready || _state == ServerState.Stop;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal bool InternalTryGetServiceHost (
|
|
||||||
string path, out WebSocketServiceHost host
|
|
||||||
)
|
|
||||||
{
|
|
||||||
path = path.TrimSlashFromEnd ();
|
|
||||||
|
|
||||||
lock (_sync)
|
|
||||||
return _hosts.TryGetValue (path, out host);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Start ()
|
|
||||||
{
|
|
||||||
lock (_sync) {
|
|
||||||
foreach (var host in _hosts.Values)
|
|
||||||
host.Start ();
|
|
||||||
|
|
||||||
_state = ServerState.Start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Stop (ushort code, string reason)
|
|
||||||
{
|
|
||||||
lock (_sync) {
|
|
||||||
_state = ServerState.ShuttingDown;
|
|
||||||
|
|
||||||
foreach (var host in _hosts.Values)
|
|
||||||
host.Stop (code, reason);
|
|
||||||
|
|
||||||
_state = ServerState.Stop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a WebSocket service with the specified behavior, path,
|
|
||||||
/// and delegate.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that specifies an absolute path to
|
|
||||||
/// the service to add.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// / is trimmed from the end of the string if present.
|
|
||||||
/// </para>
|
|
||||||
/// </param>
|
|
||||||
/// <param name="initializer">
|
|
||||||
/// <para>
|
|
||||||
/// An <c>Action<TBehavior></c> delegate or
|
|
||||||
/// <see langword="null"/> if not needed.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The delegate invokes the method called when initializing
|
|
||||||
/// a new session instance for the service.
|
|
||||||
/// </para>
|
|
||||||
/// </param>
|
|
||||||
/// <typeparam name="TBehavior">
|
|
||||||
/// <para>
|
|
||||||
/// The type of the behavior for the service.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It must inherit the <see cref="WebSocketBehavior"/> class.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// And also, it must have a public parameterless constructor.
|
|
||||||
/// </para>
|
|
||||||
/// </typeparam>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="path"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is an empty string.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is not an absolute path.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> includes either or both
|
|
||||||
/// query and fragment components.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is already in use.
|
|
||||||
/// </para>
|
|
||||||
/// </exception>
|
|
||||||
public void AddService<TBehavior> (
|
|
||||||
string path, Action<TBehavior> initializer
|
|
||||||
)
|
|
||||||
where TBehavior : WebSocketBehavior, new ()
|
|
||||||
{
|
|
||||||
if (path == null)
|
|
||||||
throw new ArgumentNullException ("path");
|
|
||||||
|
|
||||||
if (path.Length == 0)
|
|
||||||
throw new ArgumentException ("An empty string.", "path");
|
|
||||||
|
|
||||||
if (path[0] != '/') {
|
|
||||||
var msg = "It is not an absolute path.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "path");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.IndexOfAny (new[] { '?', '#' }) > -1) {
|
|
||||||
var msg = "It includes either or both query and fragment components.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "path");
|
|
||||||
}
|
|
||||||
|
|
||||||
path = path.TrimSlashFromEnd ();
|
|
||||||
|
|
||||||
lock (_sync) {
|
|
||||||
WebSocketServiceHost host;
|
|
||||||
|
|
||||||
if (_hosts.TryGetValue (path, out host)) {
|
|
||||||
var msg = "It is already in use.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "path");
|
|
||||||
}
|
|
||||||
|
|
||||||
host = new WebSocketServiceHost<TBehavior> (path, initializer, _log);
|
|
||||||
|
|
||||||
if (!_keepClean)
|
|
||||||
host.KeepClean = false;
|
|
||||||
|
|
||||||
if (_waitTime != host.WaitTime)
|
|
||||||
host.WaitTime = _waitTime;
|
|
||||||
|
|
||||||
if (_state == ServerState.Start)
|
|
||||||
host.Start ();
|
|
||||||
|
|
||||||
_hosts.Add (path, host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all WebSocket services managed by the manager.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// A service is stopped with close status 1001 (going away)
|
|
||||||
/// if it has already started.
|
|
||||||
/// </remarks>
|
|
||||||
public void Clear ()
|
|
||||||
{
|
|
||||||
List<WebSocketServiceHost> hosts = null;
|
|
||||||
|
|
||||||
lock (_sync) {
|
|
||||||
hosts = _hosts.Values.ToList ();
|
|
||||||
|
|
||||||
_hosts.Clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var host in hosts) {
|
|
||||||
if (host.State == ServerState.Start)
|
|
||||||
host.Stop (1001, String.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a WebSocket service with the specified path.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The service is stopped with close status 1001 (going away)
|
|
||||||
/// if it has already started.
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the service is successfully found and removed;
|
|
||||||
/// otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="path">
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that specifies an absolute path to
|
|
||||||
/// the service to remove.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// / is trimmed from the end of the string if present.
|
|
||||||
/// </para>
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="path"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is an empty string.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is not an absolute path.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> includes either or both
|
|
||||||
/// query and fragment components.
|
|
||||||
/// </para>
|
|
||||||
/// </exception>
|
|
||||||
public bool RemoveService (string path)
|
|
||||||
{
|
|
||||||
if (path == null)
|
|
||||||
throw new ArgumentNullException ("path");
|
|
||||||
|
|
||||||
if (path.Length == 0)
|
|
||||||
throw new ArgumentException ("An empty string.", "path");
|
|
||||||
|
|
||||||
if (path[0] != '/') {
|
|
||||||
var msg = "It is not an absolute path.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "path");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.IndexOfAny (new[] { '?', '#' }) > -1) {
|
|
||||||
var msg = "It includes either or both query and fragment components.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "path");
|
|
||||||
}
|
|
||||||
|
|
||||||
path = path.TrimSlashFromEnd ();
|
|
||||||
WebSocketServiceHost host;
|
|
||||||
|
|
||||||
lock (_sync) {
|
|
||||||
if (!_hosts.TryGetValue (path, out host))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_hosts.Remove (path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host.State == ServerState.Start)
|
|
||||||
host.Stop (1001, String.Empty);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to get the service host instance for a WebSocket service with
|
|
||||||
/// the specified path.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the service is successfully found; otherwise,
|
|
||||||
/// <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="path">
|
|
||||||
/// <para>
|
|
||||||
/// A <see cref="string"/> that specifies an absolute path to
|
|
||||||
/// the service to find.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// / is trimmed from the end of the string if present.
|
|
||||||
/// </para>
|
|
||||||
/// </param>
|
|
||||||
/// <param name="host">
|
|
||||||
/// <para>
|
|
||||||
/// When this method returns, a <see cref="WebSocketServiceHost"/>
|
|
||||||
/// instance or <see langword="null"/> if not found.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The service host instance provides the function to access
|
|
||||||
/// the information in the service.
|
|
||||||
/// </para>
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentNullException">
|
|
||||||
/// <paramref name="path"/> is <see langword="null"/>.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is an empty string.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> is not an absolute path.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// -or-
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// <paramref name="path"/> includes either or both
|
|
||||||
/// query and fragment components.
|
|
||||||
/// </para>
|
|
||||||
/// </exception>
|
|
||||||
public bool TryGetServiceHost (string path, out WebSocketServiceHost host)
|
|
||||||
{
|
|
||||||
if (path == null)
|
|
||||||
throw new ArgumentNullException ("path");
|
|
||||||
|
|
||||||
if (path.Length == 0)
|
|
||||||
throw new ArgumentException ("An empty string.", "path");
|
|
||||||
|
|
||||||
if (path[0] != '/') {
|
|
||||||
var msg = "It is not an absolute path.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "path");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.IndexOfAny (new[] { '?', '#' }) > -1) {
|
|
||||||
var msg = "It includes either or both query and fragment components.";
|
|
||||||
|
|
||||||
throw new ArgumentException (msg, "path");
|
|
||||||
}
|
|
||||||
|
|
||||||
return InternalTryGetServiceHost (path, out host);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
+285
-3966
File diff suppressed because it is too large
Load Diff
@@ -1,109 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* WebSocketException.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2016 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The exception that is thrown when a fatal error occurs in
|
|
||||||
/// the WebSocket communication.
|
|
||||||
/// </summary>
|
|
||||||
public class WebSocketException : Exception
|
|
||||||
{
|
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private CloseStatusCode _code;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal WebSocketException ()
|
|
||||||
: this (CloseStatusCode.Abnormal, null, null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal WebSocketException (Exception innerException)
|
|
||||||
: this (CloseStatusCode.Abnormal, null, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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, Exception innerException)
|
|
||||||
: this (code, null, 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 of the exception.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// One of the <see cref="CloseStatusCode"/> enum values that represents
|
|
||||||
/// the status code indicating the cause of the exception.
|
|
||||||
/// </value>
|
|
||||||
public CloseStatusCode Code {
|
|
||||||
get {
|
|
||||||
return _code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,929 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* WebSocketFrame.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012-2021 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Contributors
|
|
||||||
/*
|
|
||||||
* Contributors:
|
|
||||||
* - Chris Swiedler
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
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 Private Constructors
|
|
||||||
|
|
||||||
private WebSocketFrame ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Constructors
|
|
||||||
|
|
||||||
internal WebSocketFrame (Opcode opcode, PayloadData payloadData, bool mask)
|
|
||||||
: this (Fin.Final, opcode, payloadData, false, mask)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal WebSocketFrame (
|
|
||||||
Fin fin, Opcode opcode, byte[] data, bool compressed, bool mask
|
|
||||||
)
|
|
||||||
: this (fin, opcode, new PayloadData (data), compressed, mask)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal WebSocketFrame (
|
|
||||||
Fin fin,
|
|
||||||
Opcode opcode,
|
|
||||||
PayloadData payloadData,
|
|
||||||
bool compressed,
|
|
||||||
bool mask
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_fin = fin;
|
|
||||||
_opcode = opcode;
|
|
||||||
|
|
||||||
_rsv1 = opcode.IsData () && compressed ? Rsv.On : Rsv.Off;
|
|
||||||
_rsv2 = Rsv.Off;
|
|
||||||
_rsv3 = Rsv.Off;
|
|
||||||
|
|
||||||
var len = payloadData.Length;
|
|
||||||
|
|
||||||
if (len < 126) {
|
|
||||||
_payloadLength = (byte) len;
|
|
||||||
_extPayloadLength = WebSocket.EmptyBytes;
|
|
||||||
}
|
|
||||||
else if (len < 0x010000) {
|
|
||||||
_payloadLength = (byte) 126;
|
|
||||||
_extPayloadLength = ((ushort) len).InternalToByteArray (ByteOrder.Big);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_payloadLength = (byte) 127;
|
|
||||||
_extPayloadLength = len.InternalToByteArray (ByteOrder.Big);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask) {
|
|
||||||
_mask = Mask.On;
|
|
||||||
_maskingKey = createMaskingKey ();
|
|
||||||
|
|
||||||
payloadData.Mask (_maskingKey);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_mask = Mask.Off;
|
|
||||||
_maskingKey = WebSocket.EmptyBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
_payloadData = payloadData;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Properties
|
|
||||||
|
|
||||||
internal ulong ExactPayloadLength {
|
|
||||||
get {
|
|
||||||
return _payloadLength < 126
|
|
||||||
? _payloadLength
|
|
||||||
: _payloadLength == 126
|
|
||||||
? _extPayloadLength.ToUInt16 (ByteOrder.Big)
|
|
||||||
: _extPayloadLength.ToUInt64 (ByteOrder.Big);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int ExtendedPayloadLengthWidth {
|
|
||||||
get {
|
|
||||||
return _payloadLength < 126
|
|
||||||
? 0
|
|
||||||
: _payloadLength == 126
|
|
||||||
? 2
|
|
||||||
: 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
|
|
||||||
public byte[] ExtendedPayloadLength {
|
|
||||||
get {
|
|
||||||
return _extPayloadLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Fin Fin {
|
|
||||||
get {
|
|
||||||
return _fin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsBinary {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Binary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsClose {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Close;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCompressed {
|
|
||||||
get {
|
|
||||||
return _rsv1 == Rsv.On;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsContinuation {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Cont;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsControl {
|
|
||||||
get {
|
|
||||||
return _opcode >= Opcode.Close;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsData {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Text || _opcode == Opcode.Binary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsFinal {
|
|
||||||
get {
|
|
||||||
return _fin == Fin.Final;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsFragment {
|
|
||||||
get {
|
|
||||||
return _fin == Fin.More || _opcode == Opcode.Cont;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsMasked {
|
|
||||||
get {
|
|
||||||
return _mask == Mask.On;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPing {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Ping;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPong {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Pong;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsText {
|
|
||||||
get {
|
|
||||||
return _opcode == Opcode.Text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong Length {
|
|
||||||
get {
|
|
||||||
return 2
|
|
||||||
+ (ulong) (_extPayloadLength.Length + _maskingKey.Length)
|
|
||||||
+ _payloadData.Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mask Mask {
|
|
||||||
get {
|
|
||||||
return _mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] MaskingKey {
|
|
||||||
get {
|
|
||||||
return _maskingKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Opcode Opcode {
|
|
||||||
get {
|
|
||||||
return _opcode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PayloadData PayloadData {
|
|
||||||
get {
|
|
||||||
return _payloadData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte PayloadLength {
|
|
||||||
get {
|
|
||||||
return _payloadLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Rsv Rsv1 {
|
|
||||||
get {
|
|
||||||
return _rsv1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Rsv Rsv2 {
|
|
||||||
get {
|
|
||||||
return _rsv2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Rsv Rsv3 {
|
|
||||||
get {
|
|
||||||
return _rsv3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static byte[] createMaskingKey ()
|
|
||||||
{
|
|
||||||
var key = new byte[4];
|
|
||||||
|
|
||||||
WebSocket.RandomNumber.GetBytes (key);
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string dump (WebSocketFrame frame)
|
|
||||||
{
|
|
||||||
var len = frame.Length;
|
|
||||||
var cnt = (long) (len / 4);
|
|
||||||
var rem = (int) (len % 4);
|
|
||||||
|
|
||||||
int cntDigit;
|
|
||||||
string cntFmt;
|
|
||||||
|
|
||||||
if (cnt < 10000) {
|
|
||||||
cntDigit = 4;
|
|
||||||
cntFmt = "{0,4}";
|
|
||||||
}
|
|
||||||
else if (cnt < 0x010000) {
|
|
||||||
cntDigit = 4;
|
|
||||||
cntFmt = "{0,4:X}";
|
|
||||||
}
|
|
||||||
else if (cnt < 0x0100000000) {
|
|
||||||
cntDigit = 8;
|
|
||||||
cntFmt = "{0,8:X}";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cntDigit = 16;
|
|
||||||
cntFmt = "{0,16:X}";
|
|
||||||
}
|
|
||||||
|
|
||||||
var spFmt = String.Format ("{{0,{0}}}", cntDigit);
|
|
||||||
|
|
||||||
var headerFmt = String.Format (
|
|
||||||
@"
|
|
||||||
{0} 01234567 89ABCDEF 01234567 89ABCDEF
|
|
||||||
{0}+--------+--------+--------+--------+\n",
|
|
||||||
spFmt
|
|
||||||
);
|
|
||||||
|
|
||||||
var lineFmt = String.Format (
|
|
||||||
"{0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n", cntFmt
|
|
||||||
);
|
|
||||||
|
|
||||||
var footerFmt = String.Format (
|
|
||||||
"{0}+--------+--------+--------+--------+", spFmt
|
|
||||||
);
|
|
||||||
|
|
||||||
var buff = new StringBuilder (64);
|
|
||||||
|
|
||||||
Func<Action<string, string, string, string>> linePrinter =
|
|
||||||
() => {
|
|
||||||
long lineCnt = 0;
|
|
||||||
|
|
||||||
return (arg1, arg2, arg3, arg4) => {
|
|
||||||
buff.AppendFormat (
|
|
||||||
lineFmt, ++lineCnt, arg1, arg2, arg3, arg4
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var printLine = linePrinter ();
|
|
||||||
var bytes = frame.ToArray ();
|
|
||||||
|
|
||||||
buff.AppendFormat (headerFmt, String.Empty);
|
|
||||||
|
|
||||||
for (long i = 0; i <= cnt; i++) {
|
|
||||||
var j = i * 4;
|
|
||||||
|
|
||||||
if (i < cnt) {
|
|
||||||
printLine (
|
|
||||||
Convert.ToString (bytes[j], 2).PadLeft (8, '0'),
|
|
||||||
Convert.ToString (bytes[j + 1], 2).PadLeft (8, '0'),
|
|
||||||
Convert.ToString (bytes[j + 2], 2).PadLeft (8, '0'),
|
|
||||||
Convert.ToString (bytes[j + 3], 2).PadLeft (8, '0')
|
|
||||||
);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rem > 0) {
|
|
||||||
printLine (
|
|
||||||
Convert.ToString (bytes[j], 2).PadLeft (8, '0'),
|
|
||||||
rem >= 2
|
|
||||||
? Convert.ToString (bytes[j + 1], 2).PadLeft (8, '0')
|
|
||||||
: String.Empty,
|
|
||||||
rem == 3
|
|
||||||
? Convert.ToString (bytes[j + 2], 2).PadLeft (8, '0')
|
|
||||||
: String.Empty,
|
|
||||||
String.Empty
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buff.AppendFormat (footerFmt, String.Empty);
|
|
||||||
|
|
||||||
return buff.ToString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string print (WebSocketFrame frame)
|
|
||||||
{
|
|
||||||
// Payload Length
|
|
||||||
var payloadLen = frame._payloadLength;
|
|
||||||
|
|
||||||
// Extended Payload Length
|
|
||||||
var extPayloadLen = payloadLen > 125
|
|
||||||
? frame.ExactPayloadLength.ToString ()
|
|
||||||
: String.Empty;
|
|
||||||
|
|
||||||
// Masking Key
|
|
||||||
var maskingKey = BitConverter.ToString (frame._maskingKey);
|
|
||||||
|
|
||||||
// Payload Data
|
|
||||||
var payload = payloadLen == 0
|
|
||||||
? String.Empty
|
|
||||||
: payloadLen > 125
|
|
||||||
? "---"
|
|
||||||
: !frame.IsText
|
|
||||||
|| frame.IsFragment
|
|
||||||
|| frame.IsMasked
|
|
||||||
|| frame.IsCompressed
|
|
||||||
? frame._payloadData.ToString ()
|
|
||||||
: utf8Decode (frame._payloadData.ApplicationData);
|
|
||||||
|
|
||||||
var fmt = @"
|
|
||||||
FIN: {0}
|
|
||||||
RSV1: {1}
|
|
||||||
RSV2: {2}
|
|
||||||
RSV3: {3}
|
|
||||||
Opcode: {4}
|
|
||||||
MASK: {5}
|
|
||||||
Payload Length: {6}
|
|
||||||
Extended Payload Length: {7}
|
|
||||||
Masking Key: {8}
|
|
||||||
Payload Data: {9}";
|
|
||||||
|
|
||||||
return String.Format (
|
|
||||||
fmt,
|
|
||||||
frame._fin,
|
|
||||||
frame._rsv1,
|
|
||||||
frame._rsv2,
|
|
||||||
frame._rsv3,
|
|
||||||
frame._opcode,
|
|
||||||
frame._mask,
|
|
||||||
payloadLen,
|
|
||||||
extPayloadLen,
|
|
||||||
maskingKey,
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebSocketFrame processHeader (byte[] header)
|
|
||||||
{
|
|
||||||
if (header.Length != 2) {
|
|
||||||
var msg = "The header part of a frame could not be read.";
|
|
||||||
|
|
||||||
throw new WebSocketException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = (byte) (header[0] & 0x0f);
|
|
||||||
|
|
||||||
// MASK
|
|
||||||
var mask = (header[1] & 0x80) == 0x80 ? Mask.On : Mask.Off;
|
|
||||||
|
|
||||||
// Payload Length
|
|
||||||
var payloadLen = (byte) (header[1] & 0x7f);
|
|
||||||
|
|
||||||
if (!opcode.IsSupported ()) {
|
|
||||||
var msg = "A frame has an unsupported opcode.";
|
|
||||||
|
|
||||||
throw new WebSocketException (CloseStatusCode.ProtocolError, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!opcode.IsData () && rsv1 == Rsv.On) {
|
|
||||||
var msg = "A non data frame is compressed.";
|
|
||||||
|
|
||||||
throw new WebSocketException (CloseStatusCode.ProtocolError, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opcode.IsControl ()) {
|
|
||||||
if (fin == Fin.More) {
|
|
||||||
var msg = "A control frame is fragmented.";
|
|
||||||
|
|
||||||
throw new WebSocketException (CloseStatusCode.ProtocolError, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payloadLen > 125) {
|
|
||||||
var msg = "A control frame has too long payload length.";
|
|
||||||
|
|
||||||
throw new WebSocketException (CloseStatusCode.ProtocolError, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var frame = new WebSocketFrame ();
|
|
||||||
frame._fin = fin;
|
|
||||||
frame._rsv1 = rsv1;
|
|
||||||
frame._rsv2 = rsv2;
|
|
||||||
frame._rsv3 = rsv3;
|
|
||||||
frame._opcode = (Opcode) opcode;
|
|
||||||
frame._mask = mask;
|
|
||||||
frame._payloadLength = payloadLen;
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebSocketFrame readExtendedPayloadLength (
|
|
||||||
Stream stream, WebSocketFrame frame
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var len = frame.ExtendedPayloadLengthWidth;
|
|
||||||
|
|
||||||
if (len == 0) {
|
|
||||||
frame._extPayloadLength = WebSocket.EmptyBytes;
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bytes = stream.ReadBytes (len);
|
|
||||||
|
|
||||||
if (bytes.Length != len) {
|
|
||||||
var msg = "The extended payload length of a frame could not be read.";
|
|
||||||
|
|
||||||
throw new WebSocketException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame._extPayloadLength = bytes;
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void readExtendedPayloadLengthAsync (
|
|
||||||
Stream stream,
|
|
||||||
WebSocketFrame frame,
|
|
||||||
Action<WebSocketFrame> completed,
|
|
||||||
Action<Exception> error
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var len = frame.ExtendedPayloadLengthWidth;
|
|
||||||
|
|
||||||
if (len == 0) {
|
|
||||||
frame._extPayloadLength = WebSocket.EmptyBytes;
|
|
||||||
|
|
||||||
completed (frame);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.ReadBytesAsync (
|
|
||||||
len,
|
|
||||||
bytes => {
|
|
||||||
if (bytes.Length != len) {
|
|
||||||
var msg = "The extended payload length of a frame could not be read.";
|
|
||||||
|
|
||||||
throw new WebSocketException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame._extPayloadLength = bytes;
|
|
||||||
|
|
||||||
completed (frame);
|
|
||||||
},
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebSocketFrame readHeader (Stream stream)
|
|
||||||
{
|
|
||||||
var bytes = stream.ReadBytes (2);
|
|
||||||
|
|
||||||
return processHeader (bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void readHeaderAsync (
|
|
||||||
Stream stream, Action<WebSocketFrame> completed, Action<Exception> error
|
|
||||||
)
|
|
||||||
{
|
|
||||||
stream.ReadBytesAsync (
|
|
||||||
2,
|
|
||||||
bytes => {
|
|
||||||
var frame = processHeader (bytes);
|
|
||||||
|
|
||||||
completed (frame);
|
|
||||||
},
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebSocketFrame readMaskingKey (
|
|
||||||
Stream stream, WebSocketFrame frame
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (!frame.IsMasked) {
|
|
||||||
frame._maskingKey = WebSocket.EmptyBytes;
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
var len = 4;
|
|
||||||
var bytes = stream.ReadBytes (len);
|
|
||||||
|
|
||||||
if (bytes.Length != len) {
|
|
||||||
var msg = "The masking key of a frame could not be read.";
|
|
||||||
|
|
||||||
throw new WebSocketException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame._maskingKey = bytes;
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void readMaskingKeyAsync (
|
|
||||||
Stream stream,
|
|
||||||
WebSocketFrame frame,
|
|
||||||
Action<WebSocketFrame> completed,
|
|
||||||
Action<Exception> error
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (!frame.IsMasked) {
|
|
||||||
frame._maskingKey = WebSocket.EmptyBytes;
|
|
||||||
|
|
||||||
completed (frame);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var len = 4;
|
|
||||||
|
|
||||||
stream.ReadBytesAsync (
|
|
||||||
len,
|
|
||||||
bytes => {
|
|
||||||
if (bytes.Length != len) {
|
|
||||||
var msg = "The masking key of a frame could not be read.";
|
|
||||||
|
|
||||||
throw new WebSocketException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame._maskingKey = bytes;
|
|
||||||
|
|
||||||
completed (frame);
|
|
||||||
},
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebSocketFrame readPayloadData (
|
|
||||||
Stream stream, WebSocketFrame frame
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var exactLen = frame.ExactPayloadLength;
|
|
||||||
|
|
||||||
if (exactLen > PayloadData.MaxLength) {
|
|
||||||
var msg = "A frame has too long payload length.";
|
|
||||||
|
|
||||||
throw new WebSocketException (CloseStatusCode.TooBig, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exactLen == 0) {
|
|
||||||
frame._payloadData = PayloadData.Empty;
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
var len = (long) exactLen;
|
|
||||||
var bytes = frame._payloadLength < 127
|
|
||||||
? stream.ReadBytes ((int) exactLen)
|
|
||||||
: stream.ReadBytes (len, 1024);
|
|
||||||
|
|
||||||
if (bytes.LongLength != len) {
|
|
||||||
var msg = "The payload data of a frame could not be read.";
|
|
||||||
|
|
||||||
throw new WebSocketException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame._payloadData = new PayloadData (bytes, len);
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void readPayloadDataAsync (
|
|
||||||
Stream stream,
|
|
||||||
WebSocketFrame frame,
|
|
||||||
Action<WebSocketFrame> completed,
|
|
||||||
Action<Exception> error
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var exactLen = frame.ExactPayloadLength;
|
|
||||||
|
|
||||||
if (exactLen > PayloadData.MaxLength) {
|
|
||||||
var msg = "A frame has too long payload length.";
|
|
||||||
|
|
||||||
throw new WebSocketException (CloseStatusCode.TooBig, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exactLen == 0) {
|
|
||||||
frame._payloadData = PayloadData.Empty;
|
|
||||||
|
|
||||||
completed (frame);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var len = (long) exactLen;
|
|
||||||
|
|
||||||
Action<byte[]> comp =
|
|
||||||
bytes => {
|
|
||||||
if (bytes.LongLength != len) {
|
|
||||||
var msg = "The payload data of a frame could not be read.";
|
|
||||||
|
|
||||||
throw new WebSocketException (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame._payloadData = new PayloadData (bytes, len);
|
|
||||||
|
|
||||||
completed (frame);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (frame._payloadLength < 127) {
|
|
||||||
stream.ReadBytesAsync ((int) exactLen, comp, error);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.ReadBytesAsync (len, 1024, comp, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string utf8Decode (byte[] bytes)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Encoding.UTF8.GetString (bytes);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Methods
|
|
||||||
|
|
||||||
internal static WebSocketFrame CreateCloseFrame (
|
|
||||||
PayloadData payloadData, bool mask
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return new WebSocketFrame (
|
|
||||||
Fin.Final, Opcode.Close, payloadData, false, mask
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static WebSocketFrame CreatePingFrame (bool mask)
|
|
||||||
{
|
|
||||||
return new WebSocketFrame (
|
|
||||||
Fin.Final, Opcode.Ping, PayloadData.Empty, false, mask
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static WebSocketFrame CreatePingFrame (byte[] data, bool mask)
|
|
||||||
{
|
|
||||||
return new WebSocketFrame (
|
|
||||||
Fin.Final, Opcode.Ping, new PayloadData (data), false, mask
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static WebSocketFrame CreatePongFrame (
|
|
||||||
PayloadData payloadData, bool mask
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return new WebSocketFrame (
|
|
||||||
Fin.Final, Opcode.Pong, payloadData, false, mask
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static WebSocketFrame ReadFrame (Stream stream, bool unmask)
|
|
||||||
{
|
|
||||||
var frame = readHeader (stream);
|
|
||||||
|
|
||||||
readExtendedPayloadLength (stream, frame);
|
|
||||||
readMaskingKey (stream, frame);
|
|
||||||
readPayloadData (stream, frame);
|
|
||||||
|
|
||||||
if (unmask)
|
|
||||||
frame.Unmask ();
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ReadFrameAsync (
|
|
||||||
Stream stream,
|
|
||||||
bool unmask,
|
|
||||||
Action<WebSocketFrame> completed,
|
|
||||||
Action<Exception> error
|
|
||||||
)
|
|
||||||
{
|
|
||||||
readHeaderAsync (
|
|
||||||
stream,
|
|
||||||
frame =>
|
|
||||||
readExtendedPayloadLengthAsync (
|
|
||||||
stream,
|
|
||||||
frame,
|
|
||||||
frame1 =>
|
|
||||||
readMaskingKeyAsync (
|
|
||||||
stream,
|
|
||||||
frame1,
|
|
||||||
frame2 =>
|
|
||||||
readPayloadDataAsync (
|
|
||||||
stream,
|
|
||||||
frame2,
|
|
||||||
frame3 => {
|
|
||||||
if (unmask)
|
|
||||||
frame3.Unmask ();
|
|
||||||
|
|
||||||
completed (frame3);
|
|
||||||
},
|
|
||||||
error
|
|
||||||
),
|
|
||||||
error
|
|
||||||
),
|
|
||||||
error
|
|
||||||
),
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Unmask ()
|
|
||||||
{
|
|
||||||
if (_mask == Mask.Off)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_payloadData.Mask (_maskingKey);
|
|
||||||
|
|
||||||
_maskingKey = WebSocket.EmptyBytes;
|
|
||||||
_mask = Mask.Off;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public IEnumerator<byte> GetEnumerator ()
|
|
||||||
{
|
|
||||||
foreach (var b in ToArray ())
|
|
||||||
yield return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Print (bool dumped)
|
|
||||||
{
|
|
||||||
var val = dumped ? dump (this) : print (this);
|
|
||||||
|
|
||||||
Console.WriteLine (val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string PrintToString (bool dumped)
|
|
||||||
{
|
|
||||||
return dumped ? dump (this) : print (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] ToArray ()
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
var headerAsUshort = (ushort) header;
|
|
||||||
var headerAsBytes = headerAsUshort.InternalToByteArray (ByteOrder.Big);
|
|
||||||
|
|
||||||
buff.Write (headerAsBytes, 0, 2);
|
|
||||||
|
|
||||||
if (_payloadLength > 125) {
|
|
||||||
var cnt = _payloadLength == 126 ? 2 : 8;
|
|
||||||
|
|
||||||
buff.Write (_extPayloadLength, 0, cnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_mask == Mask.On)
|
|
||||||
buff.Write (_maskingKey, 0, 4);
|
|
||||||
|
|
||||||
if (_payloadLength > 0) {
|
|
||||||
var bytes = _payloadData.ToArray ();
|
|
||||||
|
|
||||||
if (_payloadLength < 127)
|
|
||||||
buff.Write (bytes, 0, bytes.Length);
|
|
||||||
else
|
|
||||||
buff.WriteBytes (bytes, 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
buff.Close ();
|
|
||||||
|
|
||||||
return buff.ToArray ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString ()
|
|
||||||
{
|
|
||||||
var val = ToArray ();
|
|
||||||
|
|
||||||
return BitConverter.ToString (val);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Explicit Interface Implementations
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator ()
|
|
||||||
{
|
|
||||||
return GetEnumerator ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
#region License
|
|
||||||
/*
|
|
||||||
* WebSocketState.cs
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2016 sta.blockhead
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WebSocketSharp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the state of a WebSocket connection.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The values of this enumeration are defined in
|
|
||||||
/// <see href="http://www.w3.org/TR/websockets/#dom-websocket-readystate">
|
|
||||||
/// The WebSocket API</see>.
|
|
||||||
/// </remarks>
|
|
||||||
public enum WebSocketState : ushort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 0. Indicates that the connection has not
|
|
||||||
/// yet been established.
|
|
||||||
/// </summary>
|
|
||||||
Connecting = 0,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 1. Indicates that the connection has
|
|
||||||
/// been established, and the communication is possible.
|
|
||||||
/// </summary>
|
|
||||||
Open = 1,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 2. Indicates that the connection is
|
|
||||||
/// going through the closing handshake, or the close method has
|
|
||||||
/// been invoked.
|
|
||||||
/// </summary>
|
|
||||||
Closing = 2,
|
|
||||||
/// <summary>
|
|
||||||
/// Equivalent to numeric value 3. Indicates that the connection has
|
|
||||||
/// been closed or could not be established.
|
|
||||||
/// </summary>
|
|
||||||
Closed = 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
#region License
|
#region MIT License
|
||||||
/*
|
/**
|
||||||
* ServerState.cs
|
* WsState.cs
|
||||||
*
|
*
|
||||||
* The MIT License
|
* The MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2013-2014 sta.blockhead
|
* Copyright (c) 2010 sta.blockhead
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -28,13 +28,13 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace WebSocketSharp.Server
|
namespace WebSocketSharp
|
||||||
{
|
{
|
||||||
internal enum ServerState
|
public enum WsState
|
||||||
{
|
{
|
||||||
Ready,
|
CONNECTING,
|
||||||
Start,
|
OPEN,
|
||||||
ShuttingDown,
|
CLOSING,
|
||||||
Stop
|
CLOSED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#region MIT License
|
||||||
|
/**
|
||||||
|
* WsStream.cs
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 sta.blockhead
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Security;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace WebSocketSharp
|
||||||
|
{
|
||||||
|
public class WsStream<T> : IWsStream
|
||||||
|
where T : Stream
|
||||||
|
{
|
||||||
|
private T innerStream;
|
||||||
|
|
||||||
|
public WsStream(T innerStream)
|
||||||
|
{
|
||||||
|
Type streamType = typeof(T);
|
||||||
|
if (streamType != typeof(NetworkStream) &&
|
||||||
|
streamType != typeof(SslStream))
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Unsupported Stream type: " + streamType.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (innerStream == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("innerStream");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.innerStream = innerStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
innerStream.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
innerStream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Read(byte[] buffer, int offset, int size)
|
||||||
|
{
|
||||||
|
return innerStream.Read(buffer, offset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ReadByte()
|
||||||
|
{
|
||||||
|
return innerStream.ReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
innerStream.Write(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteByte(byte value)
|
||||||
|
{
|
||||||
|
innerStream.WriteByte(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user