diff --git a/websocket-sharp/IProxy.cs b/websocket-sharp/IProxy.cs new file mode 100644 index 000000000..ca58261bc --- /dev/null +++ b/websocket-sharp/IProxy.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebSocketSharp.Net; +using System.Text; + +namespace WebSocketSharp +{ + public interface IProxy + { + System.Net.Sockets.TcpClient ConnectThroughProxy(Uri DestinationUri); + } +} diff --git a/websocket-sharp/ProxyHTTP.cs b/websocket-sharp/ProxyHTTP.cs new file mode 100644 index 000000000..dc2ca53ad --- /dev/null +++ b/websocket-sharp/ProxyHTTP.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using WebSocketSharp.Net; + +namespace WebSocketSharp +{ + public class ProxyHTTP : IProxy + { + private Logger _logger; + private Uri _proxyUri; + private NetworkCredential _proxyCredentials; + private System.IO.Stream _stream; + System.Net.Sockets.TcpClient _tcpClient; + + /// + /// Occurs when the gets an error. + /// + public event EventHandler OnError; + + public ProxyHTTP(string url, string username, string password) + { + _logger = new Logger(); + + if (url.IsNullOrEmpty()) { + _logger.Warn("The url and credentials for the proxy are initialized."); + _proxyUri = null; + _proxyCredentials = null; + + return; + } + + _proxyUri = new Uri(url); + + if (username.IsNullOrEmpty()) { + _logger.Warn("The credentials for the proxy are initialized."); + _proxyCredentials = null; + + return; + } + + _proxyCredentials = + new NetworkCredential( + username, password, String.Format("{0}:{1}", _proxyUri.DnsSafeHost, _proxyUri.Port) + ); + } + + public System.Net.Sockets.TcpClient ConnectThroughProxy(Uri uri) + { + _tcpClient = new System.Net.Sockets.TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); + _tcpClient.NoDelay = true; + + _stream = _tcpClient.GetStream(); + + var req = HttpRequest.CreateConnectRequest(uri); + var res = sendHttpRequest(req, 90000); + if (res.IsProxyAuthenticationRequired) { + var chal = res.Headers["Proxy-Authenticate"]; + _logger.Warn( + String.Format("Received a proxy authentication requirement for '{0}'.", chal)); + + if (chal.IsNullOrEmpty()) + throw new WebSocketException("No proxy authentication challenge is specified."); + + var authChal = AuthenticationChallenge.Parse(chal); + if (authChal == null) + throw new WebSocketException("An invalid proxy authentication challenge is specified."); + + if (_proxyCredentials != null) { + if (res.HasConnectionClose) { + releaseClientResources(); + _tcpClient = new System.Net.Sockets.TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); + _tcpClient.NoDelay = true; + _stream = _tcpClient.GetStream(); + } + + var authRes = new AuthenticationResponse(authChal, _proxyCredentials, 0); + req.Headers["Proxy-Authorization"] = authRes.ToString(); + res = sendHttpRequest(req, 15000); + } + + if (res.IsProxyAuthenticationRequired) + throw new WebSocketException("A proxy authentication is required."); + } + + if (res.StatusCode[0] != '2') + throw new WebSocketException( + "The proxy has failed a connection to the requested host and port."); + return _tcpClient; + } + + private void releaseClientResources() + { + if (_stream != null) { + _stream.Dispose(); + _stream = null; + } + + if (_tcpClient != null) { + _tcpClient.Close(); + _tcpClient = null; + } + } + + private HttpResponse sendHttpRequest(HttpRequest request, int millisecondsTimeout) + { + _logger.Debug("A request to the server:\n" + request.ToString()); + var res = request.GetResponse(_stream, millisecondsTimeout); + _logger.Debug("A response to this request:\n" + res.ToString()); + + return res; + } + + private static bool checkParametersForSetProxy(string url, string username, string password, out string message) + { + message = null; + + if (url.IsNullOrEmpty()) + return true; + + Uri uri; + if (!Uri.TryCreate(url, UriKind.Absolute, out uri) + || uri.Scheme != "http" + || uri.Segments.Length > 1 + ) { + message = "'url' is an invalid URL."; + return false; + } + + if (username.IsNullOrEmpty()) + return true; + + if (username.Contains(':') || !username.IsText()) { + message = "'username' contains an invalid character."; + return false; + } + + if (password.IsNullOrEmpty()) + return true; + + if (!password.IsText()) { + message = "'password' contains an invalid character."; + return false; + } + + return true; + } + + private void error(string message, Exception exception) + { + try + { + OnError.Emit(this, new ErrorEventArgs(message, exception)); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + } + } + } +} diff --git a/websocket-sharp/WebSocket.cs b/websocket-sharp/WebSocket.cs index 4167d44bf..3e7d634ba 100644 --- a/websocket-sharp/WebSocket.cs +++ b/websocket-sharp/WebSocket.cs @@ -103,8 +103,7 @@ public class WebSocket : IDisposable private string _protocol; private string[] _protocols; private bool _protocolsRequested; - private NetworkCredential _proxyCredentials; - private Uri _proxyUri; + private IProxy _proxyManager; private volatile WebSocketState _readyState; private ManualResetEvent _receivingExited; private int _retryCountForConnect; @@ -880,43 +879,6 @@ private static bool checkParametersForSetCredentials ( return true; } - private static bool checkParametersForSetProxy ( - string url, string username, string password, out string message - ) - { - message = null; - - if (url.IsNullOrEmpty ()) - return true; - - Uri uri; - if (!Uri.TryCreate (url, UriKind.Absolute, out uri) - || uri.Scheme != "http" - || uri.Segments.Length > 1 - ) { - message = "'url' is an invalid URL."; - return false; - } - - if (username.IsNullOrEmpty ()) - return true; - - if (username.Contains (':') || !username.IsText ()) { - message = "'username' contains an invalid character."; - return false; - } - - if (password.IsNullOrEmpty ()) - return true; - - if (!password.IsText ()) { - message = "'password' contains an invalid character."; - return false; - } - - return true; - } - private bool checkReceivedFrame (WebSocketFrame frame, out string message) { message = null; @@ -1871,82 +1833,48 @@ private bool sendHttpResponse (HttpResponse response) } // As client - private void sendProxyConnectRequest () + private void setClientStream() { - var req = HttpRequest.CreateConnectRequest (_uri); - var res = sendHttpRequest (req, 90000); - if (res.IsProxyAuthenticationRequired) { - var chal = res.Headers["Proxy-Authenticate"]; - _logger.Warn ( - String.Format ("Received a proxy authentication requirement for '{0}'.", chal)); - - if (chal.IsNullOrEmpty ()) - throw new WebSocketException ("No proxy authentication challenge is specified."); - - var authChal = AuthenticationChallenge.Parse (chal); - if (authChal == null) - throw new WebSocketException ("An invalid proxy authentication challenge is specified."); - - if (_proxyCredentials != null) { - if (res.HasConnectionClose) { - releaseClientResources (); - _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); - } - - var authRes = new AuthenticationResponse (authChal, _proxyCredentials, 0); - req.Headers["Proxy-Authorization"] = authRes.ToString (); - res = sendHttpRequest (req, 15000); + if (_proxyManager != null) { + _tcpClient = _proxyManager.ConnectThroughProxy(_uri); + if (_tcpClient == null) { + throw new WebSocketException("Proxy failed to create stream."); + } + _tcpClient.NoDelay = true; + _stream = _tcpClient.GetStream(); } - - if (res.IsProxyAuthenticationRequired) - throw new WebSocketException ("A proxy authentication is required."); - } - - if (res.StatusCode[0] != '2') - throw new WebSocketException ( - "The proxy has failed a connection to the requested host and port."); - } - - // As client - private void setClientStream () - { - if (_proxyUri != null) { - _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); - sendProxyConnectRequest (); - } - else { - _tcpClient = new TcpClient (_uri.DnsSafeHost, _uri.Port); - _stream = _tcpClient.GetStream (); - } - - if (_secure) { - var conf = SslConfiguration; - var host = conf.TargetHost; - if (host != _uri.DnsSafeHost) - throw new WebSocketException ( - CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified."); - - try { - var sslStream = new SslStream ( - _stream, - false, - conf.ServerCertificateValidationCallback, - conf.ClientCertificateSelectionCallback); - - sslStream.AuthenticateAsClient ( - host, - conf.ClientCertificates, - conf.EnabledSslProtocols, - conf.CheckCertificateRevocation); - - _stream = sslStream; + else { + _tcpClient = new TcpClient(_uri.DnsSafeHost, _uri.Port); + _tcpClient.NoDelay = true; + _stream = _tcpClient.GetStream(); } - catch (Exception ex) { - throw new WebSocketException (CloseStatusCode.TlsHandshakeFailure, ex); + + if (_secure) { + var conf = SslConfiguration; + var host = conf.TargetHost; + if (host != _uri.DnsSafeHost) + throw new WebSocketException( + CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified."); + + try { + var sslStream = new SslStream( + _stream, + false, + conf.ServerCertificateValidationCallback, + conf.ClientCertificateSelectionCallback); + + sslStream.AuthenticateAsClient( + host, + conf.ClientCertificates, + conf.EnabledSslProtocols, + conf.CheckCertificateRevocation); + + _stream = sslStream; + } + catch (Exception ex) { + throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, ex); + } } - } } private void startReceiving () @@ -3618,85 +3546,37 @@ public void SetCredentials (string username, string password, bool preAuth) } /// - /// Sets the HTTP proxy server URL to connect through, and if necessary, - /// a pair of and for - /// the proxy server authentication (Basic/Digest). + /// Sets the proxy server to connect through, and all the necessary configurations will pass through the interface /// /// /// This method is not available in a server. /// - /// - /// - /// A that represents the HTTP proxy server URL to - /// connect through. The syntax must be http://<host>[:<port>]. - /// + /// /// - /// If is or empty, - /// the url and credentials for the proxy will be initialized, - /// and the will not use the proxy to + /// A that represents the proxy type to /// connect through. /// /// - /// - /// - /// A that represents the user name used to authenticate. - /// - /// - /// If is or empty, - /// the credentials for the proxy will be initialized and not be sent. - /// - /// - /// - /// A that represents the password for - /// used to authenticate. - /// - public void SetProxy (string url, string username, string password) + public void SetProxy(IProxy proxy) { - string msg; - if (!checkIfAvailable (true, false, true, false, false, true, out msg)) { - _logger.Error (msg); - error ("An error has occurred in setting the proxy.", null); - - return; - } - - if (!checkParametersForSetProxy (url, username, password, out msg)) { - _logger.Error (msg); - error ("An error has occurred in setting the proxy.", null); - - return; - } - - lock (_forState) { - if (!checkIfAvailable (true, false, false, true, out msg)) { - _logger.Error (msg); - error ("An error has occurred in setting the proxy.", null); - - return; - } - - if (url.IsNullOrEmpty ()) { - _logger.Warn ("The url and credentials for the proxy are initialized."); - _proxyUri = null; - _proxyCredentials = null; + string msg; + if (!checkIfAvailable(true, false, true, false, false, true, out msg)) { + _logger.Error(msg); + error("An error has occurred in setting the proxy.", null); - return; + return; } - _proxyUri = new Uri (url); + lock (_forState) { + if (checkIfAvailable(true, false, false, true, out msg) == false) { + _logger.Error(msg); + error("An error has occurred in setting the proxy.", null); - if (username.IsNullOrEmpty ()) { - _logger.Warn ("The credentials for the proxy are initialized."); - _proxyCredentials = null; + return; + } - return; + _proxyManager = proxy; } - - _proxyCredentials = - new NetworkCredential ( - username, password, String.Format ("{0}:{1}", _uri.DnsSafeHost, _uri.Port) - ); - } } #endregion diff --git a/websocket-sharp/WebSocketException.cs b/websocket-sharp/WebSocketException.cs index 81d7c8081..9fed334a9 100644 --- a/websocket-sharp/WebSocketException.cs +++ b/websocket-sharp/WebSocketException.cs @@ -54,7 +54,7 @@ internal WebSocketException (Exception innerException) { } - internal WebSocketException (string message) + public WebSocketException (string message) : this (CloseStatusCode.Abnormal, message, null) { } diff --git a/websocket-sharp/websocket-sharp.csproj b/websocket-sharp/websocket-sharp.csproj index 0860c0313..235437c54 100644 --- a/websocket-sharp/websocket-sharp.csproj +++ b/websocket-sharp/websocket-sharp.csproj @@ -64,6 +64,8 @@ + +