#region MIT License 
 | 
/** 
 | 
 * WsStream.cs 
 | 
 * 
 | 
 * The MIT License 
 | 
 * 
 | 
 * Copyright (c) 2010-2012 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.Generic; 
 | 
using System.Configuration; 
 | 
using System.IO; 
 | 
using System.Net; 
 | 
using System.Net.Sockets; 
 | 
using System.Security.Cryptography.X509Certificates; 
 | 
using System.Text; 
 | 
using WebSocketSharp.Frame; 
 | 
using WebSocketSharp.Net.Security; 
 | 
  
 | 
namespace WebSocketSharp 
 | 
{ 
 | 
  
 | 
    internal class WsStream : IDisposable 
 | 
    { 
 | 
        #region Fields 
 | 
  
 | 
        private Stream _innerStream; 
 | 
        private bool _isSecure; 
 | 
        private Object _forRead; 
 | 
        private Object _forWrite; 
 | 
  
 | 
        #endregion 
 | 
  
 | 
        #region Private Constructor 
 | 
  
 | 
        private WsStream() 
 | 
        { 
 | 
            _forRead = new object(); 
 | 
            _forWrite = new object(); 
 | 
        } 
 | 
  
 | 
        #endregion 
 | 
  
 | 
        #region Public Constructors 
 | 
  
 | 
        public WsStream(NetworkStream innerStream) 
 | 
            : this() 
 | 
        { 
 | 
            if (innerStream == null) 
 | 
                throw new ArgumentNullException("innerStream"); 
 | 
  
 | 
            _innerStream = innerStream; 
 | 
            _isSecure = false; 
 | 
        } 
 | 
  
 | 
        public WsStream(SslStream innerStream) 
 | 
            : this() 
 | 
        { 
 | 
            if (innerStream == null) 
 | 
                throw new ArgumentNullException("innerStream"); 
 | 
  
 | 
            _innerStream = innerStream; 
 | 
            _isSecure = true; 
 | 
        } 
 | 
  
 | 
        #endregion 
 | 
  
 | 
        #region Properties 
 | 
  
 | 
        public bool DataAvailable 
 | 
        { 
 | 
            get 
 | 
            { 
 | 
                return _isSecure 
 | 
                       ? ((SslStream)_innerStream).DataAvailable 
 | 
                       : ((NetworkStream)_innerStream).DataAvailable; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public bool IsSecure 
 | 
        { 
 | 
            get 
 | 
            { 
 | 
                return _isSecure; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        #endregion 
 | 
  
 | 
        #region Private Methods 
 | 
  
 | 
        private int read(byte[] buffer, int offset, int size) 
 | 
        { 
 | 
            var readLen = _innerStream.Read(buffer, offset, size); 
 | 
            if (readLen < size) 
 | 
            { 
 | 
                var msg = String.Format("Data can not be read from {0}.", _innerStream.GetType().Name); 
 | 
                throw new IOException(msg); 
 | 
            } 
 | 
  
 | 
            return readLen; 
 | 
        } 
 | 
  
 | 
        private int readByte() 
 | 
        { 
 | 
            return _innerStream.ReadByte(); 
 | 
        } 
 | 
  
 | 
        private string[] readHandshake() 
 | 
        { 
 | 
            var buffer = new List<byte>(); 
 | 
            while (true) 
 | 
            { 
 | 
                if (Ext.EqualsAndSaveTo(readByte(), '\r', buffer) && 
 | 
                    Ext.EqualsAndSaveTo(readByte(), '\n', buffer) && 
 | 
                    Ext.EqualsAndSaveTo(readByte(), '\r', buffer) && 
 | 
                    Ext.EqualsAndSaveTo(readByte(), '\n', buffer)) 
 | 
                    break; 
 | 
            } 
 | 
  
 | 
            return Encoding.UTF8.GetString(buffer.ToArray()) 
 | 
                   .Replace("\r\n", "\n").Replace("\n\n", "\n").TrimEnd('\n') 
 | 
                   .Split('\n'); 
 | 
        } 
 | 
  
 | 
        private void write(byte[] buffer, int offset, int count) 
 | 
        { 
 | 
            _innerStream.Write(buffer, offset, count); 
 | 
        } 
 | 
  
 | 
        private void writeByte(byte value) 
 | 
        { 
 | 
            _innerStream.WriteByte(value); 
 | 
        } 
 | 
  
 | 
        #endregion 
 | 
  
 | 
        #region Internal Methods 
 | 
  
 | 
        internal static WsStream CreateClientStream(TcpClient client, string host, bool secure) 
 | 
        { 
 | 
            var netStream = client.GetStream(); 
 | 
            if (secure) 
 | 
            { 
 | 
                System.Net.Security.RemoteCertificateValidationCallback validationCb = (sender, certificate, chain, sslPolicyErrors) => 
 | 
                { 
 | 
                    // FIXME: Always returns true 
 | 
                    return true; 
 | 
                }; 
 | 
  
 | 
                var sslStream = new SslStream(netStream, false, validationCb); 
 | 
                sslStream.AuthenticateAsClient(host); 
 | 
  
 | 
                return new WsStream(sslStream); 
 | 
            } 
 | 
  
 | 
            return new WsStream(netStream); 
 | 
        } 
 | 
  
 | 
        internal static WsStream CreateServerStream(TcpClient client, bool secure) 
 | 
        { 
 | 
            var netStream = client.GetStream(); 
 | 
            if (secure) 
 | 
            { 
 | 
                var sslStream = new SslStream(netStream, false); 
 | 
                var certPath = ConfigurationManager.AppSettings["ServerCertPath"]; 
 | 
                sslStream.AuthenticateAsServer(new X509Certificate2(certPath)); 
 | 
  
 | 
                return new WsStream(sslStream); 
 | 
            } 
 | 
  
 | 
            return new WsStream(netStream); 
 | 
        } 
 | 
  
 | 
        internal static WsStream CreateServerStream(WebSocketSharp.Net.HttpListenerContext context) 
 | 
        { 
 | 
            var conn = context.Connection; 
 | 
            var stream = conn.Stream; 
 | 
  
 | 
            return conn.IsSecure 
 | 
                   ? new WsStream((SslStream)stream) 
 | 
                   : new WsStream((NetworkStream)stream); 
 | 
        } 
 | 
  
 | 
        #endregion 
 | 
  
 | 
        #region Public Methods 
 | 
  
 | 
        public void Close() 
 | 
        { 
 | 
            _innerStream.Close(); 
 | 
        } 
 | 
  
 | 
        public void Dispose() 
 | 
        { 
 | 
            _innerStream.Dispose(); 
 | 
        } 
 | 
  
 | 
        public WsFrame ReadFrame() 
 | 
        { 
 | 
            lock (_forRead) 
 | 
            { 
 | 
                try 
 | 
                { 
 | 
                    return WsFrame.Parse(_innerStream); 
 | 
                } 
 | 
                catch 
 | 
                { 
 | 
                    return null; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public void ReadFrameAsync(Action<WsFrame> completed) 
 | 
        { 
 | 
            WsFrame.ParseAsync(_innerStream, completed); 
 | 
        } 
 | 
  
 | 
        public string[] ReadHandshake() 
 | 
        { 
 | 
            lock (_forRead) 
 | 
            { 
 | 
                try 
 | 
                { 
 | 
                    return readHandshake(); 
 | 
                } 
 | 
                catch 
 | 
                { 
 | 
                    return null; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public bool WriteFrame(WsFrame frame) 
 | 
        { 
 | 
            lock (_forWrite) 
 | 
            { 
 | 
                try 
 | 
                { 
 | 
                    var buffer = frame.ToBytes(); 
 | 
                    write(buffer, 0, buffer.Length); 
 | 
  
 | 
                    return true; 
 | 
                } 
 | 
                catch 
 | 
                { 
 | 
                    return false; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public bool WriteHandshake(Handshake handshake) 
 | 
        { 
 | 
            lock (_forWrite) 
 | 
            { 
 | 
                try 
 | 
                { 
 | 
                    var buffer = handshake.ToBytes(); 
 | 
                    write(buffer, 0, buffer.Length); 
 | 
  
 | 
                    return true; 
 | 
                } 
 | 
                catch 
 | 
                { 
 | 
                    return false; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        #endregion 
 | 
    } 
 | 
} 
 |