Fix for HTTP Basic/Digest Authentication
This commit is contained in:
parent
0a263622f0
commit
537229902f
@ -122,6 +122,7 @@ namespace Example {
|
||||
//};
|
||||
//ws.SetCookie(new Cookie("nobita", "\"idiot, gunfighter\""));
|
||||
//ws.SetCookie(new Cookie("dora", "tanuki"));
|
||||
//ws.SetCredentials ("nobita", "password", false);
|
||||
ws.Connect();
|
||||
//Console.WriteLine("Compression: {0}", ws.Compression);
|
||||
|
||||
|
@ -2,6 +2,7 @@ using System;
|
||||
using System.Configuration;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using WebSocketSharp;
|
||||
using WebSocketSharp.Net;
|
||||
using WebSocketSharp.Server;
|
||||
|
||||
namespace Example2
|
||||
@ -17,10 +18,28 @@ namespace Example2
|
||||
#if DEBUG
|
||||
wssv.Log.Level = LogLevel.TRACE;
|
||||
#endif
|
||||
//var cert = ConfigurationManager.AppSettings ["ServerCertFile"];
|
||||
//var password = ConfigurationManager.AppSettings ["CertFilePassword"];
|
||||
//wssv.Certificate = new X509Certificate2 (cert, password);
|
||||
|
||||
// HTTP Basic/Digest Authentication
|
||||
/*
|
||||
wssv.AuthenticationSchemes = AuthenticationSchemes.Digest;
|
||||
wssv.Realm = "WebSocket Test";
|
||||
wssv.UserCredentialsFinder = identity => {
|
||||
var name = identity.Name;
|
||||
return name == "nobita"
|
||||
? new NetworkCredential (name, "password")
|
||||
: null;
|
||||
};
|
||||
*/
|
||||
|
||||
// Secure Connection
|
||||
/*
|
||||
var cert = ConfigurationManager.AppSettings ["ServerCertFile"];
|
||||
var password = ConfigurationManager.AppSettings ["CertFilePassword"];
|
||||
wssv.Certificate = new X509Certificate2 (cert, password);
|
||||
*/
|
||||
|
||||
//wssv.KeepClean = false;
|
||||
|
||||
wssv.AddWebSocketService<Echo> ("/Echo");
|
||||
wssv.AddWebSocketService<Chat> ("/Chat");
|
||||
//wssv.AddWebSocketService<Chat> ("/Chat", () => new Chat ("Anon#"));
|
||||
@ -28,8 +47,7 @@ namespace Example2
|
||||
//wssv.AddWebSocketService<Chat> ("/チャット");
|
||||
|
||||
wssv.Start ();
|
||||
if (wssv.IsListening)
|
||||
{
|
||||
if (wssv.IsListening) {
|
||||
Console.WriteLine (
|
||||
"A WebSocket Server listening on port: {0} service paths:", wssv.Port);
|
||||
|
||||
|
@ -19,24 +19,39 @@ namespace Example3
|
||||
_httpsv.Log.Level = LogLevel.TRACE;
|
||||
#endif
|
||||
_httpsv.RootPath = ConfigurationManager.AppSettings ["RootPath"];
|
||||
//var cert = ConfigurationManager.AppSettings ["ServerCertFile"];
|
||||
//var password = ConfigurationManager.AppSettings ["CertFilePassword"];
|
||||
//_httpsv.Certificate = new X509Certificate2 (cert, password);
|
||||
|
||||
// HTTP Basic/Digest Authentication
|
||||
/*
|
||||
_httpsv.AuthenticationSchemes = AuthenticationSchemes.Digest;
|
||||
_httpsv.Realm = "WebSocket Test";
|
||||
_httpsv.UserCredentialsFinder = identity => {
|
||||
var name = identity.Name;
|
||||
return name == "nobita"
|
||||
? new NetworkCredential (name, "password")
|
||||
: null;
|
||||
};
|
||||
*/
|
||||
|
||||
// Secure Connection
|
||||
/*
|
||||
var cert = ConfigurationManager.AppSettings ["ServerCertFile"];
|
||||
var password = ConfigurationManager.AppSettings ["CertFilePassword"];
|
||||
_httpsv.Certificate = new X509Certificate2 (cert, password);
|
||||
*/
|
||||
|
||||
//_httpsv.KeepClean = false;
|
||||
|
||||
_httpsv.AddWebSocketService<Echo> ("/Echo");
|
||||
_httpsv.AddWebSocketService<Chat> ("/Chat");
|
||||
//_httpsv.AddWebSocketService<Chat> ("/Chat", () => new Chat ("Anon#"));
|
||||
|
||||
_httpsv.OnGet += (sender, e) =>
|
||||
{
|
||||
onGet (e);
|
||||
};
|
||||
_httpsv.OnGet += (sender, e) => onGet (e);
|
||||
|
||||
_httpsv.Start ();
|
||||
if (_httpsv.IsListening)
|
||||
{
|
||||
if (_httpsv.IsListening) {
|
||||
Console.WriteLine (
|
||||
"An HTTP Server listening on port: {0} WebSocket service paths:", _httpsv.Port);
|
||||
"An HTTP Server listening on port: {0} WebSocket service paths:",
|
||||
_httpsv.Port);
|
||||
|
||||
foreach (var path in _httpsv.WebSocketServices.ServicePaths)
|
||||
Console.WriteLine (" {0}", path);
|
||||
@ -61,8 +76,7 @@ namespace Example3
|
||||
var request = eventArgs.Request;
|
||||
var response = eventArgs.Response;
|
||||
var content = getContent (request.RawUrl);
|
||||
if (content != null)
|
||||
{
|
||||
if (content != null) {
|
||||
response.WriteContent (content);
|
||||
return;
|
||||
}
|
||||
|
@ -4,8 +4,8 @@
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2013 sta.blockhead
|
||||
*
|
||||
* Copyright (c) 2013-2014 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
@ -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
|
||||
@ -27,29 +27,36 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
|
||||
namespace WebSocketSharp {
|
||||
|
||||
internal class AuthenticationChallenge {
|
||||
|
||||
namespace WebSocketSharp
|
||||
{
|
||||
internal class AuthenticationChallenge
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private string _algorithm;
|
||||
private string _domain;
|
||||
private string _nonce;
|
||||
private string _opaque;
|
||||
private string _qop;
|
||||
private string _realm;
|
||||
private string _scheme;
|
||||
private string _stale;
|
||||
private NameValueCollection _params;
|
||||
private string _scheme;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Constructors
|
||||
#region Internal Constructors
|
||||
|
||||
private AuthenticationChallenge()
|
||||
internal AuthenticationChallenge (string authScheme, string authParams)
|
||||
{
|
||||
_scheme = authScheme;
|
||||
_params = authParams.ParseAuthParams ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
internal NameValueCollection Params {
|
||||
get {
|
||||
return _params;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -58,81 +65,49 @@ namespace WebSocketSharp {
|
||||
|
||||
public string Algorithm {
|
||||
get {
|
||||
return _algorithm ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_algorithm = value;
|
||||
return _params ["algorithm"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Domain {
|
||||
get {
|
||||
return _domain ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_domain = value;
|
||||
return _params ["domain"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Nonce {
|
||||
get {
|
||||
return _nonce ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_nonce = value;
|
||||
return _params ["nonce"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Opaque {
|
||||
get {
|
||||
return _opaque ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_opaque = value;
|
||||
return _params ["opaque"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Qop {
|
||||
get {
|
||||
return _qop ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_qop = value;
|
||||
return _params ["qop"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Realm {
|
||||
get {
|
||||
return _realm ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_realm = value;
|
||||
return _params ["realm"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Scheme {
|
||||
get {
|
||||
return _scheme ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_scheme = value;
|
||||
return _scheme;
|
||||
}
|
||||
}
|
||||
|
||||
public string Stale {
|
||||
get {
|
||||
return _stale ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_stale = value;
|
||||
return _params ["stale"];
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,64 +115,13 @@ namespace WebSocketSharp {
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public static AuthenticationChallenge Parse(string challenge)
|
||||
public static AuthenticationChallenge Parse (string value)
|
||||
{
|
||||
var authChallenge = new AuthenticationChallenge();
|
||||
if (challenge.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
authChallenge.Scheme = "Basic";
|
||||
authChallenge.Realm = challenge.Substring(6).GetValueInternal("=").Trim('"');
|
||||
|
||||
return authChallenge;
|
||||
}
|
||||
|
||||
foreach (var p in challenge.SplitHeaderValue(','))
|
||||
{
|
||||
var param = p.Trim();
|
||||
if (param.StartsWith("digest", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
authChallenge.Scheme = "Digest";
|
||||
authChallenge.Realm = param.Substring(7).GetValueInternal("=").Trim('"');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = param.GetValueInternal("=").Trim('"');
|
||||
if (param.StartsWith("domain", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
authChallenge.Domain = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (param.StartsWith("nonce", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
authChallenge.Nonce = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (param.StartsWith("opaque", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
authChallenge.Opaque = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (param.StartsWith("stale", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
authChallenge.Stale = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (param.StartsWith("algorithm", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
authChallenge.Algorithm = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (param.StartsWith("qop", StringComparison.OrdinalIgnoreCase))
|
||||
authChallenge.Qop = value;
|
||||
}
|
||||
|
||||
return authChallenge;
|
||||
var challenge = value.Split (new char [] {' '}, 2);
|
||||
var scheme = challenge [0].ToLower ();
|
||||
return scheme == "basic" || scheme == "digest"
|
||||
? new AuthenticationChallenge (scheme, challenge [1])
|
||||
: null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -5,7 +5,7 @@
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 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
|
||||
@ -27,56 +27,81 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Collections.Specialized;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using WebSocketSharp.Net;
|
||||
|
||||
namespace WebSocketSharp {
|
||||
|
||||
internal class AuthenticationResponse {
|
||||
|
||||
namespace WebSocketSharp
|
||||
{
|
||||
internal class AuthenticationResponse
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private string _algorithm;
|
||||
private string _cnonce;
|
||||
private string _method;
|
||||
private string _nc;
|
||||
private string _nonce;
|
||||
private string _opaque;
|
||||
private string _password;
|
||||
private string _qop;
|
||||
private string _realm;
|
||||
private string _response;
|
||||
private string _scheme;
|
||||
private string _uri;
|
||||
private string _userName;
|
||||
private uint _nonceCount;
|
||||
private NameValueCollection _params;
|
||||
private string _scheme;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Constructors
|
||||
|
||||
private AuthenticationResponse()
|
||||
private AuthenticationResponse (
|
||||
string authScheme, NameValueCollection authParams)
|
||||
{
|
||||
_scheme = authScheme;
|
||||
_params = authParams;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
#region Internal Constructors
|
||||
|
||||
public AuthenticationResponse(WsCredential credential)
|
||||
internal AuthenticationResponse (NetworkCredential credentials)
|
||||
: this ("Basic", new NameValueCollection (), credentials, 0)
|
||||
{
|
||||
_userName = credential.UserName;
|
||||
_password = credential.Password;
|
||||
_scheme = "Basic";
|
||||
}
|
||||
|
||||
public AuthenticationResponse(WsCredential credential, AuthenticationChallenge challenge)
|
||||
internal AuthenticationResponse (
|
||||
AuthenticationChallenge challenge,
|
||||
NetworkCredential credentials,
|
||||
uint nonceCount)
|
||||
: this (challenge.Scheme, challenge.Params, credentials, nonceCount)
|
||||
{
|
||||
_userName = credential.UserName;
|
||||
_password = credential.Password;
|
||||
_scheme = challenge.Scheme;
|
||||
_realm = challenge.Realm;
|
||||
if (_scheme == "Digest")
|
||||
initForDigest(credential, challenge);
|
||||
}
|
||||
|
||||
internal AuthenticationResponse (
|
||||
string authScheme,
|
||||
NameValueCollection authParams,
|
||||
NetworkCredential credentials,
|
||||
uint nonceCount)
|
||||
{
|
||||
_scheme = authScheme.ToLower ();
|
||||
_params = authParams;
|
||||
_params ["username"] = credentials.UserName;
|
||||
_params ["password"] = credentials.Password;
|
||||
_params ["uri"] = credentials.Domain;
|
||||
_nonceCount = nonceCount;
|
||||
if (_scheme == "digest")
|
||||
initAsDigest ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
internal uint NonceCount {
|
||||
get {
|
||||
return _nonceCount < UInt32.MaxValue
|
||||
? _nonceCount
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal NameValueCollection Params {
|
||||
get {
|
||||
return _params;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -85,111 +110,73 @@ namespace WebSocketSharp {
|
||||
|
||||
public string Algorithm {
|
||||
get {
|
||||
return _algorithm ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_algorithm = value;
|
||||
return _params ["algorithm"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Cnonce {
|
||||
get {
|
||||
return _cnonce ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_cnonce = value;
|
||||
return _params ["cnonce"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Nc {
|
||||
get {
|
||||
return _nc ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_nc = value;
|
||||
return _params ["nc"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Nonce {
|
||||
get {
|
||||
return _nonce ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_nonce = value;
|
||||
return _params ["nonce"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Opaque {
|
||||
get {
|
||||
return _opaque ?? String.Empty;
|
||||
return _params ["opaque"];
|
||||
}
|
||||
}
|
||||
|
||||
private set {
|
||||
_opaque = value;
|
||||
public string Password {
|
||||
get {
|
||||
return _params ["password"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Qop {
|
||||
get {
|
||||
return _qop ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_qop = value;
|
||||
return _params ["qop"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Realm {
|
||||
get {
|
||||
return _realm ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_realm = value;
|
||||
return _params ["realm"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Response {
|
||||
get {
|
||||
return _response ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_response = value;
|
||||
return _params ["response"];
|
||||
}
|
||||
}
|
||||
|
||||
public string Scheme {
|
||||
get {
|
||||
return _scheme ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_scheme = value;
|
||||
return _scheme;
|
||||
}
|
||||
}
|
||||
|
||||
public string Uri {
|
||||
get {
|
||||
return _uri ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_uri = value;
|
||||
return _params ["uri"];
|
||||
}
|
||||
}
|
||||
|
||||
public string UserName {
|
||||
get {
|
||||
return _userName ?? String.Empty;
|
||||
}
|
||||
|
||||
private set {
|
||||
_userName = value;
|
||||
return _params ["username"];
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,130 +184,77 @@ namespace WebSocketSharp {
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private string a1()
|
||||
private static bool contains (string [] array, string item)
|
||||
{
|
||||
var result = String.Format("{0}:{1}:{2}", _userName, _realm, _password);
|
||||
return _algorithm != null && _algorithm.ToLower() == "md5-sess"
|
||||
? String.Format("{0}:{1}:{2}", hash(result), _nonce, _cnonce)
|
||||
: result;
|
||||
foreach (var i in array)
|
||||
if (i.Trim ().ToLower () == item)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string a2()
|
||||
private void initAsDigest ()
|
||||
{
|
||||
return String.Format("{0}:{1}", _method, _uri);
|
||||
}
|
||||
|
||||
private static string createNonceValue()
|
||||
{
|
||||
var src = new byte[16];
|
||||
var rand = new Random();
|
||||
rand.NextBytes(src);
|
||||
var nonce = new StringBuilder(32);
|
||||
foreach (var b in src)
|
||||
nonce.Append(b.ToString("x2"));
|
||||
|
||||
return nonce.ToString();
|
||||
}
|
||||
|
||||
private string createRequestDigest()
|
||||
{
|
||||
if (Qop == "auth")
|
||||
{
|
||||
var data = String.Format("{0}:{1}:{2}:{3}:{4}",
|
||||
_nonce, _nc, _cnonce, _qop, hash(a2()));
|
||||
return kd(hash(a1()), data);
|
||||
}
|
||||
|
||||
return kd(hash(a1()), String.Format("{0}:{1}", _nonce, hash(a2())));
|
||||
}
|
||||
|
||||
private static string hash(string value)
|
||||
{
|
||||
var md5 = MD5.Create();
|
||||
var src = Encoding.UTF8.GetBytes(value);
|
||||
var hashed = md5.ComputeHash(src);
|
||||
var result = new StringBuilder(64);
|
||||
foreach (var b in hashed)
|
||||
result.Append(b.ToString("x2"));
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private void initForDigest(WsCredential credential, AuthenticationChallenge challenge)
|
||||
{
|
||||
_nonce = challenge.Nonce;
|
||||
_method = "GET";
|
||||
_uri = credential.Domain;
|
||||
_algorithm = challenge.Algorithm;
|
||||
_opaque = challenge.Opaque;
|
||||
foreach (var qop in challenge.Qop.Split(','))
|
||||
{
|
||||
if (qop.Trim().ToLower() == "auth")
|
||||
{
|
||||
_qop = "auth";
|
||||
_nc = "00000001";
|
||||
break;
|
||||
var qops = _params ["qop"];
|
||||
if (qops != null) {
|
||||
var qop = "auth";
|
||||
if (contains (qops.Split (','), qop)) {
|
||||
_params ["qop"] = qop;
|
||||
_params ["nc"] = String.Format ("{0:x8}", ++_nonceCount);
|
||||
_params ["cnonce"] = HttpUtility.CreateNonceValue ();
|
||||
}
|
||||
else
|
||||
_params ["qop"] = null;
|
||||
}
|
||||
|
||||
_cnonce = createNonceValue();
|
||||
_response = createRequestDigest();
|
||||
}
|
||||
|
||||
private static string kd(string secret, string data)
|
||||
{
|
||||
var concatenated = String.Format("{0}:{1}", secret, data);
|
||||
return hash(concatenated);
|
||||
}
|
||||
|
||||
private string toBasicCredentials()
|
||||
{
|
||||
var userPass = String.Format("{0}:{1}", _userName, _password);
|
||||
var base64UserPass = Convert.ToBase64String(Encoding.UTF8.GetBytes(userPass));
|
||||
|
||||
return "Basic " + base64UserPass;
|
||||
}
|
||||
|
||||
private string toDigestCredentials()
|
||||
{
|
||||
var digestResponse = new StringBuilder(64);
|
||||
digestResponse.AppendFormat("username={0}", _userName.Quote());
|
||||
digestResponse.AppendFormat(", realm={0}", _realm.Quote());
|
||||
digestResponse.AppendFormat(", nonce={0}", _nonce.Quote());
|
||||
digestResponse.AppendFormat(", uri={0}", _uri.Quote());
|
||||
digestResponse.AppendFormat(", response={0}", _response.Quote());
|
||||
if (!_algorithm.IsNullOrEmpty())
|
||||
digestResponse.AppendFormat(", algorithm={0}", _algorithm);
|
||||
|
||||
if (!_opaque.IsNullOrEmpty())
|
||||
digestResponse.AppendFormat(", opaque={0}", _opaque.Quote());
|
||||
|
||||
if (!_qop.IsNullOrEmpty())
|
||||
digestResponse.AppendFormat(", qop={0}", _qop);
|
||||
|
||||
if (!_nc.IsNullOrEmpty())
|
||||
digestResponse.AppendFormat(", nc={0}", _nc);
|
||||
|
||||
if (!_qop.IsNullOrEmpty())
|
||||
digestResponse.AppendFormat(", cnonce={0}", _cnonce.Quote());
|
||||
|
||||
return "Digest " + digestResponse.ToString();
|
||||
_params ["method"] = "GET";
|
||||
_params ["response"] = HttpUtility.CreateRequestDigest (_params);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public static AuthenticationResponse Parse(string response)
|
||||
public static AuthenticationResponse Parse (string value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
try {
|
||||
var credentials = value.Split (new char [] { ' ' }, 2);
|
||||
if (credentials.Length != 2)
|
||||
return null;
|
||||
|
||||
var scheme = credentials [0].ToLower ();
|
||||
return scheme == "basic"
|
||||
? new AuthenticationResponse (
|
||||
scheme, credentials [1].ParseBasicAuthResponseParams ())
|
||||
: scheme == "digest"
|
||||
? new AuthenticationResponse (
|
||||
scheme, credentials [1].ParseAuthParams ())
|
||||
: null;
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
public IIdentity ToIdentity ()
|
||||
{
|
||||
return _scheme == "Basic"
|
||||
? toBasicCredentials()
|
||||
: toDigestCredentials();
|
||||
return _scheme == "basic"
|
||||
? new HttpBasicIdentity (
|
||||
_params ["username"], _params ["password"]) as IIdentity
|
||||
: _scheme == "digest"
|
||||
? new HttpDigestIdentity (_params)
|
||||
: null;
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return _scheme == "basic"
|
||||
? HttpUtility.CreateBasicAuthCredentials (
|
||||
_params ["username"], _params ["password"])
|
||||
: _scheme == "digest"
|
||||
? HttpUtility.CreateDigestAuthCredentials (_params)
|
||||
: String.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -1,23 +1,22 @@
|
||||
#region License
|
||||
/*
|
||||
* Ext.cs
|
||||
* IsPredefinedScheme and MaybeUri methods are derived from System.Uri.cs
|
||||
* GetStatusDescription method is derived from System.Net.HttpListenerResponse.cs
|
||||
*
|
||||
* Some part of this code are derived from Mono (http://www.mono-project.com).
|
||||
*
|
||||
* ParseBasicAuthResponseParams is derived from System.Net.HttpListenerContext.cs
|
||||
* GetStatusDescription is derived from System.Net.HttpListenerResponse.cs
|
||||
* IsPredefinedScheme and MaybeUri are derived from System.Uri.cs
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2001 Garrett Rooney
|
||||
* Copyright (c) 2003 Ian MacLean
|
||||
* Copyright (c) 2003 Ben Maurer
|
||||
* Copyright (c) 2003, 2005, 2009 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2009 Stephane Delcroix
|
||||
* Copyright (c) 2010-2013 sta.blockhead
|
||||
*
|
||||
* System.Uri.cs
|
||||
* (C) 2001 Garrett Rooney
|
||||
* (C) 2003 Ian MacLean
|
||||
* (C) 2003 Ben Maurer
|
||||
* Copyright (C) 2003, 2005, 2009 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2009 Stephane Delcroix
|
||||
*
|
||||
* System.Net.HttpListenerResponse.cs
|
||||
* Copyright (C) 2003, 2005, 2009 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
|
||||
@ -27,7 +26,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
|
||||
@ -279,6 +278,20 @@ namespace WebSocketSharp
|
||||
: null;
|
||||
}
|
||||
|
||||
internal static void Close (
|
||||
this HttpListenerResponse response, HttpStatusCode code)
|
||||
{
|
||||
response.StatusCode = (int) code;
|
||||
response.OutputStream.Close ();
|
||||
}
|
||||
|
||||
internal static void CloseWithAuthChallenge (
|
||||
this HttpListenerResponse response, string challenge)
|
||||
{
|
||||
response.Headers.SetInternal ("WWW-Authenticate", challenge, true);
|
||||
response.Close (HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
internal static byte [] Compress (this byte [] value, CompressionMethod method)
|
||||
{
|
||||
return method == CompressionMethod.DEFLATE
|
||||
@ -447,9 +460,9 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
internal static TcpListenerWebSocketContext GetWebSocketContext (
|
||||
this TcpClient client, bool secure, X509Certificate cert)
|
||||
this TcpClient client, X509Certificate cert, bool secure, Logger logger)
|
||||
{
|
||||
return new TcpListenerWebSocketContext (client, secure, cert);
|
||||
return new TcpListenerWebSocketContext (client, cert, secure, logger);
|
||||
}
|
||||
|
||||
internal static bool IsCompressionExtension (this string value)
|
||||
@ -517,6 +530,57 @@ namespace WebSocketSharp
|
||||
: String.Format ("\"{0}\"", value.Replace ("\"", "\\\""));
|
||||
}
|
||||
|
||||
internal static NameValueCollection ParseBasicAuthResponseParams (
|
||||
this string value)
|
||||
{
|
||||
// HTTP Basic Authentication response is a formatted Base64 string.
|
||||
var userPass = Encoding.Default.GetString (
|
||||
Convert.FromBase64String (value));
|
||||
|
||||
// The format is [<domain>\]<username>:<password>.
|
||||
// 'domain' is optional.
|
||||
var i = userPass.IndexOf (':');
|
||||
var username = userPass.Substring (0, i);
|
||||
var password = i < userPass.Length - 1
|
||||
? userPass.Substring (i + 1)
|
||||
: String.Empty;
|
||||
|
||||
// Check if 'domain' exists.
|
||||
i = username.IndexOf ('\\');
|
||||
if (i > 0)
|
||||
username = username.Substring (i + 1);
|
||||
|
||||
var result = new NameValueCollection ();
|
||||
result ["username"] = username;
|
||||
result ["password"] = password;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static NameValueCollection ParseAuthParams (this string value)
|
||||
{
|
||||
var result = new NameValueCollection ();
|
||||
var i = 0;
|
||||
string name, val;
|
||||
foreach (var param in value.SplitHeaderValue (',')) {
|
||||
i = param.IndexOf ('=');
|
||||
if (i > 0) {
|
||||
name = param.Substring (0, i).Trim ();
|
||||
val = i < param.Length - 1
|
||||
? param.Substring (i + 1).Trim ().Trim ('"')
|
||||
: String.Empty;
|
||||
}
|
||||
else {
|
||||
name = param;
|
||||
val = String.Empty;
|
||||
}
|
||||
|
||||
result.Add (name, val);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static byte [] ReadBytes (this Stream stream, int length)
|
||||
{
|
||||
return stream.readBytes (new byte [length], 0, length);
|
||||
@ -1642,8 +1706,9 @@ namespace WebSocketSharp
|
||||
/// Converts the specified <see cref="string"/> to a <see cref="Uri"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="Uri"/> converted from <paramref name="uriString"/>, or <see langword="null"/>
|
||||
/// if <paramref name="uriString"/> is <see langword="null"/> or <see cref="String.Empty"/>.
|
||||
/// A <see cref="Uri"/> converted from <paramref name="uriString"/>, or
|
||||
/// <see langword="null"/> if <paramref name="uriString"/> is
|
||||
/// <see langword="null"/> or empty.
|
||||
/// </returns>
|
||||
/// <param name="uriString">
|
||||
/// A <see cref="string"/> to convert.
|
||||
|
@ -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
|
||||
@ -35,6 +35,13 @@ namespace WebSocketSharp
|
||||
{
|
||||
internal abstract class HandshakeBase
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private NameValueCollection _headers;
|
||||
private Version _version;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Const Fields
|
||||
|
||||
protected const string CrLf = "\r\n";
|
||||
@ -45,8 +52,8 @@ namespace WebSocketSharp
|
||||
|
||||
protected HandshakeBase ()
|
||||
{
|
||||
ProtocolVersion = HttpVersion.Version11;
|
||||
Headers = new NameValueCollection ();
|
||||
_version = HttpVersion.Version11;
|
||||
_headers = new NameValueCollection ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -54,37 +61,29 @@ namespace WebSocketSharp
|
||||
#region Public Properties
|
||||
|
||||
public NameValueCollection Headers {
|
||||
get; protected set;
|
||||
get {
|
||||
return _headers;
|
||||
}
|
||||
|
||||
protected set {
|
||||
_headers = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Version ProtocolVersion {
|
||||
get; protected set;
|
||||
get {
|
||||
return _version;
|
||||
}
|
||||
|
||||
protected set {
|
||||
_version = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void AddHeader (string name, string value)
|
||||
{
|
||||
Headers.Add (name, value);
|
||||
}
|
||||
|
||||
public bool ContainsHeader (string name)
|
||||
{
|
||||
return Headers.Contains (name);
|
||||
}
|
||||
|
||||
public bool ContainsHeader (string name, string value)
|
||||
{
|
||||
return Headers.Contains (name, value);
|
||||
}
|
||||
|
||||
public string [] GetHeaderValues (string name)
|
||||
{
|
||||
return Headers.GetValues (name);
|
||||
}
|
||||
|
||||
public byte [] ToByteArray ()
|
||||
{
|
||||
return Encoding.UTF8.GetBytes (ToString ());
|
||||
|
@ -4,8 +4,8 @@
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
* 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
|
||||
@ -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
|
||||
@ -31,7 +31,6 @@ using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using WebSocketSharp.Net;
|
||||
using WebSocketSharp.Net.WebSockets;
|
||||
|
||||
namespace WebSocketSharp
|
||||
{
|
||||
@ -39,7 +38,10 @@ namespace WebSocketSharp
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private string _method;
|
||||
private NameValueCollection _queryString;
|
||||
private string _rawUrl;
|
||||
private Uri _uri;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -55,17 +57,31 @@ namespace WebSocketSharp
|
||||
|
||||
public HandshakeRequest (string uriString)
|
||||
{
|
||||
HttpMethod = "GET";
|
||||
RequestUri = uriString.ToUri ();
|
||||
AddHeader ("User-Agent", "websocket-sharp/1.0");
|
||||
AddHeader ("Upgrade", "websocket");
|
||||
AddHeader ("Connection", "Upgrade");
|
||||
_method = "GET";
|
||||
_uri = uriString.ToUri ();
|
||||
_rawUrl = _uri.IsAbsoluteUri
|
||||
? _uri.PathAndQuery
|
||||
: uriString;
|
||||
|
||||
var headers = Headers;
|
||||
headers ["User-Agent"] = "websocket-sharp/1.0";
|
||||
headers ["Upgrade"] = "websocket";
|
||||
headers ["Connection"] = "Upgrade";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
public AuthenticationResponse AuthResponse {
|
||||
get {
|
||||
var response = Headers ["Authorization"];
|
||||
return !response.IsNullOrEmpty ()
|
||||
? AuthenticationResponse.Parse (response)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
public CookieCollection Cookies {
|
||||
get {
|
||||
return Headers.GetCookies (false);
|
||||
@ -73,33 +89,36 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
public string HttpMethod {
|
||||
get; private set;
|
||||
get {
|
||||
return _method;
|
||||
}
|
||||
|
||||
private set {
|
||||
_method = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWebSocketRequest {
|
||||
get {
|
||||
return HttpMethod == "GET" &&
|
||||
var headers = Headers;
|
||||
return _method == "GET" &&
|
||||
ProtocolVersion >= HttpVersion.Version11 &&
|
||||
Headers.Contains ("Upgrade", "websocket") &&
|
||||
Headers.Contains ("Connection", "Upgrade");
|
||||
headers.Contains ("Upgrade", "websocket") &&
|
||||
headers.Contains ("Connection", "Upgrade");
|
||||
}
|
||||
}
|
||||
|
||||
public NameValueCollection QueryString {
|
||||
get {
|
||||
if (_queryString == null)
|
||||
{
|
||||
if (_queryString == null) {
|
||||
_queryString = new NameValueCollection ();
|
||||
var i = RawUrl.IndexOf ('?');
|
||||
if (i > 0)
|
||||
{
|
||||
if (i > 0) {
|
||||
var query = RawUrl.Substring (i + 1);
|
||||
var components = query.Split ('&');
|
||||
foreach (var c in components)
|
||||
{
|
||||
foreach (var c in components) {
|
||||
var nv = c.GetNameAndValue ("=");
|
||||
if (nv.Key != null)
|
||||
{
|
||||
if (nv.Key != null) {
|
||||
var name = nv.Key.UrlDecode ();
|
||||
var val = nv.Value.UrlDecode ();
|
||||
_queryString.Add (name, val);
|
||||
@ -114,14 +133,22 @@ namespace WebSocketSharp
|
||||
|
||||
public string RawUrl {
|
||||
get {
|
||||
return RequestUri.IsAbsoluteUri
|
||||
? RequestUri.PathAndQuery
|
||||
: RequestUri.OriginalString;
|
||||
return _rawUrl;
|
||||
}
|
||||
|
||||
private set {
|
||||
_rawUrl = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Uri RequestUri {
|
||||
get; private set;
|
||||
get {
|
||||
return _uri;
|
||||
}
|
||||
|
||||
private set {
|
||||
_uri = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -132,10 +159,8 @@ namespace WebSocketSharp
|
||||
{
|
||||
var requestLine = request [0].Split (' ');
|
||||
if (requestLine.Length != 3)
|
||||
{
|
||||
var msg = "Invalid HTTP Request-Line: " + request [0];
|
||||
throw new ArgumentException (msg, "request");
|
||||
}
|
||||
throw new ArgumentException (
|
||||
"Invalid HTTP Request-Line: " + request [0], "request");
|
||||
|
||||
var headers = new WebHeaderCollection ();
|
||||
for (int i = 1; i < request.Length; i++)
|
||||
@ -144,8 +169,9 @@ namespace WebSocketSharp
|
||||
return new HandshakeRequest {
|
||||
Headers = headers,
|
||||
HttpMethod = requestLine [0],
|
||||
RequestUri = requestLine [1].ToUri (),
|
||||
ProtocolVersion = new Version (requestLine [2].Substring (5))
|
||||
ProtocolVersion = new Version (requestLine [2].Substring (5)),
|
||||
RawUrl = requestLine [1],
|
||||
RequestUri = requestLine [1].ToUri ()
|
||||
};
|
||||
}
|
||||
|
||||
@ -160,21 +186,17 @@ namespace WebSocketSharp
|
||||
if (!sorted [i].Expired)
|
||||
header.AppendFormat ("; {0}", sorted [i].ToString ());
|
||||
|
||||
AddHeader ("Cookie", header.ToString ());
|
||||
}
|
||||
|
||||
public void SetAuthorization (AuthenticationResponse response)
|
||||
{
|
||||
var credentials = response.ToString ();
|
||||
AddHeader ("Authorization", credentials);
|
||||
Headers ["Cookie"] = header.ToString ();
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
var buffer = new StringBuilder (64);
|
||||
buffer.AppendFormat ("{0} {1} HTTP/{2}{3}", HttpMethod, RawUrl, ProtocolVersion, CrLf);
|
||||
foreach (string key in Headers.AllKeys)
|
||||
buffer.AppendFormat ("{0}: {1}{2}", key, Headers [key], CrLf);
|
||||
buffer.AppendFormat ("{0} {1} HTTP/{2}{3}", _method, _rawUrl, ProtocolVersion, CrLf);
|
||||
|
||||
var headers = Headers;
|
||||
foreach (var key in headers.AllKeys)
|
||||
buffer.AppendFormat ("{0}: {1}{2}", key, headers [key], CrLf);
|
||||
|
||||
buffer.Append (CrLf);
|
||||
return buffer.ToString ();
|
||||
|
@ -4,8 +4,8 @@
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
* 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
|
||||
@ -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
|
||||
@ -35,20 +35,34 @@ namespace WebSocketSharp
|
||||
{
|
||||
internal class HandshakeResponse : HandshakeBase
|
||||
{
|
||||
#region Public Constructors
|
||||
#region Private Fields
|
||||
|
||||
public HandshakeResponse ()
|
||||
: this (HttpStatusCode.SwitchingProtocols)
|
||||
private string _code;
|
||||
private string _reason;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Constructors
|
||||
|
||||
private HandshakeResponse ()
|
||||
{
|
||||
AddHeader ("Upgrade", "websocket");
|
||||
AddHeader ("Connection", "Upgrade");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
public HandshakeResponse (HttpStatusCode code)
|
||||
{
|
||||
StatusCode = ((int) code).ToString ();
|
||||
Reason = code.GetDescription ();
|
||||
AddHeader ("Server", "websocket-sharp/1.0");
|
||||
_code = ((int) code).ToString ();
|
||||
_reason = code.GetDescription ();
|
||||
|
||||
var headers = Headers;
|
||||
headers ["Server"] = "websocket-sharp/1.0";
|
||||
if (code == HttpStatusCode.SwitchingProtocols) {
|
||||
headers ["Upgrade"] = "websocket";
|
||||
headers ["Connection"] = "Upgrade";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -72,25 +86,38 @@ namespace WebSocketSharp
|
||||
|
||||
public bool IsUnauthorized {
|
||||
get {
|
||||
return StatusCode == "401";
|
||||
return _code == "401";
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWebSocketResponse {
|
||||
get {
|
||||
var headers = Headers;
|
||||
return ProtocolVersion >= HttpVersion.Version11 &&
|
||||
StatusCode == "101" &&
|
||||
Headers.Contains ("Upgrade", "websocket") &&
|
||||
Headers.Contains ("Connection", "Upgrade");
|
||||
_code == "101" &&
|
||||
headers.Contains ("Upgrade", "websocket") &&
|
||||
headers.Contains ("Connection", "Upgrade");
|
||||
}
|
||||
}
|
||||
|
||||
public string Reason {
|
||||
get; private set;
|
||||
get {
|
||||
return _reason;
|
||||
}
|
||||
|
||||
private set {
|
||||
_reason = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string StatusCode {
|
||||
get; private set;
|
||||
get {
|
||||
return _code;
|
||||
}
|
||||
|
||||
private set {
|
||||
_code = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -100,7 +127,7 @@ namespace WebSocketSharp
|
||||
public static HandshakeResponse CreateCloseResponse (HttpStatusCode code)
|
||||
{
|
||||
var res = new HandshakeResponse (code);
|
||||
res.AddHeader ("Connection", "close");
|
||||
res.Headers ["Connection"] = "close";
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -132,16 +159,20 @@ namespace WebSocketSharp
|
||||
if (cookies == null || cookies.Count == 0)
|
||||
return;
|
||||
|
||||
var headers = Headers;
|
||||
foreach (var cookie in cookies.Sorted)
|
||||
AddHeader ("Set-Cookie", cookie.ToResponseString ());
|
||||
headers.Add ("Set-Cookie", cookie.ToResponseString ());
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
var buffer = new StringBuilder (64);
|
||||
buffer.AppendFormat ("HTTP/{0} {1} {2}{3}", ProtocolVersion, StatusCode, Reason, CrLf);
|
||||
foreach (string key in Headers.AllKeys)
|
||||
buffer.AppendFormat ("{0}: {1}{2}", key, Headers [key], CrLf);
|
||||
buffer.AppendFormat (
|
||||
"HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf);
|
||||
|
||||
var headers = Headers;
|
||||
foreach (var key in headers.AllKeys)
|
||||
buffer.AppendFormat ("{0}: {1}{2}", key, headers [key], CrLf);
|
||||
|
||||
buffer.Append (CrLf);
|
||||
return buffer.ToString ();
|
||||
|
@ -1,69 +1,67 @@
|
||||
//
|
||||
// AuthenticationSchemes.cs
|
||||
// Copied from System.Net.AuthenticationSchemes.cs
|
||||
//
|
||||
// Author:
|
||||
// Atsushi Enomoto <atsushi@ximian.com>
|
||||
//
|
||||
// Copyright (C) 2005 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* AuthenticationSchemes.cs
|
||||
*
|
||||
* This code is derived from System.Net.AuthenticationSchemes.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Atsushi Enomoto <atsushi@ximian.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
|
||||
/// <summary>
|
||||
/// Contains the values of the schemes for authentication.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AuthenticationSchemes {
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that no authentication is allowed.
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Indicates digest authentication.
|
||||
/// </summary>
|
||||
Digest = 1,
|
||||
/// <summary>
|
||||
/// Indicates negotiating with the client to determine the authentication scheme.
|
||||
/// </summary>
|
||||
Negotiate = 2,
|
||||
/// <summary>
|
||||
/// Indicates NTLM authentication.
|
||||
/// </summary>
|
||||
Ntlm = 4,
|
||||
/// <summary>
|
||||
/// Indicates Windows authentication.
|
||||
/// </summary>
|
||||
IntegratedWindowsAuthentication = 6,
|
||||
/// <summary>
|
||||
/// Indicates basic authentication.
|
||||
/// </summary>
|
||||
Basic = 8,
|
||||
/// <summary>
|
||||
/// Indicates anonymous authentication.
|
||||
/// </summary>
|
||||
Anonymous = 0x8000,
|
||||
}
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the values of the schemes for authentication.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AuthenticationSchemes
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that no authentication is allowed.
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Indicates digest authentication.
|
||||
/// </summary>
|
||||
Digest = 1,
|
||||
/// <summary>
|
||||
/// Indicates basic authentication.
|
||||
/// </summary>
|
||||
Basic = 8,
|
||||
/// <summary>
|
||||
/// Indicates anonymous authentication.
|
||||
/// </summary>
|
||||
Anonymous = 0x8000,
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,41 @@
|
||||
//
|
||||
// EndPointListener.cs
|
||||
// Copied from System.Net.EndPointListener.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
// Copyright (c) 2012-2013 sta.blockhead
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* EndPointListener.cs
|
||||
*
|
||||
* This code is derived from System.Net.EndPointListener.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Gonzalo Paniagua Javier <gonzalo@novell.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
@ -40,411 +49,405 @@ using System.Threading;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal sealed class EndPointListener
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
List<ListenerPrefix> _all; // host = '+'
|
||||
X509Certificate2 _cert;
|
||||
IPEndPoint _endpoint;
|
||||
Dictionary<ListenerPrefix, HttpListener> _prefixes;
|
||||
bool _secure;
|
||||
Socket _socket;
|
||||
List<ListenerPrefix> _unhandled; // host = '*'
|
||||
Dictionary<HttpConnection, HttpConnection> _unregistered;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
public EndPointListener (
|
||||
IPAddress address,
|
||||
int port,
|
||||
bool secure,
|
||||
string certFolderPath,
|
||||
X509Certificate2 defaultCert
|
||||
)
|
||||
{
|
||||
if (secure) {
|
||||
_secure = secure;
|
||||
_cert = getCertificate (port, certFolderPath, defaultCert);
|
||||
if (_cert == null)
|
||||
throw new ArgumentException ("Server certificate not found.");
|
||||
}
|
||||
|
||||
_endpoint = new IPEndPoint (address, port);
|
||||
_socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
_socket.Bind (_endpoint);
|
||||
_socket.Listen (500);
|
||||
var args = new SocketAsyncEventArgs ();
|
||||
args.UserToken = this;
|
||||
args.Completed += onAccept;
|
||||
_socket.AcceptAsync (args);
|
||||
_prefixes = new Dictionary<ListenerPrefix, HttpListener> ();
|
||||
_unregistered = new Dictionary<HttpConnection, HttpConnection> ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static void addSpecial (List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (prefixes == null)
|
||||
return;
|
||||
|
||||
foreach (var p in prefixes)
|
||||
if (p.Path == prefix.Path) // TODO: code
|
||||
throw new HttpListenerException (400, "Prefix already in use.");
|
||||
|
||||
prefixes.Add (prefix);
|
||||
}
|
||||
|
||||
private void checkIfRemove ()
|
||||
{
|
||||
if (_prefixes.Count > 0)
|
||||
return;
|
||||
|
||||
if (_unhandled != null && _unhandled.Count > 0)
|
||||
return;
|
||||
|
||||
if (_all != null && _all.Count > 0)
|
||||
return;
|
||||
|
||||
EndPointManager.RemoveEndPoint (this, _endpoint);
|
||||
}
|
||||
|
||||
private static RSACryptoServiceProvider createRSAFromFile (string filename)
|
||||
{
|
||||
var rsa = new RSACryptoServiceProvider ();
|
||||
byte[] pvk = null;
|
||||
using (var fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
pvk = new byte [fs.Length];
|
||||
fs.Read (pvk, 0, pvk.Length);
|
||||
}
|
||||
|
||||
rsa.ImportCspBlob (pvk);
|
||||
return rsa;
|
||||
}
|
||||
|
||||
private static X509Certificate2 getCertificate (
|
||||
int port, string certFolderPath, X509Certificate2 defaultCert)
|
||||
{
|
||||
try {
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port));
|
||||
if (File.Exists (cer) && File.Exists (key))
|
||||
{
|
||||
var cert = new X509Certificate2 (cer);
|
||||
cert.PrivateKey = createRSAFromFile (key);
|
||||
|
||||
return cert;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
return defaultCert;
|
||||
}
|
||||
|
||||
private static HttpListener matchFromList (
|
||||
string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
if (list == null)
|
||||
return null;
|
||||
|
||||
HttpListener best_match = null;
|
||||
var best_length = -1;
|
||||
foreach (var p in list)
|
||||
{
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < best_length)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath))
|
||||
{
|
||||
best_length = ppath.Length;
|
||||
best_match = p.Listener;
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
|
||||
return best_match;
|
||||
}
|
||||
|
||||
private static void onAccept (object sender, EventArgs e)
|
||||
{
|
||||
var args = (SocketAsyncEventArgs) e;
|
||||
var epListener = (EndPointListener) args.UserToken;
|
||||
Socket accepted = null;
|
||||
if (args.SocketError == SocketError.Success)
|
||||
{
|
||||
accepted = args.AcceptSocket;
|
||||
args.AcceptSocket = null;
|
||||
}
|
||||
|
||||
try {
|
||||
epListener._socket.AcceptAsync (args);
|
||||
}
|
||||
catch {
|
||||
if (accepted != null)
|
||||
accepted.Close ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (accepted == null)
|
||||
return;
|
||||
|
||||
HttpConnection conn = null;
|
||||
try {
|
||||
conn = new HttpConnection (accepted, epListener, epListener._secure, epListener._cert);
|
||||
lock (((ICollection) epListener._unregistered).SyncRoot)
|
||||
{
|
||||
epListener._unregistered [conn] = conn;
|
||||
}
|
||||
|
||||
conn.BeginReadRequest ();
|
||||
}
|
||||
catch {
|
||||
if (conn != null)
|
||||
{
|
||||
conn.Close (true);
|
||||
return;
|
||||
}
|
||||
|
||||
accepted.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool removeSpecial (List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (prefixes == null)
|
||||
return false;
|
||||
|
||||
var count = prefixes.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (prefixes [i].Path == prefix.Path)
|
||||
{
|
||||
prefixes.RemoveAt (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private HttpListener searchListener (Uri uri, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
if (uri == null)
|
||||
return null;
|
||||
|
||||
var host = uri.Host;
|
||||
var port = uri.Port;
|
||||
var path = HttpUtility.UrlDecode (uri.AbsolutePath);
|
||||
var path_slash = path [path.Length - 1] == '/' ? path : path + "/";
|
||||
HttpListener best_match = null;
|
||||
var best_length = -1;
|
||||
if (host != null && host.Length > 0)
|
||||
{
|
||||
foreach (var p in _prefixes.Keys)
|
||||
{
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < best_length)
|
||||
continue;
|
||||
|
||||
if (p.Host != host || p.Port != port)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath) || path_slash.StartsWith (ppath))
|
||||
{
|
||||
best_length = ppath.Length;
|
||||
best_match = _prefixes [p];
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_length != -1)
|
||||
return best_match;
|
||||
}
|
||||
|
||||
var list = _unhandled;
|
||||
best_match = matchFromList (host, path, list, out prefix);
|
||||
if (path != path_slash && best_match == null)
|
||||
best_match = matchFromList (host, path_slash, list, out prefix);
|
||||
|
||||
if (best_match != null)
|
||||
return best_match;
|
||||
|
||||
list = _all;
|
||||
best_match = matchFromList (host, path, list, out prefix);
|
||||
if (path != path_slash && best_match == null)
|
||||
best_match = matchFromList (host, path_slash, list, out prefix);
|
||||
|
||||
if (best_match != null)
|
||||
return best_match;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal static bool CertificateExists (int port, string certFolderPath)
|
||||
{
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port));
|
||||
|
||||
return File.Exists (cer) && File.Exists (key);
|
||||
}
|
||||
|
||||
internal void RemoveConnection (HttpConnection connection)
|
||||
{
|
||||
lock (((ICollection) _unregistered).SyncRoot)
|
||||
{
|
||||
_unregistered.Remove (connection);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void AddPrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*")
|
||||
{
|
||||
do {
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
prefix.Listener = listener;
|
||||
addSpecial (future, prefix);
|
||||
} while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+")
|
||||
{
|
||||
do {
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
prefix.Listener = listener;
|
||||
addSpecial (future, prefix);
|
||||
} while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = _prefixes;
|
||||
if (prefs.ContainsKey (prefix))
|
||||
{
|
||||
HttpListener other = prefs [prefix];
|
||||
if (other != listener) // TODO: code.
|
||||
throw new HttpListenerException (400, "There's another listener for " + prefix);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2 [prefix] = listener;
|
||||
} while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
}
|
||||
|
||||
public bool BindContext (HttpListenerContext context)
|
||||
{
|
||||
var req = context.Request;
|
||||
ListenerPrefix prefix;
|
||||
var listener = searchListener (req.Url, out prefix);
|
||||
if (listener == null)
|
||||
return false;
|
||||
|
||||
context.Listener = listener;
|
||||
context.Connection.Prefix = prefix;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
_socket.Close ();
|
||||
lock (((ICollection) _unregistered).SyncRoot)
|
||||
{
|
||||
var copy = new Dictionary<HttpConnection, HttpConnection> (_unregistered);
|
||||
foreach (var conn in copy.Keys)
|
||||
conn.Close (true);
|
||||
|
||||
copy.Clear ();
|
||||
_unregistered.Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*")
|
||||
{
|
||||
do {
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
} while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+")
|
||||
{
|
||||
do {
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
} while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = _prefixes;
|
||||
if (!prefs.ContainsKey (prefix))
|
||||
break;
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2.Remove (prefix);
|
||||
} while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
|
||||
checkIfRemove ();
|
||||
}
|
||||
|
||||
public void UnbindContext (HttpListenerContext context)
|
||||
{
|
||||
if (context == null || context.Request == null)
|
||||
return;
|
||||
|
||||
context.Listener.UnregisterContext (context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
internal sealed class EndPointListener
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private List<ListenerPrefix> _all; // host = '+'
|
||||
private X509Certificate2 _cert;
|
||||
private IPEndPoint _endpoint;
|
||||
private Dictionary<ListenerPrefix, HttpListener> _prefixes;
|
||||
private bool _secure;
|
||||
private Socket _socket;
|
||||
private List<ListenerPrefix> _unhandled; // host = '*'
|
||||
private Dictionary<HttpConnection, HttpConnection> _unregistered;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
public EndPointListener (
|
||||
IPAddress address,
|
||||
int port,
|
||||
bool secure,
|
||||
string certFolderPath,
|
||||
X509Certificate2 defaultCert)
|
||||
{
|
||||
if (secure) {
|
||||
_secure = secure;
|
||||
_cert = getCertificate (port, certFolderPath, defaultCert);
|
||||
if (_cert == null)
|
||||
throw new ArgumentException ("Server certificate not found.");
|
||||
}
|
||||
|
||||
_endpoint = new IPEndPoint (address, port);
|
||||
_socket = new Socket (
|
||||
address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
_socket.Bind (_endpoint);
|
||||
_socket.Listen (500);
|
||||
var args = new SocketAsyncEventArgs ();
|
||||
args.UserToken = this;
|
||||
args.Completed += onAccept;
|
||||
_socket.AcceptAsync (args);
|
||||
_prefixes = new Dictionary<ListenerPrefix, HttpListener> ();
|
||||
_unregistered = new Dictionary<HttpConnection, HttpConnection> ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static void addSpecial (
|
||||
List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (prefixes == null)
|
||||
return;
|
||||
|
||||
foreach (var p in prefixes)
|
||||
if (p.Path == prefix.Path) // TODO: code
|
||||
throw new HttpListenerException (400, "Prefix already in use.");
|
||||
|
||||
prefixes.Add (prefix);
|
||||
}
|
||||
|
||||
private void checkIfRemove ()
|
||||
{
|
||||
if (_prefixes.Count > 0)
|
||||
return;
|
||||
|
||||
if (_unhandled != null && _unhandled.Count > 0)
|
||||
return;
|
||||
|
||||
if (_all != null && _all.Count > 0)
|
||||
return;
|
||||
|
||||
EndPointManager.RemoveEndPoint (this, _endpoint);
|
||||
}
|
||||
|
||||
private static RSACryptoServiceProvider createRSAFromFile (string filename)
|
||||
{
|
||||
var rsa = new RSACryptoServiceProvider ();
|
||||
byte[] pvk = null;
|
||||
using (var fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
|
||||
pvk = new byte [fs.Length];
|
||||
fs.Read (pvk, 0, pvk.Length);
|
||||
}
|
||||
|
||||
rsa.ImportCspBlob (pvk);
|
||||
return rsa;
|
||||
}
|
||||
|
||||
private static X509Certificate2 getCertificate (
|
||||
int port, string certFolderPath, X509Certificate2 defaultCert)
|
||||
{
|
||||
try {
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port));
|
||||
if (File.Exists (cer) && File.Exists (key)) {
|
||||
var cert = new X509Certificate2 (cer);
|
||||
cert.PrivateKey = createRSAFromFile (key);
|
||||
|
||||
return cert;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
return defaultCert;
|
||||
}
|
||||
|
||||
private static HttpListener matchFromList (
|
||||
string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
if (list == null)
|
||||
return null;
|
||||
|
||||
HttpListener bestMatch = null;
|
||||
var bestLength = -1;
|
||||
foreach (var p in list) {
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < bestLength)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath)) {
|
||||
bestLength = ppath.Length;
|
||||
bestMatch = p.Listener;
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
private static void onAccept (object sender, EventArgs e)
|
||||
{
|
||||
var args = (SocketAsyncEventArgs) e;
|
||||
var epListener = (EndPointListener) args.UserToken;
|
||||
Socket accepted = null;
|
||||
if (args.SocketError == SocketError.Success) {
|
||||
accepted = args.AcceptSocket;
|
||||
args.AcceptSocket = null;
|
||||
}
|
||||
|
||||
try {
|
||||
epListener._socket.AcceptAsync (args);
|
||||
}
|
||||
catch {
|
||||
if (accepted != null)
|
||||
accepted.Close ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (accepted == null)
|
||||
return;
|
||||
|
||||
HttpConnection conn = null;
|
||||
try {
|
||||
conn = new HttpConnection (
|
||||
accepted, epListener, epListener._secure, epListener._cert);
|
||||
lock (((ICollection) epListener._unregistered).SyncRoot) {
|
||||
epListener._unregistered [conn] = conn;
|
||||
}
|
||||
|
||||
conn.BeginReadRequest ();
|
||||
}
|
||||
catch {
|
||||
if (conn != null) {
|
||||
conn.Close (true);
|
||||
return;
|
||||
}
|
||||
|
||||
accepted.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool removeSpecial (
|
||||
List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (prefixes == null)
|
||||
return false;
|
||||
|
||||
var count = prefixes.Count;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (prefixes [i].Path == prefix.Path) {
|
||||
prefixes.RemoveAt (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private HttpListener searchListener (Uri uri, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
if (uri == null)
|
||||
return null;
|
||||
|
||||
var host = uri.Host;
|
||||
var port = uri.Port;
|
||||
var path = HttpUtility.UrlDecode (uri.AbsolutePath);
|
||||
var pathSlash = path [path.Length - 1] == '/' ? path : path + "/";
|
||||
HttpListener bestMatch = null;
|
||||
var bestLength = -1;
|
||||
if (host != null && host.Length > 0) {
|
||||
foreach (var p in _prefixes.Keys) {
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < bestLength)
|
||||
continue;
|
||||
|
||||
if (p.Host != host || p.Port != port)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath) || pathSlash.StartsWith (ppath)) {
|
||||
bestLength = ppath.Length;
|
||||
bestMatch = _prefixes [p];
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestLength != -1)
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
var list = _unhandled;
|
||||
bestMatch = matchFromList (host, path, list, out prefix);
|
||||
if (path != pathSlash && bestMatch == null)
|
||||
bestMatch = matchFromList (host, pathSlash, list, out prefix);
|
||||
|
||||
if (bestMatch != null)
|
||||
return bestMatch;
|
||||
|
||||
list = _all;
|
||||
bestMatch = matchFromList (host, path, list, out prefix);
|
||||
if (path != pathSlash && bestMatch == null)
|
||||
bestMatch = matchFromList (host, pathSlash, list, out prefix);
|
||||
|
||||
if (bestMatch != null)
|
||||
return bestMatch;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal static bool CertificateExists (int port, string certFolderPath)
|
||||
{
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port));
|
||||
|
||||
return File.Exists (cer) && File.Exists (key);
|
||||
}
|
||||
|
||||
internal void RemoveConnection (HttpConnection connection)
|
||||
{
|
||||
lock (((ICollection) _unregistered).SyncRoot)
|
||||
_unregistered.Remove (connection);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void AddPrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*") {
|
||||
do {
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
|
||||
prefix.Listener = listener;
|
||||
addSpecial (future, prefix);
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+") {
|
||||
do {
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
|
||||
prefix.Listener = listener;
|
||||
addSpecial (future, prefix);
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = _prefixes;
|
||||
if (prefs.ContainsKey (prefix)) {
|
||||
var other = prefs [prefix];
|
||||
if (other != listener) // TODO: code.
|
||||
throw new HttpListenerException (
|
||||
400, "There's another listener for " + prefix);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2 [prefix] = listener;
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
}
|
||||
|
||||
public bool BindContext (HttpListenerContext context)
|
||||
{
|
||||
var req = context.Request;
|
||||
ListenerPrefix prefix;
|
||||
var listener = searchListener (req.Url, out prefix);
|
||||
if (listener == null)
|
||||
return false;
|
||||
|
||||
context.Listener = listener;
|
||||
context.Connection.Prefix = prefix;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
_socket.Close ();
|
||||
lock (((ICollection) _unregistered).SyncRoot) {
|
||||
var copy = new Dictionary<HttpConnection, HttpConnection> (_unregistered);
|
||||
foreach (var conn in copy.Keys)
|
||||
conn.Close (true);
|
||||
|
||||
copy.Clear ();
|
||||
_unregistered.Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*") {
|
||||
do {
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+") {
|
||||
do {
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = _prefixes;
|
||||
if (!prefs.ContainsKey (prefix))
|
||||
break;
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2.Remove (prefix);
|
||||
}
|
||||
while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
|
||||
checkIfRemove ();
|
||||
}
|
||||
|
||||
public void UnbindContext (HttpListenerContext context)
|
||||
{
|
||||
if (context == null || context.Listener == null)
|
||||
return;
|
||||
|
||||
context.Listener.UnregisterContext (context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
81
websocket-sharp/Net/HttpBasicIdentity.cs
Normal file
81
websocket-sharp/Net/HttpBasicIdentity.cs
Normal file
@ -0,0 +1,81 @@
|
||||
#region License
|
||||
/*
|
||||
* HttpBasicIdentity.cs
|
||||
*
|
||||
* This code is derived from System.Net.HttpListenerBasicIdentity.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2014 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Gonzalo Paniagua Javier <gonzalo@novell.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the user name and password from an HTTP Basic authentication request.
|
||||
/// </summary>
|
||||
public class HttpBasicIdentity : GenericIdentity
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private string _password;
|
||||
|
||||
#endregion
|
||||
|
||||
#region internal Constructors
|
||||
|
||||
internal HttpBasicIdentity (string username, string password)
|
||||
: base (username, "Basic")
|
||||
{
|
||||
_password = password;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the password from an HTTP Basic authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the password.
|
||||
/// </value>
|
||||
public virtual string Password {
|
||||
get {
|
||||
return _password;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
185
websocket-sharp/Net/HttpDigestIdentity.cs
Normal file
185
websocket-sharp/Net/HttpDigestIdentity.cs
Normal file
@ -0,0 +1,185 @@
|
||||
#region License
|
||||
/*
|
||||
* HttpDigestIdentity.cs
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2014 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the user name and other authentication parameters from an HTTP Digest
|
||||
/// authentication request.
|
||||
/// </summary>
|
||||
public class HttpDigestIdentity : GenericIdentity
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private NameValueCollection _params;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal HttpDigestIdentity (NameValueCollection authParams)
|
||||
: base (authParams ["username"], "Digest")
|
||||
{
|
||||
_params = authParams;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the algorithm parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the algorithm parameter.
|
||||
/// </value>
|
||||
public string Algorithm {
|
||||
get {
|
||||
return _params ["algorithm"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cnonce parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the cnonce parameter.
|
||||
/// </value>
|
||||
public string Cnonce {
|
||||
get {
|
||||
return _params ["cnonce"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nc parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the nc parameter.
|
||||
/// </value>
|
||||
public string Nc {
|
||||
get {
|
||||
return _params ["nc"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nonce parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the nonce parameter.
|
||||
/// </value>
|
||||
public string Nonce {
|
||||
get {
|
||||
return _params ["nonce"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the opaque parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the opaque parameter.
|
||||
/// </value>
|
||||
public string Opaque {
|
||||
get {
|
||||
return _params ["opaque"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the qop parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the qop parameter.
|
||||
/// </value>
|
||||
public string Qop {
|
||||
get {
|
||||
return _params ["qop"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the realm parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the realm parameter.
|
||||
/// </value>
|
||||
public string Realm {
|
||||
get {
|
||||
return _params ["realm"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the response parameter.
|
||||
/// </value>
|
||||
public string Response {
|
||||
get {
|
||||
return _params ["response"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the uri parameter from an HTTP Digest authentication request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the uri parameter.
|
||||
/// </value>
|
||||
public string Uri {
|
||||
get {
|
||||
return _params ["uri"];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal bool IsValid (
|
||||
string password, string realm, string method, string entity)
|
||||
{
|
||||
var parameters = new NameValueCollection (_params);
|
||||
parameters ["password"] = password;
|
||||
parameters ["realm"] = realm;
|
||||
parameters ["method"] = method;
|
||||
parameters ["entity"] = entity;
|
||||
|
||||
return _params ["response"] == HttpUtility.CreateRequestDigest (parameters);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,33 +1,40 @@
|
||||
#region License
|
||||
//
|
||||
// HttpListenerContext.cs
|
||||
// Copied from System.Net.HttpListenerContext.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
// Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
/*
|
||||
* HttpListenerContext.cs
|
||||
*
|
||||
* This code is derived from System.Net.HttpListenerContext.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Gonzalo Paniagua Javier <gonzalo@novell.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
@ -37,182 +44,184 @@ using System.Security.Principal;
|
||||
using System.Text;
|
||||
using WebSocketSharp.Net.WebSockets;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the HTTP request and response information used by the
|
||||
/// <see cref="HttpListener"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The HttpListenerContext class cannot be inherited.
|
||||
/// </remarks>
|
||||
public sealed class HttpListenerContext
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the HTTP request and response objects used by the <see cref="HttpListener"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The HttpListenerContext class cannot be inherited.
|
||||
/// </remarks>
|
||||
public sealed class HttpListenerContext {
|
||||
private HttpConnection _connection;
|
||||
private string _error;
|
||||
private int _errorStatus;
|
||||
private HttpListenerRequest _request;
|
||||
private HttpListenerResponse _response;
|
||||
private IPrincipal _user;
|
||||
|
||||
#region Private Fields
|
||||
#endregion
|
||||
|
||||
private HttpConnection _connection;
|
||||
private string _error;
|
||||
private int _errorStatus;
|
||||
private HttpListenerRequest _request;
|
||||
private HttpListenerResponse _response;
|
||||
private IPrincipal _user;
|
||||
#region Internal Fields
|
||||
|
||||
#endregion
|
||||
internal HttpListener Listener;
|
||||
|
||||
#region Internal Fields
|
||||
#endregion
|
||||
|
||||
internal HttpListener Listener;
|
||||
#region Internal Constructors
|
||||
|
||||
#endregion
|
||||
internal HttpListenerContext (HttpConnection connection)
|
||||
{
|
||||
_connection = connection;
|
||||
_errorStatus = 400;
|
||||
_request = new HttpListenerRequest (this);
|
||||
_response = new HttpListenerResponse (this);
|
||||
}
|
||||
|
||||
#region Constructor
|
||||
#endregion
|
||||
|
||||
internal HttpListenerContext (HttpConnection connection)
|
||||
{
|
||||
_connection = connection;
|
||||
_errorStatus = 400;
|
||||
_request = new HttpListenerRequest (this);
|
||||
_response = new HttpListenerResponse (this);
|
||||
}
|
||||
#region Internal Properties
|
||||
|
||||
#endregion
|
||||
internal HttpConnection Connection {
|
||||
get {
|
||||
return _connection;
|
||||
}
|
||||
}
|
||||
|
||||
#region Internal Properties
|
||||
internal string ErrorMessage {
|
||||
get {
|
||||
return _error;
|
||||
}
|
||||
|
||||
internal HttpConnection Connection {
|
||||
get {
|
||||
return _connection;
|
||||
}
|
||||
}
|
||||
set {
|
||||
_error = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal string ErrorMessage {
|
||||
get {
|
||||
return _error;
|
||||
}
|
||||
internal int ErrorStatus {
|
||||
get {
|
||||
return _errorStatus;
|
||||
}
|
||||
|
||||
set {
|
||||
_error = value;
|
||||
}
|
||||
}
|
||||
set {
|
||||
_errorStatus = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal int ErrorStatus {
|
||||
get {
|
||||
return _errorStatus;
|
||||
}
|
||||
internal bool HaveError {
|
||||
get {
|
||||
return _error != null && _error.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
set {
|
||||
_errorStatus = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
internal bool HaveError {
|
||||
get {
|
||||
return _error != null;
|
||||
}
|
||||
}
|
||||
#region Public Properties
|
||||
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpListenerRequest"/> that contains the HTTP
|
||||
/// request information from a client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="HttpListenerRequest"/> that contains the HTTP request
|
||||
/// information.
|
||||
/// </value>
|
||||
public HttpListenerRequest Request {
|
||||
get {
|
||||
return _request;
|
||||
}
|
||||
}
|
||||
|
||||
#region Public Properties
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpListenerResponse"/> that contains the HTTP
|
||||
/// response information to send to the client in response to the client's
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="HttpListenerResponse"/> that contains the HTTP response
|
||||
/// information.
|
||||
/// </value>
|
||||
public HttpListenerResponse Response {
|
||||
get {
|
||||
return _response;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpListenerRequest"/> that contains the HTTP request from a client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="HttpListenerRequest"/> that contains the HTTP request objects.
|
||||
/// </value>
|
||||
public HttpListenerRequest Request {
|
||||
get {
|
||||
return _request;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information, and
|
||||
/// security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> contains the client information.
|
||||
/// </value>
|
||||
public IPrincipal User {
|
||||
get {
|
||||
return _user;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpListenerResponse"/> that contains the HTTP response to send to
|
||||
/// the client in response to the client's request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="HttpListenerResponse"/> that contains the HTTP response objects.
|
||||
/// </value>
|
||||
public HttpListenerResponse Response {
|
||||
get {
|
||||
return _response;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information and security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> contains the client information.
|
||||
/// </value>
|
||||
public IPrincipal User {
|
||||
get {
|
||||
return _user;
|
||||
}
|
||||
}
|
||||
#region Internal Methods
|
||||
|
||||
#endregion
|
||||
internal void SetUser (
|
||||
AuthenticationSchemes expectedScheme,
|
||||
string realm,
|
||||
Func<IIdentity, NetworkCredential> credentialsFinder)
|
||||
{
|
||||
var authRes = AuthenticationResponse.Parse (_request.Headers ["Authorization"]);
|
||||
if (authRes == null)
|
||||
return;
|
||||
|
||||
#region Internal Methods
|
||||
var identity = authRes.ToIdentity ();
|
||||
if (identity == null)
|
||||
return;
|
||||
|
||||
internal void ParseAuthentication (AuthenticationSchemes expectedSchemes)
|
||||
{
|
||||
if (expectedSchemes == AuthenticationSchemes.Anonymous)
|
||||
return;
|
||||
NetworkCredential credentials = null;
|
||||
try {
|
||||
credentials = credentialsFinder (identity);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
// TODO: Handle NTLM/Digest modes.
|
||||
var header = _request.Headers ["Authorization"];
|
||||
if (header == null || header.Length < 2)
|
||||
return;
|
||||
if (credentials == null)
|
||||
return;
|
||||
|
||||
var authData = header.Split (new char [] {' '}, 2);
|
||||
if (authData [0].ToLower () == "basic")
|
||||
_user = ParseBasicAuthentication (authData [1]);
|
||||
var valid = expectedScheme == AuthenticationSchemes.Basic
|
||||
? ((HttpBasicIdentity) identity).Password == credentials.Password
|
||||
: expectedScheme == AuthenticationSchemes.Digest
|
||||
? ((HttpDigestIdentity) identity).IsValid (
|
||||
credentials.Password, realm, _request.HttpMethod, null)
|
||||
: false;
|
||||
|
||||
// TODO: Throw if malformed -> 400 bad request.
|
||||
}
|
||||
if (valid)
|
||||
_user = new GenericPrincipal (identity, credentials.Roles);
|
||||
}
|
||||
|
||||
internal IPrincipal ParseBasicAuthentication (string authData)
|
||||
{
|
||||
try {
|
||||
// HTTP Basic Authentication data is a formatted Base64 string.
|
||||
var authString = Encoding.Default.GetString (Convert.FromBase64String (authData));
|
||||
#endregion
|
||||
|
||||
// The format is domain\username:password.
|
||||
// Domain is optional.
|
||||
#region Public Method
|
||||
|
||||
var pos = authString.IndexOf (':');
|
||||
var user = authString.Substring (0, pos);
|
||||
var password = authString.Substring (pos + 1);
|
||||
/// <summary>
|
||||
/// Accepts a WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="HttpListenerWebSocketContext"/> that contains a WebSocket
|
||||
/// connection request information.
|
||||
/// </returns>
|
||||
/// <param name="logger">
|
||||
/// A <see cref="Logger"/> that provides the logging functions used in the
|
||||
/// WebSocket attempts.
|
||||
/// </param>
|
||||
public HttpListenerWebSocketContext AcceptWebSocket (Logger logger)
|
||||
{
|
||||
return new HttpListenerWebSocketContext (this, logger);
|
||||
}
|
||||
|
||||
// Check if there is a domain.
|
||||
pos = user.IndexOf ('\\');
|
||||
if (pos > 0)
|
||||
user = user.Substring (pos + 1);
|
||||
|
||||
var identity = new System.Net.HttpListenerBasicIdentity (user, password);
|
||||
// TODO: What are the roles MS sets?
|
||||
return new GenericPrincipal (identity, new string [0]);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Method
|
||||
|
||||
/// <summary>
|
||||
/// Accepts a WebSocket connection by the <see cref="HttpListener"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="HttpListenerWebSocketContext"/> that contains a WebSocket connection.
|
||||
/// </returns>
|
||||
public HttpListenerWebSocketContext AcceptWebSocket ()
|
||||
{
|
||||
return new HttpListenerWebSocketContext (this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,222 +1,200 @@
|
||||
//
|
||||
// ListenerAsyncResult.cs
|
||||
// Copied from System.Net.ListenerAsyncResult.cs
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
//
|
||||
// Copyright (c) 2005 Ximian, Inc (http://www.ximian.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* ListenerAsyncResult.cs
|
||||
*
|
||||
* This code is derived from System.Net.ListenerAsyncResult.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Ximian, Inc. (http://www.ximian.com)
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Gonzalo Paniagua Javier <gonzalo@ximian.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal class ListenerAsyncResult : IAsyncResult
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
class ListenerAsyncResult : IAsyncResult
|
||||
{
|
||||
#region Private Static Field
|
||||
private AsyncCallback _callback;
|
||||
private bool _completed;
|
||||
private HttpListenerContext _context;
|
||||
private Exception _exception;
|
||||
private ManualResetEvent _waitHandle;
|
||||
private object _state;
|
||||
private object _sync;
|
||||
private bool _syncCompleted;
|
||||
|
||||
static WaitCallback InvokeCB = new WaitCallback (InvokeCallback);
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Internal Fields
|
||||
|
||||
#region Private Fields
|
||||
internal bool EndCalled;
|
||||
internal bool InGet;
|
||||
|
||||
AsyncCallback cb;
|
||||
bool completed;
|
||||
HttpListenerContext context;
|
||||
Exception exception;
|
||||
ListenerAsyncResult forward;
|
||||
ManualResetEvent handle;
|
||||
object locker;
|
||||
object state;
|
||||
bool synch;
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Public Constructors
|
||||
|
||||
#region Internal Fields
|
||||
public ListenerAsyncResult (AsyncCallback callback, object state)
|
||||
{
|
||||
_callback = callback;
|
||||
_state = state;
|
||||
_sync = new object ();
|
||||
}
|
||||
|
||||
internal bool EndCalled;
|
||||
internal bool InGet;
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Public Properties
|
||||
|
||||
#region Constructor
|
||||
public object AsyncState {
|
||||
get {
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
public ListenerAsyncResult (AsyncCallback cb, object state)
|
||||
{
|
||||
this.cb = cb;
|
||||
this.state = state;
|
||||
this.locker = new object();
|
||||
}
|
||||
public WaitHandle AsyncWaitHandle {
|
||||
get {
|
||||
lock (_sync)
|
||||
return _waitHandle ?? (_waitHandle = new ManualResetEvent (_completed));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
public bool CompletedSynchronously {
|
||||
get {
|
||||
return _syncCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
public bool IsCompleted {
|
||||
get {
|
||||
lock (_sync)
|
||||
return _completed;
|
||||
}
|
||||
}
|
||||
|
||||
public object AsyncState {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.AsyncState;
|
||||
#endregion
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
#region Private Methods
|
||||
|
||||
public WaitHandle AsyncWaitHandle {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.AsyncWaitHandle;
|
||||
private static void invokeCallback (object state)
|
||||
{
|
||||
try {
|
||||
var ares = (ListenerAsyncResult) state;
|
||||
ares._callback (ares);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
|
||||
lock (locker) {
|
||||
if (handle == null)
|
||||
handle = new ManualResetEvent (completed);
|
||||
}
|
||||
#endregion
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
#region Internal Methods
|
||||
|
||||
public bool CompletedSynchronously {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.CompletedSynchronously;
|
||||
internal void Complete (Exception exception)
|
||||
{
|
||||
_exception = InGet && (exception is ObjectDisposedException)
|
||||
? new HttpListenerException (500, "Listener closed")
|
||||
: exception;
|
||||
|
||||
return synch;
|
||||
}
|
||||
}
|
||||
lock (_sync) {
|
||||
_completed = true;
|
||||
if (_waitHandle != null)
|
||||
_waitHandle.Set ();
|
||||
|
||||
public bool IsCompleted {
|
||||
get {
|
||||
if (forward != null)
|
||||
return forward.IsCompleted;
|
||||
if (_callback != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (invokeCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
lock (locker) {
|
||||
return completed;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal void Complete (HttpListenerContext context)
|
||||
{
|
||||
Complete (context, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
internal void Complete (HttpListenerContext context, bool syncCompleted)
|
||||
{
|
||||
var listener = context.Listener;
|
||||
var scheme = listener.SelectAuthenticationScheme (context);
|
||||
if (scheme == AuthenticationSchemes.None) {
|
||||
context.Response.Close (HttpStatusCode.Forbidden);
|
||||
listener.BeginGetContext (this);
|
||||
|
||||
#region Private Method
|
||||
return;
|
||||
}
|
||||
|
||||
static void InvokeCallback (object o)
|
||||
{
|
||||
ListenerAsyncResult ares = (ListenerAsyncResult) o;
|
||||
if (ares.forward != null) {
|
||||
InvokeCallback (ares.forward);
|
||||
return;
|
||||
}
|
||||
var header = context.Request.Headers ["Authorization"];
|
||||
if (scheme == AuthenticationSchemes.Basic &&
|
||||
(header == null ||
|
||||
!header.StartsWith ("basic", StringComparison.OrdinalIgnoreCase))) {
|
||||
context.Response.CloseWithAuthChallenge (
|
||||
HttpUtility.CreateBasicAuthChallenge (listener.Realm));
|
||||
listener.BeginGetContext (this);
|
||||
|
||||
try {
|
||||
ares.cb (ares);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
if (scheme == AuthenticationSchemes.Digest &&
|
||||
(header == null ||
|
||||
!header.StartsWith ("digest", StringComparison.OrdinalIgnoreCase))) {
|
||||
context.Response.CloseWithAuthChallenge (
|
||||
HttpUtility.CreateDigestAuthChallenge (listener.Realm));
|
||||
listener.BeginGetContext (this);
|
||||
|
||||
#region Internal Methods
|
||||
return;
|
||||
}
|
||||
|
||||
internal void Complete (Exception exc)
|
||||
{
|
||||
if (forward != null) {
|
||||
forward.Complete (exc);
|
||||
return;
|
||||
}
|
||||
_context = context;
|
||||
_syncCompleted = syncCompleted;
|
||||
|
||||
exception = exc;
|
||||
if (InGet && (exc is ObjectDisposedException))
|
||||
exception = new HttpListenerException (500, "Listener closed");
|
||||
lock (_sync) {
|
||||
_completed = true;
|
||||
if (_waitHandle != null)
|
||||
_waitHandle.Set ();
|
||||
|
||||
lock (locker) {
|
||||
completed = true;
|
||||
if (handle != null)
|
||||
handle.Set ();
|
||||
if (_callback != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (invokeCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
if (cb != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (InvokeCB, this);
|
||||
}
|
||||
}
|
||||
internal HttpListenerContext GetContext ()
|
||||
{
|
||||
if (_exception != null)
|
||||
throw _exception;
|
||||
|
||||
internal void Complete (HttpListenerContext context)
|
||||
{
|
||||
Complete (context, false);
|
||||
}
|
||||
return _context;
|
||||
}
|
||||
|
||||
internal void Complete (HttpListenerContext context, bool synch)
|
||||
{
|
||||
if (forward != null) {
|
||||
forward.Complete (context, synch);
|
||||
return;
|
||||
}
|
||||
|
||||
this.synch = synch;
|
||||
this.context = context;
|
||||
lock (locker) {
|
||||
AuthenticationSchemes schemes = context.Listener.SelectAuthenticationScheme (context);
|
||||
if ((schemes == AuthenticationSchemes.Basic || context.Listener.AuthenticationSchemes == AuthenticationSchemes.Negotiate) && context.Request.Headers ["Authorization"] == null) {
|
||||
context.Response.StatusCode = 401;
|
||||
context.Response.Headers ["WWW-Authenticate"] = schemes + " realm=\"" + context.Listener.Realm + "\"";
|
||||
context.Response.OutputStream.Close ();
|
||||
IAsyncResult ares = context.Listener.BeginGetContext (cb, state);
|
||||
this.forward = (ListenerAsyncResult) ares;
|
||||
lock (forward.locker) {
|
||||
if (handle != null)
|
||||
forward.handle = handle;
|
||||
}
|
||||
|
||||
ListenerAsyncResult next = forward;
|
||||
for (int i = 0; next.forward != null; i++) {
|
||||
if (i > 20)
|
||||
Complete (new HttpListenerException (400, "Too many authentication errors"));
|
||||
|
||||
next = next.forward;
|
||||
}
|
||||
} else {
|
||||
completed = true;
|
||||
if (handle != null)
|
||||
handle.Set ();
|
||||
|
||||
if (cb != null)
|
||||
ThreadPool.UnsafeQueueUserWorkItem (InvokeCB, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal HttpListenerContext GetContext ()
|
||||
{
|
||||
if (forward != null)
|
||||
return forward.GetContext ();
|
||||
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
179
websocket-sharp/Net/NetworkCredential.cs
Normal file
179
websocket-sharp/Net/NetworkCredential.cs
Normal file
@ -0,0 +1,179 @@
|
||||
#region License
|
||||
/*
|
||||
* NetworkCredential.cs
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2014 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the credentials for HTTP authentication (Basic/Digest).
|
||||
/// </summary>
|
||||
public class NetworkCredential
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private string _domain;
|
||||
private string _password;
|
||||
private string [] _roles;
|
||||
private string _username;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NetworkCredential"/> class
|
||||
/// with the specified user name and password.
|
||||
/// </summary>
|
||||
/// <param name="username">
|
||||
/// A <see cref="string"/> that represents the user name associated with the
|
||||
/// credentials.
|
||||
/// </param>
|
||||
/// <param name="password">
|
||||
/// A <see cref="string"/> that represents the password for the user name
|
||||
/// associated with the credentials.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="username"/> is <see langword="null"/> or empty.
|
||||
/// </exception>
|
||||
public NetworkCredential (string username, string password)
|
||||
: this (username, password, null, new string [0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NetworkCredential"/> class
|
||||
/// with the specified user name, password, domain, and roles.
|
||||
/// </summary>
|
||||
/// <param name="username">
|
||||
/// A <see cref="string"/> that represents the user name associated with the
|
||||
/// credentials.
|
||||
/// </param>
|
||||
/// <param name="password">
|
||||
/// A <see cref="string"/> that represents the password for the user name
|
||||
/// associated with the credentials.
|
||||
/// </param>
|
||||
/// <param name="domain">
|
||||
/// A <see cref="string"/> that represents the name of the user domain
|
||||
/// associated with the credentials.
|
||||
/// </param>
|
||||
/// <param name="roles">
|
||||
/// An array of <see cref="string"/> that contains the role names to which
|
||||
/// the user associated with the credentials belongs if any.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="username"/> is <see langword="null"/> or empty.
|
||||
/// </exception>
|
||||
public NetworkCredential (
|
||||
string username, string password, string domain, params string [] roles)
|
||||
{
|
||||
if (username == null || username.Length == 0)
|
||||
throw new ArgumentException ("Must not be null or empty.", "username");
|
||||
|
||||
_username = username;
|
||||
_password = password;
|
||||
_domain = domain;
|
||||
_roles = roles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the user domain associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the name of the user domain
|
||||
/// associated with the credentials.
|
||||
/// </value>
|
||||
public string Domain {
|
||||
get {
|
||||
return _domain ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_domain = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the password for the user name associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the password for the user name
|
||||
/// associated with the credentials.
|
||||
/// </value>
|
||||
public string Password {
|
||||
get {
|
||||
return _password ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_password = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role names to which the user associated with the credentials
|
||||
/// belongs.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An array of <see cref="string"/> that contains the role names to which
|
||||
/// the user associated with the credentials belongs.
|
||||
/// </value>
|
||||
public string [] Roles {
|
||||
get {
|
||||
return _roles;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_roles = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user name associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that represents the user name associated with the
|
||||
/// credentials.
|
||||
/// </value>
|
||||
public string UserName {
|
||||
get {
|
||||
return _username;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_username = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,253 +1,281 @@
|
||||
//
|
||||
// RequestStream.cs
|
||||
// Copied from System.Net.RequestStream.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* RequestStream.cs
|
||||
*
|
||||
* This code is derived from System.Net.RequestStream.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Gonzalo Paniagua Javier <gonzalo@novell.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal class RequestStream : Stream
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
class RequestStream : Stream {
|
||||
private byte [] _buffer;
|
||||
private bool _disposed;
|
||||
private int _length;
|
||||
private int _offset;
|
||||
private long _remainingBody;
|
||||
private Stream _stream;
|
||||
|
||||
#region Fields
|
||||
#endregion
|
||||
|
||||
byte [] buffer;
|
||||
bool disposed;
|
||||
int length;
|
||||
int offset;
|
||||
long remaining_body;
|
||||
Stream stream;
|
||||
#region Internal Constructors
|
||||
|
||||
#endregion
|
||||
internal RequestStream (
|
||||
Stream stream, byte [] buffer, int offset, int length)
|
||||
: this (stream, buffer, offset, length, -1)
|
||||
{
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
internal RequestStream (
|
||||
Stream stream, byte [] buffer, int offset, int length, long contentlength)
|
||||
{
|
||||
_stream = stream;
|
||||
_buffer = buffer;
|
||||
_offset = offset;
|
||||
_length = length;
|
||||
_remainingBody = contentlength;
|
||||
}
|
||||
|
||||
internal RequestStream (Stream stream, byte [] buffer, int offset, int length)
|
||||
: this (stream, buffer, offset, length, -1)
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
internal RequestStream (Stream stream, byte [] buffer, int offset, int length, long contentlength)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.buffer = buffer;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
this.remaining_body = contentlength;
|
||||
}
|
||||
#region Public Properties
|
||||
|
||||
#endregion
|
||||
public override bool CanRead {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
public override bool CanSeek {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead {
|
||||
get { return true; }
|
||||
}
|
||||
public override bool CanWrite {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get { return false; }
|
||||
}
|
||||
public override long Length {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite {
|
||||
get { return false; }
|
||||
}
|
||||
public override long Position {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override long Length {
|
||||
get { throw new NotSupportedException (); }
|
||||
}
|
||||
set {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position {
|
||||
get { throw new NotSupportedException (); }
|
||||
set { throw new NotSupportedException (); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Private Methods
|
||||
|
||||
#region Private Method
|
||||
// Returns 0 if we can keep reading from the base stream,
|
||||
// > 0 if we read something from the buffer.
|
||||
// -1 if we had a content length set and we finished reading that many bytes.
|
||||
private int fillFromBuffer (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException ("buffer");
|
||||
|
||||
// Returns 0 if we can keep reading from the base stream,
|
||||
// > 0 if we read something from the buffer.
|
||||
// -1 if we had a content length set and we finished reading that many bytes.
|
||||
int FillFromBuffer (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException ("buffer");
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException ("offset", "Less than zero.");
|
||||
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException ("offset", "< 0");
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException ("count", "Less than zero.");
|
||||
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException ("count", "< 0");
|
||||
var len = buffer.Length;
|
||||
if (offset > len)
|
||||
throw new ArgumentException ("'offset' is greater than 'buffer' size.");
|
||||
|
||||
int len = buffer.Length;
|
||||
if (offset > len)
|
||||
throw new ArgumentException ("Destination offset is beyond array size.");
|
||||
if (offset > len - count)
|
||||
throw new ArgumentException ("Reading would overrun 'buffer'.");
|
||||
|
||||
if (offset > len - count)
|
||||
throw new ArgumentException ("Reading would overrun buffer.");
|
||||
if (_remainingBody == 0)
|
||||
return -1;
|
||||
|
||||
if (this.remaining_body == 0)
|
||||
return -1;
|
||||
if (_length == 0)
|
||||
return 0;
|
||||
|
||||
if (this.length == 0)
|
||||
return 0;
|
||||
var size = _length < count ? _length : count;
|
||||
if (_remainingBody > 0 && _remainingBody < size)
|
||||
size = (int) _remainingBody;
|
||||
|
||||
int size = Math.Min (this.length, count);
|
||||
if (this.remaining_body > 0)
|
||||
size = (int) Math.Min (size, this.remaining_body);
|
||||
var remainingBuffer = _buffer.Length - _offset;
|
||||
if (remainingBuffer < size)
|
||||
size = remainingBuffer;
|
||||
|
||||
if (this.offset > this.buffer.Length - size) {
|
||||
size = Math.Min (size, this.buffer.Length - this.offset);
|
||||
}
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
Buffer.BlockCopy (_buffer, _offset, buffer, offset, size);
|
||||
_offset += size;
|
||||
_length -= size;
|
||||
if (_remainingBody > 0)
|
||||
_remainingBody -= size;
|
||||
|
||||
Buffer.BlockCopy (this.buffer, this.offset, buffer, offset, size);
|
||||
this.offset += size;
|
||||
this.length -= size;
|
||||
if (this.remaining_body > 0)
|
||||
remaining_body -= size;
|
||||
return size;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Public Methods
|
||||
|
||||
#region Public Methods
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer, int offset, int count, AsyncCallback cback, object state)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
var read = fillFromBuffer (buffer, offset, count);
|
||||
if (read > 0 || read == -1) {
|
||||
var ares = new HttpStreamAsyncResult ();
|
||||
ares.Buffer = buffer;
|
||||
ares.Offset = offset;
|
||||
ares.Count = count;
|
||||
ares.Callback = callback;
|
||||
ares.State = state;
|
||||
ares.SyncRead = read;
|
||||
ares.Complete ();
|
||||
|
||||
int nread = FillFromBuffer (buffer, offset, count);
|
||||
if (nread > 0 || nread == -1) {
|
||||
var ares = new HttpStreamAsyncResult ();
|
||||
ares.Buffer = buffer;
|
||||
ares.Offset = offset;
|
||||
ares.Count = count;
|
||||
ares.Callback = cback;
|
||||
ares.State = state;
|
||||
ares.SyncRead = nread;
|
||||
ares.Complete ();
|
||||
return ares;
|
||||
}
|
||||
return ares;
|
||||
}
|
||||
|
||||
// Avoid reading past the end of the request to allow
|
||||
// for HTTP pipelining
|
||||
if (remaining_body >= 0 && count > remaining_body)
|
||||
count = (int) Math.Min (Int32.MaxValue, remaining_body);
|
||||
// Avoid reading past the end of the request to allow for HTTP pipelining.
|
||||
if (_remainingBody >= 0 && _remainingBody < count)
|
||||
count = (int) _remainingBody;
|
||||
|
||||
return stream.BeginRead (buffer, offset, count, cback, state);
|
||||
}
|
||||
return _stream.BeginRead (buffer, offset, count, callback, state);
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer, int offset, int count, AsyncCallback cback, object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer, int offset, int count, AsyncCallback callback, object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
disposed = true;
|
||||
}
|
||||
public override void Close ()
|
||||
{
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public override int EndRead (IAsyncResult ares)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
public override int EndRead (IAsyncResult asyncResult)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
if (ares == null)
|
||||
throw new ArgumentNullException ("ares");
|
||||
if (asyncResult == null)
|
||||
throw new ArgumentNullException ("asyncResult");
|
||||
|
||||
if (ares is HttpStreamAsyncResult) {
|
||||
var ares_ = (HttpStreamAsyncResult) ares;
|
||||
if (!ares.IsCompleted)
|
||||
ares.AsyncWaitHandle.WaitOne ();
|
||||
if (asyncResult is HttpStreamAsyncResult) {
|
||||
var ares = (HttpStreamAsyncResult) asyncResult;
|
||||
if (!ares.IsCompleted)
|
||||
ares.AsyncWaitHandle.WaitOne ();
|
||||
|
||||
return ares_.SyncRead;
|
||||
}
|
||||
return ares.SyncRead;
|
||||
}
|
||||
|
||||
// Close on exception?
|
||||
int nread = stream.EndRead (ares);
|
||||
if (remaining_body > 0 && nread > 0)
|
||||
remaining_body -= nread;
|
||||
// Close on exception?
|
||||
var read = _stream.EndRead (asyncResult);
|
||||
if (read > 0 && _remainingBody > 0)
|
||||
_remainingBody -= read;
|
||||
|
||||
return nread;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override void EndWrite (IAsyncResult async_result)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override void EndWrite (IAsyncResult asyncResult)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read ([In,Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType () .ToString ());
|
||||
public override int Read (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
// Call FillFromBuffer to check for buffer boundaries even when remaining_body is 0
|
||||
int nread = FillFromBuffer (buffer, offset, count);
|
||||
if (nread == -1) { // No more bytes available (Content-Length)
|
||||
return 0;
|
||||
} else if (nread > 0) {
|
||||
return nread;
|
||||
}
|
||||
// Call fillFromBuffer to check for buffer boundaries even when
|
||||
// _remainingBody is 0.
|
||||
var read = fillFromBuffer (buffer, offset, count);
|
||||
if (read == -1) // No more bytes available (Content-Length).
|
||||
return 0;
|
||||
else if (read > 0)
|
||||
return read;
|
||||
|
||||
nread = stream.Read (buffer, offset, count);
|
||||
if (nread > 0 && remaining_body > 0)
|
||||
remaining_body -= nread;
|
||||
read = _stream.Read (buffer, offset, count);
|
||||
if (read > 0 && _remainingBody > 0)
|
||||
_remainingBody -= read;
|
||||
|
||||
return nread;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override void Write (byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -1,284 +1,322 @@
|
||||
//
|
||||
// ResponseStream.cs
|
||||
// Copied from System.Net.ResponseStream.cs
|
||||
//
|
||||
// Author:
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
#region License
|
||||
/*
|
||||
* ResponseStream.cs
|
||||
*
|
||||
* This code is derived from System.Net.ResponseStream.cs of Mono
|
||||
* (http://www.mono-project.com).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
* Copyright (c) 2012-2014 sta.blockhead
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#region Authors
|
||||
/*
|
||||
* Authors:
|
||||
* Gonzalo Paniagua Javier <gonzalo@novell.com>
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
// FIXME: Does this buffer the response until Close?
|
||||
// Update: we send a single packet for the first non-chunked Write
|
||||
// What happens when we set content-length to X and write X-1 bytes then close?
|
||||
// what if we don't set content-length at all?
|
||||
internal class ResponseStream : Stream
|
||||
{
|
||||
#region Private Static Fields
|
||||
|
||||
// FIXME: Does this buffer the response until Close?
|
||||
// Update: we send a single packet for the first non-chunked Write
|
||||
// What happens when we set content-length to X and write X-1 bytes then close?
|
||||
// what if we don't set content-length at all?
|
||||
class ResponseStream : Stream {
|
||||
private static byte [] _crlf = new byte [] { 13, 10 };
|
||||
|
||||
#region Private Static Field
|
||||
#endregion
|
||||
|
||||
static byte [] crlf = new byte [] { 13, 10 };
|
||||
#region Private Fields
|
||||
|
||||
#endregion
|
||||
private bool _disposed;
|
||||
private bool _ignoreErrors;
|
||||
private HttpListenerResponse _response;
|
||||
private Stream _stream;
|
||||
private bool _trailerSent;
|
||||
|
||||
#region Private Fields
|
||||
#endregion
|
||||
|
||||
bool disposed;
|
||||
bool ignore_errors;
|
||||
HttpListenerResponse response;
|
||||
Stream stream;
|
||||
bool trailer_sent;
|
||||
#region Internal Constructors
|
||||
|
||||
#endregion
|
||||
internal ResponseStream (
|
||||
Stream stream, HttpListenerResponse response, bool ignoreErrors)
|
||||
{
|
||||
_stream = stream;
|
||||
_response = response;
|
||||
_ignoreErrors = ignoreErrors;
|
||||
}
|
||||
|
||||
#region Constructor
|
||||
#endregion
|
||||
|
||||
internal ResponseStream (System.IO.Stream stream, HttpListenerResponse response, bool ignore_errors)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.response = response;
|
||||
this.ignore_errors = ignore_errors;
|
||||
}
|
||||
#region Public Properties
|
||||
|
||||
#endregion
|
||||
public override bool CanRead {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
public override bool CanSeek {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead {
|
||||
get { return false; }
|
||||
}
|
||||
public override bool CanWrite {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get { return false; }
|
||||
}
|
||||
public override long Length {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite {
|
||||
get { return true; }
|
||||
}
|
||||
public override long Position {
|
||||
get {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override long Length {
|
||||
get { throw new NotSupportedException (); }
|
||||
}
|
||||
set {
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position {
|
||||
get { throw new NotSupportedException (); }
|
||||
set { throw new NotSupportedException (); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Private Methods
|
||||
|
||||
#region Private Methods
|
||||
private static byte [] getChunkSizeBytes (int size, bool final)
|
||||
{
|
||||
return Encoding.ASCII.GetBytes (
|
||||
String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : ""));
|
||||
}
|
||||
|
||||
static byte [] GetChunkSizeBytes (int size, bool final)
|
||||
{
|
||||
string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "");
|
||||
return Encoding.ASCII.GetBytes (str);
|
||||
}
|
||||
private MemoryStream getHeaders (bool closing)
|
||||
{
|
||||
if (_response.HeadersSent)
|
||||
return null;
|
||||
|
||||
MemoryStream GetHeaders (bool closing)
|
||||
{
|
||||
if (response.HeadersSent)
|
||||
return null;
|
||||
var stream = new MemoryStream ();
|
||||
_response.SendHeaders (closing, stream);
|
||||
|
||||
MemoryStream ms = new MemoryStream ();
|
||||
response.SendHeaders (closing, ms);
|
||||
return stream;
|
||||
}
|
||||
|
||||
return ms;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Internal Methods
|
||||
|
||||
#region Internal Method
|
||||
internal void InternalWrite (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (_ignoreErrors) {
|
||||
try {
|
||||
_stream.Write (buffer, offset, count);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
else {
|
||||
_stream.Write (buffer, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
internal void InternalWrite (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (ignore_errors) {
|
||||
try {
|
||||
stream.Write (buffer, offset, count);
|
||||
} catch {
|
||||
}
|
||||
} else {
|
||||
stream.Write (buffer, offset, count);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Public Methods
|
||||
|
||||
#region Public Methods
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginRead (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback cback,
|
||||
object state)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
public override IAsyncResult BeginWrite (
|
||||
byte [] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
AsyncCallback cback,
|
||||
object state)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
var stream = getHeaders (false);
|
||||
var chunked = _response.SendChunked;
|
||||
byte [] bytes = null;
|
||||
if (stream != null) {
|
||||
var start = stream.Position;
|
||||
stream.Position = stream.Length;
|
||||
if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
byte [] bytes = null;
|
||||
MemoryStream ms = GetHeaders (false);
|
||||
bool chunked = response.SendChunked;
|
||||
if (ms != null) {
|
||||
long start = ms.Position;
|
||||
ms.Position = ms.Length;
|
||||
if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
ms.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
ms.Write (buffer, offset, count);
|
||||
buffer = ms.GetBuffer ();
|
||||
offset = (int) start;
|
||||
count = (int) (ms.Position - start);
|
||||
} else if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
stream.Write (buffer, offset, count);
|
||||
buffer = stream.GetBuffer ();
|
||||
offset = (int) start;
|
||||
count = (int) (stream.Position - start);
|
||||
}
|
||||
else if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
return stream.BeginWrite (buffer, offset, count, cback, state);
|
||||
}
|
||||
return _stream.BeginWrite (buffer, offset, count, callback, state);
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
if (disposed == false) {
|
||||
disposed = true;
|
||||
byte [] bytes = null;
|
||||
MemoryStream ms = GetHeaders (true);
|
||||
bool chunked = response.SendChunked;
|
||||
if (ms != null) {
|
||||
long start = ms.Position;
|
||||
if (chunked && !trailer_sent) {
|
||||
bytes = GetChunkSizeBytes (0, true);
|
||||
ms.Position = ms.Length;
|
||||
ms.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
|
||||
trailer_sent = true;
|
||||
} else if (chunked && !trailer_sent) {
|
||||
bytes = GetChunkSizeBytes (0, true);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
trailer_sent = true;
|
||||
}
|
||||
public override void Close ()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
response.Close ();
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
|
||||
public override int EndRead (IAsyncResult ares)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
var stream = getHeaders (true);
|
||||
var chunked = _response.SendChunked;
|
||||
byte [] bytes = null;
|
||||
if (stream != null) {
|
||||
var start = stream.Position;
|
||||
if (chunked && !_trailerSent) {
|
||||
bytes = getChunkSizeBytes (0, true);
|
||||
stream.Position = stream.Length;
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public override void EndWrite (IAsyncResult ares)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
InternalWrite (
|
||||
stream.GetBuffer (), (int) start, (int) (stream.Length - start));
|
||||
_trailerSent = true;
|
||||
}
|
||||
else if (chunked && !_trailerSent) {
|
||||
bytes = getChunkSizeBytes (0, true);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
_trailerSent = true;
|
||||
}
|
||||
|
||||
if (ignore_errors) {
|
||||
try {
|
||||
stream.EndWrite (ares);
|
||||
if (response.SendChunked)
|
||||
stream.Write (crlf, 0, 2);
|
||||
} catch {
|
||||
}
|
||||
} else {
|
||||
stream.EndWrite (ares);
|
||||
if (response.SendChunked)
|
||||
stream.Write (crlf, 0, 2);
|
||||
}
|
||||
}
|
||||
_response.Close ();
|
||||
}
|
||||
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
public override int EndRead (IAsyncResult asyncResult)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
public override int Read ([In,Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
public override void EndWrite (IAsyncResult asyncResult)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
Action<IAsyncResult> endWrite = ares => {
|
||||
_stream.EndWrite (ares);
|
||||
if (_response.SendChunked)
|
||||
_stream.Write (_crlf, 0, 2);
|
||||
};
|
||||
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
if (_ignoreErrors) {
|
||||
try {
|
||||
endWrite (asyncResult);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
else {
|
||||
endWrite (asyncResult);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
public override void Flush ()
|
||||
{
|
||||
}
|
||||
|
||||
byte [] bytes = null;
|
||||
MemoryStream ms = GetHeaders (false);
|
||||
bool chunked = response.SendChunked;
|
||||
if (ms != null) {
|
||||
long start = ms.Position; // After the possible preamble for the encoding
|
||||
ms.Position = ms.Length;
|
||||
if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
ms.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
public override int Read (byte [] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
int new_count = Math.Min (count, 16384 - (int) ms.Position + (int) start);
|
||||
ms.Write (buffer, offset, new_count);
|
||||
count -= new_count;
|
||||
offset += new_count;
|
||||
InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
|
||||
ms.SetLength (0);
|
||||
ms.Capacity = 0; // 'dispose' the buffer in ms.
|
||||
} else if (chunked) {
|
||||
bytes = GetChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
InternalWrite (buffer, offset, count);
|
||||
public override void SetLength (long value)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
if (chunked)
|
||||
InternalWrite (crlf, 0, 2);
|
||||
}
|
||||
public override void Write (byte [] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
|
||||
#endregion
|
||||
}
|
||||
var stream = getHeaders (false);
|
||||
var chunked = _response.SendChunked;
|
||||
byte [] bytes = null;
|
||||
if (stream != null) {
|
||||
// After the possible preamble for the encoding.
|
||||
var start = stream.Position;
|
||||
stream.Position = stream.Length;
|
||||
if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
stream.Write (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
var newCount = Math.Min (
|
||||
count, 16384 - (int) stream.Position + (int) start);
|
||||
stream.Write (buffer, offset, newCount);
|
||||
count -= newCount;
|
||||
offset += newCount;
|
||||
InternalWrite (
|
||||
stream.GetBuffer (), (int) start, (int) (stream.Length - start));
|
||||
stream.SetLength (0);
|
||||
stream.Capacity = 0; // 'dispose' the buffer in stream.
|
||||
}
|
||||
else if (chunked) {
|
||||
bytes = getChunkSizeBytes (count, false);
|
||||
InternalWrite (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
InternalWrite (buffer, offset, count);
|
||||
|
||||
if (chunked)
|
||||
InternalWrite (_crlf, 0, 2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
@ -34,7 +34,8 @@ using System.Security.Principal;
|
||||
namespace WebSocketSharp.Net.WebSockets
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the WebSocket connection request objects received by the <see cref="HttpListener"/>.
|
||||
/// Provides access to the WebSocket connection request information received by
|
||||
/// the <see cref="HttpListener"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
@ -50,11 +51,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal HttpListenerWebSocketContext (HttpListenerContext context)
|
||||
internal HttpListenerWebSocketContext (
|
||||
HttpListenerContext context, Logger logger)
|
||||
{
|
||||
_context = context;
|
||||
_stream = WsStream.CreateServerStream (context);
|
||||
_websocket = new WebSocket (this);
|
||||
_websocket = new WebSocket (this, logger ?? new Logger ());
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -72,10 +74,11 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies used in the WebSocket opening handshake.
|
||||
/// Gets the cookies used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the cookies.
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the
|
||||
/// cookies.
|
||||
/// </value>
|
||||
public override CookieCollection CookieCollection {
|
||||
get {
|
||||
@ -84,10 +87,10 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP headers used in the WebSocket opening handshake.
|
||||
/// Gets the HTTP headers used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Collections.Specialized.NameValueCollection"/> that contains the HTTP headers.
|
||||
/// A <see cref="NameValueCollection"/> that contains the HTTP headers.
|
||||
/// </value>
|
||||
public override NameValueCollection Headers {
|
||||
get {
|
||||
@ -96,10 +99,11 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Host header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Host header field used in the WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Host header field.
|
||||
/// A <see cref="string"/> that represents the value of the Host header field.
|
||||
/// </value>
|
||||
public override string Host {
|
||||
get {
|
||||
@ -120,10 +124,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the client connected from the local computer.
|
||||
/// Gets a value indicating whether the client connected from the local
|
||||
/// computer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsLocal {
|
||||
get {
|
||||
@ -135,7 +141,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets a value indicating whether the WebSocket connection is secured.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsSecureConnection {
|
||||
get {
|
||||
@ -144,10 +151,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the request is a WebSocket connection request.
|
||||
/// Gets a value indicating whether the request is a WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsWebSocketRequest {
|
||||
get {
|
||||
@ -156,10 +165,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Origin header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Origin header field used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Origin header field.
|
||||
/// A <see cref="string"/> that represents the value of the Origin header
|
||||
/// field.
|
||||
/// </value>
|
||||
public override string Origin {
|
||||
get {
|
||||
@ -171,19 +182,22 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the absolute path of the requested WebSocket URI.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the absolute path of the requested WebSocket URI.
|
||||
/// A <see cref="string"/> that represents the absolute path of the requested
|
||||
/// WebSocket URI.
|
||||
/// </value>
|
||||
public override string Path {
|
||||
get {
|
||||
return RequestUri.GetAbsolutePath ();
|
||||
return _context.Request.Url.GetAbsolutePath ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of query string variables used in the WebSocket opening handshake.
|
||||
/// Gets the collection of query string variables used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query string variables.
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query
|
||||
/// string variables.
|
||||
/// </value>
|
||||
public override NameValueCollection QueryString {
|
||||
get {
|
||||
@ -195,22 +209,26 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the WebSocket URI requested by the client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="Uri"/> that contains the WebSocket URI.
|
||||
/// A <see cref="Uri"/> that represents the WebSocket URI requested by the
|
||||
/// client.
|
||||
/// </value>
|
||||
public override Uri RequestUri {
|
||||
get {
|
||||
return _context.Request.RawUrl.ToUri ();
|
||||
return _context.Request.Url;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketKey property provides a part of the information used by the server to prove that it received a valid WebSocket opening handshake.
|
||||
/// This property provides a part of the information used by the server to
|
||||
/// prove that it received a valid WebSocket connection request.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Key header field.
|
||||
/// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key
|
||||
/// header field.
|
||||
/// </value>
|
||||
public override string SecWebSocketKey {
|
||||
get {
|
||||
@ -219,13 +237,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the WebSocket opening handshake.
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketProtocols property indicates the subprotocols of the WebSocket connection.
|
||||
/// This property represents the subprotocols of the WebSocket connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// An IEnumerable<string> that contains the values of the Sec-WebSocket-Protocol header field.
|
||||
/// An IEnumerable<string> that contains the values of the
|
||||
/// Sec-WebSocket-Protocol header field.
|
||||
/// </value>
|
||||
public override IEnumerable<string> SecWebSocketProtocols {
|
||||
get {
|
||||
@ -234,13 +254,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketVersion property indicates the WebSocket protocol version of the connection.
|
||||
/// This property represents the WebSocket protocol version of the connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Version header field.
|
||||
/// A <see cref="string"/> that represents the value of the
|
||||
/// Sec-WebSocket-Version header field.
|
||||
/// </value>
|
||||
public override string SecWebSocketVersion {
|
||||
get {
|
||||
@ -252,7 +274,7 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the server endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the server endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint.
|
||||
/// </value>
|
||||
public override System.Net.IPEndPoint ServerEndPoint {
|
||||
get {
|
||||
@ -261,10 +283,11 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information and security roles).
|
||||
/// Gets the client information (identity, authentication information and
|
||||
/// security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> that contains the client information.
|
||||
/// A <see cref="IPrincipal"/> that represents the client information.
|
||||
/// </value>
|
||||
public override IPrincipal User {
|
||||
get {
|
||||
@ -276,7 +299,7 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the client endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the client endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint.
|
||||
/// </value>
|
||||
public override System.Net.IPEndPoint UserEndPoint {
|
||||
get {
|
||||
@ -285,7 +308,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WebSocket instance used for two-way communication between client and server.
|
||||
/// Gets the WebSocket instance used for two-way communication between client
|
||||
/// and server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.WebSocket"/>.
|
||||
@ -305,15 +329,22 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
_context.Connection.Close (true);
|
||||
}
|
||||
|
||||
internal void Close (HttpStatusCode code)
|
||||
{
|
||||
_context.Response.Close (code);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="string"/> that represents the current <see cref="HttpListenerWebSocketContext"/>.
|
||||
/// Returns a <see cref="string"/> that represents the current
|
||||
/// <see cref="HttpListenerWebSocketContext"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="string"/> that represents the current <see cref="HttpListenerWebSocketContext"/>.
|
||||
/// A <see cref="string"/> that represents the current
|
||||
/// <see cref="HttpListenerWebSocketContext"/>.
|
||||
/// </returns>
|
||||
public override string ToString ()
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
@ -36,7 +36,8 @@ using System.Security.Principal;
|
||||
namespace WebSocketSharp.Net.WebSockets
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the WebSocket connection request objects received by the <see cref="TcpListener"/>.
|
||||
/// Provides access to the WebSocket connection request information received by
|
||||
/// the <see cref="TcpListener"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
@ -49,19 +50,22 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
private HandshakeRequest _request;
|
||||
private bool _secure;
|
||||
private WsStream _stream;
|
||||
private Uri _uri;
|
||||
private IPrincipal _user;
|
||||
private WebSocket _websocket;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal TcpListenerWebSocketContext (TcpClient client, bool secure, X509Certificate cert)
|
||||
internal TcpListenerWebSocketContext (
|
||||
TcpClient client, X509Certificate cert, bool secure, Logger logger)
|
||||
{
|
||||
_client = client;
|
||||
_secure = secure;
|
||||
_stream = WsStream.CreateServerStream (client, secure, cert);
|
||||
_stream = WsStream.CreateServerStream (client, cert, secure);
|
||||
_request = HandshakeRequest.Parse (_stream.ReadHandshake ());
|
||||
_websocket = new WebSocket (this);
|
||||
_websocket = new WebSocket (this, logger);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -79,25 +83,23 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies used in the WebSocket opening handshake.
|
||||
/// Gets the cookies used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="CookieCollection"/> that contains the cookies.
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the
|
||||
/// cookies.
|
||||
/// </value>
|
||||
public override CookieCollection CookieCollection {
|
||||
get {
|
||||
if (_cookies == null)
|
||||
_cookies = _request.Cookies;
|
||||
|
||||
return _cookies;
|
||||
return _cookies ?? (_cookies = _request.Cookies);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP headers used in the WebSocket opening handshake.
|
||||
/// Gets the HTTP headers used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Collections.Specialized.NameValueCollection"/> that contains the HTTP headers.
|
||||
/// A <see cref="NameValueCollection"/> that contains the HTTP headers.
|
||||
/// </value>
|
||||
public override NameValueCollection Headers {
|
||||
get {
|
||||
@ -106,10 +108,11 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Host header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Host header field used in the WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Host header field.
|
||||
/// A <see cref="string"/> that represents the value of the Host header field.
|
||||
/// </value>
|
||||
public override string Host {
|
||||
get {
|
||||
@ -123,20 +126,19 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// <value>
|
||||
/// <c>true</c> if the client is authenticated; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
/// <exception cref="NotImplementedException">
|
||||
/// This property is not implemented.
|
||||
/// </exception>
|
||||
public override bool IsAuthenticated {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
return _user != null && _user.Identity.IsAuthenticated;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the client connected from the local computer.
|
||||
/// Gets a value indicating whether the client connected from the local
|
||||
/// computer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsLocal {
|
||||
get {
|
||||
@ -148,7 +150,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets a value indicating whether the WebSocket connection is secured.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsSecureConnection {
|
||||
get {
|
||||
@ -157,10 +160,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the request is a WebSocket connection request.
|
||||
/// Gets a value indicating whether the request is a WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public override bool IsWebSocketRequest {
|
||||
get {
|
||||
@ -169,10 +174,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Origin header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Origin header field used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Origin header field.
|
||||
/// A <see cref="string"/> that represents the value of the Origin header
|
||||
/// field.
|
||||
/// </value>
|
||||
public override string Origin {
|
||||
get {
|
||||
@ -184,7 +191,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the absolute path of the requested WebSocket URI.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the absolute path of the requested WebSocket URI.
|
||||
/// A <see cref="string"/> that represents the absolute path of the requested
|
||||
/// WebSocket URI.
|
||||
/// </value>
|
||||
public override string Path {
|
||||
get {
|
||||
@ -193,10 +201,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of query string variables used in the WebSocket opening handshake.
|
||||
/// Gets the collection of query string variables used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query string variables.
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query
|
||||
/// string variables.
|
||||
/// </value>
|
||||
public override NameValueCollection QueryString {
|
||||
get {
|
||||
@ -208,22 +218,26 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the WebSocket URI requested by the client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="Uri"/> that contains the WebSocket URI.
|
||||
/// A <see cref="Uri"/> that represents the WebSocket URI requested by the
|
||||
/// client.
|
||||
/// </value>
|
||||
public override Uri RequestUri {
|
||||
get {
|
||||
return _request.RequestUri;
|
||||
return _uri ?? (_uri = createRequestUri ());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketKey property provides a part of the information used by the server to prove that it received a valid WebSocket opening handshake.
|
||||
/// This property provides a part of the information used by the server to
|
||||
/// prove that it received a valid WebSocket connection request.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Key header field.
|
||||
/// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key
|
||||
/// header field.
|
||||
/// </value>
|
||||
public override string SecWebSocketKey {
|
||||
get {
|
||||
@ -232,13 +246,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the WebSocket opening handshake.
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property indicates the subprotocols of the WebSocket connection.
|
||||
/// This property represents the subprotocols of the WebSocket connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// An IEnumerable<string> that contains the values of the Sec-WebSocket-Protocol header field.
|
||||
/// An IEnumerable<string> that contains the values of the
|
||||
/// Sec-WebSocket-Protocol header field.
|
||||
/// </value>
|
||||
public override IEnumerable<string> SecWebSocketProtocols {
|
||||
get {
|
||||
@ -247,13 +263,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketVersion property indicates the WebSocket protocol version of the connection.
|
||||
/// This property represents the WebSocket protocol version of the connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Version header field.
|
||||
/// A <see cref="string"/> that represents the value of the
|
||||
/// Sec-WebSocket-Version header field.
|
||||
/// </value>
|
||||
public override string SecWebSocketVersion {
|
||||
get {
|
||||
@ -265,7 +283,7 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the server endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the server endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint.
|
||||
/// </value>
|
||||
public override System.Net.IPEndPoint ServerEndPoint {
|
||||
get {
|
||||
@ -274,17 +292,15 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information and security roles).
|
||||
/// Gets the client information (identity, authentication information and
|
||||
/// security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> that contains the client information.
|
||||
/// A <see cref="IPrincipal"/> that represents the client information.
|
||||
/// </value>
|
||||
/// <exception cref="NotImplementedException">
|
||||
/// This property is not implemented.
|
||||
/// </exception>
|
||||
public override IPrincipal User {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
return _user;
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,7 +308,7 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the client endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the client endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint.
|
||||
/// </value>
|
||||
public override System.Net.IPEndPoint UserEndPoint {
|
||||
get {
|
||||
@ -301,7 +317,8 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WebSocket instance used for two-way communication between client and server.
|
||||
/// Gets the WebSocket instance used for two-way communication between client
|
||||
/// and server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.WebSocket"/>.
|
||||
@ -314,6 +331,22 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private Uri createRequestUri ()
|
||||
{
|
||||
var scheme = _secure ? "wss" : "ws";
|
||||
var host = _request.Headers ["Host"];
|
||||
var rawUri = _request.RequestUri;
|
||||
var path = rawUri.IsAbsoluteUri
|
||||
? rawUri.PathAndQuery
|
||||
: HttpUtility.UrlDecode (_request.RawUrl);
|
||||
|
||||
return String.Format ("{0}://{1}{2}", scheme, host, path).ToUri ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
internal void Close ()
|
||||
@ -322,15 +355,64 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
_client.Close ();
|
||||
}
|
||||
|
||||
internal void Close (HttpStatusCode code)
|
||||
{
|
||||
_websocket.Close (HandshakeResponse.CreateCloseResponse (code));
|
||||
}
|
||||
|
||||
internal void SendAuthChallenge (string challenge)
|
||||
{
|
||||
var res = new HandshakeResponse (HttpStatusCode.Unauthorized);
|
||||
res.Headers ["WWW-Authenticate"] = challenge;
|
||||
_stream.WriteHandshake (res);
|
||||
_request = HandshakeRequest.Parse (_stream.ReadHandshake ());
|
||||
}
|
||||
|
||||
internal void SetUser (
|
||||
AuthenticationSchemes expectedScheme,
|
||||
string realm,
|
||||
Func<IIdentity, NetworkCredential> credentialsFinder)
|
||||
{
|
||||
var authRes = _request.AuthResponse;
|
||||
if (authRes == null)
|
||||
return;
|
||||
|
||||
var identity = authRes.ToIdentity ();
|
||||
if (identity == null)
|
||||
return;
|
||||
|
||||
NetworkCredential credentials = null;
|
||||
try {
|
||||
credentials = credentialsFinder (identity);
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
if (credentials == null)
|
||||
return;
|
||||
|
||||
var valid = expectedScheme == AuthenticationSchemes.Basic
|
||||
? ((HttpBasicIdentity) identity).Password == credentials.Password
|
||||
: expectedScheme == AuthenticationSchemes.Digest
|
||||
? ((HttpDigestIdentity) identity).IsValid (
|
||||
credentials.Password, realm, _request.HttpMethod, null)
|
||||
: false;
|
||||
|
||||
if (valid)
|
||||
_user = new GenericPrincipal (identity, credentials.Roles);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="string"/> that represents the current <see cref="TcpListenerWebSocketContext"/>.
|
||||
/// Returns a <see cref="string"/> that represents the current
|
||||
/// <see cref="TcpListenerWebSocketContext"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="string"/> that represents the current <see cref="TcpListenerWebSocketContext"/>.
|
||||
/// A <see cref="string"/> that represents the current
|
||||
/// <see cref="TcpListenerWebSocketContext"/>.
|
||||
/// </returns>
|
||||
public override string ToString ()
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2013 sta.blockhead
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
@ -34,7 +34,7 @@ using System.Security.Principal;
|
||||
namespace WebSocketSharp.Net.WebSockets
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the WebSocket connection request objects.
|
||||
/// Provides access to the WebSocket connection request information.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The WebSocketContext class is an abstract class.
|
||||
@ -55,26 +55,28 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies used in the WebSocket opening handshake.
|
||||
/// Gets the cookies used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the cookies.
|
||||
/// A <see cref="WebSocketSharp.Net.CookieCollection"/> that contains the
|
||||
/// cookies.
|
||||
/// </value>
|
||||
public abstract CookieCollection CookieCollection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP headers used in the WebSocket opening handshake.
|
||||
/// Gets the HTTP headers used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Collections.Specialized.NameValueCollection"/> that contains the HTTP headers.
|
||||
/// A <see cref="NameValueCollection"/> that contains the HTTP headers.
|
||||
/// </value>
|
||||
public abstract NameValueCollection Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Host header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Host header field used in the WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Host header field.
|
||||
/// A <see cref="string"/> that represents the value of the Host header field.
|
||||
/// </value>
|
||||
public abstract string Host { get; }
|
||||
|
||||
@ -87,10 +89,12 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
public abstract bool IsAuthenticated { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the client connected from the local computer.
|
||||
/// Gets a value indicating whether the client connected from the local
|
||||
/// computer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the client connected from the local computer; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public abstract bool IsLocal { get; }
|
||||
|
||||
@ -98,23 +102,28 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets a value indicating whether the WebSocket connection is secured.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the WebSocket connection is secured; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public abstract bool IsSecureConnection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the request is a WebSocket connection request.
|
||||
/// Gets a value indicating whether the request is a WebSocket connection
|
||||
/// request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the request is a WebSocket connection request; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public abstract bool IsWebSocketRequest { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Origin header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Origin header field used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Origin header field.
|
||||
/// A <see cref="string"/> that represents the value of the Origin header
|
||||
/// field.
|
||||
/// </value>
|
||||
public abstract string Origin { get; }
|
||||
|
||||
@ -122,15 +131,18 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the absolute path of the requested WebSocket URI.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the absolute path of the requested WebSocket URI.
|
||||
/// A <see cref="string"/> that represents the absolute path of the requested
|
||||
/// WebSocket URI.
|
||||
/// </value>
|
||||
public abstract string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of query string variables used in the WebSocket opening handshake.
|
||||
/// Gets the collection of query string variables used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query string variables.
|
||||
/// A <see cref="NameValueCollection"/> that contains the collection of query
|
||||
/// string variables.
|
||||
/// </value>
|
||||
public abstract NameValueCollection QueryString { get; }
|
||||
|
||||
@ -138,40 +150,47 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the WebSocket URI requested by the client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="Uri"/> that contains the WebSocket URI.
|
||||
/// A <see cref="Uri"/> that represents the WebSocket URI.
|
||||
/// </value>
|
||||
public abstract Uri RequestUri { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Key header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketKey property provides a part of the information used by the server to prove that it received a valid WebSocket opening handshake.
|
||||
/// This property provides a part of the information used by the server to
|
||||
/// prove that it received a valid WebSocket connection request.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Key header field.
|
||||
/// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key
|
||||
/// header field.
|
||||
/// </value>
|
||||
public abstract string SecWebSocketKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the WebSocket opening handshake.
|
||||
/// Gets the values of the Sec-WebSocket-Protocol header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketProtocols property indicates the subprotocols of the WebSocket connection.
|
||||
/// This property represents the subprotocols of the WebSocket connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// An IEnumerable<string> that contains the values of the Sec-WebSocket-Protocol header field.
|
||||
/// An IEnumerable<string> that contains the values of the
|
||||
/// Sec-WebSocket-Protocol header field.
|
||||
/// </value>
|
||||
public abstract IEnumerable<string> SecWebSocketProtocols { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the WebSocket opening handshake.
|
||||
/// Gets the value of the Sec-WebSocket-Version header field used in the
|
||||
/// WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The SecWebSocketVersion property indicates the WebSocket protocol version of the connection.
|
||||
/// This property represents the WebSocket protocol version of the connection.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the value of the Sec-WebSocket-Version header field.
|
||||
/// A <see cref="string"/> that represents the value of the
|
||||
/// Sec-WebSocket-Version header field.
|
||||
/// </value>
|
||||
public abstract string SecWebSocketVersion { get; }
|
||||
|
||||
@ -179,15 +198,16 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the server endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the server endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint.
|
||||
/// </value>
|
||||
public abstract System.Net.IPEndPoint ServerEndPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client information (identity, authentication information and security roles).
|
||||
/// Gets the client information (identity, authentication information and
|
||||
/// security roles).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="IPrincipal"/> that contains the client information.
|
||||
/// A <see cref="IPrincipal"/> that represents the client information.
|
||||
/// </value>
|
||||
public abstract IPrincipal User { get; }
|
||||
|
||||
@ -195,12 +215,13 @@ namespace WebSocketSharp.Net.WebSockets
|
||||
/// Gets the client endpoint as an IP address and a port number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that contains the client endpoint.
|
||||
/// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint.
|
||||
/// </value>
|
||||
public abstract System.Net.IPEndPoint UserEndPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WebSocket instance used for two-way communication between client and server.
|
||||
/// Gets the WebSocket instance used for two-way communication between client
|
||||
/// and server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketSharp.WebSocket"/>.
|
||||
|
@ -7,7 +7,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
|
||||
@ -17,7 +17,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
|
||||
@ -40,16 +40,19 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using WebSocketSharp.Net;
|
||||
using WebSocketSharp.Net.WebSockets;
|
||||
|
||||
namespace WebSocketSharp.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a simple HTTP server that allows to accept the WebSocket connection requests.
|
||||
/// Provides a simple HTTP server that allows to accept the WebSocket
|
||||
/// connection requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The HttpServer instance can provide the multi WebSocket services.
|
||||
/// The HttpServer class can provide the multi WebSocket services.
|
||||
/// </remarks>
|
||||
public class HttpServer
|
||||
{
|
||||
@ -71,8 +74,8 @@ namespace WebSocketSharp.Server
|
||||
#region Public Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpServer"/> class that listens for
|
||||
/// incoming requests on port 80.
|
||||
/// Initializes a new instance of the <see cref="HttpServer"/> class that
|
||||
/// listens for incoming requests on port 80.
|
||||
/// </summary>
|
||||
public HttpServer ()
|
||||
: this (80)
|
||||
@ -80,8 +83,8 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpServer"/> class that listens for
|
||||
/// incoming requests on the specified <paramref name="port"/>.
|
||||
/// Initializes a new instance of the <see cref="HttpServer"/> class that
|
||||
/// listens for incoming requests on the specified <paramref name="port"/>.
|
||||
/// </summary>
|
||||
/// <param name="port">
|
||||
/// An <see cref="int"/> that contains a port number.
|
||||
@ -95,8 +98,9 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpServer"/> class that listens for
|
||||
/// incoming requests on the specified <paramref name="port"/> and <paramref name="secure"/>.
|
||||
/// Initializes a new instance of the <see cref="HttpServer"/> class that
|
||||
/// listens for incoming requests on the specified <paramref name="port"/>
|
||||
/// and <paramref name="secure"/>.
|
||||
/// </summary>
|
||||
/// <param name="port">
|
||||
/// An <see cref="int"/> that contains a port number.
|
||||
@ -114,16 +118,28 @@ namespace WebSocketSharp.Server
|
||||
public HttpServer (int port, bool secure)
|
||||
{
|
||||
if (!port.IsPortNumber ())
|
||||
throw new ArgumentOutOfRangeException ("port", "Must be between 1 and 65535: " + port);
|
||||
throw new ArgumentOutOfRangeException (
|
||||
"port", "Must be between 1 and 65535: " + port);
|
||||
|
||||
if ((port == 80 && secure) || (port == 443 && !secure))
|
||||
throw new ArgumentException (String.Format (
|
||||
"Invalid pair of 'port' and 'secure': {0}, {1}", port, secure));
|
||||
throw new ArgumentException (
|
||||
String.Format (
|
||||
"Invalid pair of 'port' and 'secure': {0}, {1}", port, secure));
|
||||
|
||||
_port = port;
|
||||
_secure = secure;
|
||||
_listener = new HttpListener ();
|
||||
_logger = new Logger ();
|
||||
_serviceHosts = new WebSocketServiceHostManager (_logger);
|
||||
_state = ServerState.READY;
|
||||
_sync = new object ();
|
||||
|
||||
init ();
|
||||
var os = Environment.OSVersion;
|
||||
if (os.Platform != PlatformID.Unix && os.Platform != PlatformID.MacOSX)
|
||||
_windows = true;
|
||||
|
||||
var prefix = String.Format ("http{0}://*:{1}/", _secure ? "s" : "", _port);
|
||||
_listener.Prefixes.Add (prefix);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -131,7 +147,29 @@ namespace WebSocketSharp.Server
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the certificate used to authenticate the server on the secure connection.
|
||||
/// Gets or sets the scheme used to authenticate the clients.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// One of the <see cref="WebSocketSharp.Net.AuthenticationSchemes"/> values
|
||||
/// that indicates the scheme used to authenticate the clients. The default
|
||||
/// value is <see cref="WebSocketSharp.Net.AuthenticationSchemes.Anonymous"/>.
|
||||
/// </value>
|
||||
public AuthenticationSchemes AuthenticationSchemes {
|
||||
get {
|
||||
return _listener.AuthenticationSchemes;
|
||||
}
|
||||
|
||||
set {
|
||||
if (!canSet ("AuthenticationSchemes"))
|
||||
return;
|
||||
|
||||
_listener.AuthenticationSchemes = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the certificate used to authenticate the server on the
|
||||
/// secure connection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="X509Certificate2"/> used to authenticate the server.
|
||||
@ -142,16 +180,12 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
set {
|
||||
if (_state == ServerState.START || _state == ServerState.SHUTDOWN)
|
||||
{
|
||||
_logger.Error (
|
||||
"The value of Certificate property cannot be changed because the server has already been started.");
|
||||
|
||||
if (!canSet ("Certificate"))
|
||||
return;
|
||||
}
|
||||
|
||||
if (EndPointListener.CertificateExists (_port, _listener.CertificateFolderPath))
|
||||
_logger.Warn ("The server certificate associated with the port number already exists.");
|
||||
_logger.Warn (
|
||||
"The server certificate associated with the port number already exists.");
|
||||
|
||||
_listener.DefaultCertificate = value;
|
||||
}
|
||||
@ -173,7 +207,8 @@ namespace WebSocketSharp.Server
|
||||
/// Gets a value indicating whether the server provides secure connection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the server provides secure connection; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the server provides secure connection; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsSecure {
|
||||
get {
|
||||
@ -182,12 +217,12 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the server cleans up the inactive WebSocket sessions
|
||||
/// periodically.
|
||||
/// Gets or sets a value indicating whether the server cleans up the inactive
|
||||
/// WebSocket sessions periodically.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the server cleans up the inactive WebSocket sessions every 60 seconds;
|
||||
/// otherwise, <c>false</c>. The default value is <c>true</c>.
|
||||
/// <c>true</c> if the server cleans up the inactive WebSocket sessions every
|
||||
/// 60 seconds; otherwise, <c>false</c>. The default value is <c>true</c>.
|
||||
/// </value>
|
||||
public bool KeepClean {
|
||||
get {
|
||||
@ -203,9 +238,9 @@ namespace WebSocketSharp.Server
|
||||
/// Gets the logging functions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default logging level is the <see cref="LogLevel.ERROR"/>.
|
||||
/// If you want to change the current logging level, you set the <c>Log.Level</c> property
|
||||
/// to one of the <see cref="LogLevel"/> values which you want.
|
||||
/// The default logging level is the <see cref="LogLevel.ERROR"/>. If you
|
||||
/// change the current logging level, you set the <c>Log.Level</c> property
|
||||
/// to any of the <see cref="LogLevel"/> values.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="Logger"/> that provides the logging functions.
|
||||
@ -228,6 +263,27 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the realm associated with the
|
||||
/// <see cref="HttpServer"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the name of the realm.
|
||||
/// The default value is <c>SECRET AREA</c>.
|
||||
/// </value>
|
||||
public string Realm {
|
||||
get {
|
||||
return _listener.Realm;
|
||||
}
|
||||
|
||||
set {
|
||||
if (!canSet ("Realm"))
|
||||
return;
|
||||
|
||||
_listener.Realm = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the document root path of server.
|
||||
/// </summary>
|
||||
@ -243,23 +299,42 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
set {
|
||||
if (_state == ServerState.START || _state == ServerState.SHUTDOWN)
|
||||
{
|
||||
_logger.Error (
|
||||
"The value of RootPath property cannot be changed because the server has already been started.");
|
||||
|
||||
if (!canSet ("RootPath"))
|
||||
return;
|
||||
}
|
||||
|
||||
_rootPath = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the functions for the WebSocket services that the server provides.
|
||||
/// Gets or sets the delegate called to find the credentials for an identity
|
||||
/// used to authenticate a client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketServiceHostManager"/> that manages the WebSocket services.
|
||||
/// A Func<<see cref="IIdentity"/>, <see cref="NetworkCredential"/>>
|
||||
/// delegate that references the method(s) used to find the credentials. The
|
||||
/// default value is a function that only returns <see langword="null"/>.
|
||||
/// </value>
|
||||
public Func<IIdentity, NetworkCredential> UserCredentialsFinder {
|
||||
get {
|
||||
return _listener.UserCredentialsFinder;
|
||||
}
|
||||
|
||||
set {
|
||||
if (!canSet ("UserCredentialsFinder"))
|
||||
return;
|
||||
|
||||
_listener.UserCredentialsFinder = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the functions for the WebSocket services provided by the
|
||||
/// <see cref="HttpServer"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketServiceHostManager"/> that manages the WebSocket
|
||||
/// services.
|
||||
/// </value>
|
||||
public WebSocketServiceHostManager WebSocketServices {
|
||||
get {
|
||||
@ -322,8 +397,7 @@ namespace WebSocketSharp.Server
|
||||
|
||||
private void abort ()
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
if (!IsListening)
|
||||
return;
|
||||
|
||||
@ -331,12 +405,136 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
_serviceHosts.Stop (
|
||||
((ushort) CloseStatusCode.SERVER_ERROR).ToByteArrayInternally (ByteOrder.BIG), true);
|
||||
((ushort) CloseStatusCode.SERVER_ERROR).ToByteArrayInternally (ByteOrder.BIG),
|
||||
true);
|
||||
_listener.Abort ();
|
||||
|
||||
_state = ServerState.STOP;
|
||||
}
|
||||
|
||||
private void acceptHttpRequest (HttpListenerContext context)
|
||||
{
|
||||
var args = new HttpRequestEventArgs (context);
|
||||
var method = context.Request.HttpMethod;
|
||||
if (method == "GET" && OnGet != null) {
|
||||
OnGet (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "HEAD" && OnHead != null) {
|
||||
OnHead (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "POST" && OnPost != null) {
|
||||
OnPost (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "PUT" && OnPut != null) {
|
||||
OnPut (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "DELETE" && OnDelete != null) {
|
||||
OnDelete (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "OPTIONS" && OnOptions != null) {
|
||||
OnOptions (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "TRACE" && OnTrace != null) {
|
||||
OnTrace (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "CONNECT" && OnConnect != null) {
|
||||
OnConnect (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "PATCH" && OnPatch != null) {
|
||||
OnPatch (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
context.Response.Close (HttpStatusCode.NotImplemented);
|
||||
}
|
||||
|
||||
private void acceptRequestAsync (HttpListenerContext context)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem (
|
||||
state => {
|
||||
try {
|
||||
var authScheme = _listener.SelectAuthenticationScheme (context);
|
||||
if (authScheme != AuthenticationSchemes.Anonymous &&
|
||||
!authenticateRequest (authScheme, context))
|
||||
return;
|
||||
|
||||
if (context.Request.IsUpgradeTo ("websocket")) {
|
||||
acceptWebSocketRequest (context.AcceptWebSocket (_logger));
|
||||
return;
|
||||
}
|
||||
|
||||
acceptHttpRequest (context);
|
||||
context.Response.Close ();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.Fatal (ex.ToString ());
|
||||
context.Connection.Close (true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void acceptWebSocketRequest (HttpListenerWebSocketContext context)
|
||||
{
|
||||
var path = context.Path;
|
||||
|
||||
WebSocketServiceHost host;
|
||||
if (path == null ||
|
||||
!_serviceHosts.TryGetServiceHostInternally (path, out host)) {
|
||||
context.Close (HttpStatusCode.NotImplemented);
|
||||
return;
|
||||
}
|
||||
|
||||
host.StartSession (context);
|
||||
}
|
||||
|
||||
private bool authenticateRequest (
|
||||
AuthenticationSchemes scheme, HttpListenerContext context)
|
||||
{
|
||||
if (context.Request.IsAuthenticated)
|
||||
return true;
|
||||
|
||||
if (scheme == AuthenticationSchemes.Basic)
|
||||
context.Response.CloseWithAuthChallenge (
|
||||
HttpUtility.CreateBasicAuthChallenge (_listener.Realm));
|
||||
else if (scheme == AuthenticationSchemes.Digest)
|
||||
context.Response.CloseWithAuthChallenge (
|
||||
HttpUtility.CreateDigestAuthChallenge (_listener.Realm));
|
||||
else
|
||||
context.Response.Close (HttpStatusCode.Forbidden);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool canSet (string property)
|
||||
{
|
||||
if (_state == ServerState.START || _state == ServerState.SHUTDOWN) {
|
||||
_logger.Error (
|
||||
String.Format (
|
||||
"The '{0}' property cannot set a value because the server has already been started.",
|
||||
property));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string checkIfCertExists ()
|
||||
{
|
||||
return _secure &&
|
||||
@ -346,137 +544,17 @@ namespace WebSocketSharp.Server
|
||||
: null;
|
||||
}
|
||||
|
||||
private void init ()
|
||||
{
|
||||
_listener = new HttpListener ();
|
||||
_logger = new Logger ();
|
||||
_serviceHosts = new WebSocketServiceHostManager (_logger);
|
||||
_state = ServerState.READY;
|
||||
_sync = new object ();
|
||||
|
||||
_windows = false;
|
||||
var os = Environment.OSVersion;
|
||||
if (os.Platform != PlatformID.Unix && os.Platform != PlatformID.MacOSX)
|
||||
_windows = true;
|
||||
|
||||
var prefix = String.Format ("http{0}://*:{1}/", _secure ? "s" : "", _port);
|
||||
_listener.Prefixes.Add (prefix);
|
||||
}
|
||||
|
||||
private void processHttpRequest (HttpListenerContext context)
|
||||
{
|
||||
var args = new HttpRequestEventArgs (context);
|
||||
var method = context.Request.HttpMethod;
|
||||
if (method == "GET" && OnGet != null)
|
||||
{
|
||||
OnGet (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "HEAD" && OnHead != null)
|
||||
{
|
||||
OnHead (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "POST" && OnPost != null)
|
||||
{
|
||||
OnPost (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "PUT" && OnPut != null)
|
||||
{
|
||||
OnPut (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "DELETE" && OnDelete != null)
|
||||
{
|
||||
OnDelete (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "OPTIONS" && OnOptions != null)
|
||||
{
|
||||
OnOptions (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "TRACE" && OnTrace != null)
|
||||
{
|
||||
OnTrace (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "CONNECT" && OnConnect != null)
|
||||
{
|
||||
OnConnect (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == "PATCH" && OnPatch != null)
|
||||
{
|
||||
OnPatch (this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
context.Response.StatusCode = (int) HttpStatusCode.NotImplemented;
|
||||
}
|
||||
|
||||
private bool processWebSocketRequest (HttpListenerContext context)
|
||||
{
|
||||
var wsContext = context.AcceptWebSocket ();
|
||||
|
||||
var path = wsContext.Path;
|
||||
WebSocketServiceHost host;
|
||||
if (path == null || !_serviceHosts.TryGetServiceHostInternally (path, out host))
|
||||
{
|
||||
context.Response.StatusCode = (int) HttpStatusCode.NotImplemented;
|
||||
return false;
|
||||
}
|
||||
|
||||
wsContext.WebSocket.Log = _logger;
|
||||
host.StartSession (wsContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void processRequestAsync (HttpListenerContext context)
|
||||
{
|
||||
WaitCallback callback = state =>
|
||||
{
|
||||
try {
|
||||
if (context.Request.IsUpgradeTo ("websocket"))
|
||||
{
|
||||
if (processWebSocketRequest (context))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
processHttpRequest (context);
|
||||
}
|
||||
|
||||
context.Response.Close ();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.Fatal (ex.ToString ());
|
||||
context.Connection.Close (true);
|
||||
}
|
||||
};
|
||||
|
||||
ThreadPool.QueueUserWorkItem (callback);
|
||||
}
|
||||
|
||||
private void receiveRequest ()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
while (true) {
|
||||
try {
|
||||
processRequestAsync (_listener.GetContext ());
|
||||
acceptRequestAsync (_listener.GetContext ());
|
||||
}
|
||||
catch (HttpListenerException ex) {
|
||||
_logger.Warn (String.Format ("Receiving has been stopped.\nreason: {0}.", ex.Message));
|
||||
_logger.Warn (
|
||||
String.Format (
|
||||
"Receiving has been stopped.\nreason: {0}.", ex.Message));
|
||||
|
||||
break;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
@ -489,9 +567,9 @@ namespace WebSocketSharp.Server
|
||||
abort ();
|
||||
}
|
||||
|
||||
private void startReceiveRequestThread ()
|
||||
private void startReceiving ()
|
||||
{
|
||||
_receiveRequestThread = new Thread (new ThreadStart (receiveRequest));
|
||||
_receiveRequestThread = new Thread (new ThreadStart (receiveRequest));
|
||||
_receiveRequestThread.IsBackground = true;
|
||||
_receiveRequestThread.Start ();
|
||||
}
|
||||
@ -507,18 +585,21 @@ namespace WebSocketSharp.Server
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified typed WebSocket service with the specified <paramref name="servicePath"/>.
|
||||
/// Adds the specified typed WebSocket service with the specified
|
||||
/// <paramref name="servicePath"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string and
|
||||
/// removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string
|
||||
/// and removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// </remarks>
|
||||
/// <param name="servicePath">
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket service.
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket
|
||||
/// service.
|
||||
/// </param>
|
||||
/// <typeparam name="TWithNew">
|
||||
/// The type of the WebSocket service. The TWithNew must inherit the <see cref="WebSocketService"/> class and
|
||||
/// must have a public parameterless constructor.
|
||||
/// The type of the WebSocket service. The TWithNew must inherit the
|
||||
/// <see cref="WebSocketService"/> class and must have a public parameterless
|
||||
/// constructor.
|
||||
/// </typeparam>
|
||||
public void AddWebSocketService<TWithNew> (string servicePath)
|
||||
where TWithNew : WebSocketService, new ()
|
||||
@ -527,42 +608,50 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified typed WebSocket service with the specified <paramref name="servicePath"/> and
|
||||
/// <paramref name="serviceConstructor"/>.
|
||||
/// Adds the specified typed WebSocket service with the specified
|
||||
/// <paramref name="servicePath"/> and <paramref name="serviceConstructor"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string and
|
||||
/// removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded
|
||||
/// string and removes <c>'/'</c> from tail end of
|
||||
/// <paramref name="servicePath"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <paramref name="serviceConstructor"/> returns a initialized specified typed WebSocket service
|
||||
/// instance.
|
||||
/// <paramref name="serviceConstructor"/> returns a initialized specified
|
||||
/// typed WebSocket service instance.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="servicePath">
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket service.
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket
|
||||
/// service.
|
||||
/// </param>
|
||||
/// <param name="serviceConstructor">
|
||||
/// A Func<T> delegate that references the method used to initialize a new WebSocket service
|
||||
/// instance (a new WebSocket session).
|
||||
/// A Func<T> delegate that references the method used to initialize
|
||||
/// a new WebSocket service instance (a new WebSocket session).
|
||||
/// </param>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the WebSocket service. The T must inherit the <see cref="WebSocketService"/> class.
|
||||
/// The type of the WebSocket service. The T must inherit the
|
||||
/// <see cref="WebSocketService"/> class.
|
||||
/// </typeparam>
|
||||
public void AddWebSocketService<T> (string servicePath, Func<T> serviceConstructor)
|
||||
where T : WebSocketService
|
||||
{
|
||||
var msg = servicePath.CheckIfValidServicePath () ??
|
||||
(serviceConstructor == null ? "'serviceConstructor' must not be null." : null);
|
||||
(serviceConstructor == null
|
||||
? "'serviceConstructor' must not be null."
|
||||
: null);
|
||||
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format ("{0}\nservice path: {1}", msg, servicePath ?? ""));
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format ("{0}\nservice path: {1}", msg, servicePath ?? ""));
|
||||
return;
|
||||
}
|
||||
|
||||
var host = new WebSocketServiceHost<T> (servicePath, serviceConstructor, _logger);
|
||||
var host = new WebSocketServiceHost<T> (
|
||||
servicePath, serviceConstructor, _logger);
|
||||
|
||||
if (!KeepClean)
|
||||
host.KeepClean = false;
|
||||
|
||||
@ -573,8 +662,8 @@ namespace WebSocketSharp.Server
|
||||
/// Gets the contents of the file with the specified <paramref name="path"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An array of <see cref="byte"/> that contains the contents of the file if exists;
|
||||
/// otherwise, <see langword="null"/>.
|
||||
/// An array of <see cref="byte"/> that contains the contents of the file if
|
||||
/// it exists; otherwise, <see langword="null"/>.
|
||||
/// </returns>
|
||||
/// <param name="path">
|
||||
/// A <see cref="string"/> that contains a virtual path to the file to get.
|
||||
@ -591,24 +680,28 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the WebSocket service with the specified <paramref name="servicePath"/>.
|
||||
/// Removes the WebSocket service with the specified
|
||||
/// <paramref name="servicePath"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string and
|
||||
/// removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string
|
||||
/// and removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the WebSocket service is successfully found and removed; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the WebSocket service is successfully found and removed;
|
||||
/// otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="servicePath">
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket
|
||||
/// service to find.
|
||||
/// </param>
|
||||
public bool RemoveWebSocketService (string servicePath)
|
||||
{
|
||||
var msg = servicePath.CheckIfValidServicePath ();
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format ("{0}\nservice path: {1}", msg, servicePath ?? ""));
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format ("{0}\nservice path: {1}", msg, servicePath));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -616,22 +709,23 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts to receive the HTTP requests.
|
||||
/// Starts receiving the HTTP requests.
|
||||
/// </summary>
|
||||
public void Start ()
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
var msg = _state.CheckIfStopped () ?? checkIfCertExists ();
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format ("{0}\nstate: {1}\nsecure: {2}", msg, _state, _secure));
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format (
|
||||
"{0}\nstate: {1}\nsecure: {2}", msg, _state, _secure));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_serviceHosts.Start ();
|
||||
_listener.Start ();
|
||||
startReceiveRequestThread ();
|
||||
startReceiving ();
|
||||
|
||||
_state = ServerState.START;
|
||||
}
|
||||
@ -642,11 +736,9 @@ namespace WebSocketSharp.Server
|
||||
/// </summary>
|
||||
public void Stop ()
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
var msg = _state.CheckIfStarted ();
|
||||
if (msg != null)
|
||||
{
|
||||
if (msg != null) {
|
||||
_logger.Error (String.Format ("{0}\nstate: {1}", msg, _state));
|
||||
return;
|
||||
}
|
||||
@ -654,18 +746,19 @@ namespace WebSocketSharp.Server
|
||||
_state = ServerState.SHUTDOWN;
|
||||
}
|
||||
|
||||
_serviceHosts.Stop (new byte []{}, true);
|
||||
_serviceHosts.Stop (new byte [0], true);
|
||||
stopListener (5000);
|
||||
|
||||
_state = ServerState.STOP;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops receiving the HTTP requests with the specified <see cref="ushort"/> and
|
||||
/// <see cref="string"/> used to stop the WebSocket services.
|
||||
/// Stops receiving the HTTP requests with the specified <see cref="ushort"/>
|
||||
/// and <see cref="string"/> used to stop the WebSocket services.
|
||||
/// </summary>
|
||||
/// <param name="code">
|
||||
/// A <see cref="ushort"/> that contains a status code indicating the reason for stop.
|
||||
/// A <see cref="ushort"/> that contains a status code indicating the reason
|
||||
/// for stop.
|
||||
/// </param>
|
||||
/// <param name="reason">
|
||||
/// A <see cref="string"/> that contains the reason for stop.
|
||||
@ -673,16 +766,15 @@ namespace WebSocketSharp.Server
|
||||
public void Stop (ushort code, string reason)
|
||||
{
|
||||
byte [] data = null;
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
var msg = _state.CheckIfStarted () ??
|
||||
code.CheckIfValidCloseStatusCode () ??
|
||||
(data = code.Append (reason)).CheckIfValidCloseData ();
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format (
|
||||
"{0}\nstate: {1}\ncode: {2}\nreason: {3}", msg, _state, code, reason));
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format (
|
||||
"{0}\nstate: {1}\ncode: {2}\nreason: {3}", msg, _state, code, reason));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -697,12 +789,13 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops receiving the HTTP requests with the specified <see cref="CloseStatusCode"/>
|
||||
/// and <see cref="string"/> used to stop the WebSocket services.
|
||||
/// Stops receiving the HTTP requests with the specified
|
||||
/// <see cref="CloseStatusCode"/> and <see cref="string"/> used to stop the
|
||||
/// WebSocket services.
|
||||
/// </summary>
|
||||
/// <param name="code">
|
||||
/// One of the <see cref="CloseStatusCode"/> values that represent the status codes indicating
|
||||
/// the reasons for stop.
|
||||
/// One of the <see cref="CloseStatusCode"/> values that represent the status
|
||||
/// codes indicating the reasons for stop.
|
||||
/// </param>
|
||||
/// <param name="reason">
|
||||
/// A <see cref="string"/> that contains the reason for stop.
|
||||
@ -710,14 +803,14 @@ namespace WebSocketSharp.Server
|
||||
public void Stop (CloseStatusCode code, string reason)
|
||||
{
|
||||
byte [] data = null;
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
var msg = _state.CheckIfStarted () ??
|
||||
(data = ((ushort) code).Append (reason)).CheckIfValidCloseData ();
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format ("{0}\nstate: {1}\nreason: {2}", msg, _state, reason));
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format ("{0}\nstate: {1}\nreason: {2}", msg, _state, reason));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,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
|
||||
@ -17,7 +17,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
|
||||
@ -39,6 +39,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using WebSocketSharp.Net;
|
||||
@ -47,7 +48,8 @@ using WebSocketSharp.Net.WebSockets;
|
||||
namespace WebSocketSharp.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the functions of the server that receives the WebSocket connection requests.
|
||||
/// Provides the functions of the server that receives the WebSocket connection
|
||||
/// requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The WebSocketServer class provides the multi WebSocket service.
|
||||
@ -56,25 +58,28 @@ namespace WebSocketSharp.Server
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private System.Net.IPAddress _address;
|
||||
private X509Certificate2 _cert;
|
||||
private TcpListener _listener;
|
||||
private Logger _logger;
|
||||
private int _port;
|
||||
private Thread _receiveRequestThread;
|
||||
private bool _secure;
|
||||
private WebSocketServiceHostManager _serviceHosts;
|
||||
private volatile ServerState _state;
|
||||
private object _sync;
|
||||
private Uri _uri;
|
||||
private System.Net.IPAddress _address;
|
||||
private AuthenticationSchemes _authSchemes;
|
||||
private X509Certificate2 _cert;
|
||||
private Func<IIdentity, NetworkCredential> _credentialsFinder;
|
||||
private TcpListener _listener;
|
||||
private Logger _logger;
|
||||
private int _port;
|
||||
private string _realm;
|
||||
private Thread _receiveRequestThread;
|
||||
private bool _secure;
|
||||
private WebSocketServiceHostManager _serviceHosts;
|
||||
private volatile ServerState _state;
|
||||
private object _sync;
|
||||
private Uri _uri;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class that listens for
|
||||
/// incoming requests on port 80.
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class
|
||||
/// that listens for incoming requests on port 80.
|
||||
/// </summary>
|
||||
public WebSocketServer ()
|
||||
: this (80)
|
||||
@ -82,8 +87,9 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class that listens for
|
||||
/// incoming connection attempts on the specified <paramref name="port"/>.
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class
|
||||
/// that listens for incoming connection attempts on the specified
|
||||
/// <paramref name="port"/>.
|
||||
/// </summary>
|
||||
/// <param name="port">
|
||||
/// An <see cref="int"/> that contains a port number.
|
||||
@ -97,8 +103,9 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class that listens for
|
||||
/// incoming connection attempts on the specified WebSocket URL.
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class
|
||||
/// that listens for incoming connection attempts on the specified WebSocket
|
||||
/// URL.
|
||||
/// </summary>
|
||||
/// <param name="url">
|
||||
/// A <see cref="string"/> that contains a WebSocket URL.
|
||||
@ -121,8 +128,9 @@ namespace WebSocketSharp.Server
|
||||
var host = _uri.DnsSafeHost;
|
||||
_address = host.ToIPAddress ();
|
||||
if (_address == null || !_address.IsLocal ())
|
||||
throw new ArgumentException (String.Format (
|
||||
"The host part must be the local host name: {0}", host), "url");
|
||||
throw new ArgumentException (
|
||||
String.Format (
|
||||
"The host part must be the local host name: {0}", host), "url");
|
||||
|
||||
_port = _uri.Port;
|
||||
_secure = _uri.Scheme == "wss" ? true : false;
|
||||
@ -131,8 +139,9 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class that listens for
|
||||
/// incoming connection attempts on the specified <paramref name="port"/> and <paramref name="secure"/>.
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class
|
||||
/// that listens for incoming connection attempts on the specified
|
||||
/// <paramref name="port"/> and <paramref name="secure"/>.
|
||||
/// </summary>
|
||||
/// <param name="port">
|
||||
/// An <see cref="int"/> that contains a port number.
|
||||
@ -153,11 +162,13 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class that listens for
|
||||
/// incoming connection attempts on the specified <paramref name="address"/> and <paramref name="port"/>.
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class
|
||||
/// that listens for incoming connection attempts on the specified
|
||||
/// <paramref name="address"/> and <paramref name="port"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">
|
||||
/// A <see cref="System.Net.IPAddress"/> that represents the local IP address.
|
||||
/// A <see cref="System.Net.IPAddress"/> that represents the local IP
|
||||
/// address.
|
||||
/// </param>
|
||||
/// <param name="port">
|
||||
/// An <see cref="int"/> that contains a port number.
|
||||
@ -177,12 +188,14 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class that listens for
|
||||
/// incoming connection attempts on the specified <paramref name="address"/>, <paramref name="port"/>
|
||||
/// and <paramref name="secure"/>.
|
||||
/// Initializes a new instance of the <see cref="WebSocketServer"/> class
|
||||
/// that listens for incoming connection attempts on the specified
|
||||
/// <paramref name="address"/>, <paramref name="port"/> and
|
||||
/// <paramref name="secure"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">
|
||||
/// A <see cref="System.Net.IPAddress"/> that represents the local IP address.
|
||||
/// A <see cref="System.Net.IPAddress"/> that represents the local IP
|
||||
/// address.
|
||||
/// </param>
|
||||
/// <param name="port">
|
||||
/// An <see cref="int"/> that contains a port number.
|
||||
@ -205,21 +218,25 @@ namespace WebSocketSharp.Server
|
||||
/// -or-
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Pair of <paramref name="port"/> and <paramref name="secure"/> is invalid.
|
||||
/// Pair of <paramref name="port"/> and <paramref name="secure"/> is
|
||||
/// invalid.
|
||||
/// </para>
|
||||
/// </exception>
|
||||
public WebSocketServer (System.Net.IPAddress address, int port, bool secure)
|
||||
{
|
||||
if (!address.IsLocal ())
|
||||
throw new ArgumentException (String.Format (
|
||||
"Must be the local IP address: {0}", address), "address");
|
||||
throw new ArgumentException (
|
||||
String.Format (
|
||||
"Must be the local IP address: {0}", address), "address");
|
||||
|
||||
if (!port.IsPortNumber ())
|
||||
throw new ArgumentOutOfRangeException ("port", "Must be between 1 and 65535: " + port);
|
||||
throw new ArgumentOutOfRangeException (
|
||||
"port", "Must be between 1 and 65535: " + port);
|
||||
|
||||
if ((port == 80 && secure) || (port == 443 && !secure))
|
||||
throw new ArgumentException (String.Format (
|
||||
"Invalid pair of 'port' and 'secure': {0}, {1}", port, secure));
|
||||
throw new ArgumentException (
|
||||
String.Format (
|
||||
"Invalid pair of 'port' and 'secure': {0}, {1}", port, secure));
|
||||
|
||||
_address = address;
|
||||
_port = port;
|
||||
@ -234,10 +251,12 @@ namespace WebSocketSharp.Server
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local IP address on which to listen for incoming connection attempts.
|
||||
/// Gets the local IP address on which to listen for incoming connection
|
||||
/// attempts.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="System.Net.IPAddress"/> that represents the local IP address.
|
||||
/// A <see cref="System.Net.IPAddress"/> that represents the local IP
|
||||
/// address.
|
||||
/// </value>
|
||||
public System.Net.IPAddress Address {
|
||||
get {
|
||||
@ -246,7 +265,29 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the certificate used to authenticate the server on the secure connection.
|
||||
/// Gets or sets the scheme used to authenticate the clients.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// One of the <see cref="WebSocketSharp.Net.AuthenticationSchemes"/> values
|
||||
/// that indicates the scheme used to authenticate the clients. The default
|
||||
/// value is <see cref="WebSocketSharp.Net.AuthenticationSchemes.Anonymous"/>.
|
||||
/// </value>
|
||||
public AuthenticationSchemes AuthenticationSchemes {
|
||||
get {
|
||||
return _authSchemes;
|
||||
}
|
||||
|
||||
set {
|
||||
if (!canSet ("AuthenticationSchemes"))
|
||||
return;
|
||||
|
||||
_authSchemes = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the certificate used to authenticate the server on the
|
||||
/// secure connection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="X509Certificate2"/> used to authenticate the server.
|
||||
@ -257,13 +298,8 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
set {
|
||||
if (_state == ServerState.START || _state == ServerState.SHUTDOWN)
|
||||
{
|
||||
_logger.Error (
|
||||
"The value of Certificate property cannot be changed because the server has already been started.");
|
||||
|
||||
if (!canSet ("Certificate"))
|
||||
return;
|
||||
}
|
||||
|
||||
_cert = value;
|
||||
}
|
||||
@ -285,7 +321,8 @@ namespace WebSocketSharp.Server
|
||||
/// Gets a value indicating whether the server provides secure connection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the server provides secure connection; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the server provides secure connection; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsSecure {
|
||||
get {
|
||||
@ -294,11 +331,12 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the server cleans up the inactive sessions periodically.
|
||||
/// Gets or sets a value indicating whether the server cleans up the inactive
|
||||
/// sessions periodically.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the server cleans up the inactive sessions every 60 seconds;
|
||||
/// otherwise, <c>false</c>. The default value is <c>true</c>.
|
||||
/// <c>true</c> if the server cleans up the inactive sessions every 60
|
||||
/// seconds; otherwise, <c>false</c>. The default value is <c>true</c>.
|
||||
/// </value>
|
||||
public bool KeepClean {
|
||||
get {
|
||||
@ -314,9 +352,9 @@ namespace WebSocketSharp.Server
|
||||
/// Gets the logging functions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default logging level is the <see cref="LogLevel.ERROR"/>.
|
||||
/// If you want to change the current logging level, you set the <c>Log.Level</c> property
|
||||
/// to one of the <see cref="LogLevel"/> values which you want.
|
||||
/// The default logging level is the <see cref="LogLevel.ERROR"/>. If you
|
||||
/// change the current logging level, you set the <c>Log.Level</c> property
|
||||
/// to any of the <see cref="LogLevel"/> values.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="Logger"/> that provides the logging functions.
|
||||
@ -340,10 +378,55 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the functions for the WebSocket services provided by the server.
|
||||
/// Gets or sets the name of the realm associated with the
|
||||
/// <see cref="WebSocketServer"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketServiceHostManager"/> that manages the WebSocket services.
|
||||
/// A <see cref="string"/> that contains the name of the realm.
|
||||
/// The default value is <c>SECRET AREA</c>.
|
||||
/// </value>
|
||||
public string Realm {
|
||||
get {
|
||||
return _realm ?? (_realm = "SECRET AREA");
|
||||
}
|
||||
|
||||
set {
|
||||
if (!canSet ("Realm"))
|
||||
return;
|
||||
|
||||
_realm = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delegate called to find the credentials for an identity
|
||||
/// used to authenticate a client.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A Func<<see cref="IIdentity"/>, <see cref="NetworkCredential"/>>
|
||||
/// delegate that references the method(s) used to find the credentials. The
|
||||
/// default value is a function that only returns <see langword="null"/>.
|
||||
/// </value>
|
||||
public Func<IIdentity, NetworkCredential> UserCredentialsFinder {
|
||||
get {
|
||||
return _credentialsFinder ?? (_credentialsFinder = identity => null);
|
||||
}
|
||||
|
||||
set {
|
||||
if (!canSet ("UserCredentialsFinder"))
|
||||
return;
|
||||
|
||||
_credentialsFinder = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the functions for the WebSocket services provided by the
|
||||
/// <see cref="WebSocketServer"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WebSocketServiceHostManager"/> that manages the WebSocket
|
||||
/// services.
|
||||
/// </value>
|
||||
public WebSocketServiceHostManager WebSocketServices {
|
||||
get {
|
||||
@ -357,8 +440,7 @@ namespace WebSocketSharp.Server
|
||||
|
||||
private void abort ()
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
if (!IsListening)
|
||||
return;
|
||||
|
||||
@ -367,30 +449,103 @@ namespace WebSocketSharp.Server
|
||||
|
||||
_listener.Stop ();
|
||||
_serviceHosts.Stop (
|
||||
((ushort) CloseStatusCode.SERVER_ERROR).ToByteArrayInternally (ByteOrder.BIG), true);
|
||||
((ushort) CloseStatusCode.SERVER_ERROR).ToByteArrayInternally (ByteOrder.BIG),
|
||||
true);
|
||||
|
||||
_state = ServerState.STOP;
|
||||
}
|
||||
|
||||
private void acceptRequestAsync (TcpClient client)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem (
|
||||
state => {
|
||||
try {
|
||||
var context = client.GetWebSocketContext (_cert, _secure, _logger);
|
||||
if (_authSchemes != AuthenticationSchemes.Anonymous &&
|
||||
!authenticateRequest (_authSchemes, context))
|
||||
return;
|
||||
|
||||
acceptWebSocket (context);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.Fatal (ex.ToString ());
|
||||
client.Close ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void acceptWebSocket (TcpListenerWebSocketContext context)
|
||||
{
|
||||
var websocket = context.WebSocket;
|
||||
websocket.Log = _logger;
|
||||
|
||||
var path = context.Path;
|
||||
|
||||
WebSocketServiceHost host;
|
||||
if (path == null || !_serviceHosts.TryGetServiceHostInternally (path, out host))
|
||||
{
|
||||
websocket.Close (HttpStatusCode.NotImplemented);
|
||||
if (path == null ||
|
||||
!_serviceHosts.TryGetServiceHostInternally (path, out host)) {
|
||||
context.Close (HttpStatusCode.NotImplemented);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_uri.IsAbsoluteUri)
|
||||
websocket.Url = new Uri (_uri, path);
|
||||
|
||||
host.StartSession (context);
|
||||
}
|
||||
|
||||
private bool authenticateRequest (
|
||||
AuthenticationSchemes authScheme, TcpListenerWebSocketContext context)
|
||||
{
|
||||
var challenge = authScheme == AuthenticationSchemes.Basic
|
||||
? HttpUtility.CreateBasicAuthChallenge (Realm)
|
||||
: authScheme == AuthenticationSchemes.Digest
|
||||
? HttpUtility.CreateDigestAuthChallenge (Realm)
|
||||
: null;
|
||||
|
||||
if (challenge == null) {
|
||||
context.Close (HttpStatusCode.Forbidden);
|
||||
return false;
|
||||
}
|
||||
|
||||
var retry = -1;
|
||||
var expected = authScheme.ToString ();
|
||||
var realm = Realm;
|
||||
var credentialsFinder = UserCredentialsFinder;
|
||||
Func<bool> auth = null;
|
||||
auth = () => {
|
||||
retry++;
|
||||
if (retry > 99) {
|
||||
context.Close (HttpStatusCode.Forbidden);
|
||||
return false;
|
||||
}
|
||||
|
||||
var header = context.Headers ["Authorization"];
|
||||
if (header == null ||
|
||||
!header.StartsWith (expected, StringComparison.OrdinalIgnoreCase)) {
|
||||
context.SendAuthChallenge (challenge);
|
||||
return auth ();
|
||||
}
|
||||
|
||||
context.SetUser (authScheme, realm, credentialsFinder);
|
||||
if (context.IsAuthenticated)
|
||||
return true;
|
||||
|
||||
context.SendAuthChallenge (challenge);
|
||||
return auth ();
|
||||
};
|
||||
|
||||
return auth ();
|
||||
}
|
||||
|
||||
private bool canSet (string property)
|
||||
{
|
||||
if (_state == ServerState.START || _state == ServerState.SHUTDOWN) {
|
||||
_logger.Error (
|
||||
String.Format (
|
||||
"The '{0}' property cannot set a value because the server has already been started.",
|
||||
property));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string checkIfCertExists ()
|
||||
{
|
||||
return _secure && _cert == null
|
||||
@ -400,6 +555,7 @@ namespace WebSocketSharp.Server
|
||||
|
||||
private void init ()
|
||||
{
|
||||
_authSchemes = AuthenticationSchemes.Anonymous;
|
||||
_listener = new TcpListener (_address, _port);
|
||||
_logger = new Logger ();
|
||||
_serviceHosts = new WebSocketServiceHostManager (_logger);
|
||||
@ -407,32 +563,17 @@ namespace WebSocketSharp.Server
|
||||
_sync = new object ();
|
||||
}
|
||||
|
||||
private void processRequestAsync (TcpClient client)
|
||||
{
|
||||
WaitCallback callback = state =>
|
||||
{
|
||||
try {
|
||||
acceptWebSocket (client.GetWebSocketContext (_secure, _cert));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Fatal (ex.ToString ());
|
||||
client.Close ();
|
||||
}
|
||||
};
|
||||
|
||||
ThreadPool.QueueUserWorkItem (callback);
|
||||
}
|
||||
|
||||
private void receiveRequest ()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
while (true) {
|
||||
try {
|
||||
processRequestAsync (_listener.AcceptTcpClient ());
|
||||
acceptRequestAsync (_listener.AcceptTcpClient ());
|
||||
}
|
||||
catch (SocketException ex) {
|
||||
_logger.Warn (String.Format ("Receiving has been stopped.\nreason: {0}.", ex.Message));
|
||||
_logger.Warn (
|
||||
String.Format (
|
||||
"Receiving has been stopped.\nreason: {0}.", ex.Message));
|
||||
|
||||
break;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
@ -445,7 +586,7 @@ namespace WebSocketSharp.Server
|
||||
abort ();
|
||||
}
|
||||
|
||||
private void startReceiveRequestThread ()
|
||||
private void startReceiving ()
|
||||
{
|
||||
_receiveRequestThread = new Thread (new ThreadStart (receiveRequest));
|
||||
_receiveRequestThread.IsBackground = true;
|
||||
@ -463,8 +604,7 @@ namespace WebSocketSharp.Server
|
||||
if (!uriString.TryCreateWebSocketUri (out result, out message))
|
||||
return false;
|
||||
|
||||
if (result.PathAndQuery != "/")
|
||||
{
|
||||
if (result.PathAndQuery != "/") {
|
||||
result = null;
|
||||
message = "Must not contain the path or query component: " + uriString;
|
||||
|
||||
@ -479,18 +619,21 @@ namespace WebSocketSharp.Server
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified typed WebSocket service with the specified <paramref name="servicePath"/>.
|
||||
/// Adds the specified typed WebSocket service with the specified
|
||||
/// <paramref name="servicePath"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string and
|
||||
/// removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string
|
||||
/// and removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// </remarks>
|
||||
/// <param name="servicePath">
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket service.
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket
|
||||
/// service.
|
||||
/// </param>
|
||||
/// <typeparam name="TWithNew">
|
||||
/// The type of the WebSocket service. The TWithNew must inherit the <see cref="WebSocketService"/>
|
||||
/// class and must have a public parameterless constructor.
|
||||
/// The type of the WebSocket service. The TWithNew must inherit the
|
||||
/// <see cref="WebSocketService"/> class and must have a public parameterless
|
||||
/// constructor.
|
||||
/// </typeparam>
|
||||
public void AddWebSocketService<TWithNew> (string servicePath)
|
||||
where TWithNew : WebSocketService, new ()
|
||||
@ -499,42 +642,50 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified typed WebSocket service with the specified <paramref name="servicePath"/> and
|
||||
/// <paramref name="serviceConstructor"/>.
|
||||
/// Adds the specified typed WebSocket service with the specified
|
||||
/// <paramref name="servicePath"/> and <paramref name="serviceConstructor"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string and
|
||||
/// removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded
|
||||
/// string and removes <c>'/'</c> from tail end of
|
||||
/// <paramref name="servicePath"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <paramref name="serviceConstructor"/> returns a initialized specified typed WebSocket service
|
||||
/// instance.
|
||||
/// <paramref name="serviceConstructor"/> returns a initialized specified
|
||||
/// typed WebSocket service instance.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="servicePath">
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket service.
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket
|
||||
/// service.
|
||||
/// </param>
|
||||
/// <param name="serviceConstructor">
|
||||
/// A Func<T> delegate that references the method used to initialize a new WebSocket service
|
||||
/// instance (a new WebSocket session).
|
||||
/// A Func<T> delegate that references the method used to initialize
|
||||
/// a new WebSocket service instance (a new WebSocket session).
|
||||
/// </param>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the WebSocket service. The T must inherit the <see cref="WebSocketService"/> class.
|
||||
/// The type of the WebSocket service. The T must inherit the
|
||||
/// <see cref="WebSocketService"/> class.
|
||||
/// </typeparam>
|
||||
public void AddWebSocketService<T> (string servicePath, Func<T> serviceConstructor)
|
||||
where T : WebSocketService
|
||||
{
|
||||
var msg = servicePath.CheckIfValidServicePath () ??
|
||||
(serviceConstructor == null ? "'serviceConstructor' must not be null." : null);
|
||||
(serviceConstructor == null
|
||||
? "'serviceConstructor' must not be null."
|
||||
: null);
|
||||
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format ("{0}\nservice path: {1}", msg, servicePath ?? ""));
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format ("{0}\nservice path: {1}", msg, servicePath ?? ""));
|
||||
return;
|
||||
}
|
||||
|
||||
var host = new WebSocketServiceHost<T> (servicePath, serviceConstructor, _logger);
|
||||
var host = new WebSocketServiceHost<T> (
|
||||
servicePath, serviceConstructor, _logger);
|
||||
|
||||
if (!KeepClean)
|
||||
host.KeepClean = false;
|
||||
|
||||
@ -542,24 +693,28 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the WebSocket service with the specified <paramref name="servicePath"/>.
|
||||
/// Removes the WebSocket service with the specified
|
||||
/// <paramref name="servicePath"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string and
|
||||
/// removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// This method converts <paramref name="servicePath"/> to URL-decoded string
|
||||
/// and removes <c>'/'</c> from tail end of <paramref name="servicePath"/>.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the WebSocket service is successfully found and removed; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the WebSocket service is successfully found and removed;
|
||||
/// otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="servicePath">
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket
|
||||
/// service to find.
|
||||
/// </param>
|
||||
public bool RemoveWebSocketService (string servicePath)
|
||||
{
|
||||
var msg = servicePath.CheckIfValidServicePath ();
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format ("{0}\nservice path: {1}", msg, servicePath ?? ""));
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format ("{0}\nservice path: {1}", msg, servicePath));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -567,22 +722,23 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts to receive the WebSocket connection requests.
|
||||
/// Starts receiving the WebSocket connection requests.
|
||||
/// </summary>
|
||||
public void Start ()
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
var msg = _state.CheckIfStopped () ?? checkIfCertExists ();
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format ("{0}\nstate: {1}\nsecure: {2}", msg, _state, _secure));
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format (
|
||||
"{0}\nstate: {1}\nsecure: {2}", msg, _state, _secure));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_serviceHosts.Start ();
|
||||
_listener.Start ();
|
||||
startReceiveRequestThread ();
|
||||
startReceiving ();
|
||||
|
||||
_state = ServerState.START;
|
||||
}
|
||||
@ -593,11 +749,9 @@ namespace WebSocketSharp.Server
|
||||
/// </summary>
|
||||
public void Stop ()
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
var msg = _state.CheckIfStarted ();
|
||||
if (msg != null)
|
||||
{
|
||||
if (msg != null) {
|
||||
_logger.Error (String.Format ("{0}\nstate: {1}", msg, _state));
|
||||
return;
|
||||
}
|
||||
@ -606,17 +760,18 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
stopListener (5000);
|
||||
_serviceHosts.Stop (new byte []{}, true);
|
||||
_serviceHosts.Stop (new byte [0], true);
|
||||
|
||||
_state = ServerState.STOP;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops receiving the WebSocket connection requests with the specified <see cref="ushort"/> and
|
||||
/// <see cref="string"/>.
|
||||
/// Stops receiving the WebSocket connection requests with the specified
|
||||
/// <see cref="ushort"/> and <see cref="string"/>.
|
||||
/// </summary>
|
||||
/// <param name="code">
|
||||
/// A <see cref="ushort"/> that contains a status code indicating the reason for stop.
|
||||
/// A <see cref="ushort"/> that contains a status code indicating the reason
|
||||
/// for stop.
|
||||
/// </param>
|
||||
/// <param name="reason">
|
||||
/// A <see cref="string"/> that contains the reason for stop.
|
||||
@ -624,16 +779,15 @@ namespace WebSocketSharp.Server
|
||||
public void Stop (ushort code, string reason)
|
||||
{
|
||||
byte [] data = null;
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
var msg = _state.CheckIfStarted () ??
|
||||
code.CheckIfValidCloseStatusCode () ??
|
||||
(data = code.Append (reason)).CheckIfValidCloseData ();
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format (
|
||||
"{0}\nstate: {1}\ncode: {2}\nreason: {3}", msg, _state, code, reason));
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format (
|
||||
"{0}\nstate: {1}\ncode: {2}\nreason: {3}", msg, _state, code, reason));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -648,12 +802,12 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops receiving the WebSocket connection requests with the specified <see cref="CloseStatusCode"/>
|
||||
/// and <see cref="string"/>.
|
||||
/// Stops receiving the WebSocket connection requests with the specified
|
||||
/// <see cref="CloseStatusCode"/> and <see cref="string"/>.
|
||||
/// </summary>
|
||||
/// <param name="code">
|
||||
/// One of the <see cref="CloseStatusCode"/> values that represent the status codes indicating
|
||||
/// the reasons for stop.
|
||||
/// One of the <see cref="CloseStatusCode"/> values that represent the status
|
||||
/// codes indicating the reasons for stop.
|
||||
/// </param>
|
||||
/// <param name="reason">
|
||||
/// A <see cref="string"/> that contains the reason for stop.
|
||||
@ -661,14 +815,14 @@ namespace WebSocketSharp.Server
|
||||
public void Stop (CloseStatusCode code, string reason)
|
||||
{
|
||||
byte [] data = null;
|
||||
lock (_sync)
|
||||
{
|
||||
lock (_sync) {
|
||||
var msg = _state.CheckIfStarted () ??
|
||||
(data = ((ushort) code).Append (reason)).CheckIfValidCloseData ();
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
_logger.Error (String.Format ("{0}\nstate: {1}\nreason: {2}", msg, _state, reason));
|
||||
if (msg != null) {
|
||||
_logger.Error (
|
||||
String.Format ("{0}\nstate: {1}\nreason: {2}", msg, _state, reason));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,15 @@
|
||||
* WebSocket.cs
|
||||
*
|
||||
* A C# implementation of the WebSocket interface.
|
||||
* This code derived from WebSocket.java (http://github.com/adamac/Java-WebSocket-client).
|
||||
*
|
||||
* This code is derived from WebSocket.java
|
||||
* (http://github.com/adamac/Java-WebSocket-client).
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2009 Adam MacBeth
|
||||
* Copyright (c) 2010-2013 sta.blockhead
|
||||
*
|
||||
* Copyright (c) 2010-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
|
||||
@ -19,7 +21,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
|
||||
@ -51,8 +53,9 @@ namespace WebSocketSharp
|
||||
/// Implements the WebSocket interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The WebSocket class provides a set of methods and properties for two-way communication
|
||||
/// using the WebSocket protocol (<see href="http://tools.ietf.org/html/rfc6455">RFC 6455</see>).
|
||||
/// The WebSocket class provides a set of methods and properties for two-way
|
||||
/// communication using the WebSocket protocol
|
||||
/// (<see href="http://tools.ietf.org/html/rfc6455">RFC 6455</see>).
|
||||
/// </remarks>
|
||||
public class WebSocket : IDisposable
|
||||
{
|
||||
@ -65,6 +68,7 @@ namespace WebSocketSharp
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private AuthenticationChallenge _authChallenge;
|
||||
private string _base64key;
|
||||
private RemoteCertificateValidationCallback
|
||||
_certValidationCallback;
|
||||
@ -75,12 +79,13 @@ namespace WebSocketSharp
|
||||
private CookieCollection _cookies;
|
||||
private Func<CookieCollection, CookieCollection, bool>
|
||||
_cookiesValidation;
|
||||
private WsCredential _credentials;
|
||||
private NetworkCredential _credentials;
|
||||
private string _extensions;
|
||||
private AutoResetEvent _exitReceiving;
|
||||
private object _forClose;
|
||||
private object _forSend;
|
||||
private volatile Logger _logger;
|
||||
private uint _nonceCount;
|
||||
private string _origin;
|
||||
private bool _preAuth;
|
||||
private string _protocol;
|
||||
@ -109,8 +114,6 @@ namespace WebSocketSharp
|
||||
_extensions = String.Empty;
|
||||
_forClose = new object ();
|
||||
_forSend = new object ();
|
||||
_origin = String.Empty;
|
||||
_preAuth = false;
|
||||
_protocol = String.Empty;
|
||||
_readyState = WebSocketState.CONNECTING;
|
||||
}
|
||||
@ -119,20 +122,20 @@ namespace WebSocketSharp
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal WebSocket (HttpListenerWebSocketContext context)
|
||||
internal WebSocket (HttpListenerWebSocketContext context, Logger logger)
|
||||
: this ()
|
||||
{
|
||||
_stream = context.Stream;
|
||||
_closeContext = () => context.Close ();
|
||||
init (context);
|
||||
_closeContext = context.Close;
|
||||
init (context, logger);
|
||||
}
|
||||
|
||||
internal WebSocket (TcpListenerWebSocketContext context)
|
||||
internal WebSocket (TcpListenerWebSocketContext context, Logger logger)
|
||||
: this ()
|
||||
{
|
||||
_stream = context.Stream;
|
||||
_closeContext = () => context.Close ();
|
||||
init (context);
|
||||
_closeContext = context.Close;
|
||||
init (context, logger);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -140,14 +143,15 @@ namespace WebSocketSharp
|
||||
#region Public Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocket"/> class with the specified WebSocket URL
|
||||
/// and subprotocols.
|
||||
/// Initializes a new instance of the <see cref="WebSocket"/> class with the
|
||||
/// specified WebSocket URL and subprotocols.
|
||||
/// </summary>
|
||||
/// <param name="url">
|
||||
/// A <see cref="string"/> that contains a WebSocket URL to connect.
|
||||
/// </param>
|
||||
/// <param name="protocols">
|
||||
/// An array of <see cref="string"/> that contains the WebSocket subprotocols if any.
|
||||
/// An array of <see cref="string"/> that contains the WebSocket subprotocols
|
||||
/// if any.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="url"/> is <see langword="null"/>.
|
||||
@ -173,12 +177,13 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSocket"/> class with the specified WebSocket URL,
|
||||
/// OnOpen, OnMessage, OnError, OnClose event handlers and subprotocols.
|
||||
/// Initializes a new instance of the <see cref="WebSocket"/> class with the
|
||||
/// specified WebSocket URL, OnOpen, OnMessage, OnError, OnClose event
|
||||
/// handlers and subprotocols.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This constructor initializes a new instance of the <see cref="WebSocket"/> class and
|
||||
/// establishes a WebSocket connection.
|
||||
/// This constructor initializes a new instance of the <see cref="WebSocket"/>
|
||||
/// class and establishes a WebSocket connection.
|
||||
/// </remarks>
|
||||
/// <param name="url">
|
||||
/// A <see cref="string"/> that contains a WebSocket URL to connect.
|
||||
@ -196,7 +201,8 @@ namespace WebSocketSharp
|
||||
/// An <see cref="OnClose"/> event handler.
|
||||
/// </param>
|
||||
/// <param name="protocols">
|
||||
/// An array of <see cref="string"/> that contains the WebSocket subprotocols if any.
|
||||
/// An array of <see cref="string"/> that contains the WebSocket subprotocols
|
||||
/// if any.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="url"/> is <see langword="null"/>.
|
||||
@ -237,7 +243,8 @@ namespace WebSocketSharp
|
||||
|
||||
internal bool IsOpened {
|
||||
get {
|
||||
return _readyState == WebSocketState.OPEN || _readyState == WebSocketState.CLOSING;
|
||||
return _readyState == WebSocketState.OPEN ||
|
||||
_readyState == WebSocketState.CLOSING;
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,11 +253,13 @@ namespace WebSocketSharp
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compression method used to compress the payload data of the WebSocket Data frame.
|
||||
/// Gets or sets the compression method used to compress the payload data of
|
||||
/// the WebSocket Data frame.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// One of the <see cref="CompressionMethod"/> values that indicates the compression method to use.
|
||||
/// The default is <see cref="CompressionMethod.NONE"/>.
|
||||
/// One of the <see cref="CompressionMethod"/> values that represents the
|
||||
/// compression method to use.
|
||||
/// The default value is <see cref="CompressionMethod.NONE"/>.
|
||||
/// </value>
|
||||
public CompressionMethod Compression {
|
||||
get {
|
||||
@ -258,9 +267,13 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
set {
|
||||
if (IsOpened)
|
||||
{
|
||||
var msg = "A WebSocket connection has already been established.";
|
||||
var msg = !_client
|
||||
? "Set operation of Compression isn't available as a server."
|
||||
: IsOpened
|
||||
? "A WebSocket connection has already been established."
|
||||
: null;
|
||||
|
||||
if (msg != null) {
|
||||
_logger.Error (msg);
|
||||
error (msg);
|
||||
|
||||
@ -272,16 +285,15 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies used in the WebSocket opening handshake.
|
||||
/// Gets the cookies used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An IEnumerable<Cookie> interface that provides an enumerator which supports the iteration
|
||||
/// over the collection of cookies.
|
||||
/// An IEnumerable<Cookie> interface that provides an enumerator which
|
||||
/// supports the iteration over the collection of cookies.
|
||||
/// </value>
|
||||
public IEnumerable<Cookie> Cookies {
|
||||
get {
|
||||
lock (_cookies.SyncRoot)
|
||||
{
|
||||
lock (_cookies.SyncRoot) {
|
||||
return from Cookie cookie in _cookies
|
||||
select cookie;
|
||||
}
|
||||
@ -292,9 +304,10 @@ namespace WebSocketSharp
|
||||
/// Gets the credentials for HTTP authentication (Basic/Digest).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WsCredential"/> that contains the credentials for HTTP authentication.
|
||||
/// A <see cref="NetworkCredential"/> that represents the credentials for
|
||||
/// HTTP authentication. The default value is <see langword="null"/>.
|
||||
/// </value>
|
||||
public WsCredential Credentials {
|
||||
public NetworkCredential Credentials {
|
||||
get {
|
||||
return _credentials;
|
||||
}
|
||||
@ -304,7 +317,8 @@ namespace WebSocketSharp
|
||||
/// Gets the WebSocket extensions selected by the server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the extensions if any. The default is <see cref="String.Empty"/>.
|
||||
/// A <see cref="string"/> that represents the WebSocket extensions if any.
|
||||
/// The default value is <see cref="String.Empty"/>.
|
||||
/// </value>
|
||||
public string Extensions {
|
||||
get {
|
||||
@ -340,9 +354,9 @@ namespace WebSocketSharp
|
||||
/// Gets the logging functions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default logging level is the <see cref="LogLevel.ERROR"/>.
|
||||
/// If you want to change the current logging level, you set the <c>Log.Level</c> property
|
||||
/// to one of the <see cref="LogLevel"/> values which you want.
|
||||
/// The default logging level is the <see cref="LogLevel.ERROR"/>. If you
|
||||
/// change the current logging level, you set the <c>Log.Level</c> property
|
||||
/// to any of the <see cref="LogLevel"/> values.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="Logger"/> that provides the logging functions.
|
||||
@ -361,19 +375,22 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the Origin header used in the WebSocket opening handshake.
|
||||
/// Gets or sets the value of the Origin header used in the WebSocket
|
||||
/// connection request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A <see cref="WebSocket"/> instance does not send the Origin header in the WebSocket opening handshake
|
||||
/// if the value of this property is <see cref="String.Empty"/>.
|
||||
/// The <see cref="WebSocket"/> sends the Origin header if this property has
|
||||
/// any.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// <para>
|
||||
/// A <see cref="string"/> that contains the value of the <see href="http://tools.ietf.org/html/rfc6454#section-7">HTTP Origin header</see> to send.
|
||||
/// The default is <see cref="String.Empty"/>.
|
||||
/// A <see cref="string"/> that represents the value of the
|
||||
/// <see href="http://tools.ietf.org/html/rfc6454#section-7">HTTP Origin
|
||||
/// header</see> to send. The default value is <see langword="null"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The value of the Origin header has the following syntax: <c><scheme>://<host>[:<port>]</c>
|
||||
/// The Origin header has the following syntax:
|
||||
/// <c><scheme>://<host>[:<port>]</c>
|
||||
/// </para>
|
||||
/// </value>
|
||||
public string Origin {
|
||||
@ -383,24 +400,22 @@ namespace WebSocketSharp
|
||||
|
||||
set {
|
||||
string msg = null;
|
||||
if (IsOpened)
|
||||
{
|
||||
if (!_client)
|
||||
msg = "Set operation of Origin isn't available as a server.";
|
||||
else if (IsOpened)
|
||||
msg = "A WebSocket connection has already been established.";
|
||||
}
|
||||
else if (value.IsNullOrEmpty ())
|
||||
{
|
||||
_origin = String.Empty;
|
||||
else if (value.IsNullOrEmpty ()) {
|
||||
_origin = value;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var origin = new Uri (value);
|
||||
if (!origin.IsAbsoluteUri || origin.Segments.Length > 1)
|
||||
msg = "The syntax of value of Origin must be '<scheme>://<host>[:<port>]'.";
|
||||
else {
|
||||
Uri origin;
|
||||
if (!Uri.TryCreate (value, UriKind.Absolute, out origin) ||
|
||||
origin.Segments.Length > 1)
|
||||
msg = "The syntax of Origin must be '<scheme>://<host>[:<port>]'.";
|
||||
}
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
if (msg != null) {
|
||||
_logger.Error (msg);
|
||||
error (msg);
|
||||
|
||||
@ -415,7 +430,8 @@ namespace WebSocketSharp
|
||||
/// Gets the WebSocket subprotocol selected by the server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the subprotocol if any. The default is <see cref="String.Empty"/>.
|
||||
/// A <see cref="string"/> that represents the subprotocol if any.
|
||||
/// The default value is <see cref="String.Empty"/>.
|
||||
/// </value>
|
||||
public string Protocol {
|
||||
get {
|
||||
@ -437,15 +453,17 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback used to validate the certificate supplied by the server.
|
||||
/// Gets or sets the callback used to validate the certificate supplied by
|
||||
/// the server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the value of this property is <see langword="null"/>, the validation does nothing
|
||||
/// with the server certificate, always returns valid.
|
||||
/// If the value of this property is <see langword="null"/>, the validation
|
||||
/// does nothing with the server certificate, always returns valid.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="RemoteCertificateValidationCallback"/> delegate that references the method(s)
|
||||
/// used to validate the server certificate. The default is <see langword="null"/>.
|
||||
/// A <see cref="RemoteCertificateValidationCallback"/> delegate that
|
||||
/// references the method(s) used to validate the server certificate.
|
||||
/// The default value is <see langword="null"/>.
|
||||
/// </value>
|
||||
public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
|
||||
get {
|
||||
@ -453,6 +471,19 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
set {
|
||||
var msg = !_client
|
||||
? "Set operation of ServerCertificateValidationCallback isn't available as a server."
|
||||
: IsOpened
|
||||
? "A WebSocket connection has already been established."
|
||||
: null;
|
||||
|
||||
if (msg != null) {
|
||||
_logger.Error (msg);
|
||||
error (msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_certValidationCallback = value;
|
||||
}
|
||||
}
|
||||
@ -461,7 +492,7 @@ namespace WebSocketSharp
|
||||
/// Gets the WebSocket URL to connect.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="Uri"/> that contains the WebSocket URL to connect.
|
||||
/// A <see cref="Uri"/> that represents the WebSocket URL to connect.
|
||||
/// </value>
|
||||
public Uri Url {
|
||||
get {
|
||||
@ -469,8 +500,7 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
internal set {
|
||||
if (_readyState == WebSocketState.CONNECTING && !_client)
|
||||
_uri = value;
|
||||
_uri = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -684,6 +714,18 @@ namespace WebSocketSharp
|
||||
return Convert.ToBase64String (src);
|
||||
}
|
||||
|
||||
// As client
|
||||
private string createExtensionsRequest ()
|
||||
{
|
||||
var extensions = new StringBuilder (64);
|
||||
if (_compression != CompressionMethod.NONE)
|
||||
extensions.Append (_compression.ToCompressionExtension ());
|
||||
|
||||
return extensions.Length > 0
|
||||
? extensions.ToString ()
|
||||
: String.Empty;
|
||||
}
|
||||
|
||||
// As client
|
||||
private HandshakeRequest createHandshakeRequest ()
|
||||
{
|
||||
@ -693,24 +735,35 @@ namespace WebSocketSharp
|
||||
: _uri.Authority;
|
||||
|
||||
var req = new HandshakeRequest (path);
|
||||
req.AddHeader ("Host", host);
|
||||
var headers = req.Headers;
|
||||
|
||||
if (_origin.Length > 0)
|
||||
req.AddHeader ("Origin", _origin);
|
||||
headers ["Host"] = host;
|
||||
|
||||
req.AddHeader ("Sec-WebSocket-Key", _base64key);
|
||||
if (!_origin.IsNullOrEmpty ())
|
||||
headers ["Origin"] = _origin;
|
||||
|
||||
headers ["Sec-WebSocket-Key"] = _base64key;
|
||||
|
||||
if (!_protocols.IsNullOrEmpty ())
|
||||
req.AddHeader ("Sec-WebSocket-Protocol", _protocols);
|
||||
headers ["Sec-WebSocket-Protocol"] = _protocols;
|
||||
|
||||
var extensions = createRequestExtensions ();
|
||||
var extensions = createExtensionsRequest ();
|
||||
if (extensions.Length > 0)
|
||||
req.AddHeader ("Sec-WebSocket-Extensions", extensions);
|
||||
headers ["Sec-WebSocket-Extensions"] = extensions;
|
||||
|
||||
req.AddHeader ("Sec-WebSocket-Version", _version);
|
||||
headers ["Sec-WebSocket-Version"] = _version;
|
||||
|
||||
if (_preAuth && _credentials != null)
|
||||
req.SetAuthorization (new AuthenticationResponse (_credentials));
|
||||
AuthenticationResponse authRes = null;
|
||||
if (_authChallenge != null && _credentials != null) {
|
||||
authRes = new AuthenticationResponse (
|
||||
_authChallenge, _credentials, _nonceCount);
|
||||
_nonceCount = authRes.NonceCount;
|
||||
}
|
||||
else if (_preAuth)
|
||||
authRes = new AuthenticationResponse (_credentials);
|
||||
|
||||
if (authRes != null)
|
||||
headers ["Authorization"] = authRes.ToString ();
|
||||
|
||||
if (_cookies.Count > 0)
|
||||
req.SetCookies (_cookies);
|
||||
@ -721,14 +774,16 @@ namespace WebSocketSharp
|
||||
// As server
|
||||
private HandshakeResponse createHandshakeResponse ()
|
||||
{
|
||||
var res = new HandshakeResponse ();
|
||||
res.AddHeader ("Sec-WebSocket-Accept", createResponseKey ());
|
||||
var res = new HandshakeResponse (HttpStatusCode.SwitchingProtocols);
|
||||
var headers = res.Headers;
|
||||
|
||||
headers ["Sec-WebSocket-Accept"] = createResponseKey ();
|
||||
|
||||
if (_protocol.Length > 0)
|
||||
res.AddHeader ("Sec-WebSocket-Protocol", _protocol);
|
||||
headers ["Sec-WebSocket-Protocol"] = _protocol;
|
||||
|
||||
if (_extensions.Length > 0)
|
||||
res.AddHeader ("Sec-WebSocket-Extensions", _extensions);
|
||||
headers ["Sec-WebSocket-Extensions"] = _extensions;
|
||||
|
||||
if (_cookies.Count > 0)
|
||||
res.SetCookies (_cookies);
|
||||
@ -740,23 +795,11 @@ namespace WebSocketSharp
|
||||
private HandshakeResponse createHandshakeResponse (HttpStatusCode code)
|
||||
{
|
||||
var res = HandshakeResponse.CreateCloseResponse (code);
|
||||
res.AddHeader ("Sec-WebSocket-Version", _version);
|
||||
res.Headers ["Sec-WebSocket-Version"] = _version;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// As client
|
||||
private string createRequestExtensions ()
|
||||
{
|
||||
var extensions = new StringBuilder (64);
|
||||
if (_compression != CompressionMethod.NONE)
|
||||
extensions.Append (_compression.ToCompressionExtension ());
|
||||
|
||||
return extensions.Length > 0
|
||||
? extensions.ToString ()
|
||||
: String.Empty;
|
||||
}
|
||||
|
||||
private string createResponseKey ()
|
||||
{
|
||||
var buffer = new StringBuilder (_base64key, 64);
|
||||
@ -808,12 +851,12 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
// As server
|
||||
private void init (WebSocketContext context)
|
||||
private void init (WebSocketContext context, Logger logger)
|
||||
{
|
||||
_context = context;
|
||||
_uri = context.Path.ToUri ();
|
||||
_logger = logger;
|
||||
_uri = context.RequestUri;
|
||||
_secure = context.IsSecureConnection;
|
||||
_client = false;
|
||||
}
|
||||
|
||||
private void open ()
|
||||
@ -1168,7 +1211,7 @@ namespace WebSocketSharp
|
||||
int readLen = 0;
|
||||
byte [] buffer = null;
|
||||
|
||||
// Not fragmented
|
||||
// Not fragment
|
||||
if (quo == 0)
|
||||
{
|
||||
buffer = new byte [rem];
|
||||
@ -1219,11 +1262,21 @@ namespace WebSocketSharp
|
||||
{
|
||||
var req = createHandshakeRequest ();
|
||||
var res = sendHandshakeRequest (req);
|
||||
if (!_preAuth && res.IsUnauthorized && _credentials != null)
|
||||
{
|
||||
var challenge = res.AuthChallenge;
|
||||
req.SetAuthorization (new AuthenticationResponse (_credentials, challenge));
|
||||
res = sendHandshakeRequest (req);
|
||||
if (res.IsUnauthorized) {
|
||||
_authChallenge = res.AuthChallenge;
|
||||
if (_credentials != null &&
|
||||
(!_preAuth || _authChallenge.Scheme == "digest")) {
|
||||
if (res.Headers.Contains ("Connection", "close")) {
|
||||
closeClientResources ();
|
||||
setClientStream ();
|
||||
}
|
||||
|
||||
var authRes = new AuthenticationResponse (
|
||||
_authChallenge, _credentials, _nonceCount);
|
||||
_nonceCount = authRes.NonceCount;
|
||||
req.Headers ["Authorization"] = authRes.ToString ();
|
||||
res = sendHandshakeRequest (req);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
@ -1316,16 +1369,22 @@ namespace WebSocketSharp
|
||||
#region Internal Methods
|
||||
|
||||
// As server
|
||||
internal void Close (HttpStatusCode code)
|
||||
internal void Close (HandshakeResponse response)
|
||||
{
|
||||
_readyState = WebSocketState.CLOSING;
|
||||
|
||||
send (createHandshakeResponse (code));
|
||||
send (response);
|
||||
closeServerResources ();
|
||||
|
||||
|
||||
_readyState = WebSocketState.CLOSED;
|
||||
}
|
||||
|
||||
// As server
|
||||
internal void Close (HttpStatusCode code)
|
||||
{
|
||||
Close (createHandshakeResponse (code));
|
||||
}
|
||||
|
||||
// As server
|
||||
internal void Close (CloseEventArgs args, byte [] frameAsBytes, int waitTimeOut)
|
||||
{
|
||||
@ -1824,78 +1883,79 @@ namespace WebSocketSharp
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a <see cref="Cookie"/> used in the WebSocket opening handshake.
|
||||
/// Sets a <see cref="Cookie"/> used in the WebSocket connection request.
|
||||
/// </summary>
|
||||
/// <param name="cookie">
|
||||
/// A <see cref="Cookie"/> that contains an HTTP Cookie to set.
|
||||
/// A <see cref="Cookie"/> that represents an HTTP Cookie to set.
|
||||
/// </param>
|
||||
public void SetCookie (Cookie cookie)
|
||||
{
|
||||
var msg = IsOpened
|
||||
? "A WebSocket connection has already been established."
|
||||
: cookie == null
|
||||
? "'cookie' must not be null."
|
||||
: null;
|
||||
var msg = !_client
|
||||
? "SetCookie isn't available as a server."
|
||||
: IsOpened
|
||||
? "A WebSocket connection has already been established."
|
||||
: cookie == null
|
||||
? "'cookie' must not be null."
|
||||
: null;
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
if (msg != null) {
|
||||
_logger.Error (msg);
|
||||
error (msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_cookies.SyncRoot)
|
||||
{
|
||||
lock (_cookies.SyncRoot) {
|
||||
_cookies.SetOrRemove (cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the credentials for HTTP authentication (Basic/Digest).
|
||||
/// Sets a pair of the <paramref name="username"/> and
|
||||
/// <paramref name="password"/> for HTTP authentication (Basic/Digest).
|
||||
/// </summary>
|
||||
/// <param name="userName">
|
||||
/// A <see cref="string"/> that contains a user name associated with the credentials.
|
||||
/// <param name="username">
|
||||
/// A <see cref="string"/> that represents the user name used to authenticate.
|
||||
/// </param>
|
||||
/// <param name="password">
|
||||
/// A <see cref="string"/> that contains a password for <paramref name="userName"/> associated with the credentials.
|
||||
/// A <see cref="string"/> that represents the password for
|
||||
/// <paramref name="username"/> used to authenticate.
|
||||
/// </param>
|
||||
/// <param name="preAuth">
|
||||
/// <c>true</c> if sends the credentials as a Basic authorization with the first request handshake;
|
||||
/// otherwise, <c>false</c>.
|
||||
/// <c>true</c> if the <see cref="WebSocket"/> sends a Basic authentication
|
||||
/// credentials with the first connection request; otherwise, <c>false</c>.
|
||||
/// </param>
|
||||
public void SetCredentials (string userName, string password, bool preAuth)
|
||||
public void SetCredentials (string username, string password, bool preAuth)
|
||||
{
|
||||
string msg = null;
|
||||
if (IsOpened)
|
||||
{
|
||||
if (!_client)
|
||||
msg = "SetCredentials isn't available as a server.";
|
||||
else if (IsOpened)
|
||||
msg = "A WebSocket connection has already been established.";
|
||||
}
|
||||
else if (userName == null)
|
||||
{
|
||||
else if (username.IsNullOrEmpty ()) {
|
||||
_credentials = null;
|
||||
_preAuth = false;
|
||||
_logger.Warn ("Credentials was set back to the default.");
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = userName.Length > 0 && (userName.Contains (':') || !userName.IsText ())
|
||||
? "'userName' contains an invalid character."
|
||||
else {
|
||||
msg = username.Contains (':') || !username.IsText ()
|
||||
? "'username' contains an invalid character."
|
||||
: !password.IsNullOrEmpty () && !password.IsText ()
|
||||
? "'password' contains an invalid character."
|
||||
: null;
|
||||
}
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
if (msg != null) {
|
||||
_logger.Error (msg);
|
||||
error (msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_credentials = new WsCredential (userName, password, _uri.PathAndQuery);
|
||||
_credentials = new NetworkCredential (
|
||||
username, password, _uri.PathAndQuery);
|
||||
_preAuth = preAuth;
|
||||
}
|
||||
|
||||
|
@ -1,119 +0,0 @@
|
||||
#region License
|
||||
/*
|
||||
* WsCredential.cs
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 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
|
||||
|
||||
using System;
|
||||
|
||||
namespace WebSocketSharp {
|
||||
|
||||
/// <summary>
|
||||
/// Provides the credentials for HTTP authentication (Basic/Digest).
|
||||
/// </summary>
|
||||
public class WsCredential {
|
||||
|
||||
#region Private Fields
|
||||
|
||||
string _domain;
|
||||
string _password;
|
||||
string _userName;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructors
|
||||
|
||||
internal WsCredential()
|
||||
{
|
||||
}
|
||||
|
||||
internal WsCredential(string userName, string password)
|
||||
: this(userName, password, null)
|
||||
{
|
||||
}
|
||||
|
||||
internal WsCredential(string userName, string password, string domain)
|
||||
{
|
||||
_userName = userName;
|
||||
_password = password;
|
||||
_domain = domain;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the user domain associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the name of the user domain associated with the credentials.
|
||||
/// Currently, returns the request uri of a WebSocket opening handshake.
|
||||
/// </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 contains 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 user name associated with the credentials.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the user name associated with the credentials.
|
||||
/// </value>
|
||||
public string UserName {
|
||||
get {
|
||||
return _userName ?? String.Empty;
|
||||
}
|
||||
|
||||
internal set {
|
||||
_userName = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010-2013 sta.blockhead
|
||||
*
|
||||
* Copyright (c) 2010-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
|
||||
@ -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
|
||||
@ -122,11 +122,11 @@ namespace WebSocketSharp
|
||||
return new WsStream (netStream);
|
||||
}
|
||||
|
||||
internal static WsStream CreateServerStream (TcpClient client, bool secure, X509Certificate cert)
|
||||
internal static WsStream CreateServerStream (
|
||||
TcpClient client, X509Certificate cert, bool secure)
|
||||
{
|
||||
var netStream = client.GetStream ();
|
||||
if (secure)
|
||||
{
|
||||
if (secure) {
|
||||
var sslStream = new SslStream (netStream, false);
|
||||
sslStream.AuthenticateAsServer (cert);
|
||||
|
||||
|
@ -116,7 +116,6 @@
|
||||
<Compile Include="WebSocketException.cs" />
|
||||
<Compile Include="AuthenticationChallenge.cs" />
|
||||
<Compile Include="AuthenticationResponse.cs" />
|
||||
<Compile Include="WsCredential.cs" />
|
||||
<Compile Include="LogData.cs" />
|
||||
<Compile Include="LogLevel.cs" />
|
||||
<Compile Include="Logger.cs" />
|
||||
@ -128,6 +127,9 @@
|
||||
<Compile Include="Server\IWebSocketSession.cs" />
|
||||
<Compile Include="Server\WebSocketSessionManager.cs" />
|
||||
<Compile Include="Server\ServerState.cs" />
|
||||
<Compile Include="Net\HttpBasicIdentity.cs" />
|
||||
<Compile Include="Net\HttpDigestIdentity.cs" />
|
||||
<Compile Include="Net\NetworkCredential.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user