Fix for HTTP Basic/Digest Authentication

This commit is contained in:
sta
2014-01-01 21:43:18 +09:00
parent 0a263622f0
commit 537229902f
32 changed files with 7488 additions and 6273 deletions

View File

@@ -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,
}
}

View File

@@ -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
}
}

View 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

View 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

View File

@@ -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

View File

@@ -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
}
}

View 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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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&lt;string&gt; that contains the values of the Sec-WebSocket-Protocol header field.
/// An IEnumerable&lt;string&gt; 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 ()
{

View File

@@ -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&lt;string&gt; that contains the values of the Sec-WebSocket-Protocol header field.
/// An IEnumerable&lt;string&gt; 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 ()
{

View File

@@ -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&lt;string&gt; that contains the values of the Sec-WebSocket-Protocol header field.
/// An IEnumerable&lt;string&gt; 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"/>.