Fix for HTTP Basic/Digest Authentication
This commit is contained in:
@@ -1,69 +1,67 @@
|
||||
//
|
||||
// AuthenticationSchemes.cs
|
||||
// Copied from System.Net.AuthenticationSchemes.cs
|
||||
//
|
||||
// Author:
|
||||
// Atsushi Enomoto <atsushi@ximian.com>
|
||||
//
|
||||
// Copyright (C) 2005 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* AuthenticationSchemes.cs
|
||||
*
|
||||
* This code is derived from System.Net.AuthenticationSchemes.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2013 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>
|
||||
/// Contains the values of the schemes for authentication.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AuthenticationSchemes {
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that no authentication is allowed.
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Indicates digest authentication.
|
||||
/// </summary>
|
||||
Digest = 1,
|
||||
/// <summary>
|
||||
/// Indicates negotiating with the client to determine the authentication scheme.
|
||||
/// </summary>
|
||||
Negotiate = 2,
|
||||
/// <summary>
|
||||
/// Indicates NTLM authentication.
|
||||
/// </summary>
|
||||
Ntlm = 4,
|
||||
/// <summary>
|
||||
/// Indicates Windows authentication.
|
||||
/// </summary>
|
||||
IntegratedWindowsAuthentication = 6,
|
||||
/// <summary>
|
||||
/// Indicates basic authentication.
|
||||
/// </summary>
|
||||
Basic = 8,
|
||||
/// <summary>
|
||||
/// Indicates anonymous authentication.
|
||||
/// </summary>
|
||||
Anonymous = 0x8000,
|
||||
}
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the values of the schemes for authentication.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AuthenticationSchemes
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that no authentication is allowed.
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Indicates digest authentication.
|
||||
/// </summary>
|
||||
Digest = 1,
|
||||
/// <summary>
|
||||
/// Indicates basic authentication.
|
||||
/// </summary>
|
||||
Basic = 8,
|
||||
/// <summary>
|
||||
/// Indicates anonymous authentication.
|
||||
/// </summary>
|
||||
Anonymous = 0x8000,
|
||||
}
|
||||
}
|
||||
|
@@ -1,32 +1,41 @@
|
||||
//
|
||||
// EndPointListener.cs
|
||||
// Copied from System.Net.EndPointListener.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
// Copyright (c) 2012-2013 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.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* EndPointListener.cs
|
||||
*
|
||||
* This code is derived from System.Net.EndPointListener.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2013 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;
|
||||
@@ -40,411 +49,405 @@ using System.Threading;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal sealed class EndPointListener
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
List<ListenerPrefix> _all; // host = '+'
|
||||
X509Certificate2 _cert;
|
||||
IPEndPoint _endpoint;
|
||||
Dictionary<ListenerPrefix, HttpListener> _prefixes;
|
||||
bool _secure;
|
||||
Socket _socket;
|
||||
List<ListenerPrefix> _unhandled; // host = '*'
|
||||
Dictionary<HttpConnection, HttpConnection> _unregistered;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
public EndPointListener (
|
||||
IPAddress address,
|
||||
int port,
|
||||
bool secure,
|
||||
string certFolderPath,
|
||||
X509Certificate2 defaultCert
|
||||
)
|
||||
{
|
||||
if (secure) {
|
||||
_secure = secure;
|
||||
_cert = getCertificate (port, certFolderPath, defaultCert);
|
||||
if (_cert == null)
|
||||
throw new ArgumentException ("Server certificate not found.");
|
||||
}
|
||||
|
||||
_endpoint = new IPEndPoint (address, port);
|
||||
_socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
_socket.Bind (_endpoint);
|
||||
_socket.Listen (500);
|
||||
var args = new SocketAsyncEventArgs ();
|
||||
args.UserToken = this;
|
||||
args.Completed += onAccept;
|
||||
_socket.AcceptAsync (args);
|
||||
_prefixes = new Dictionary<ListenerPrefix, HttpListener> ();
|
||||
_unregistered = new Dictionary<HttpConnection, HttpConnection> ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static void addSpecial (List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (prefixes == null)
|
||||
return;
|
||||
|
||||
foreach (var p in prefixes)
|
||||
if (p.Path == prefix.Path) // TODO: code
|
||||
throw new HttpListenerException (400, "Prefix already in use.");
|
||||
|
||||
prefixes.Add (prefix);
|
||||
}
|
||||
|
||||
private void checkIfRemove ()
|
||||
{
|
||||
if (_prefixes.Count > 0)
|
||||
return;
|
||||
|
||||
if (_unhandled != null && _unhandled.Count > 0)
|
||||
return;
|
||||
|
||||
if (_all != null && _all.Count > 0)
|
||||
return;
|
||||
|
||||
EndPointManager.RemoveEndPoint (this, _endpoint);
|
||||
}
|
||||
|
||||
private static RSACryptoServiceProvider createRSAFromFile (string filename)
|
||||
{
|
||||
var rsa = new RSACryptoServiceProvider ();
|
||||
byte[] pvk = null;
|
||||
using (var fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
pvk = new byte [fs.Length];
|
||||
fs.Read (pvk, 0, pvk.Length);
|
||||
}
|
||||
|
||||
rsa.ImportCspBlob (pvk);
|
||||
return rsa;
|
||||
}
|
||||
|
||||
private static X509Certificate2 getCertificate (
|
||||
int port, string certFolderPath, X509Certificate2 defaultCert)
|
||||
{
|
||||
try {
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, 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 defaultCert;
|
||||
}
|
||||
|
||||
private static HttpListener matchFromList (
|
||||
string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
if (list == null)
|
||||
return null;
|
||||
|
||||
HttpListener best_match = null;
|
||||
var best_length = -1;
|
||||
foreach (var p in list)
|
||||
{
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < best_length)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath))
|
||||
{
|
||||
best_length = ppath.Length;
|
||||
best_match = p.Listener;
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
|
||||
return best_match;
|
||||
}
|
||||
|
||||
private static void onAccept (object sender, EventArgs e)
|
||||
{
|
||||
var args = (SocketAsyncEventArgs) e;
|
||||
var epListener = (EndPointListener) args.UserToken;
|
||||
Socket accepted = null;
|
||||
if (args.SocketError == SocketError.Success)
|
||||
{
|
||||
accepted = args.AcceptSocket;
|
||||
args.AcceptSocket = null;
|
||||
}
|
||||
|
||||
try {
|
||||
epListener._socket.AcceptAsync (args);
|
||||
}
|
||||
catch {
|
||||
if (accepted != null)
|
||||
accepted.Close ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (accepted == null)
|
||||
return;
|
||||
|
||||
HttpConnection conn = null;
|
||||
try {
|
||||
conn = new HttpConnection (accepted, epListener, epListener._secure, epListener._cert);
|
||||
lock (((ICollection) epListener._unregistered).SyncRoot)
|
||||
{
|
||||
epListener._unregistered [conn] = conn;
|
||||
}
|
||||
|
||||
conn.BeginReadRequest ();
|
||||
}
|
||||
catch {
|
||||
if (conn != null)
|
||||
{
|
||||
conn.Close (true);
|
||||
return;
|
||||
}
|
||||
|
||||
accepted.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool removeSpecial (List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (prefixes == null)
|
||||
return false;
|
||||
|
||||
var count = prefixes.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (prefixes [i].Path == prefix.Path)
|
||||
{
|
||||
prefixes.RemoveAt (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private HttpListener searchListener (Uri uri, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
if (uri == null)
|
||||
return null;
|
||||
|
||||
var host = uri.Host;
|
||||
var port = uri.Port;
|
||||
var path = HttpUtility.UrlDecode (uri.AbsolutePath);
|
||||
var path_slash = path [path.Length - 1] == '/' ? path : path + "/";
|
||||
HttpListener best_match = null;
|
||||
var best_length = -1;
|
||||
if (host != null && host.Length > 0)
|
||||
{
|
||||
foreach (var p in _prefixes.Keys)
|
||||
{
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < best_length)
|
||||
continue;
|
||||
|
||||
if (p.Host != host || p.Port != port)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath) || path_slash.StartsWith (ppath))
|
||||
{
|
||||
best_length = ppath.Length;
|
||||
best_match = _prefixes [p];
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_length != -1)
|
||||
return best_match;
|
||||
}
|
||||
|
||||
var list = _unhandled;
|
||||
best_match = matchFromList (host, path, list, out prefix);
|
||||
if (path != path_slash && best_match == null)
|
||||
best_match = matchFromList (host, path_slash, list, out prefix);
|
||||
|
||||
if (best_match != null)
|
||||
return best_match;
|
||||
|
||||
list = _all;
|
||||
best_match = matchFromList (host, path, list, out prefix);
|
||||
if (path != path_slash && best_match == null)
|
||||
best_match = matchFromList (host, path_slash, list, out prefix);
|
||||
|
||||
if (best_match != null)
|
||||
return best_match;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal static bool CertificateExists (int port, string certFolderPath)
|
||||
{
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port));
|
||||
|
||||
return File.Exists (cer) && File.Exists (key);
|
||||
}
|
||||
|
||||
internal void RemoveConnection (HttpConnection connection)
|
||||
{
|
||||
lock (((ICollection) _unregistered).SyncRoot)
|
||||
{
|
||||
_unregistered.Remove (connection);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void AddPrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*")
|
||||
{
|
||||
do {
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
prefix.Listener = listener;
|
||||
addSpecial (future, prefix);
|
||||
} while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+")
|
||||
{
|
||||
do {
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
prefix.Listener = listener;
|
||||
addSpecial (future, prefix);
|
||||
} while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = _prefixes;
|
||||
if (prefs.ContainsKey (prefix))
|
||||
{
|
||||
HttpListener other = prefs [prefix];
|
||||
if (other != listener) // TODO: code.
|
||||
throw new HttpListenerException (400, "There's another listener for " + prefix);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2 [prefix] = listener;
|
||||
} while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
}
|
||||
|
||||
public bool BindContext (HttpListenerContext context)
|
||||
{
|
||||
var req = context.Request;
|
||||
ListenerPrefix prefix;
|
||||
var listener = searchListener (req.Url, out prefix);
|
||||
if (listener == null)
|
||||
return false;
|
||||
|
||||
context.Listener = listener;
|
||||
context.Connection.Prefix = prefix;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
_socket.Close ();
|
||||
lock (((ICollection) _unregistered).SyncRoot)
|
||||
{
|
||||
var copy = new Dictionary<HttpConnection, HttpConnection> (_unregistered);
|
||||
foreach (var conn in copy.Keys)
|
||||
conn.Close (true);
|
||||
|
||||
copy.Clear ();
|
||||
_unregistered.Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*")
|
||||
{
|
||||
do {
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
} while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+")
|
||||
{
|
||||
do {
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
} while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = _prefixes;
|
||||
if (!prefs.ContainsKey (prefix))
|
||||
break;
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2.Remove (prefix);
|
||||
} while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
|
||||
checkIfRemove ();
|
||||
}
|
||||
|
||||
public void UnbindContext (HttpListenerContext context)
|
||||
{
|
||||
if (context == null || context.Request == null)
|
||||
return;
|
||||
|
||||
context.Listener.UnregisterContext (context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
internal sealed class EndPointListener
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private List<ListenerPrefix> _all; // host = '+'
|
||||
private X509Certificate2 _cert;
|
||||
private IPEndPoint _endpoint;
|
||||
private Dictionary<ListenerPrefix, HttpListener> _prefixes;
|
||||
private bool _secure;
|
||||
private Socket _socket;
|
||||
private List<ListenerPrefix> _unhandled; // host = '*'
|
||||
private Dictionary<HttpConnection, HttpConnection> _unregistered;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
public EndPointListener (
|
||||
IPAddress address,
|
||||
int port,
|
||||
bool secure,
|
||||
string certFolderPath,
|
||||
X509Certificate2 defaultCert)
|
||||
{
|
||||
if (secure) {
|
||||
_secure = secure;
|
||||
_cert = getCertificate (port, certFolderPath, defaultCert);
|
||||
if (_cert == null)
|
||||
throw new ArgumentException ("Server certificate not found.");
|
||||
}
|
||||
|
||||
_endpoint = new IPEndPoint (address, port);
|
||||
_socket = new Socket (
|
||||
address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
_socket.Bind (_endpoint);
|
||||
_socket.Listen (500);
|
||||
var args = new SocketAsyncEventArgs ();
|
||||
args.UserToken = this;
|
||||
args.Completed += onAccept;
|
||||
_socket.AcceptAsync (args);
|
||||
_prefixes = new Dictionary<ListenerPrefix, HttpListener> ();
|
||||
_unregistered = new Dictionary<HttpConnection, HttpConnection> ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static void addSpecial (
|
||||
List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (prefixes == null)
|
||||
return;
|
||||
|
||||
foreach (var p in prefixes)
|
||||
if (p.Path == prefix.Path) // TODO: code
|
||||
throw new HttpListenerException (400, "Prefix already in use.");
|
||||
|
||||
prefixes.Add (prefix);
|
||||
}
|
||||
|
||||
private void checkIfRemove ()
|
||||
{
|
||||
if (_prefixes.Count > 0)
|
||||
return;
|
||||
|
||||
if (_unhandled != null && _unhandled.Count > 0)
|
||||
return;
|
||||
|
||||
if (_all != null && _all.Count > 0)
|
||||
return;
|
||||
|
||||
EndPointManager.RemoveEndPoint (this, _endpoint);
|
||||
}
|
||||
|
||||
private static RSACryptoServiceProvider createRSAFromFile (string filename)
|
||||
{
|
||||
var rsa = new RSACryptoServiceProvider ();
|
||||
byte[] pvk = null;
|
||||
using (var fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
|
||||
pvk = new byte [fs.Length];
|
||||
fs.Read (pvk, 0, pvk.Length);
|
||||
}
|
||||
|
||||
rsa.ImportCspBlob (pvk);
|
||||
return rsa;
|
||||
}
|
||||
|
||||
private static X509Certificate2 getCertificate (
|
||||
int port, string certFolderPath, X509Certificate2 defaultCert)
|
||||
{
|
||||
try {
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, 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 defaultCert;
|
||||
}
|
||||
|
||||
private static HttpListener matchFromList (
|
||||
string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
if (list == null)
|
||||
return null;
|
||||
|
||||
HttpListener bestMatch = null;
|
||||
var bestLength = -1;
|
||||
foreach (var p in list) {
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < bestLength)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath)) {
|
||||
bestLength = ppath.Length;
|
||||
bestMatch = p.Listener;
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
private static void onAccept (object sender, EventArgs e)
|
||||
{
|
||||
var args = (SocketAsyncEventArgs) e;
|
||||
var epListener = (EndPointListener) args.UserToken;
|
||||
Socket accepted = null;
|
||||
if (args.SocketError == SocketError.Success) {
|
||||
accepted = args.AcceptSocket;
|
||||
args.AcceptSocket = null;
|
||||
}
|
||||
|
||||
try {
|
||||
epListener._socket.AcceptAsync (args);
|
||||
}
|
||||
catch {
|
||||
if (accepted != null)
|
||||
accepted.Close ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (accepted == null)
|
||||
return;
|
||||
|
||||
HttpConnection conn = null;
|
||||
try {
|
||||
conn = new HttpConnection (
|
||||
accepted, epListener, epListener._secure, epListener._cert);
|
||||
lock (((ICollection) epListener._unregistered).SyncRoot) {
|
||||
epListener._unregistered [conn] = conn;
|
||||
}
|
||||
|
||||
conn.BeginReadRequest ();
|
||||
}
|
||||
catch {
|
||||
if (conn != null) {
|
||||
conn.Close (true);
|
||||
return;
|
||||
}
|
||||
|
||||
accepted.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool removeSpecial (
|
||||
List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (prefixes == null)
|
||||
return false;
|
||||
|
||||
var count = prefixes.Count;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (prefixes [i].Path == prefix.Path) {
|
||||
prefixes.RemoveAt (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private HttpListener searchListener (Uri uri, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
if (uri == null)
|
||||
return null;
|
||||
|
||||
var host = uri.Host;
|
||||
var port = uri.Port;
|
||||
var path = HttpUtility.UrlDecode (uri.AbsolutePath);
|
||||
var pathSlash = path [path.Length - 1] == '/' ? path : path + "/";
|
||||
HttpListener bestMatch = null;
|
||||
var bestLength = -1;
|
||||
if (host != null && host.Length > 0) {
|
||||
foreach (var p in _prefixes.Keys) {
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < bestLength)
|
||||
continue;
|
||||
|
||||
if (p.Host != host || p.Port != port)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath) || pathSlash.StartsWith (ppath)) {
|
||||
bestLength = ppath.Length;
|
||||
bestMatch = _prefixes [p];
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestLength != -1)
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
var list = _unhandled;
|
||||
bestMatch = matchFromList (host, path, list, out prefix);
|
||||
if (path != pathSlash && bestMatch == null)
|
||||
bestMatch = matchFromList (host, pathSlash, list, out prefix);
|
||||
|
||||
if (bestMatch != null)
|
||||
return bestMatch;
|
||||
|
||||
list = _all;
|
||||
bestMatch = matchFromList (host, path, list, out prefix);
|
||||
if (path != pathSlash && bestMatch == null)
|
||||
bestMatch = matchFromList (host, pathSlash, list, out prefix);
|
||||
|
||||
if (bestMatch != null)
|
||||
return bestMatch;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal static bool CertificateExists (int port, string certFolderPath)
|
||||
{
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port));
|
||||
|
||||
return File.Exists (cer) && File.Exists (key);
|
||||
}
|
||||
|
||||
internal void RemoveConnection (HttpConnection connection)
|
||||
{
|
||||
lock (((ICollection) _unregistered).SyncRoot)
|
||||
_unregistered.Remove (connection);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void AddPrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*") {
|
||||
do {
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
|
||||
prefix.Listener = listener;
|
||||
addSpecial (future, prefix);
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+") {
|
||||
do {
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
|
||||
prefix.Listener = listener;
|
||||
addSpecial (future, prefix);
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = _prefixes;
|
||||
if (prefs.ContainsKey (prefix)) {
|
||||
var other = prefs [prefix];
|
||||
if (other != listener) // TODO: code.
|
||||
throw new HttpListenerException (
|
||||
400, "There's another listener for " + prefix);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2 [prefix] = listener;
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
}
|
||||
|
||||
public bool BindContext (HttpListenerContext context)
|
||||
{
|
||||
var req = context.Request;
|
||||
ListenerPrefix prefix;
|
||||
var listener = searchListener (req.Url, out prefix);
|
||||
if (listener == null)
|
||||
return false;
|
||||
|
||||
context.Listener = listener;
|
||||
context.Connection.Prefix = prefix;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
_socket.Close ();
|
||||
lock (((ICollection) _unregistered).SyncRoot) {
|
||||
var copy = new Dictionary<HttpConnection, HttpConnection> (_unregistered);
|
||||
foreach (var conn in copy.Keys)
|
||||
conn.Close (true);
|
||||
|
||||
copy.Clear ();
|
||||
_unregistered.Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*") {
|
||||
do {
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+") {
|
||||
do {
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = _prefixes;
|
||||
if (!prefs.ContainsKey (prefix))
|
||||
break;
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2.Remove (prefix);
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
|
||||
checkIfRemove ();
|
||||
}
|
||||
|
||||
public void UnbindContext (HttpListenerContext context)
|
||||
{
|
||||
if (context == null || context.Listener == null)
|
||||
return;
|
||||
|
||||
context.Listener.UnregisterContext (context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
81
websocket-sharp/Net/HttpBasicIdentity.cs
Normal file
81
websocket-sharp/Net/HttpBasicIdentity.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
#region License
|
||||
/*
|
||||
* HttpBasicIdentity.cs
|
||||
*
|
||||
* This code is derived from System.Net.HttpListenerBasicIdentity.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* 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
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Gonzalo Paniagua Javier <gonzalo@novell.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the user name and password from an HTTP Basic authentication request.
|
||||
/// </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 an HTTP Basic authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the password.
|
||||
/// </value>
|
||||
public virtual string Password {
|
||||
get {
|
||||
return _password;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
185
websocket-sharp/Net/HttpDigestIdentity.cs
Normal file
185
websocket-sharp/Net/HttpDigestIdentity.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
#region License
|
||||
/*
|
||||
* HttpDigestIdentity.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.Security.Principal;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the user name and other authentication parameters from an HTTP Digest
|
||||
/// authentication request.
|
||||
/// </summary>
|
||||
public class HttpDigestIdentity : GenericIdentity
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private NameValueCollection _params;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal HttpDigestIdentity (NameValueCollection authParams)
|
||||
: base (authParams ["username"], "Digest")
|
||||
{
|
||||
_params = authParams;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the algorithm parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the algorithm parameter.
|
||||
/// </value>
|
||||
public string Algorithm {
|
||||
get {
|
||||
return _params ["algorithm"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cnonce parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the cnonce parameter.
|
||||
/// </value>
|
||||
public string Cnonce {
|
||||
get {
|
||||
return _params ["cnonce"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nc parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the nc parameter.
|
||||
/// </value>
|
||||
public string Nc {
|
||||
get {
|
||||
return _params ["nc"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nonce parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the nonce parameter.
|
||||
/// </value>
|
||||
public string Nonce {
|
||||
get {
|
||||
return _params ["nonce"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the opaque parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the opaque parameter.
|
||||
/// </value>
|
||||
public string Opaque {
|
||||
get {
|
||||
return _params ["opaque"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the qop parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the qop parameter.
|
||||
/// </value>
|
||||
public string Qop {
|
||||
get {
|
||||
return _params ["qop"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the realm parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the realm parameter.
|
||||
/// </value>
|
||||
public string Realm {
|
||||
get {
|
||||
return _params ["realm"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the response parameter.
|
||||
/// </value>
|
||||
public string Response {
|
||||
get {
|
||||
return _params ["response"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the uri parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the uri parameter.
|
||||
/// </value>
|
||||
public string Uri {
|
||||
get {
|
||||
return _params ["uri"];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal bool IsValid (
|
||||
string password, string realm, string method, string entity)
|
||||
{
|
||||
var parameters = new NameValueCollection (_params);
|
||||
parameters ["password"] = password;
|
||||
parameters ["realm"] = realm;
|
||||
parameters ["method"] = method;
|
||||
parameters ["entity"] = entity;
|
||||
|
||||
return _params ["response"] == HttpUtility.CreateRequestDigest (parameters);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,40 @@
|
||||
#region License
|
||||
//
|
||||
// HttpListenerContext.cs
|
||||
// Copied from System.Net.HttpListenerContext.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
// Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
/*
|
||||
* HttpListenerContext.cs
|
||||
*
|
||||
* This code 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) 2012-2013 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;
|
||||
@@ -37,182 +44,184 @@ using System.Security.Principal;
|
||||
using System.Text;
|
||||
using WebSocketSharp.Net.WebSockets;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the HTTP request and response information used by the
|
||||
/// <see cref="HttpListener"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The HttpListenerContext class cannot be inherited.
|
||||
/// </remarks>
|
||||
public sealed class HttpListenerContext
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the HTTP request and response objects used by the <see cref="HttpListener"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The HttpListenerContext class cannot be inherited.
|
||||
/// </remarks>
|
||||
public sealed class HttpListenerContext {
|
||||
private HttpConnection _connection;
|
||||
private string _error;
|
||||
private int _errorStatus;
|
||||
private HttpListenerRequest _request;
|
||||
private HttpListenerResponse _response;
|
||||
private IPrincipal _user;
|
||||
|
||||
#region Private Fields
|
||||
#endregion
|
||||
|
||||
private HttpConnection _connection;
|
||||
private string _error;
|
||||
private int _errorStatus;
|
||||
private HttpListenerRequest _request;
|
||||
private HttpListenerResponse _response;
|
||||
private IPrincipal _user;
|
||||
#region Internal Fields
|
||||
|
||||
#endregion
|
||||
internal HttpListener Listener;
|
||||
|
||||
#region Internal Fields
|
||||
#endregion
|
||||
|
||||
internal HttpListener Listener;
|
||||
#region Internal Constructors
|
||||
|
||||
#endregion
|
||||
internal HttpListenerContext (HttpConnection connection)
|
||||
{
|
||||
_connection = connection;
|
||||
_errorStatus = 400;
|
||||
_request = new HttpListenerRequest (this);
|
||||
_response = new HttpListenerResponse (this);
|
||||
}
|
||||
|
||||
#region Constructor
|
||||
#endregion
|
||||
|
||||
internal HttpListenerContext (HttpConnection connection)
|
||||
{
|
||||
_connection = connection;
|
||||
_errorStatus = 400;
|
||||
_request = new HttpListenerRequest (this);
|
||||
_response = new HttpListenerResponse (this);
|
||||
}
|
||||
#region Internal Properties
|
||||
|
||||
#endregion
|
||||
internal HttpConnection Connection {
|
||||
get {
|
||||
return _connection;
|
||||
}
|
||||
}
|
||||
|
||||
#region Internal Properties
|
||||
internal string ErrorMessage {
|
||||
get {
|
||||
return _error;
|
||||
}
|
||||
|
||||
internal HttpConnection Connection {
|
||||
get {
|
||||
return _connection;
|
||||
}
|
||||
}
|
||||
set {
|
||||
_error = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal string ErrorMessage {
|
||||
get {
|
||||
return _error;
|
||||
}
|
||||
internal int ErrorStatus {
|
||||
get {
|
||||
return _errorStatus;
|
||||
}
|
||||
|
||||
set {
|
||||
_error = value;
|
||||
}
|
||||
}
|
||||
set {
|
||||
_errorStatus = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal int ErrorStatus {
|
||||
get {
|
||||
return _errorStatus;
|
||||
}
|
||||
internal bool HaveError {
|
||||
get {
|
||||
return _error != null && _error.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
set {
|
||||
_errorStatus = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
internal bool HaveError {
|
||||
get {
|
||||
return _error != null;
|
||||
}
|
||||
}
|
||||
#region Public Properties
|
||||
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpListenerRequest"/> that contains the HTTP
|
||||
/// request information from a client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="HttpListenerRequest"/> that contains the HTTP request
|
||||
/// information.
|
||||
/// </value>
|
||||
public HttpListenerRequest Request {
|
||||
get {
|
||||
return _request;
|
||||
}
|
||||
}
|
||||
|
||||
#region Public Properties
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpListenerResponse"/> that contains the HTTP
|
||||
/// response information to send to the client in response to the client's
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="HttpListenerResponse"/> that contains the HTTP response
|
||||
/// information.
|
||||
/// </value>
|
||||
public HttpListenerResponse Response {
|
||||
get {
|
||||
return _response;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpListenerRequest"/> that contains the HTTP request from a client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="HttpListenerRequest"/> that contains the HTTP request objects.
|
||||
/// </value>
|
||||
public HttpListenerRequest Request {
|
||||
get {
|
||||
return _request;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information, and
|
||||
/// security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> contains the client information.
|
||||
/// </value>
|
||||
public IPrincipal User {
|
||||
get {
|
||||
return _user;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpListenerResponse"/> that contains the HTTP response to send to
|
||||
/// the client in response to the client's request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="HttpListenerResponse"/> that contains the HTTP response objects.
|
||||
/// </value>
|
||||
public HttpListenerResponse Response {
|
||||
get {
|
||||
return _response;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information and security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> contains the client information.
|
||||
/// </value>
|
||||
public IPrincipal User {
|
||||
get {
|
||||
return _user;
|
||||
}
|
||||
}
|
||||
#region Internal Methods
|
||||
|
||||
#endregion
|
||||
internal void SetUser (
|
||||
AuthenticationSchemes expectedScheme,
|
||||
string realm,
|
||||
Func<IIdentity, NetworkCredential> credentialsFinder)
|
||||
{
|
||||
var authRes = AuthenticationResponse.Parse (_request.Headers ["Authorization"]);
|
||||
if (authRes == null)
|
||||
return;
|
||||
|
||||
#region Internal Methods
|
||||
var identity = authRes.ToIdentity ();
|
||||
if (identity == null)
|
||||
return;
|
||||
|
||||
internal void ParseAuthentication (AuthenticationSchemes expectedSchemes)
|
||||
{
|
||||
if (expectedSchemes == AuthenticationSchemes.Anonymous)
|
||||
return;
|
||||
NetworkCredential credentials = null;
|
||||
try {
|
||||
credentials = credentialsFinder (identity);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
// TODO: Handle NTLM/Digest modes.
|
||||
var header = _request.Headers ["Authorization"];
|
||||
if (header == null || header.Length < 2)
|
||||
return;
|
||||
if (credentials == null)
|
||||
return;
|
||||
|
||||
var authData = header.Split (new char [] {' '}, 2);
|
||||
if (authData [0].ToLower () == "basic")
|
||||
_user = ParseBasicAuthentication (authData [1]);
|
||||
var valid = expectedScheme == AuthenticationSchemes.Basic
|
||||
? ((HttpBasicIdentity) identity).Password == credentials.Password
|
||||
: expectedScheme == AuthenticationSchemes.Digest
|
||||
? ((HttpDigestIdentity) identity).IsValid (
|
||||
credentials.Password, realm, _request.HttpMethod, null)
|
||||
: false;
|
||||
|
||||
// TODO: Throw if malformed -> 400 bad request.
|
||||
}
|
||||
if (valid)
|
||||
_user = new GenericPrincipal (identity, credentials.Roles);
|
||||
}
|
||||
|
||||
internal IPrincipal ParseBasicAuthentication (string authData)
|
||||
{
|
||||
try {
|
||||
// HTTP Basic Authentication data is a formatted Base64 string.
|
||||
var authString = Encoding.Default.GetString (Convert.FromBase64String (authData));
|
||||
#endregion
|
||||
|
||||
// The format is domain\username:password.
|
||||
// Domain is optional.
|
||||
#region Public Method
|
||||
|
||||
var pos = authString.IndexOf (':');
|
||||
var user = authString.Substring (0, pos);
|
||||
var password = authString.Substring (pos + 1);
|
||||
/// <summary>
|
||||
/// Accepts a WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="HttpListenerWebSocketContext"/> that contains a WebSocket
|
||||
/// connection request information.
|
||||
/// </returns>
|
||||
/// <param name="logger">
|
||||
/// A <see cref="Logger"/> that provides the logging functions used in the
|
||||
/// WebSocket attempts.
|
||||
/// </param>
|
||||
public HttpListenerWebSocketContext AcceptWebSocket (Logger logger)
|
||||
{
|
||||
return new HttpListenerWebSocketContext (this, logger);
|
||||
}
|
||||
|
||||
// Check if there is a domain.
|
||||
pos = user.IndexOf ('\\');
|
||||
if (pos > 0)
|
||||
user = user.Substring (pos + 1);
|
||||
|
||||
var identity = new System.Net.HttpListenerBasicIdentity (user, password);
|
||||
// TODO: What are the roles MS sets?
|
||||
return new GenericPrincipal (identity, new string [0]);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Method
|
||||
|
||||
/// <summary>
|
||||
/// Accepts a WebSocket connection by the <see cref="HttpListener"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="HttpListenerWebSocketContext"/> that contains a WebSocket connection.
|
||||
/// </returns>
|
||||
public HttpListenerWebSocketContext AcceptWebSocket ()
|
||||
{
|
||||
return new HttpListenerWebSocketContext (this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,222 +1,200 @@
|
||||
//
|
||||
// ListenerAsyncResult.cs
|
||||
// Copied from System.Net.ListenerAsyncResult.cs
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
//
|
||||
// Copyright (c) 2005 Ximian, Inc (http://www.ximian.com)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* ListenerAsyncResult.cs
|
||||
*
|
||||
* This code is derived from System.Net.ListenerAsyncResult.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Ximian, Inc. (http://www.ximian.com)
|
||||
* Copyright (c) 2012-2013 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.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal class ListenerAsyncResult : IAsyncResult
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
class ListenerAsyncResult : IAsyncResult
|
||||
{
|
||||
#region Private Static Field
|
||||
private AsyncCallback _callback;
|
||||
private bool _completed;
|
||||
private HttpListenerContext _context;
|
||||
private Exception _exception;
|
||||
private ManualResetEvent _waitHandle;
|
||||
private object _state;
|
||||
private object _sync;
|
||||
private bool _syncCompleted;
|
||||
|
||||
static WaitCallback InvokeCB = new WaitCallback (InvokeCallback);
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Internal Fields
|
||||
|
||||
#region Private Fields
|
||||
internal bool EndCalled;
|
||||
internal bool InGet;
|
||||
|
||||
AsyncCallback cb;
|
||||
bool completed;
|
||||
HttpListenerContext context;
|
||||
Exception exception;
|
||||
ListenerAsyncResult forward;
|
||||
ManualResetEvent handle;
|
||||
object locker;
|
||||
object state;
|
||||
bool synch;
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Public Constructors
|
||||
|
||||
#region Internal Fields
|
||||
public ListenerAsyncResult (AsyncCallback callback, object state)
|
||||
{
|
||||
_callback = callback;
|
||||
_state = state;
|
||||
_sync = new object ();
|
||||
}
|
||||
|
||||
internal bool EndCalled;
|
||||
internal bool InGet;
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Public Properties
|
||||
|
||||
#region Constructor
|
||||
public object AsyncState {
|
||||
get {
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
public ListenerAsyncResult (AsyncCallback cb, object state)
|
||||
{
|
||||
this.cb = cb;
|
||||
this.state = state;
|
||||
this.locker = new object();
|
||||
}
|
||||
public WaitHandle AsyncWaitHandle {
|
||||
get {
|
||||
lock (_sync)
|
||||
return _waitHandle ?? (_waitHandle = new ManualResetEvent (_completed));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
public bool CompletedSynchronously {
|
||||
get {
|
||||
return _syncCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
public bool IsCompleted {
|
||||
get {
|
||||
lock (_sync)
|
||||
return _completed;
|
||||
}
|
||||
}
|
||||
|
||||
public object AsyncState {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.AsyncState;
|
||||
#endregion
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
#region Private Methods
|
||||
|
||||
public WaitHandle AsyncWaitHandle {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.AsyncWaitHandle;
|
||||
private static void invokeCallback (object state)
|
||||
{
|
||||
try {
|
||||
var ares = (ListenerAsyncResult) state;
|
||||
ares._callback (ares);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
|
||||
lock (locker) {
|
||||
if (handle == null)
|
||||
handle = new ManualResetEvent (completed);
|
||||
}
|
||||
#endregion
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
#region Internal Methods
|
||||
|
||||
public bool CompletedSynchronously {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.CompletedSynchronously;
|
||||
internal void Complete (Exception exception)
|
||||
{
|
||||
_exception = InGet && (exception is ObjectDisposedException)
|
||||
? new HttpListenerException (500, "Listener closed")
|
||||
: exception;
|
||||
|
||||
return synch;
|
||||
}
|
||||
}
|
||||
lock (_sync) {
|
||||
_completed = true;
|
||||
if (_waitHandle != null)
|
||||
_waitHandle.Set ();
|
||||
|
||||
public bool IsCompleted {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.IsCompleted;
|
||||
if (_callback != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (invokeCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
lock (locker) {
|
||||
return completed;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal void Complete (HttpListenerContext context)
|
||||
{
|
||||
Complete (context, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
internal void Complete (HttpListenerContext context, bool syncCompleted)
|
||||
{
|
||||
var listener = context.Listener;
|
||||
var scheme = listener.SelectAuthenticationScheme (context);
|
||||
if (scheme == AuthenticationSchemes.None) {
|
||||
context.Response.Close (HttpStatusCode.Forbidden);
|
||||
listener.BeginGetContext (this);
|
||||
|
||||
#region Private Method
|
||||
return;
|
||||
}
|
||||
|
||||
static void InvokeCallback (object o)
|
||||
{
|
||||
ListenerAsyncResult ares = (ListenerAsyncResult) o;
|
||||
if (ares.forward != null) {
|
||||
InvokeCallback (ares.forward);
|
||||
return;
|
||||
}
|
||||
var header = context.Request.Headers ["Authorization"];
|
||||
if (scheme == AuthenticationSchemes.Basic &&
|
||||
(header == null ||
|
||||
!header.StartsWith ("basic", StringComparison.OrdinalIgnoreCase))) {
|
||||
context.Response.CloseWithAuthChallenge (
|
||||
HttpUtility.CreateBasicAuthChallenge (listener.Realm));
|
||||
listener.BeginGetContext (this);
|
||||
|
||||
try {
|
||||
ares.cb (ares);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
if (scheme == AuthenticationSchemes.Digest &&
|
||||
(header == null ||
|
||||
!header.StartsWith ("digest", StringComparison.OrdinalIgnoreCase))) {
|
||||
context.Response.CloseWithAuthChallenge (
|
||||
HttpUtility.CreateDigestAuthChallenge (listener.Realm));
|
||||
listener.BeginGetContext (this);
|
||||
|
||||
#region Internal Methods
|
||||
return;
|
||||
}
|
||||
|
||||
internal void Complete (Exception exc)
|
||||
{
|
||||
if (forward != null) {
|
||||
forward.Complete (exc);
|
||||
return;
|
||||
}
|
||||
_context = context;
|
||||
_syncCompleted = syncCompleted;
|
||||
|
||||
exception = exc;
|
||||
if (InGet && (exc is ObjectDisposedException))
|
||||
exception = new HttpListenerException (500, "Listener closed");
|
||||
lock (_sync) {
|
||||
_completed = true;
|
||||
if (_waitHandle != null)
|
||||
_waitHandle.Set ();
|
||||
|
||||
lock (locker) {
|
||||
completed = true;
|
||||
if (handle != null)
|
||||
handle.Set ();
|
||||
if (_callback != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (invokeCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
if (cb != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (InvokeCB, this);
|
||||
}
|
||||
}
|
||||
internal HttpListenerContext GetContext ()
|
||||
{
|
||||
if (_exception != null)
|
||||
throw _exception;
|
||||
|
||||
internal void Complete (HttpListenerContext context)
|
||||
{
|
||||
Complete (context, false);
|
||||
}
|
||||
return _context;
|
||||
}
|
||||
|
||||
internal void Complete (HttpListenerContext context, bool synch)
|
||||
{
|
||||
if (forward != null) {
|
||||
forward.Complete (context, synch);
|
||||
return;
|
||||
}
|
||||
|
||||
this.synch = synch;
|
||||
this.context = context;
|
||||
lock (locker) {
|
||||
AuthenticationSchemes schemes = context.Listener.SelectAuthenticationScheme (context);
|
||||
if ((schemes == AuthenticationSchemes.Basic || context.Listener.AuthenticationSchemes == AuthenticationSchemes.Negotiate) && context.Request.Headers ["Authorization"] == null) {
|
||||
context.Response.StatusCode = 401;
|
||||
context.Response.Headers ["WWW-Authenticate"] = schemes + " realm=\"" + context.Listener.Realm + "\"";
|
||||
context.Response.OutputStream.Close ();
|
||||
IAsyncResult ares = context.Listener.BeginGetContext (cb, state);
|
||||
this.forward = (ListenerAsyncResult) ares;
|
||||
lock (forward.locker) {
|
||||
if (handle != null)
|
||||
forward.handle = handle;
|
||||
}
|
||||
|
||||
ListenerAsyncResult next = forward;
|
||||
for (int i = 0; next.forward != null; i++) {
|
||||
if (i > 20)
|
||||
Complete (new HttpListenerException (400, "Too many authentication errors"));
|
||||
|
||||
next = next.forward;
|
||||
}
|
||||
} else {
|
||||
completed = true;
|
||||
if (handle != null)
|
||||
handle.Set ();
|
||||
|
||||
if (cb != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (InvokeCB, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal HttpListenerContext GetContext ()
|
||||
{
|
||||
if (forward != null)
|
||||
return forward.GetContext ();
|
||||
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
179
websocket-sharp/Net/NetworkCredential.cs
Normal file
179
websocket-sharp/Net/NetworkCredential.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
#region License
|
||||
/*
|
||||
* NetworkCredential.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;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the credentials for HTTP authentication (Basic/Digest).
|
||||
/// </summary>
|
||||
public class NetworkCredential
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private string _domain;
|
||||
private string _password;
|
||||
private string [] _roles;
|
||||
private string _username;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NetworkCredential"/> class
|
||||
/// with the specified user name and password.
|
||||
/// </summary>
|
||||
/// <param name="username">
|
||||
/// A <see cref="string"/> that represents the user name associated with the
|
||||
/// credentials.
|
||||
/// </param>
|
||||
/// <param name="password">
|
||||
/// A <see cref="string"/> that represents the password for the user name
|
||||
/// associated with the credentials.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="username"/> is <see langword="null"/> or empty.
|
||||
/// </exception>
|
||||
public NetworkCredential (string username, string password)
|
||||
: this (username, password, null, new string [0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NetworkCredential"/> class
|
||||
/// with the specified user name, password, domain, and roles.
|
||||
/// </summary>
|
||||
/// <param name="username">
|
||||
/// A <see cref="string"/> that represents the user name associated with the
|
||||
/// credentials.
|
||||
/// </param>
|
||||
/// <param name="password">
|
||||
/// A <see cref="string"/> that represents the password for the user name
|
||||
/// associated with the credentials.
|
||||
/// </param>
|
||||
/// <param name="domain">
|
||||
/// A <see cref="string"/> that represents the name of the user domain
|
||||
/// associated with the credentials.
|
||||
/// </param>
|
||||
/// <param name="roles">
|
||||
/// An array of <see cref="string"/> that contains the role names to which
|
||||
/// the user associated with the credentials belongs if any.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="username"/> is <see langword="null"/> or empty.
|
||||
/// </exception>
|
||||
public NetworkCredential (
|
||||
string username, string password, string domain, params string [] roles)
|
||||
{
|
||||
if (username == null || username.Length == 0)
|
||||
throw new ArgumentException ("Must not be null or empty.", "username");
|
||||
|
||||
_username = username;
|
||||
_password = password;
|
||||
_domain = domain;
|
||||
_roles = roles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the user domain associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the name of the user domain
|
||||
/// associated with the credentials.
|
||||
/// </value>
|
||||
public string Domain {
|
||||
get {
|
||||
return _domain ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_domain = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the password for the user name associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the password for the user name
|
||||
/// associated with the credentials.
|
||||
/// </value>
|
||||
public string Password {
|
||||
get {
|
||||
return _password ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_password = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role names to which the user associated with the credentials
|
||||
/// belongs.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An array of <see cref="string"/> that contains the role names to which
|
||||
/// the user associated with the credentials belongs.
|
||||
/// </value>
|
||||
public string [] Roles {
|
||||
get {
|
||||
return _roles;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_roles = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user name associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the user name associated with the
|
||||
/// credentials.
|
||||
/// </value>
|
||||
public string UserName {
|
||||
get {
|
||||
return _username;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_username = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -1,253 +1,281 @@
|
||||
//
|
||||
// RequestStream.cs
|
||||
// Copied from System.Net.RequestStream.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* RequestStream.cs
|
||||
*
|
||||
* This code is derived from System.Net.RequestStream.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2013 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.Runtime.InteropServices;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal class RequestStream : Stream
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
class RequestStream : Stream {
|
||||
private byte [] _buffer;
|
||||
private bool _disposed;
|
||||
private int _length;
|
||||
private int _offset;
|
||||
private long _remainingBody;
|
||||
private Stream _stream;
|
||||
|
||||
#region Fields
|
||||
#endregion
|
||||
|
||||
byte [] buffer;
|
||||
bool disposed;
|
||||
int length;
|
||||
int offset;
|
||||
long remaining_body;
|
||||
Stream stream;
|
||||
#region Internal Constructors
|
||||
|
||||
#endregion
|
||||
internal RequestStream (
|
||||
Stream stream, byte [] buffer, int offset, int length)
|
||||
: this (stream, buffer, offset, length, -1)
|
||||
{
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
internal RequestStream (
|
||||
Stream stream, byte [] buffer, int offset, int length, long contentlength)
|
||||
{
|
||||
_stream = stream;
|
||||
_buffer = buffer;
|
||||
_offset = offset;
|
||||
_length = length;
|
||||
_remainingBody = contentlength;
|
||||
}
|
||||
|
||||
internal RequestStream (Stream stream, byte [] buffer, int offset, int length)
|
||||
: this (stream, buffer, offset, length, -1)
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
internal RequestStream (Stream stream, byte [] buffer, int offset, int length, long contentlength)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.buffer = buffer;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
this.remaining_body = contentlength;
|
||||
}
|
||||
#region Public Properties
|
||||
|
||||
#endregion
|
||||
public override bool CanRead {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
public override bool CanSeek {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead {
|
||||
get { return true; }
|
||||
}
|
||||
public override bool CanWrite {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get { return false; }
|
||||
}
|
||||
public override long Length {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite {
|
||||
get { return false; }
|
||||
}
|
||||
public override long Position {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override long Length {
|
||||
get { throw new NotSupportedException (); }
|
||||
}
|
||||
set {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position {
|
||||
get { throw new NotSupportedException (); }
|
||||
set { throw new NotSupportedException (); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Private Methods
|
||||
|
||||
#region Private Method
|
||||
// Returns 0 if we can keep reading from the base stream,
|
||||
// > 0 if we read something from the buffer.
|
||||
// -1 if we had a content length set and we finished reading that many bytes.
|
||||
private int fillFromBuffer (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException ("buffer");
|
||||
|
||||
// Returns 0 if we can keep reading from the base stream,
|
||||
// > 0 if we read something from the buffer.
|
||||
// -1 if we had a content length set and we finished reading that many bytes.
|
||||
int FillFromBuffer (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException ("buffer");
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException ("offset", "Less than zero.");
|
||||
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException ("offset", "< 0");
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException ("count", "Less than zero.");
|
||||
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException ("count", "< 0");
|
||||
var len = buffer.Length;
|
||||
if (offset > len)
|
||||
throw new ArgumentException ("'offset' is greater than 'buffer' size.");
|
||||
|
||||
int len = buffer.Length;
|
||||
if (offset > len)
|
||||
throw new ArgumentException ("Destination offset is beyond array size.");
|
||||
if (offset > len - count)
|
||||
throw new ArgumentException ("Reading would overrun 'buffer'.");
|
||||
|
||||
if (offset > len - count)
|
||||
throw new ArgumentException ("Reading would overrun buffer.");
|
||||
if (_remainingBody == 0)
|
||||
return -1;
|
||||
|
||||
if (this.remaining_body == 0)
|
||||
return -1;
|
||||
if (_length == 0)
|
||||
return 0;
|
||||
|
||||
if (this.length == 0)
|
||||
return 0;
|
||||
var size = _length < count ? _length : count;
|
||||
if (_remainingBody > 0 && _remainingBody < size)
|
||||
size = (int) _remainingBody;
|
||||
|
||||
int size = Math.Min (this.length, count);
|
||||
if (this.remaining_body > 0)
|
||||
size = (int) Math.Min (size, this.remaining_body);
|
||||
var remainingBuffer = _buffer.Length - _offset;
|
||||
if (remainingBuffer < size)
|
||||
size = remainingBuffer;
|
||||
|
||||
if (this.offset > this.buffer.Length - size) {
|
||||
size = Math.Min (size, this.buffer.Length - this.offset);
|
||||
}
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
Buffer.BlockCopy (_buffer, _offset, buffer, offset, size);
|
||||
_offset += size;
|
||||
_length -= size;
|
||||
if (_remainingBody > 0)
|
||||
_remainingBody -= size;
|
||||
|
||||
Buffer.BlockCopy (this.buffer, this.offset, buffer, offset, size);
|
||||
this.offset += size;
|
||||
this.length -= size;
|
||||
if (this.remaining_body > 0)
|
||||
remaining_body -= size;
|
||||
return size;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Public Methods
|
||||
|
||||
#region Public Methods
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer, int offset, int count, AsyncCallback cback, object state)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
var read = fillFromBuffer (buffer, offset, count);
|
||||
if (read > 0 || read == -1) {
|
||||
var ares = new HttpStreamAsyncResult ();
|
||||
ares.Buffer = buffer;
|
||||
ares.Offset = offset;
|
||||
ares.Count = count;
|
||||
ares.Callback = callback;
|
||||
ares.State = state;
|
||||
ares.SyncRead = read;
|
||||
ares.Complete ();
|
||||
|
||||
int nread = FillFromBuffer (buffer, offset, count);
|
||||
if (nread > 0 || nread == -1) {
|
||||
var ares = new HttpStreamAsyncResult ();
|
||||
ares.Buffer = buffer;
|
||||
ares.Offset = offset;
|
||||
ares.Count = count;
|
||||
ares.Callback = cback;
|
||||
ares.State = state;
|
||||
ares.SyncRead = nread;
|
||||
ares.Complete ();
|
||||
return ares;
|
||||
}
|
||||
return ares;
|
||||
}
|
||||
|
||||
// Avoid reading past the end of the request to allow
|
||||
// for HTTP pipelining
|
||||
if (remaining_body >= 0 && count > remaining_body)
|
||||
count = (int) Math.Min (Int32.MaxValue, remaining_body);
|
||||
// Avoid reading past the end of the request to allow for HTTP pipelining.
|
||||
if (_remainingBody >= 0 && _remainingBody < count)
|
||||
count = (int) _remainingBody;
|
||||
|
||||
return stream.BeginRead (buffer, offset, count, cback, state);
|
||||
}
|
||||
return _stream.BeginRead (buffer, offset, count, callback, state);
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer, int offset, int count, AsyncCallback cback, object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
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 void Close ()
|
||||
{
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public override int EndRead (IAsyncResult ares)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
public override int EndRead (IAsyncResult asyncResult)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
if (ares == null)
|
||||
throw new ArgumentNullException ("ares");
|
||||
if (asyncResult == null)
|
||||
throw new ArgumentNullException ("asyncResult");
|
||||
|
||||
if (ares is HttpStreamAsyncResult) {
|
||||
var ares_ = (HttpStreamAsyncResult) ares;
|
||||
if (!ares.IsCompleted)
|
||||
ares.AsyncWaitHandle.WaitOne ();
|
||||
if (asyncResult is HttpStreamAsyncResult) {
|
||||
var ares = (HttpStreamAsyncResult) asyncResult;
|
||||
if (!ares.IsCompleted)
|
||||
ares.AsyncWaitHandle.WaitOne ();
|
||||
|
||||
return ares_.SyncRead;
|
||||
}
|
||||
return ares.SyncRead;
|
||||
}
|
||||
|
||||
// Close on exception?
|
||||
int nread = stream.EndRead (ares);
|
||||
if (remaining_body > 0 && nread > 0)
|
||||
remaining_body -= nread;
|
||||
// Close on exception?
|
||||
var read = _stream.EndRead (asyncResult);
|
||||
if (read > 0 && _remainingBody > 0)
|
||||
_remainingBody -= read;
|
||||
|
||||
return nread;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override void EndWrite (IAsyncResult async_result)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override void EndWrite (IAsyncResult asyncResult)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read ([In,Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType () .ToString ());
|
||||
public override int Read (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
// Call FillFromBuffer to check for buffer boundaries even when remaining_body is 0
|
||||
int nread = FillFromBuffer (buffer, offset, count);
|
||||
if (nread == -1) { // No more bytes available (Content-Length)
|
||||
return 0;
|
||||
} else if (nread > 0) {
|
||||
return nread;
|
||||
}
|
||||
// Call fillFromBuffer to check for buffer boundaries even when
|
||||
// _remainingBody is 0.
|
||||
var read = fillFromBuffer (buffer, offset, count);
|
||||
if (read == -1) // No more bytes available (Content-Length).
|
||||
return 0;
|
||||
else if (read > 0)
|
||||
return read;
|
||||
|
||||
nread = stream.Read (buffer, offset, count);
|
||||
if (nread > 0 && remaining_body > 0)
|
||||
remaining_body -= nread;
|
||||
read = _stream.Read (buffer, offset, count);
|
||||
if (read > 0 && _remainingBody > 0)
|
||||
_remainingBody -= read;
|
||||
|
||||
return nread;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
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 SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Write (byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@@ -1,284 +1,322 @@
|
||||
//
|
||||
// ResponseStream.cs
|
||||
// Copied from System.Net.ResponseStream.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* ResponseStream.cs
|
||||
*
|
||||
* This code is derived from System.Net.ResponseStream.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* 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:
|
||||
* Gonzalo Paniagua Javier <gonzalo@novell.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
// FIXME: Does this buffer the response until Close?
|
||||
// Update: we send a single packet for the first non-chunked Write
|
||||
// What happens when we set content-length to X and write X-1 bytes then close?
|
||||
// what if we don't set content-length at all?
|
||||
internal class ResponseStream : Stream
|
||||
{
|
||||
#region Private Static Fields
|
||||
|
||||
// FIXME: Does this buffer the response until Close?
|
||||
// Update: we send a single packet for the first non-chunked Write
|
||||
// What happens when we set content-length to X and write X-1 bytes then close?
|
||||
// what if we don't set content-length at all?
|
||||
class ResponseStream : Stream {
|
||||
private static byte [] _crlf = new byte [] { 13, 10 };
|
||||
|
||||
#region Private Static Field
|
||||
#endregion
|
||||
|
||||
static byte [] crlf = new byte [] { 13, 10 };
|
||||
#region Private Fields
|
||||
|
||||
#endregion
|
||||
private bool _disposed;
|
||||
private bool _ignoreErrors;
|
||||
private HttpListenerResponse _response;
|
||||
private Stream _stream;
|
||||
private bool _trailerSent;
|
||||
|
||||
#region Private Fields
|
||||
#endregion
|
||||
|
||||
bool disposed;
|
||||
bool ignore_errors;
|
||||
HttpListenerResponse response;
|
||||
Stream stream;
|
||||
bool trailer_sent;
|
||||
#region Internal Constructors
|
||||
|
||||
#endregion
|
||||
internal ResponseStream (
|
||||
Stream stream, HttpListenerResponse response, bool ignoreErrors)
|
||||
{
|
||||
_stream = stream;
|
||||
_response = response;
|
||||
_ignoreErrors = ignoreErrors;
|
||||
}
|
||||
|
||||
#region Constructor
|
||||
#endregion
|
||||
|
||||
internal ResponseStream (System.IO.Stream stream, HttpListenerResponse response, bool ignore_errors)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.response = response;
|
||||
this.ignore_errors = ignore_errors;
|
||||
}
|
||||
#region Public Properties
|
||||
|
||||
#endregion
|
||||
public override bool CanRead {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
public override bool CanSeek {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead {
|
||||
get { return false; }
|
||||
}
|
||||
public override bool CanWrite {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get { return false; }
|
||||
}
|
||||
public override long Length {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite {
|
||||
get { return true; }
|
||||
}
|
||||
public override long Position {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override long Length {
|
||||
get { throw new NotSupportedException (); }
|
||||
}
|
||||
set {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position {
|
||||
get { throw new NotSupportedException (); }
|
||||
set { throw new NotSupportedException (); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Private Methods
|
||||
|
||||
#region Private Methods
|
||||
private static byte [] getChunkSizeBytes (int size, bool final)
|
||||
{
|
||||
return Encoding.ASCII.GetBytes (
|
||||
String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : ""));
|
||||
}
|
||||
|
||||
static byte [] GetChunkSizeBytes (int size, bool final)
|
||||
{
|
||||
string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "");
|
||||
return Encoding.ASCII.GetBytes (str);
|
||||
}
|
||||
private MemoryStream getHeaders (bool closing)
|
||||
{
|
||||
if (_response.HeadersSent)
|
||||
return null;
|
||||
|
||||
MemoryStream GetHeaders (bool closing)
|
||||
{
|
||||
if (response.HeadersSent)
|
||||
return null;
|
||||
var stream = new MemoryStream ();
|
||||
_response.SendHeaders (closing, stream);
|
||||
|
||||
MemoryStream ms = new MemoryStream ();
|
||||
response.SendHeaders (closing, ms);
|
||||
return stream;
|
||||
}
|
||||
|
||||
return ms;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Internal Methods
|
||||
|
||||
#region Internal Method
|
||||
internal void InternalWrite (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (_ignoreErrors) {
|
||||
try {
|
||||
_stream.Write (buffer, offset, count);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
else {
|
||||
_stream.Write (buffer, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
internal void InternalWrite (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (ignore_errors) {
|
||||
try {
|
||||
stream.Write (buffer, offset, count);
|
||||
} catch {
|
||||
}
|
||||
} else {
|
||||
stream.Write (buffer, offset, count);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Public Methods
|
||||
|
||||
#region Public Methods
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback cback,
|
||||
object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback cback,
|
||||
object state)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
var stream = getHeaders (false);
|
||||
var chunked = _response.SendChunked;
|
||||
byte [] bytes = null;
|
||||
if (stream != null) {
|
||||
var start = stream.Position;
|
||||
stream.Position = stream.Length;
|
||||
if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
byte [] bytes = null;
|
||||
MemoryStream ms = GetHeaders (false);
|
||||
bool chunked = response.SendChunked;
|
||||
if (ms != null) {
|
||||
long start = ms.Position;
|
||||
ms.Position = ms.Length;
|
||||
if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
ms.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
ms.Write (buffer, offset, count);
|
||||
buffer = ms.GetBuffer ();
|
||||
offset = (int) start;
|
||||
count = (int) (ms.Position - start);
|
||||
} else if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
stream.Write (buffer, offset, count);
|
||||
buffer = stream.GetBuffer ();
|
||||
offset = (int) start;
|
||||
count = (int) (stream.Position - start);
|
||||
}
|
||||
else if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
return stream.BeginWrite (buffer, offset, count, cback, state);
|
||||
}
|
||||
return _stream.BeginWrite (buffer, offset, count, callback, state);
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
if (disposed == false) {
|
||||
disposed = true;
|
||||
byte [] bytes = null;
|
||||
MemoryStream ms = GetHeaders (true);
|
||||
bool chunked = response.SendChunked;
|
||||
if (ms != null) {
|
||||
long start = ms.Position;
|
||||
if (chunked && !trailer_sent) {
|
||||
bytes = GetChunkSizeBytes (0, true);
|
||||
ms.Position = ms.Length;
|
||||
ms.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
|
||||
trailer_sent = true;
|
||||
} else if (chunked && !trailer_sent) {
|
||||
bytes = GetChunkSizeBytes (0, true);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
trailer_sent = true;
|
||||
}
|
||||
public override void Close ()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
response.Close ();
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
|
||||
public override int EndRead (IAsyncResult ares)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
var stream = getHeaders (true);
|
||||
var chunked = _response.SendChunked;
|
||||
byte [] bytes = null;
|
||||
if (stream != null) {
|
||||
var start = stream.Position;
|
||||
if (chunked && !_trailerSent) {
|
||||
bytes = getChunkSizeBytes (0, true);
|
||||
stream.Position = stream.Length;
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public override void EndWrite (IAsyncResult ares)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
InternalWrite (
|
||||
stream.GetBuffer (), (int) start, (int) (stream.Length - start));
|
||||
_trailerSent = true;
|
||||
}
|
||||
else if (chunked && !_trailerSent) {
|
||||
bytes = getChunkSizeBytes (0, true);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
_trailerSent = true;
|
||||
}
|
||||
|
||||
if (ignore_errors) {
|
||||
try {
|
||||
stream.EndWrite (ares);
|
||||
if (response.SendChunked)
|
||||
stream.Write (crlf, 0, 2);
|
||||
} catch {
|
||||
}
|
||||
} else {
|
||||
stream.EndWrite (ares);
|
||||
if (response.SendChunked)
|
||||
stream.Write (crlf, 0, 2);
|
||||
}
|
||||
}
|
||||
_response.Close ();
|
||||
}
|
||||
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
public override int EndRead (IAsyncResult asyncResult)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override int Read ([In,Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override void EndWrite (IAsyncResult asyncResult)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
Action<IAsyncResult> endWrite = ares => {
|
||||
_stream.EndWrite (ares);
|
||||
if (_response.SendChunked)
|
||||
_stream.Write (_crlf, 0, 2);
|
||||
};
|
||||
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
if (_ignoreErrors) {
|
||||
try {
|
||||
endWrite (asyncResult);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
else {
|
||||
endWrite (asyncResult);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
|
||||
byte [] bytes = null;
|
||||
MemoryStream ms = GetHeaders (false);
|
||||
bool chunked = response.SendChunked;
|
||||
if (ms != null) {
|
||||
long start = ms.Position; // After the possible preamble for the encoding
|
||||
ms.Position = ms.Length;
|
||||
if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
ms.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
public override int Read (byte [] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
int new_count = Math.Min (count, 16384 - (int) ms.Position + (int) start);
|
||||
ms.Write (buffer, offset, new_count);
|
||||
count -= new_count;
|
||||
offset += new_count;
|
||||
InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
|
||||
ms.SetLength (0);
|
||||
ms.Capacity = 0; // 'dispose' the buffer in ms.
|
||||
} else if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
InternalWrite (buffer, offset, count);
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
if (chunked)
|
||||
InternalWrite (crlf, 0, 2);
|
||||
}
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
#endregion
|
||||
}
|
||||
var stream = getHeaders (false);
|
||||
var chunked = _response.SendChunked;
|
||||
byte [] bytes = null;
|
||||
if (stream != null) {
|
||||
// After the possible preamble for the encoding.
|
||||
var start = stream.Position;
|
||||
stream.Position = stream.Length;
|
||||
if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
var newCount = Math.Min (
|
||||
count, 16384 - (int) stream.Position + (int) start);
|
||||
stream.Write (buffer, offset, newCount);
|
||||
count -= newCount;
|
||||
offset += newCount;
|
||||
InternalWrite (
|
||||
stream.GetBuffer (), (int) start, (int) (stream.Length - start));
|
||||
stream.SetLength (0);
|
||||
stream.Capacity = 0; // 'dispose' the buffer in stream.
|
||||
}
|
||||
else if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
InternalWrite (buffer, offset, count);
|
||||
|
||||
if (chunked)
|
||||
InternalWrite (_crlf, 0, 2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2013 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
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* 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
|
||||
@@ -34,7 +34,8 @@ using System.Security.Principal;
|
||||
namespace WebSocketSharp.Net.WebSockets
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the WebSocket connection request objects received by the <see cref="HttpListener"/>.
|
||||
/// Provides access to the WebSocket connection request information received by
|
||||
/// the <see cref="HttpListener"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
@@ -50,11 +51,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal HttpListenerWebSocketContext (HttpListenerContext context)
|
||||
internal HttpListenerWebSocketContext (
|
||||
HttpListenerContext context, Logger logger)
|
||||
{
|
||||
_context = context;
|
||||
_stream = WsStream.CreateServerStream (context);
|
||||
_websocket = new WebSocket (this);
|
||||
_websocket = new WebSocket (this, logger ?? new Logger ());
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -72,10 +74,11 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies used in the WebSocket opening handshake.
|
||||
/// Gets the cookies used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the cookies.
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the
|
||||
/// cookies.
|
||||
/// </value>
|
||||
public override CookieCollection CookieCollection {
|
||||
get {
|
||||
@@ -84,10 +87,10 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP headers used in the WebSocket opening handshake.
|
||||
/// Gets the HTTP headers used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Collections.Specialized.NameValueCollection"/> that contains the HTTP headers.
|
||||
/// A <see cref="NameValueCollection"/> that contains the HTTP headers.
|
||||
/// </value>
|
||||
public override NameValueCollection Headers {
|
||||
get {
|
||||
@@ -96,10 +99,11 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Host header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Host header field used in the WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Host header field.
|
||||
/// A <see cref="string"/> that represents the value of the Host header field.
|
||||
/// </value>
|
||||
public override string Host {
|
||||
get {
|
||||
@@ -120,10 +124,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the client connected from the local computer.
|
||||
/// Gets a value indicating whether the client connected from the local
|
||||
/// computer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsLocal {
|
||||
get {
|
||||
@@ -135,7 +141,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets a value indicating whether the WebSocket connection is secured.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsSecureConnection {
|
||||
get {
|
||||
@@ -144,10 +151,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the request is a WebSocket connection request.
|
||||
/// Gets a value indicating whether the request is a WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsWebSocketRequest {
|
||||
get {
|
||||
@@ -156,10 +165,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Origin header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Origin header field used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Origin header field.
|
||||
/// A <see cref="string"/> that represents the value of the Origin header
|
||||
/// field.
|
||||
/// </value>
|
||||
public override string Origin {
|
||||
get {
|
||||
@@ -171,19 +182,22 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the absolute path of the requested WebSocket URI.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the absolute path of the requested WebSocket URI.
|
||||
/// A <see cref="string"/> that represents the absolute path of the requested
|
||||
/// WebSocket URI.
|
||||
/// </value>
|
||||
public override string Path {
|
||||
get {
|
||||
return RequestUri.GetAbsolutePath ();
|
||||
return _context.Request.Url.GetAbsolutePath ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of query string variables used in the WebSocket opening handshake.
|
||||
/// Gets the collection of query string variables used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query string variables.
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query
|
||||
/// string variables.
|
||||
/// </value>
|
||||
public override NameValueCollection QueryString {
|
||||
get {
|
||||
@@ -195,22 +209,26 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the WebSocket URI requested by the client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="Uri"/> that contains the WebSocket URI.
|
||||
/// A <see cref="Uri"/> that represents the WebSocket URI requested by the
|
||||
/// client.
|
||||
/// </value>
|
||||
public override Uri RequestUri {
|
||||
get {
|
||||
return _context.Request.RawUrl.ToUri ();
|
||||
return _context.Request.Url;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketKey property provides a part of the information used by the server to prove that it received a valid WebSocket opening handshake.
|
||||
/// This property provides a part of the information used by the server to
|
||||
/// prove that it received a valid WebSocket connection request.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Key header field.
|
||||
/// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key
|
||||
/// header field.
|
||||
/// </value>
|
||||
public override string SecWebSocketKey {
|
||||
get {
|
||||
@@ -219,13 +237,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the WebSocket opening handshake.
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketProtocols property indicates the subprotocols of the WebSocket connection.
|
||||
/// This property represents the subprotocols of the WebSocket connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// An IEnumerable<string> that contains the values of the Sec-WebSocket-Protocol header field.
|
||||
/// An IEnumerable<string> that contains the values of the
|
||||
/// Sec-WebSocket-Protocol header field.
|
||||
/// </value>
|
||||
public override IEnumerable<string> SecWebSocketProtocols {
|
||||
get {
|
||||
@@ -234,13 +254,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketVersion property indicates the WebSocket protocol version of the connection.
|
||||
/// This property represents the WebSocket protocol version of the connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Version header field.
|
||||
/// A <see cref="string"/> that represents the value of the
|
||||
/// Sec-WebSocket-Version header field.
|
||||
/// </value>
|
||||
public override string SecWebSocketVersion {
|
||||
get {
|
||||
@@ -252,7 +274,7 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the server endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the server endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint.
|
||||
/// </value>
|
||||
public override System.Net.IPEndPoint ServerEndPoint {
|
||||
get {
|
||||
@@ -261,10 +283,11 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information and security roles).
|
||||
/// Gets the client information (identity, authentication information and
|
||||
/// security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> that contains the client information.
|
||||
/// A <see cref="IPrincipal"/> that represents the client information.
|
||||
/// </value>
|
||||
public override IPrincipal User {
|
||||
get {
|
||||
@@ -276,7 +299,7 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the client endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the client endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint.
|
||||
/// </value>
|
||||
public override System.Net.IPEndPoint UserEndPoint {
|
||||
get {
|
||||
@@ -285,7 +308,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WebSocket instance used for two-way communication between client and server.
|
||||
/// Gets the WebSocket instance used for two-way communication between client
|
||||
/// and server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.WebSocket"/>.
|
||||
@@ -305,15 +329,22 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
_context.Connection.Close (true);
|
||||
}
|
||||
|
||||
internal void Close (HttpStatusCode code)
|
||||
{
|
||||
_context.Response.Close (code);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="string"/> that represents the current <see cref="HttpListenerWebSocketContext"/>.
|
||||
/// Returns a <see cref="string"/> that represents the current
|
||||
/// <see cref="HttpListenerWebSocketContext"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="string"/> that represents the current <see cref="HttpListenerWebSocketContext"/>.
|
||||
/// A <see cref="string"/> that represents the current
|
||||
/// <see cref="HttpListenerWebSocketContext"/>.
|
||||
/// </returns>
|
||||
public override string ToString ()
|
||||
{
|
||||
|
@@ -5,7 +5,7 @@
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2013 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
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* 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
|
||||
@@ -36,7 +36,8 @@ using System.Security.Principal;
|
||||
namespace WebSocketSharp.Net.WebSockets
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the WebSocket connection request objects received by the <see cref="TcpListener"/>.
|
||||
/// Provides access to the WebSocket connection request information received by
|
||||
/// the <see cref="TcpListener"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
@@ -49,19 +50,22 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
private HandshakeRequest _request;
|
||||
private bool _secure;
|
||||
private WsStream _stream;
|
||||
private Uri _uri;
|
||||
private IPrincipal _user;
|
||||
private WebSocket _websocket;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal TcpListenerWebSocketContext (TcpClient client, bool secure, X509Certificate cert)
|
||||
internal TcpListenerWebSocketContext (
|
||||
TcpClient client, X509Certificate cert, bool secure, Logger logger)
|
||||
{
|
||||
_client = client;
|
||||
_secure = secure;
|
||||
_stream = WsStream.CreateServerStream (client, secure, cert);
|
||||
_stream = WsStream.CreateServerStream (client, cert, secure);
|
||||
_request = HandshakeRequest.Parse (_stream.ReadHandshake ());
|
||||
_websocket = new WebSocket (this);
|
||||
_websocket = new WebSocket (this, logger);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -79,25 +83,23 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies used in the WebSocket opening handshake.
|
||||
/// Gets the cookies used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="CookieCollection"/> that contains the cookies.
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the
|
||||
/// cookies.
|
||||
/// </value>
|
||||
public override CookieCollection CookieCollection {
|
||||
get {
|
||||
if (_cookies == null)
|
||||
_cookies = _request.Cookies;
|
||||
|
||||
return _cookies;
|
||||
return _cookies ?? (_cookies = _request.Cookies);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP headers used in the WebSocket opening handshake.
|
||||
/// Gets the HTTP headers used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Collections.Specialized.NameValueCollection"/> that contains the HTTP headers.
|
||||
/// A <see cref="NameValueCollection"/> that contains the HTTP headers.
|
||||
/// </value>
|
||||
public override NameValueCollection Headers {
|
||||
get {
|
||||
@@ -106,10 +108,11 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Host header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Host header field used in the WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Host header field.
|
||||
/// A <see cref="string"/> that represents the value of the Host header field.
|
||||
/// </value>
|
||||
public override string Host {
|
||||
get {
|
||||
@@ -123,20 +126,19 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// <value>
|
||||
/// <c>true</c> if the client is authenticated; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
/// <exception cref="NotImplementedException">
|
||||
/// This property is not implemented.
|
||||
/// </exception>
|
||||
public override bool IsAuthenticated {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
return _user != null && _user.Identity.IsAuthenticated;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the client connected from the local computer.
|
||||
/// Gets a value indicating whether the client connected from the local
|
||||
/// computer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsLocal {
|
||||
get {
|
||||
@@ -148,7 +150,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets a value indicating whether the WebSocket connection is secured.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsSecureConnection {
|
||||
get {
|
||||
@@ -157,10 +160,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the request is a WebSocket connection request.
|
||||
/// Gets a value indicating whether the request is a WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsWebSocketRequest {
|
||||
get {
|
||||
@@ -169,10 +174,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Origin header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Origin header field used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Origin header field.
|
||||
/// A <see cref="string"/> that represents the value of the Origin header
|
||||
/// field.
|
||||
/// </value>
|
||||
public override string Origin {
|
||||
get {
|
||||
@@ -184,7 +191,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the absolute path of the requested WebSocket URI.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the absolute path of the requested WebSocket URI.
|
||||
/// A <see cref="string"/> that represents the absolute path of the requested
|
||||
/// WebSocket URI.
|
||||
/// </value>
|
||||
public override string Path {
|
||||
get {
|
||||
@@ -193,10 +201,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of query string variables used in the WebSocket opening handshake.
|
||||
/// Gets the collection of query string variables used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query string variables.
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query
|
||||
/// string variables.
|
||||
/// </value>
|
||||
public override NameValueCollection QueryString {
|
||||
get {
|
||||
@@ -208,22 +218,26 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the WebSocket URI requested by the client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="Uri"/> that contains the WebSocket URI.
|
||||
/// A <see cref="Uri"/> that represents the WebSocket URI requested by the
|
||||
/// client.
|
||||
/// </value>
|
||||
public override Uri RequestUri {
|
||||
get {
|
||||
return _request.RequestUri;
|
||||
return _uri ?? (_uri = createRequestUri ());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketKey property provides a part of the information used by the server to prove that it received a valid WebSocket opening handshake.
|
||||
/// This property provides a part of the information used by the server to
|
||||
/// prove that it received a valid WebSocket connection request.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Key header field.
|
||||
/// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key
|
||||
/// header field.
|
||||
/// </value>
|
||||
public override string SecWebSocketKey {
|
||||
get {
|
||||
@@ -232,13 +246,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the WebSocket opening handshake.
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property indicates the subprotocols of the WebSocket connection.
|
||||
/// This property represents the subprotocols of the WebSocket connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// An IEnumerable<string> that contains the values of the Sec-WebSocket-Protocol header field.
|
||||
/// An IEnumerable<string> that contains the values of the
|
||||
/// Sec-WebSocket-Protocol header field.
|
||||
/// </value>
|
||||
public override IEnumerable<string> SecWebSocketProtocols {
|
||||
get {
|
||||
@@ -247,13 +263,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketVersion property indicates the WebSocket protocol version of the connection.
|
||||
/// This property represents the WebSocket protocol version of the connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Version header field.
|
||||
/// A <see cref="string"/> that represents the value of the
|
||||
/// Sec-WebSocket-Version header field.
|
||||
/// </value>
|
||||
public override string SecWebSocketVersion {
|
||||
get {
|
||||
@@ -265,7 +283,7 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the server endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the server endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint.
|
||||
/// </value>
|
||||
public override System.Net.IPEndPoint ServerEndPoint {
|
||||
get {
|
||||
@@ -274,17 +292,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information and security roles).
|
||||
/// Gets the client information (identity, authentication information and
|
||||
/// security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> that contains the client information.
|
||||
/// A <see cref="IPrincipal"/> that represents the client information.
|
||||
/// </value>
|
||||
/// <exception cref="NotImplementedException">
|
||||
/// This property is not implemented.
|
||||
/// </exception>
|
||||
public override IPrincipal User {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
return _user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +308,7 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the client endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the client endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint.
|
||||
/// </value>
|
||||
public override System.Net.IPEndPoint UserEndPoint {
|
||||
get {
|
||||
@@ -301,7 +317,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WebSocket instance used for two-way communication between client and server.
|
||||
/// Gets the WebSocket instance used for two-way communication between client
|
||||
/// and server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.WebSocket"/>.
|
||||
@@ -314,6 +331,22 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private Uri createRequestUri ()
|
||||
{
|
||||
var scheme = _secure ? "wss" : "ws";
|
||||
var host = _request.Headers ["Host"];
|
||||
var rawUri = _request.RequestUri;
|
||||
var path = rawUri.IsAbsoluteUri
|
||||
? rawUri.PathAndQuery
|
||||
: HttpUtility.UrlDecode (_request.RawUrl);
|
||||
|
||||
return String.Format ("{0}://{1}{2}", scheme, host, path).ToUri ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal void Close ()
|
||||
@@ -322,15 +355,64 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
_client.Close ();
|
||||
}
|
||||
|
||||
internal void Close (HttpStatusCode code)
|
||||
{
|
||||
_websocket.Close (HandshakeResponse.CreateCloseResponse (code));
|
||||
}
|
||||
|
||||
internal void SendAuthChallenge (string challenge)
|
||||
{
|
||||
var res = new HandshakeResponse (HttpStatusCode.Unauthorized);
|
||||
res.Headers ["WWW-Authenticate"] = challenge;
|
||||
_stream.WriteHandshake (res);
|
||||
_request = HandshakeRequest.Parse (_stream.ReadHandshake ());
|
||||
}
|
||||
|
||||
internal void SetUser (
|
||||
AuthenticationSchemes expectedScheme,
|
||||
string realm,
|
||||
Func<IIdentity, NetworkCredential> credentialsFinder)
|
||||
{
|
||||
var authRes = _request.AuthResponse;
|
||||
if (authRes == null)
|
||||
return;
|
||||
|
||||
var identity = authRes.ToIdentity ();
|
||||
if (identity == null)
|
||||
return;
|
||||
|
||||
NetworkCredential credentials = null;
|
||||
try {
|
||||
credentials = credentialsFinder (identity);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
if (credentials == null)
|
||||
return;
|
||||
|
||||
var valid = expectedScheme == AuthenticationSchemes.Basic
|
||||
? ((HttpBasicIdentity) identity).Password == credentials.Password
|
||||
: expectedScheme == AuthenticationSchemes.Digest
|
||||
? ((HttpDigestIdentity) identity).IsValid (
|
||||
credentials.Password, realm, _request.HttpMethod, null)
|
||||
: false;
|
||||
|
||||
if (valid)
|
||||
_user = new GenericPrincipal (identity, credentials.Roles);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="string"/> that represents the current <see cref="TcpListenerWebSocketContext"/>.
|
||||
/// Returns a <see cref="string"/> that represents the current
|
||||
/// <see cref="TcpListenerWebSocketContext"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="string"/> that represents the current <see cref="TcpListenerWebSocketContext"/>.
|
||||
/// A <see cref="string"/> that represents the current
|
||||
/// <see cref="TcpListenerWebSocketContext"/>.
|
||||
/// </returns>
|
||||
public override string ToString ()
|
||||
{
|
||||
|
@@ -5,7 +5,7 @@
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2013 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
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* 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
|
||||
@@ -34,7 +34,7 @@ using System.Security.Principal;
|
||||
namespace WebSocketSharp.Net.WebSockets
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the WebSocket connection request objects.
|
||||
/// Provides access to the WebSocket connection request information.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The WebSocketContext class is an abstract class.
|
||||
@@ -55,26 +55,28 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies used in the WebSocket opening handshake.
|
||||
/// Gets the cookies used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the cookies.
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the
|
||||
/// cookies.
|
||||
/// </value>
|
||||
public abstract CookieCollection CookieCollection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP headers used in the WebSocket opening handshake.
|
||||
/// Gets the HTTP headers used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Collections.Specialized.NameValueCollection"/> that contains the HTTP headers.
|
||||
/// A <see cref="NameValueCollection"/> that contains the HTTP headers.
|
||||
/// </value>
|
||||
public abstract NameValueCollection Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Host header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Host header field used in the WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Host header field.
|
||||
/// A <see cref="string"/> that represents the value of the Host header field.
|
||||
/// </value>
|
||||
public abstract string Host { get; }
|
||||
|
||||
@@ -87,10 +89,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
public abstract bool IsAuthenticated { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the client connected from the local computer.
|
||||
/// Gets a value indicating whether the client connected from the local
|
||||
/// computer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public abstract bool IsLocal { get; }
|
||||
|
||||
@@ -98,23 +102,28 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets a value indicating whether the WebSocket connection is secured.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public abstract bool IsSecureConnection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the request is a WebSocket connection request.
|
||||
/// Gets a value indicating whether the request is a WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public abstract bool IsWebSocketRequest { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Origin header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Origin header field used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Origin header field.
|
||||
/// A <see cref="string"/> that represents the value of the Origin header
|
||||
/// field.
|
||||
/// </value>
|
||||
public abstract string Origin { get; }
|
||||
|
||||
@@ -122,15 +131,18 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the absolute path of the requested WebSocket URI.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the absolute path of the requested WebSocket URI.
|
||||
/// A <see cref="string"/> that represents the absolute path of the requested
|
||||
/// WebSocket URI.
|
||||
/// </value>
|
||||
public abstract string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of query string variables used in the WebSocket opening handshake.
|
||||
/// Gets the collection of query string variables used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query string variables.
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query
|
||||
/// string variables.
|
||||
/// </value>
|
||||
public abstract NameValueCollection QueryString { get; }
|
||||
|
||||
@@ -138,40 +150,47 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the WebSocket URI requested by the client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="Uri"/> that contains the WebSocket URI.
|
||||
/// A <see cref="Uri"/> that represents the WebSocket URI.
|
||||
/// </value>
|
||||
public abstract Uri RequestUri { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketKey property provides a part of the information used by the server to prove that it received a valid WebSocket opening handshake.
|
||||
/// This property provides a part of the information used by the server to
|
||||
/// prove that it received a valid WebSocket connection request.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Key header field.
|
||||
/// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key
|
||||
/// header field.
|
||||
/// </value>
|
||||
public abstract string SecWebSocketKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the WebSocket opening handshake.
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketProtocols property indicates the subprotocols of the WebSocket connection.
|
||||
/// This property represents the subprotocols of the WebSocket connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// An IEnumerable<string> that contains the values of the Sec-WebSocket-Protocol header field.
|
||||
/// An IEnumerable<string> that contains the values of the
|
||||
/// Sec-WebSocket-Protocol header field.
|
||||
/// </value>
|
||||
public abstract IEnumerable<string> SecWebSocketProtocols { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketVersion property indicates the WebSocket protocol version of the connection.
|
||||
/// This property represents the WebSocket protocol version of the connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Version header field.
|
||||
/// A <see cref="string"/> that represents the value of the
|
||||
/// Sec-WebSocket-Version header field.
|
||||
/// </value>
|
||||
public abstract string SecWebSocketVersion { get; }
|
||||
|
||||
@@ -179,15 +198,16 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the server endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the server endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint.
|
||||
/// </value>
|
||||
public abstract System.Net.IPEndPoint ServerEndPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information and security roles).
|
||||
/// Gets the client information (identity, authentication information and
|
||||
/// security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> that contains the client information.
|
||||
/// A <see cref="IPrincipal"/> that represents the client information.
|
||||
/// </value>
|
||||
public abstract IPrincipal User { get; }
|
||||
|
||||
@@ -195,12 +215,13 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the client endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the client endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint.
|
||||
/// </value>
|
||||
public abstract System.Net.IPEndPoint UserEndPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WebSocket instance used for two-way communication between client and server.
|
||||
/// Gets the WebSocket instance used for two-way communication between client
|
||||
/// and server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.WebSocket"/>.
|
||||
|
Reference in New Issue
Block a user