Fix for the secure connection

This commit is contained in:
sta 2013-07-19 17:29:58 +09:00
parent 49dc8800d3
commit 3e6c589953
11 changed files with 308 additions and 243 deletions

View File

@ -8,17 +8,17 @@ using System.Threading;
using WebSocketSharp; using WebSocketSharp;
using WebSocketSharp.Net; using WebSocketSharp.Net;
namespace Example namespace Example {
{
public struct NfMessage public struct NfMessage {
{
public string Summary; public string Summary;
public string Body; public string Body;
public string Icon; public string Icon;
} }
public class ThreadState public class ThreadState {
{
public bool Enabled { get; set; } public bool Enabled { get; set; }
public AutoResetEvent Notification { get; private set; } public AutoResetEvent Notification { get; private set; }
@ -29,8 +29,8 @@ namespace Example
} }
} }
public class Program public class Program {
{
private static Queue _msgQ = Queue.Synchronized(new Queue()); private static Queue _msgQ = Queue.Synchronized(new Queue());
private static void enNfMessage(string summary, string body, string icon) private static void enNfMessage(string summary, string body, string icon)
@ -47,7 +47,7 @@ namespace Example
public static void Main(string[] args) public static void Main(string[] args)
{ {
ThreadState ts = new ThreadState(); var ts = new ThreadState();
WaitCallback notifyMsg = state => WaitCallback notifyMsg = state =>
{ {
@ -57,11 +57,9 @@ namespace Example
if (_msgQ.Count > 0) if (_msgQ.Count > 0)
{ {
NfMessage msg = (NfMessage)_msgQ.Dequeue(); var msg = (NfMessage)_msgQ.Dequeue();
#if NOTIFY #if NOTIFY
Notification nf = new Notification(msg.Summary, var nf = new Notification(msg.Summary, msg.Body, msg.Icon);
msg.Body,
msg.Icon);
nf.AddHint("append", "allowed"); nf.AddHint("append", "allowed");
nf.Show(); nf.Show();
#else #else
@ -75,15 +73,16 @@ namespace Example
ThreadPool.QueueUserWorkItem(notifyMsg); ThreadPool.QueueUserWorkItem(notifyMsg);
using (WebSocket ws = new WebSocket("ws://echo.websocket.org", "echo")) using (var ws = new WebSocket("ws://echo.websocket.org", "echo"))
//using (WebSocket ws = new WebSocket("wss://echo.websocket.org", "echo")) //using (var ws = new WebSocket("wss://echo.websocket.org", "echo"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649")) //using (var ws = new WebSocket("ws://localhost:4649"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/Echo")) //using (var ws = new WebSocket("ws://localhost:4649/Echo"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/Echo?name=nobita")) //using (var ws = new WebSocket("wss://localhost:4649/Echo"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/エコー?name=のび太")) //using (var ws = new WebSocket("ws://localhost:4649/Echo?name=nobita"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/Chat")) //using (var ws = new WebSocket("ws://localhost:4649/エコー?name=のび太"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/Chat?name=nobita")) //using (var ws = new WebSocket("ws://localhost:4649/Chat"))
//using (WebSocket ws = new WebSocket("ws://localhost:4649/チャット?name=のび太")) //using (var ws = new WebSocket("ws://localhost:4649/Chat?name=nobita"))
//using (var ws = new WebSocket("ws://localhost:4649/チャット?name=のび太"))
{ {
ws.OnOpen += (sender, e) => ws.OnOpen += (sender, e) =>
{ {
@ -111,11 +110,16 @@ namespace Example
"notification-message-im"); "notification-message-im");
}; };
//ws.Origin = "http://echo.websocket.org";
//ws.Compression = CompressionMethod.DEFLATE;
#if DEBUG #if DEBUG
ws.Log.Level = LogLevel.TRACE; ws.Log.Level = LogLevel.TRACE;
#endif #endif
//ws.Compression = CompressionMethod.DEFLATE;
//ws.Origin = "http://echo.websocket.org";
//ws.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
//{
// ws.Log.Debug(String.Format("\n{0}\n{1}", certificate.Issuer, certificate.Subject));
// return true;
//};
//ws.SetCookie(new Cookie("nobita", "\"idiot, gunfighter\"")); //ws.SetCookie(new Cookie("nobita", "\"idiot, gunfighter\""));
//ws.SetCookie(new Cookie("dora", "tanuki")); //ws.SetCookie(new Cookie("dora", "tanuki"));
ws.Connect(); ws.Connect();

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<appSettings> <appSettings>
<add key="ServerCertPath" value="/path/to/server.cer" /> <add key="ServerCertFile" value="/path/to/cert.pfx"/>
<add key="CertFilePassword" value="password"/>
</appSettings> </appSettings>
</configuration> </configuration>

View File

@ -49,6 +49,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Configuration" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AssemblyInfo.cs" /> <Compile Include="AssemblyInfo.cs" />

View File

@ -1,16 +1,18 @@
using System; using System;
using System.Configuration;
using System.Security.Cryptography.X509Certificates;
using WebSocketSharp; using WebSocketSharp;
using WebSocketSharp.Server; using WebSocketSharp.Server;
namespace Example2 namespace Example2 {
{
public class Program public class Program {
{
public static void Main (string [] args) public static void Main (string [] args)
{ {
/* Single service server /* Single service server
//var wssv = new WebSocketServiceHost<Echo>("ws://localhost:4649"); var wssv = new WebSocketServiceHost<Echo> ("ws://localhost:4649");
var wssv = new WebSocketServiceHost<Echo>("ws://localhost:4649/Echo"); //var wssv = new WebSocketServiceHost<Echo> ("ws://localhost:4649/Echo");
//var wssv = new WebSocketServiceHost<Echo> ("ws://localhost:4649/エコー"); //var wssv = new WebSocketServiceHost<Echo> ("ws://localhost:4649/エコー");
//var wssv = new WebSocketServiceHost<Echo> (4649); //var wssv = new WebSocketServiceHost<Echo> (4649);
//var wssv = new WebSocketServiceHost<Echo> (4649, "/Echo"); //var wssv = new WebSocketServiceHost<Echo> (4649, "/Echo");
@ -21,7 +23,10 @@ namespace Example2
//var wssv = new WebSocketServiceHost<Chat> (4649); //var wssv = new WebSocketServiceHost<Chat> (4649);
//var wssv = new WebSocketServiceHost<Chat> (4649, "/Chat"); //var wssv = new WebSocketServiceHost<Chat> (4649, "/Chat");
//var wssv = new WebSocketServiceHost<Chat> (4649, "/チャット"); //var wssv = new WebSocketServiceHost<Chat> (4649, "/チャット");
//wssv.Sweeping = false; // Stop the sweep inactive session timer. #if DEBUG
wssv.Log.Level = LogLevel.TRACE;
#endif
//wssv.Sweeping = false;
wssv.Start (); wssv.Start ();
Console.WriteLine ( Console.WriteLine (
@ -29,13 +34,18 @@ namespace Example2
wssv.Uri, wssv.Address, wssv.Port); wssv.Uri, wssv.Address, wssv.Port);
*/ */
// Multi services server /* Multi services server */
var wssv = new WebSocketServer (4649); var wssv = new WebSocketServer (4649);
//var wssv = new WebSocketServer (4649, true);
//var wssv = new WebSocketServer ("ws://localhost:4649"); //var wssv = new WebSocketServer ("ws://localhost:4649");
//var wssv = new WebSocketServer ("wss://localhost:4649");
#if DEBUG #if DEBUG
wssv.Log.Level = LogLevel.TRACE; wssv.Log.Level = LogLevel.TRACE;
#endif #endif
//wssv.Sweeping = false; // Stop the sweep inactive session timer. //var file = ConfigurationManager.AppSettings ["ServerCertFile"];
//var password = ConfigurationManager.AppSettings ["CertFilePassword"];
//wssv.Certificate = new X509Certificate2 (file, password);
//wssv.Sweeping = false;
wssv.AddWebSocketService<Echo> ("/Echo"); wssv.AddWebSocketService<Echo> ("/Echo");
wssv.AddWebSocketService<Chat> ("/Chat"); wssv.AddWebSocketService<Chat> ("/Chat");
//wssv.AddWebSocketService<Echo> ("/エコー"); //wssv.AddWebSocketService<Echo> ("/エコー");
@ -46,10 +56,9 @@ namespace Example2
"WebSocket Server listening on port: {0} service path:", wssv.Port); "WebSocket Server listening on port: {0} service path:", wssv.Port);
foreach (var path in wssv.ServicePaths) foreach (var path in wssv.ServicePaths)
Console.WriteLine (" {0}", path); Console.WriteLine (" {0}", path);
Console.WriteLine (); Console.WriteLine ();
Console.WriteLine ("Press enter key to stop server...");
Console.WriteLine("Press any key to stop server...");
Console.ReadLine (); Console.ReadLine ();
wssv.Stop (); wssv.Stop ();

View File

@ -71,7 +71,7 @@ ws.OnOpen += (sender, e) =>
}; };
``` ```
`e` has come across as `EventArgs.Empty`, so there is no operation on `e`. `e` has come across as `EventArgs.Empty`, so you don't use `e`.
##### WebSocket.OnMessage event ##### ##### WebSocket.OnMessage event #####
@ -84,7 +84,11 @@ ws.OnMessage += (sender, e) =>
}; };
``` ```
`e.Type` (`WebSocketSharp.MessageEventArgs.Type`, the type of this property is `WebSocketSharp.Opcode`) indicates the **Frame type** of a WebSocket frame, so by checking this property, you determine which item you should operate. `e.Type` (`WebSocketSharp.MessageEventArgs.Type`, the type of this property is `WebSocketSharp.Opcode`) indicates the **Frame Type** of a WebSocket frame, so by checking this property, you determine which item you should use.
If `e.Type` equals `Opcode.TEXT`, you use `e.Data` (`WebSocketSharp.MessageEventArgs.Data`, the type of this property is `string`) that contains the received data.
If `e.Type` equals `Opcode.BINARY`, you use `e.RawData` (`WebSocketSharp.MessageEventArgs.RawData`, the type of this property is `byte[]`) that contains the received data.
```cs ```cs
if (e.Type == Opcode.TEXT) if (e.Type == Opcode.TEXT)
@ -100,10 +104,6 @@ if (e.Type == Opcode.BINARY)
} }
``` ```
If `e.Type` equaled `Opcode.TEXT`, you would operate `e.Data` (`WebSocketSharp.MessageEventArgs.Data`, the type of this property is `string`).
If `e.Type` equaled `Opcode.BINARY`, you would operate `e.RawData` (`WebSocketSharp.MessageEventArgs.RawData`, the type of this property is `byte[]`).
##### WebSocket.OnError event ##### ##### WebSocket.OnError event #####
A `WebSocket.OnError` event occurs when the `WebSocket` gets an error. A `WebSocket.OnError` event occurs when the `WebSocket` gets an error.
@ -114,7 +114,7 @@ ws.OnError += (sender, e) =>
... ...
}; };
``` ```
`e.Message` (`WebSocketSharp.ErrorEventArgs.Message`, the type of this property is `string`) contains an error message, so you operate this. `e.Message` (`WebSocketSharp.ErrorEventArgs.Message`, the type of this property is `string`) contains an error message, so you use this.
##### WebSocket.OnClose event ##### ##### WebSocket.OnClose event #####
@ -127,7 +127,7 @@ ws.OnClose += (sender, e) =>
}; };
``` ```
`e.Code` (`WebSocketSharp.CloseEventArgs.Code`, the type of this property is `ushort`) contains a status code indicating the reason for closure and `e.Reason` (`WebSocketSharp.CloseEventArgs.Reason`, the type of this property is `string`) contains the reason for closure, so you operate these. `e.Code` (`WebSocketSharp.CloseEventArgs.Code`, the type of this property is `ushort`) contains a status code indicating the reason for closure and `e.Reason` (`WebSocketSharp.CloseEventArgs.Reason`, the type of this property is `string`) contains the reason for closure, so you use these.
#### Step 4 #### #### Step 4 ####
@ -157,7 +157,7 @@ Closing the WebSocket connection.
ws.Close(code, reason); ws.Close(code, reason);
``` ```
If you wanted to close the WebSocket connection explicitly, you would use the `Close` method. If you want to close the WebSocket connection explicitly, you can use the `Close` method.
And the `Close` method is overloaded. The types of `code` are `WebSocketSharp.CloseStatusCode` and `ushort`, the type of `reason` is `string`. And the `Close` method is overloaded. The types of `code` are `WebSocketSharp.CloseStatusCode` and `ushort`, the type of `reason` is `string`.
@ -176,7 +176,7 @@ namespace Example {
{ {
protected override void OnMessage(MessageEventArgs e) protected override void OnMessage(MessageEventArgs e)
{ {
var msg = e.Data.ToLower().Equals("balus") var msg = e.Data.ToLower() == "balus"
? "I've been balused already..." ? "I've been balused already..."
: "I'm not available now."; : "I'm not available now.";
Send(msg); Send(msg);
@ -266,7 +266,7 @@ You can add any WebSocket service with a specified path to the service to your `
The type of `T` inherits `WebSocketService` class, so you can use a class that was created in **Step 2**. The type of `T` inherits `WebSocketService` class, so you can use a class that was created in **Step 2**.
If you created a instance of the `WebSocketServer` class without the port number, the `WebSocketServer` would set the port number to **80** automatically. So it is necessary to run with root permission. If you create a instance of the `WebSocketServer` class without the port number, the `WebSocketServer` set the port number to **80** automatically. So it is necessary to run with root permission.
$ sudo mono example2.exe $ sudo mono example2.exe
@ -285,7 +285,7 @@ wssv.OnError += (sender, e) =>
}; };
``` ```
`e.Message` (`WebSocketSharp.ErrorEventArgs.Message`, the type of this property is `string`) contains an error message, so you operate this. `e.Message` (`WebSocketSharp.ErrorEventArgs.Message`, the type of this property is `string`) contains an error message, so you use this.
##### WebSocketServer.OnError event ##### ##### WebSocketServer.OnError event #####
@ -320,21 +320,51 @@ httpsv.AddWebSocketService<Echo>("/");
For more information, could you see **[Example3]**? For more information, could you see **[Example3]**?
### Secure Connection ###
As a **WebSocket Client**, creating a instance of the `WebSocket` class with the WebSocket URL with the **wss** scheme.
```cs
using (var ws = new WebSocket("wss://example.com"))
{
...
}
```
If you want to set the custom validation for the server certificate, you can use the `WebSocket.ServerCertificateValidationCallback` property.
```cs
ws.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
// Do something to validate the server certificate.
return true; // The server certificate is valid.
};
```
If you set this property to nothing, the validation does nothing with the server certificate, always returns valid.
As a **WebSocket Server**, creating and setting a instance of the WebSocket server with some settings for the secure connection.
```cs
var wssv = new WebSocketServer(4649, true);
wssv.Certificate = new X509Certificate2("/path/to/cert.pfx", "password for cert.pfx");
```
### Logging ### ### Logging ###
The `WebSocket` class includes own logging functions. The `WebSocket` class includes own logging functions.
The `WebSocket.Log` property provides the logging functions. The `WebSocket.Log` property provides the logging functions.
If you wanted to change the current logging level (the default is the `LogLevel.ERROR`), you would operate the `WebSocket.Log.Level` property. If you want to change the current logging level (the default is `LogLevel.ERROR`), you can use the `WebSocket.Log.Level` property.
```cs ```cs
ws.Log.Level = LogLevel.DEBUG; ws.Log.Level = LogLevel.DEBUG;
``` ```
This setting means that the logging outputs with a less than the `LogLevel.DEBUG` are not outputted. This setting means that the logging outputs with a less than `LogLevel.DEBUG` are not outputted.
And if you wanted to output a log, you would use some output methods. The following outputs a log with the `LogLevel.DEBUG`. And if you want to output a log, you can use some output methods. The following outputs a log with `LogLevel.DEBUG`.
```cs ```cs
ws.Log.Debug("This is a debug message."); ws.Log.Debug("This is a debug message.");
@ -354,7 +384,7 @@ Examples of using **websocket-sharp**.
[Example1] connects to the [Audio Data delivery server] using the WebSocket ([Example1] is only implemented the chat feature, still unfinished). [Example1] connects to the [Audio Data delivery server] using the WebSocket ([Example1] is only implemented the chat feature, still unfinished).
And [Example1] uses the [Json.NET]. And [Example1] uses [Json.NET].
### Example2 ### ### Example2 ###

View File

@ -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 & server")] [assembly: AssemblyDescription("A C# implementation of the WebSocket protocol client and server")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("websocket-sharp.dll")] [assembly: AssemblyProduct("websocket-sharp.dll")]

View File

@ -45,6 +45,7 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using WebSocketSharp.Net; using WebSocketSharp.Net;
using WebSocketSharp.Net.WebSockets; using WebSocketSharp.Net.WebSockets;
@ -282,6 +283,12 @@ namespace WebSocketSharp {
: null; : null;
} }
internal static TcpListenerWebSocketContext GetWebSocketContext(
this TcpClient client, bool secure, X509Certificate cert)
{
return new TcpListenerWebSocketContext(client, secure, cert);
}
// <summary> // <summary>
// Determines whether the specified object is <see langword="null"/>. // Determines whether the specified object is <see langword="null"/>.
// </summary> // </summary>
@ -532,60 +539,6 @@ namespace WebSocketSharp {
#region Public Methods #region Public Methods
/// <summary>
/// Accepts a WebSocket connection by the <see cref="TcpListener"/>.
/// </summary>
/// <returns>
/// A <see cref="TcpListenerWebSocketContext"/> that contains a WebSocket connection.
/// </returns>
/// <param name="listener">
/// A <see cref="TcpListener"/> that provides a TCP connection to accept a WebSocket connection.
/// </param>
/// <param name="secure">
/// A <see cref="bool"/> that indicates a secure connection or not. (<c>true</c> indicates a secure connection.)
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="listener"/> is <see langword="null"/>.
/// </exception>
public static TcpListenerWebSocketContext AcceptWebSocket(this TcpListener listener, bool secure)
{
if (listener == null)
throw new ArgumentNullException("listener");
var client = listener.AcceptTcpClient();
return new TcpListenerWebSocketContext(client, secure);
}
/// <summary>
/// Accepts a WebSocket connection asynchronously by the <see cref="TcpListener"/>.
/// </summary>
/// <param name="listener">
/// A <see cref="TcpListener"/> that provides a TCP connection to accept a WebSocket connection.
/// </param>
/// <param name="secure">
/// A <see cref="bool"/> that indicates a secure connection or not. (<c>true</c> indicates a secure connection.)
/// </param>
/// <param name="completed">
/// An Action&lt;TcpListenerWebSocketContext&gt; delegate that contains the method(s) that is called when an asynchronous operation completes.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="listener"/> is <see langword="null"/>.
/// </exception>
public static void AcceptWebSocketAsync(this TcpListener listener, bool secure, Action<TcpListenerWebSocketContext> completed)
{
if (listener == null)
throw new ArgumentNullException("listener");
AsyncCallback callback = (ar) =>
{
var client = listener.EndAcceptTcpClient(ar);
var context = new TcpListenerWebSocketContext(client, secure);
completed(context);
};
listener.BeginAcceptTcpClient(callback, null);
}
/// <summary> /// <summary>
/// Determines whether the specified <see cref="string"/> contains any of characters /// Determines whether the specified <see cref="string"/> contains any of characters
/// in the specified array of <see cref="char"/>. /// in the specified array of <see cref="char"/>.

View File

@ -30,12 +30,13 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal; using System.Security.Principal;
namespace WebSocketSharp.Net.WebSockets { namespace WebSocketSharp.Net.WebSockets {
/// <summary> /// <summary>
/// Provides access to the WebSocket connection request objects received by the <see cref="TcpListener"/> class. /// Provides access to the WebSocket connection request objects received by the <see cref="TcpListener"/>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// </remarks> /// </remarks>
@ -44,22 +45,22 @@ namespace WebSocketSharp.Net.WebSockets {
#region Private Fields #region Private Fields
private CookieCollection _cookies; private CookieCollection _cookies;
private TcpClient _tcpClient; private TcpClient _client;
private bool _isSecure;
private RequestHandshake _request; private RequestHandshake _request;
private bool _secure;
private WsStream _stream;
private WebSocket _websocket; private WebSocket _websocket;
private WsStream _wsStream;
#endregion #endregion
#region Internal Constructors #region Internal Constructors
internal TcpListenerWebSocketContext(TcpClient tcpClient, bool secure) internal TcpListenerWebSocketContext(TcpClient client, bool secure, X509Certificate cert)
{ {
_tcpClient = tcpClient; _client = client;
_isSecure = secure; _secure = secure;
_wsStream = WsStream.CreateServerStream(tcpClient, secure); _stream = WsStream.CreateServerStream(client, secure, cert);
_request = RequestHandshake.Parse(_wsStream.ReadHandshake()); _request = RequestHandshake.Parse(_stream.ReadHandshake());
_websocket = new WebSocket(this); _websocket = new WebSocket(this);
} }
@ -69,7 +70,7 @@ namespace WebSocketSharp.Net.WebSockets {
internal WsStream Stream { internal WsStream Stream {
get { get {
return _wsStream; return _stream;
} }
} }
@ -85,7 +86,7 @@ namespace WebSocketSharp.Net.WebSockets {
/// </value> /// </value>
public override CookieCollection CookieCollection { public override CookieCollection CookieCollection {
get { get {
if (_cookies.IsNull()) if (_cookies == null)
_cookies = _request.Cookies; _cookies = _request.Cookies;
return _cookies; return _cookies;
@ -139,7 +140,7 @@ namespace WebSocketSharp.Net.WebSockets {
/// </value> /// </value>
public override bool IsSecureConnection { public override bool IsSecureConnection {
get { get {
return _isSecure; return _secure;
} }
} }
@ -260,7 +261,7 @@ namespace WebSocketSharp.Net.WebSockets {
/// </value> /// </value>
public virtual System.Net.IPEndPoint ServerEndPoint { public virtual System.Net.IPEndPoint ServerEndPoint {
get { get {
return (System.Net.IPEndPoint)_tcpClient.Client.LocalEndPoint; return (System.Net.IPEndPoint)_client.Client.LocalEndPoint;
} }
} }
@ -287,7 +288,7 @@ namespace WebSocketSharp.Net.WebSockets {
/// </value> /// </value>
public virtual System.Net.IPEndPoint UserEndPoint { public virtual System.Net.IPEndPoint UserEndPoint {
get { get {
return (System.Net.IPEndPoint)_tcpClient.Client.RemoteEndPoint; return (System.Net.IPEndPoint)_client.Client.RemoteEndPoint;
} }
} }
@ -309,8 +310,8 @@ namespace WebSocketSharp.Net.WebSockets {
internal void Close() internal void Close()
{ {
_wsStream.Close(); _stream.Close();
_tcpClient.Close(); _client.Close();
} }
#endregion #endregion

View File

@ -30,6 +30,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using WebSocketSharp.Net.WebSockets; using WebSocketSharp.Net.WebSockets;
@ -46,13 +47,14 @@ namespace WebSocketSharp.Server {
#region Private Fields #region Private Fields
private IPAddress _address; private IPAddress _address;
private X509Certificate2 _cert;
private bool _listening; private bool _listening;
private Logger _logger; private Logger _logger;
private int _port; private int _port;
private Thread _receiveRequestThread; private Thread _receiveRequestThread;
private bool _secure; private bool _secure;
private bool _selfHost; private bool _selfHost;
private TcpListener _tcpListener; private TcpListener _listener;
private Uri _uri; private Uri _uri;
#endregion #endregion
@ -63,7 +65,7 @@ namespace WebSocketSharp.Server {
/// Initializes a new instance of the <see cref="WebSocketServerBase"/> class. /// Initializes a new instance of the <see cref="WebSocketServerBase"/> class.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This constructor initializes a new instance of this class as non self host. /// This constructor initializes a new instance of this class as non self hosted server.
/// </remarks> /// </remarks>
protected WebSocketServerBase() protected WebSocketServerBase()
: this(new Logger()) : this(new Logger())
@ -75,7 +77,7 @@ namespace WebSocketSharp.Server {
/// with the specified <paramref name="logger"/>. /// with the specified <paramref name="logger"/>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This constructor initializes a new instance of this class as non self host. /// This constructor initializes a new instance of this class as non self hosted server.
/// </remarks> /// </remarks>
/// <param name="logger"> /// <param name="logger">
/// A <see cref="Logger"/> that provides the logging functions. /// A <see cref="Logger"/> that provides the logging functions.
@ -208,6 +210,25 @@ namespace WebSocketSharp.Server {
} }
} }
/// <summary>
/// Gets or sets the certificate used to authenticate the server on the secure connection.
/// </summary>
/// <value>
/// A <see cref="X509Certificate2"/> used to authenticate the server.
/// </value>
public X509Certificate2 Certificate {
get {
return _cert;
}
set {
if (_listening)
return;
_cert = value;
}
}
/// <summary> /// <summary>
/// Gets a value indicating whether the server has been started. /// Gets a value indicating whether the server has been started.
/// </summary> /// </summary>
@ -303,7 +324,7 @@ namespace WebSocketSharp.Server {
_listening = false; _listening = false;
_logger = new Logger(); _logger = new Logger();
_selfHost = true; _selfHost = true;
_tcpListener = new TcpListener(_address, _port); _listener = new TcpListener(_address, _port);
} }
private void init(Uri uri) private void init(Uri uri)
@ -320,13 +341,13 @@ namespace WebSocketSharp.Server {
init(); init();
} }
private void processRequestAsync(TcpListenerWebSocketContext context) private void processRequestAsync(TcpClient client)
{ {
WaitCallback callback = (state) => WaitCallback callback = state =>
{ {
try try
{ {
AcceptWebSocket(context); AcceptWebSocket(client.GetWebSocketContext(_secure, _cert));
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -344,11 +365,11 @@ namespace WebSocketSharp.Server {
{ {
try try
{ {
processRequestAsync(_tcpListener.AcceptWebSocket(_secure)); processRequestAsync(_listener.AcceptTcpClient());
} }
catch (SocketException) catch (SocketException)
{ {
// TcpListener has been stopped. _logger.Info("TcpListener has been stopped.");
break; break;
} }
catch (Exception ex) catch (Exception ex)
@ -368,7 +389,7 @@ namespace WebSocketSharp.Server {
_receiveRequestThread.Start(); _receiveRequestThread.Start();
} }
private bool tryCreateUri(string uriString, out Uri result, out string message) private static bool tryCreateUri(string uriString, out Uri result, out string message)
{ {
if (!uriString.TryCreateWebSocketUri(out result, out message)) if (!uriString.TryCreateWebSocketUri(out result, out message))
return false; return false;
@ -422,7 +443,16 @@ namespace WebSocketSharp.Server {
if (!_selfHost || _listening) if (!_selfHost || _listening)
return; return;
_tcpListener.Start(); if (_secure && _cert == null)
{
var msg = "Secure connection requires a server certificate.";
_logger.Error(msg);
error(msg);
return;
}
_listener.Start();
startReceiveRequestThread(); startReceiveRequestThread();
_listening = true; _listening = true;
} }
@ -435,7 +465,7 @@ namespace WebSocketSharp.Server {
if (!_selfHost || !_listening) if (!_selfHost || !_listening)
return; return;
_tcpListener.Stop(); _listener.Stop();
_receiveRequestThread.Join(5 * 1000); _receiveRequestThread.Join(5 * 1000);
_listening = false; _listening = false;
} }

View File

@ -38,6 +38,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Sockets; using System.Net.Sockets;
using System.Net.Security;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -66,6 +67,8 @@ namespace WebSocketSharp {
#region Private Fields #region Private Fields
private string _base64key; private string _base64key;
private RemoteCertificateValidationCallback
_certValidationCallback;
private bool _client; private bool _client;
private Action _closeContext; private Action _closeContext;
private CookieCollection _cookies; private CookieCollection _cookies;
@ -162,9 +165,8 @@ namespace WebSocketSharp {
_uri = uri; _uri = uri;
_protocols = protocols.ToString(", "); _protocols = protocols.ToString(", ");
_client = true; _client = true;
_secure = uri.Scheme == "wss" _secure = uri.Scheme == "wss" ? true : false;
? true _base64key = createBase64Key();
: false;
} }
/// <summary> /// <summary>
@ -226,6 +228,12 @@ namespace WebSocketSharp {
} }
} }
internal bool IsOpened {
get {
return _readyState == WsState.OPEN || _readyState == WsState.CLOSING;
}
}
#endregion #endregion
#region Public Properties #region Public Properties
@ -243,8 +251,14 @@ namespace WebSocketSharp {
} }
set { set {
if (isOpened(true)) if (IsOpened)
{
var msg = "The WebSocket connection has already been established.";
_logger.Error(msg);
error(msg);
return; return;
}
_compression = value; _compression = value;
} }
@ -363,19 +377,25 @@ namespace WebSocketSharp {
} }
set { set {
if (isOpened(true)) string msg = null;
return; if (IsOpened)
{
if (value.IsNullOrEmpty()) msg = "The WebSocket connection has already been established.";
}
else if (value.IsNullOrEmpty())
{ {
_origin = String.Empty; _origin = String.Empty;
return; return;
} }
else
{
var origin = new Uri(value); var origin = new Uri(value);
if (!origin.IsAbsoluteUri || origin.Segments.Length > 1) if (!origin.IsAbsoluteUri || origin.Segments.Length > 1)
msg = "The syntax of value of Origin must be '<scheme>://<host>[:<port>]'.";
}
if (msg != null)
{ {
var msg = "The syntax of value of Origin must be '<scheme>://<host>[:<port>]'.";
_logger.Error(msg); _logger.Error(msg);
error(msg); error(msg);
@ -410,6 +430,27 @@ namespace WebSocketSharp {
} }
} }
/// <summary>
/// Gets or sets the callback used to validate the certificate supplied by the server.
/// </summary>
/// <remarks>
/// If the value of this property is <see langword="null"/>, the validation does nothing
/// with the server certificate, always returns valid.
/// </remarks>
/// <value>
/// A <see cref="RemoteCertificateValidationCallback"/> delegate that references the method(s)
/// used to validate the server certificate. The default is <see langword="null"/>.
/// </value>
public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
get {
return _certValidationCallback;
}
set {
_certValidationCallback = value;
}
}
/// <summary> /// <summary>
/// Gets the WebSocket URL to connect. /// Gets the WebSocket URL to connect.
/// </summary> /// </summary>
@ -777,7 +818,7 @@ namespace WebSocketSharp {
// As client // As client
private bool doHandshake() private bool doHandshake()
{ {
init(); setWsStream();
return processResponseHandshake(sendRequestHandshake()); return processResponseHandshake(sendRequestHandshake());
} }
@ -799,17 +840,6 @@ namespace WebSocketSharp {
return CompressionMethod.NONE; return CompressionMethod.NONE;
} }
// As client
private void init()
{
_base64key = createBase64Key();
var host = _uri.DnsSafeHost;
var port = _uri.Port;
_tcpClient = new TcpClient(host, port);
_wsStream = WsStream.CreateClientStream(_tcpClient, host, _secure);
}
// As server // As server
private void init(WebSocketContext context) private void init(WebSocketContext context)
{ {
@ -832,21 +862,6 @@ namespace WebSocketSharp {
: false; : false;
} }
private bool isOpened(bool errorIfOpened)
{
if (_readyState != WsState.OPEN && _readyState != WsState.CLOSING)
return false;
if (errorIfOpened)
{
var msg = "The WebSocket connection has been established already.";
_logger.Error(msg);
error(msg);
}
return true;
}
// As server // As server
private bool isValidHostHeader() private bool isValidHostHeader()
{ {
@ -1351,6 +1366,15 @@ namespace WebSocketSharp {
send(res); send(res);
} }
// As client
private void setWsStream()
{
var host = _uri.DnsSafeHost;
var port = _uri.Port;
_tcpClient = new TcpClient(host, port);
_wsStream = WsStream.CreateClientStream(_tcpClient, _secure, host, _certValidationCallback);
}
private void startReceiving() private void startReceiving()
{ {
_exitReceiving = new AutoResetEvent(false); _exitReceiving = new AutoResetEvent(false);
@ -1480,8 +1504,14 @@ namespace WebSocketSharp {
/// </summary> /// </summary>
public void Connect() public void Connect()
{ {
if (isOpened(true)) if (IsOpened)
{
var msg = "The WebSocket connection has already been established.";
_logger.Error(msg);
error(msg);
return; return;
}
try try
{ {
@ -1690,12 +1720,14 @@ namespace WebSocketSharp {
/// </param> /// </param>
public void SetCookie(Cookie cookie) public void SetCookie(Cookie cookie)
{ {
if (isOpened(true)) var msg = IsOpened
return; ? "The WebSocket connection has already been established."
: cookie == null
? "'cookie' must not be null."
: null;
if (cookie == null) if (msg != null)
{ {
var msg = "'cookie' must not be null.";
_logger.Error(msg); _logger.Error(msg);
error(msg); error(msg);
@ -1723,24 +1755,28 @@ namespace WebSocketSharp {
/// </param> /// </param>
public void SetCredentials(string userName, string password, bool preAuth) public void SetCredentials(string userName, string password, bool preAuth)
{ {
if (isOpened(true)) string msg = null;
return; if (IsOpened)
{
if (userName == null) msg = "The WebSocket connection has already been established.";
}
else if (userName == null)
{ {
_credentials = null; _credentials = null;
_preAuth = false; _preAuth = false;
return; return;
} }
else
var msg = userName.Length > 0 && (userName.Contains(':') || !userName.IsText()) {
msg = userName.Length > 0 && (userName.Contains(':') || !userName.IsText())
? "'userName' contains an invalid character." ? "'userName' contains an invalid character."
: !password.IsNullOrEmpty() && !password.IsText() : !password.IsNullOrEmpty() && !password.IsText()
? "'password' contains an invalid character." ? "'password' contains an invalid character."
: String.Empty; : null;
}
if (msg.Length > 0) if (msg != null)
{ {
_logger.Error(msg); _logger.Error(msg);
error(msg); error(msg);

View File

@ -28,7 +28,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Configuration;
using System.IO; using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
@ -119,18 +118,20 @@ namespace WebSocketSharp {
#region Internal Methods #region Internal Methods
internal static WsStream CreateClientStream(TcpClient tcpClient, string host, bool secure) internal static WsStream CreateClientStream(
TcpClient client,
bool secure,
string host,
System.Net.Security.RemoteCertificateValidationCallback validationCallback
)
{ {
var netStream = tcpClient.GetStream(); var netStream = client.GetStream();
if (secure) if (secure)
{ {
System.Net.Security.RemoteCertificateValidationCallback callback = (sender, certificate, chain, sslPolicyErrors) => if (validationCallback == null)
{ validationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
// FIXME: Always returns true
return true;
};
var sslStream = new SslStream(netStream, false, callback); var sslStream = new SslStream(netStream, false, validationCallback);
sslStream.AuthenticateAsClient(host); sslStream.AuthenticateAsClient(host);
return new WsStream(sslStream); return new WsStream(sslStream);
@ -139,14 +140,13 @@ namespace WebSocketSharp {
return new WsStream(netStream); return new WsStream(netStream);
} }
internal static WsStream CreateServerStream(TcpClient tcpClient, bool secure) internal static WsStream CreateServerStream(TcpClient client, bool secure, X509Certificate cert)
{ {
var netStream = tcpClient.GetStream(); var netStream = client.GetStream();
if (secure) if (secure)
{ {
var sslStream = new SslStream(netStream, false); var sslStream = new SslStream(netStream, false);
var certPath = ConfigurationManager.AppSettings["ServerCertPath"]; sslStream.AuthenticateAsServer(cert);
sslStream.AuthenticateAsServer(new X509Certificate2(certPath));
return new WsStream(sslStream); return new WsStream(sslStream);
} }