Example #1
0
 protected override bool CheckAuthentication(Context context)
 {
     if (context.ReceivedByteCount > 8)
     {
         var handshake = new ClientHandshake(context.Header);
         // See if our header had the required information
         if (handshake.IsValid())
         {
             // Optionally check Origin and Location if they're set.
             if (!String.IsNullOrEmpty(Origin))
             {
                 if (handshake.Origin != "http://" + Origin)
                 {
                     return false;
                 }
             }
             if (!String.IsNullOrEmpty(Destination))
             {
                 if (handshake.Host != Destination + ":" + context.Server.Port)
                 {
                     return false;
                 }
             }
             // Generate response handshake for the client
             var serverShake = GenerateResponseHandshake(handshake, context.Server);
             // Send the response handshake
             SendServerHandshake(serverShake, context);
             return true;
         }
     }
     return false;
 }
Example #2
0
 private static void SendServerHandshake(ServerHandshake handshake, Context context)
 {
     // generate a byte array representation of the handshake including the answer to the challenge
     string temp = handshake.ToString();
     byte[] handshakeBytes = Encoding.UTF8.GetBytes(temp);
     context.UserContext.Send(handshakeBytes, true);
 }
        private static void SendServerHandshake(ServerHandshake handshake, Context context)
        {
            // generate a byte array representation of the handshake including the answer to the challenge
            byte[] handshakeBytes = Encoding.UTF8.GetBytes(handshake.ToString());
            Array.Copy(handshake.AnswerBytes, 0, handshakeBytes, handshakeBytes.Length - 16, 16);

            context.UserContext.Send(handshakeBytes, true);
        }
Example #4
0
 /// <summary>
 /// Handles the initial request.
 /// Attempts to process the header that should have been sent.
 /// Otherwise, through magic and wizardry, the client gets disconnected.
 /// </summary>
 /// <param name="context">The user context.</param>
 public virtual void HandleRequest(Context context)
 {
     if (context.IsSetup)
     {
         context.Disconnect();
     }
     else
     {
         ProcessHeader(context);
     }
 }
 /// <summary>
 /// Attempts to authenticates the specified user context.
 /// If authentication fails it kills the connection.
 /// </summary>
 /// <param name="context">The user context.</param>
 public void Authenticate(Context context)
 {
     if (CheckAuthentication(context))
     {
         context.UserContext.Protocol = context.Header.Protocol;
         context.UserContext.RequestPath = context.Header.RequestPath;
         context.Header = null;
         context.IsSetup = true;
         context.UserContext.OnConnected();
     }
     else
     {
         context.Disconnect();
     }
 }
        /// <summary>
        /// Handles the request.
        /// </summary>
        /// <param name="context">The user context.</param>
        public override void HandleRequest(Context context)
        {
            if (context.IsSetup)
            {
                context.UserContext.DataFrame.Append(context.Buffer, true);
                if (context.UserContext.DataFrame.Length <= context.MaxFrameSize)
                {
                    switch (context.UserContext.DataFrame.State)
                    {
                        case DataFrame.DataState.Complete:
                            context.UserContext.OnReceive();
                            break;
                        case DataFrame.DataState.Closed:
                            DataFrame closeFrame = context.UserContext.DataFrame.CreateInstance();
							closeFrame.State = DataFrame.DataState.Closed;
							closeFrame.Append(new byte[] { 0x8 }, true);
							context.UserContext.Send(closeFrame, false, true);
                            break;
                        case DataFrame.DataState.Ping:
                            context.UserContext.DataFrame.State = DataFrame.DataState.Complete;
                            DataFrame dataFrame = context.UserContext.DataFrame.CreateInstance();
                            dataFrame.State = DataFrame.DataState.Pong;
                            List<ArraySegment<byte>> pingData = context.UserContext.DataFrame.AsRaw();
                            foreach (var item in pingData)
                            {
                                dataFrame.Append(item.Array);
                            }
                            context.UserContext.Send(dataFrame);
                            break;
                        case DataFrame.DataState.Pong:
                            context.UserContext.DataFrame.State = DataFrame.DataState.Complete;
                            break;
                    }
                }
                else
                {
                    context.Disconnect(); //Disconnect if over MaxFrameSize
                }
            }
            else
            {
                Authentication.Authenticate(context);
            }
        }
 /// <summary>
 /// Processes the header.
 /// </summary>
 /// <param name="context">The user context.</param>
 public void ProcessHeader(Context context)
 {
     string data = Encoding.UTF8.GetString(context.Buffer, 0, context.ReceivedByteCount);
     //Check first to see if this is a flash socket XML request.
     if (data == "<policy-file-request/>\0")
     {
         //if it is, we access the Access Policy Server instance to send the appropriate response.
         context.Server.AccessPolicyServer.SendResponse(context.Connection);
         context.Disconnect();
     }
     else //If it isn't, process http/websocket header as normal.
     {
         context.Header = new Header(data);
         switch (context.Header.Protocol)
         {
             case Protocol.WebSocketHybi00:
                 context.Handler.UnregisterContext(context);
                 context.Handler = WebSocket.hybi00.Handler.Instance;
                 context.UserContext.DataFrame = new WebSocket.hybi00.DataFrame();
                 context.Handler.RegisterContext(context);
                 break;
             case Protocol.WebSocketRFC6455:
                 context.Handler.UnregisterContext(context);
                 context.Handler = WebSocket.rfc6455.Handler.Instance;
                 context.UserContext.DataFrame = new WebSocket.rfc6455.DataFrame();
                 context.Handler.RegisterContext(context);
                 break;
             default:
                 context.Header.Protocol = Protocol.None;
                 break;
         }
         if (context.Header.Protocol != Protocol.None)
         {
             context.Handler.HandleRequest(context);
         }
         else
         {
             context.UserContext.Send(Response.NotImplemented, true, true);
         }
     }
 }
        protected override bool CheckAuthentication(Context context)
        {
            if (context.ReceivedByteCount > 8)
            {
                var handshake =
                    new ClientHandshake(new ArraySegment<byte>(context.Buffer, context.ReceivedByteCount - 8, 8),
                                        context.Header);
                // See if our header had the required information
                if (handshake.IsValid())
                {
                    // Optionally check Origin and Location if they're set.
                    if (!String.IsNullOrEmpty(Origin))
                    {
                        var expectedOrigin = Origin;
                        if (!Origin.Contains("://"))
                        {
                            expectedOrigin = "http://" + Origin;
                        }

                        if (!handshake.Origin.Equals(expectedOrigin, StringComparison.InvariantCultureIgnoreCase))
                        {
                            return false;
                        }
                    }
                    if (!String.IsNullOrEmpty(Destination))
                    {
                        if (handshake.Host != Destination + ":" + context.Server.Port)
                        {
                            return false;
                        }
                    }
                    // Generate response handshake for the client
                    var serverShake = GenerateResponseHandshake(handshake, context.Server);
                    // Send the response handshake
                    SendServerHandshake(serverShake, context);
                    return true;
                }
            }
            return false;
        }
Example #9
0
        /// <summary>
        /// Fires when a client connects.
        /// </summary>
        /// <param name="result">null</param>
        protected void OnRunClient(IAsyncResult result)
        {
            bool connectError = false;
            try
            {
                _client.EndConnect(result);
            }
            catch
            {
                Disconnect();
                connectError = true;
            }

            using (_context = new Context(null, _client))
            {
                _context = new Context(null, _client);
                _context.BufferSize = 512;
                _context.UserContext.DataFrame = new DataFrame();
                _context.UserContext.SetOnConnect(OnConnect);
                _context.UserContext.SetOnConnected(OnConnected);
                _context.UserContext.SetOnDisconnect(OnDisconnect);
                _context.UserContext.SetOnSend(OnSend);
                _context.UserContext.SetOnReceive(OnReceive);
                _context.UserContext.OnConnect();

                if (connectError)
                {
                    _context.UserContext.OnDisconnect();
                    return;
                }

                lock (ContextMapping)
                {
                    ContextMapping[_context] = this;
                }

                lock (NewClients)
                {
                    NewClients.Enqueue(_context);
                }
            }
        }
 private void StartReceive(Context _context)
 {
     try
     {
         if (!_context.ReceiveEventArgs_StartAsync())
         {
             ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
         }
     }
     catch (OperationCanceledException) { }
     catch (SocketException ex)
     {
         //logger.Error("SocketException in StartReceive", ex);
         _context.UserContext.LatestException = ex;
         _context.Disconnect();
     }
 }
        private void SetupContext(Context context)
        {
            _context.ReceiveEventArgs.UserToken = _context;
            _context.ReceiveEventArgs.Completed += ReceiveEventArgs_Completed;
            _context.ReceiveEventArgs.SetBuffer(_context.Buffer, 0, _context.Buffer.Length);

            if (_context.Connection != null && _context.Connection.Connected)
            {
                _context.ReceiveReady.Wait();

                // mjb

                /*
                if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
                {
                    ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
                }
                 */

                if (_context.SslStream != null)
                {
                    ReceiveWorker rw = new ReceiveWorker() { webSocketClient = this, context = _context };
                    Thread tw = new Thread(rw.Receive);
                    tw.Start();
                }
                else
                {
                    if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
                    {
                        ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
                    }
                }

                if (!IsAuthenticated)
                {

                    Authenticate();
                }
            }
        }
        ///<summary>
        /// Starts the asynchronous connection process.
        /// User callbacks are: OnConnected() when successful - or OnDisconnect() in case of failure.
        /// UserContext.Data will contain the data object passed here.
        /// Timeout is defined by the TCP stack.
        ///</summary>
        public void BeginConnect(object data)
        {
            if (_client != null) return;

            _context = new Context(null, null);
            _context.BufferSize = 512;
            _context.UserContext.DataFrame = new DataFrame();
            _context.UserContext.SetOnConnect(OnConnect);
            _context.UserContext.SetOnConnected(OnConnected);
            _context.UserContext.SetOnDisconnect(OnDisconnect);
            _context.UserContext.SetOnSend(OnSend);
            _context.UserContext.SetOnReceive(OnReceive);
            _context.UserContext.Data = data;

            try
            {
                ReadyState = ReadyStates.CONNECTING;
                _context.UserContext.OnConnect();

                _client = new TcpClient();
                _context.Connection = _client;
                _connecting = true;
                _client.BeginConnect(_host, _port, OnClientConnected, null);
            }
            catch (Exception ex)
            {
                Disconnect();
                _context.UserContext.LatestException = ex;
                _context.UserContext.OnDisconnect();
            }
        }
 /// <summary>
 /// Fires when a client connects.
 /// </summary>
 /// <param name="data">The TCP Connection.</param>
 protected override void OnRunClient(object data)
 {
     var connection = (TcpClient)data;
     using (var context = new Context(this, connection))
     {
         context.UserContext.ClientAddress = context.Connection.Client.RemoteEndPoint;
         context.UserContext.SetOnConnect(OnConnect);
         context.UserContext.SetOnConnected(OnConnected);
         context.UserContext.SetOnDisconnect(OnDisconnect);
         context.UserContext.SetOnSend(OnSend);
         context.UserContext.SetOnReceive(OnReceive);
         context.BufferSize = BufferSize;
         context.UserContext.OnConnect();
         while (context.Connected)
         {
             if (context.ReceiveReady.Wait(TimeOut))
             {
                 try
                 {
                     context.Connection.Client.BeginReceive(context.Buffer, 0, context.Buffer.Length,
                                                            SocketFlags.None, DoReceive, context);
                 }
                 catch (SocketException)
                 {
                     break;
                 }
             }
             else
             {
                 break;
             }
         }
     }
 }
Example #14
0
        private void SetupContext(Context _context)
        {
            _context.ReceiveEventArgs.UserToken = _context;
            _context.ReceiveEventArgs.Completed += ReceiveEventArgs_Completed;
            _context.ReceiveEventArgs.SetBuffer(_context.Buffer, 0, _context.Buffer.Length);

            StartReceive(_context);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="UserContext"/> class.
 /// </summary>
 /// <param name="context">The user context.</param>
 public UserContext(Context context)
 {
     Context = context;
     UniqueKey = Guid.NewGuid().ToString("N");
 }
Example #16
0
 /// <summary>
 /// Initializes a new instance of the <see cref="UserContext"/> class.
 /// </summary>
 /// <param name="context">The user context.</param>
 public UserContext(Context context)
 {
     Context = context;
 }
Example #17
0
        private void SetupContext(Context context)
        {
            _context.ReceiveEventArgs.UserToken = _context;
            _context.ReceiveEventArgs.Completed += ReceiveEventArgs_Completed;
            _context.ReceiveEventArgs.SetBuffer(_context.Buffer, 0, _context.Buffer.Length);

            if (_context.Connection != null && _context.Connection.Connected)
            {
                _context.ReceiveReady.Wait();

                if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
                {
                    ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
                }

                if (!IsAuthenticated)
                {

                    Authenticate();
                }
            }
        }
Example #18
0
        private bool CheckAuthenticationResponse(Context context)
        {
            var receivedData = context.UserContext.DataFrame.ToString();
            var header = new Header(receivedData);
            var handshake = new ServerHandshake(header);

            if (Authentication.GenerateAccept(_handshake.Key) != handshake.Accept) return false;

            if (SubProtocols != null)
            {
                if (header.SubProtocols == null)
                {
                    return false;
                }

                foreach (var s in SubProtocols)
                {
                    if (header.SubProtocols.Contains(s) && String.IsNullOrEmpty(CurrentProtocol))
                    {
                        CurrentProtocol = s;
                    }

                }
                if(String.IsNullOrEmpty(CurrentProtocol))
                {
                    return false;
                }
            }

            ReadyState = ReadyStates.OPEN;
            IsAuthenticated = true;
            _connecting = false;
            context.UserContext.OnConnected();
            return true;
        }
Example #19
0
 private void StartReceive(Context _context)
 {
     if (_context.ReceiveReady.Wait(TimeOut))
     {
         try
         {
             if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
             {
                 ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
             }
         }
         catch
         {
             //logger.Error("SocketException in ReceieveAsync", ex);
             _context.Disconnect();
         }
     }
     else
     {
         //logger.Error("Timeout waiting for ReceiveReady");
         _context.Disconnect();
     }
 }
        private void StartReceive(Context _context)
        {
            if (_context.ReceiveReady.Wait(TimeOut))
            {
                try
                {
                    // mjb
                    /*
                    if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
                    {
                        ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
                    }
                     */

                    if (_context.SslStream != null)
                    {
                        ReceiveWorker rw = new ReceiveWorker() { context = _context };
                        Thread tw = new Thread(rw.Receive);
                        tw.Start();
                    }
                    else
                    {
                        if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
                        {
                            ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
                        }
                    }

                }
                catch (SocketException ex)
                {
                    //logger.Error("SocketException in ReceieveAsync", ex);
                    _context.Disconnect();
                }
            }
            else
            {
                //logger.Error("Timeout waiting for ReceiveReady");
                _context.Disconnect();
            }
        }
Example #21
0
        private void ReceiveData(Context context)
        {
            if (!IsAuthenticated)
            {
                var someBytes = new byte[context.ReceivedByteCount];
                Array.Copy(context.Buffer, 0, someBytes, 0, context.ReceivedByteCount);
                context.UserContext.DataFrame.Append(someBytes);
                var authenticated = CheckAuthenticationResponse(context);
                context.UserContext.DataFrame.Reset();

                if (!authenticated)
                {
                    Disconnect();
                }
            }
            else
            {
                context.UserContext.DataFrame.Append(context.Buffer, true);
                if (context.UserContext.DataFrame.State == Handlers.WebSocket.DataFrame.DataState.Complete)
                {
                    context.UserContext.OnReceive();
                    context.UserContext.DataFrame.Reset();
                }
            }
        }
        /// <summary>
        /// Fires when a client connects.
        /// </summary>
        /// <param name="result">null</param>
        protected void OnRunClient(IAsyncResult result)
        {
            try
            {
                _client.EndConnect(result);
            }
            catch (Exception)
            {
                Disconnect();
                OnFailedConnection(null);
                return;
            }

            using (_context = new Context(null, _client))
            {
                _context.BufferSize = 512;
                _context.UserContext.DataFrame = new DataFrame();
                _context.UserContext.SetOnConnect(OnConnect);
                _context.UserContext.SetOnConnected(OnConnected);
                _context.UserContext.SetOnDisconnect(OnDisconnect);
                _context.UserContext.SetOnSend(OnSend);
                _context.UserContext.SetOnReceive(OnReceive);
                _context.UserContext.OnConnect();


                while (_context.Connection.Connected)
                {
                    _context.ReceiveReady.Wait();

                    try
                    {
                        _context.Connection.Client.BeginReceive(_context.Buffer, 0, _context.Buffer.Length, SocketFlags.None, DoReceive, _context);
                    }
                    catch (Exception)
                    {
                        break;
                    }

                    if (!IsAuthenticated)
                    {
                        Authenticate();
                    }
                }
            }

            Disconnect();
        }
        /// <summary>
        /// Handles the request.
        /// </summary>
        /// <param name="context">The user context.</param>
        public override void HandleRequest(Context context)
        {
            int count = 0;
            if (context.IsSetup)
            {
                int remaining = context.ReceivedByteCount;
                while (remaining > 0)
                {
                    count++;
                    // add bytes to existing or empty frame
                    int readCount = context.UserContext.DataFrame.Append(context.Buffer, remaining, true);

                    if (readCount <= 0)
                    {
                        break; // partial header
                    }
                    else if (context.UserContext.DataFrame.Length >= context.MaxFrameSize)
                    {
                        context.Disconnect(); //Disconnect if over MaxFrameSize
                        break;
                    }
                    else
                    {
                        switch (context.UserContext.DataFrame.State)
                        {
                            case DataFrame.DataState.Complete:
                                context.UserContext.OnReceive();
                                break;
                            case DataFrame.DataState.Closed:
                                context.UserContext.DataFrame.State = DataFrame.DataState.Complete;
                                // see http://stackoverflow.com/questions/17176827/websocket-close-packet, DataState.Closed is only set by rfc6455
                                var closeFrame = new byte[] { 0x88, 0x00 };
                                context.UserContext.Send(closeFrame, raw:true, close:true);
                                break;
                            case DataFrame.DataState.Ping:
                                context.UserContext.DataFrame.State = DataFrame.DataState.Complete;
                                DataFrame dataFrame = context.UserContext.DataFrame.CreateInstance();
                                dataFrame.State = DataFrame.DataState.Pong;
                                List<ArraySegment<byte>> pingData = context.UserContext.DataFrame.AsRaw();
                                foreach (var item in pingData)
                                {
                                    dataFrame.Append(item.Array);
                                }
                                context.UserContext.Send(dataFrame);
                                break;
                            case DataFrame.DataState.Pong:
                                context.UserContext.DataFrame.State = DataFrame.DataState.Complete;
                                break;
                        }

                        remaining -= readCount; // process rest of received bytes
                        if (remaining > 0)
                        {
                            context.Reset(); // starts new message when DataState.Complete
                            // move remaining bytes to beginning of array
                            Array.Copy(context.Buffer, readCount, context.Buffer, 0, remaining);
                        }
                    }
                }
            }
            else
            {
                Authentication.Authenticate(context);
            }
        }
Example #24
0
 /// <summary>
 /// Sends the specified data.
 /// </summary>
 /// <param name="dataFrame">The data.</param>
 /// <param name="context">The user context.</param>
 /// <param name="raw">whether or not to send raw data</param>
 /// <param name="close">if set to <c>true</c> [close].</param>
 public void Send(DataFrame dataFrame, Context context, bool raw = false, bool close = false)
 {
     if (context.Connected)
     {
         AsyncCallback callback = EndSend;
         if (close)
         {
             callback = EndSendAndClose;
         }
         context.SendReady.Wait();
         try
         {
             List<ArraySegment<byte>> data = raw ? dataFrame.AsRaw() : dataFrame.AsFrame();
             context.Connection.Client.BeginSend(data, SocketFlags.None,
                                                 callback,
                                                 context);
         }
         catch
         {
             context.Disconnect();
         }
     }
 }
Example #25
0
        /// <summary>
        /// Fires when a client connects.
        /// </summary>
        /// <param name="data">The TCP Connection.</param>
        protected override void OnRunClient(object data)
        {
            var connection = (TcpClient)data;
            var context = new Context(this, connection);

            context.UserContext.ClientAddress = context.Connection.Client.RemoteEndPoint;
            context.UserContext.SetOnConnect(OnConnect);
            context.UserContext.SetOnConnected(OnConnected);
            context.UserContext.SetOnDisconnect(OnDisconnect);
            context.UserContext.SetOnSend(OnSend);
            context.UserContext.SetOnReceive(OnReceive);
            context.BufferSize = BufferSize;
            context.UserContext.OnConnect();

            if (context.Connected)
            {
                lock (ContextMapping)
                {
                    ContextMapping[context] = this;
                }

                ContextQueue.Enqueue(context);
            }
        }
Example #26
0
 /// <summary>
 /// Sends the specified data.
 /// </summary>
 /// <param name="dataFrame">The data.</param>
 /// <param name="context">The user context.</param>
 /// <param name="raw">whether or not to send raw data</param>
 /// <param name="close">if set to <c>true</c> [close].</param>
 public void Send(DataFrame dataFrame, Context context, bool raw = false, bool close = false)
 {
     if (context.Connected)
     {
         HandlerMessage message = new HandlerMessage { DataFrame = dataFrame, Context = context, IsRaw = raw, DoClose = close };
         MessageQueue.Enqueue(message);
     }
 }
Example #27
0
        private void StartReceive(Context _context)
        {
            if (!_context.Connected)
                return;

            try
            {
                if (_context.ReceiveReady.Wait(TimeOut, cancellation.Token))
                {
                    try
                    {
                        if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
                        {
                            ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
                        }
                    }
                    catch (SocketException ex)
                    {
                        //logger.Error("SocketException in ReceieveAsync", ex);
                        _context.Disconnect();
                    }
                }
                else
                {
                    //logger.Error("Timeout waiting for ReceiveReady");
                    _context.Disconnect();
                }
            }
            catch (OperationCanceledException) { }
        }
Example #28
0
 public void UnregisterContext(Context context)
 {
     context.SendEventArgs.Completed -= SendEventArgs_Completed;
 }
 protected abstract bool CheckAuthentication(Context context);
        private void ReceiveData(Context context)
        {
            if (!IsAuthenticated)
            {
                var someBytes = new byte[context.ReceivedByteCount];
                Array.Copy(context.Buffer, 0, someBytes, 0, context.ReceivedByteCount);
                context.UserContext.DataFrame.Append(someBytes);
                var authenticated = CheckAuthenticationResponse(context);
                context.UserContext.DataFrame.Reset();

                if (!authenticated)
                {
                    Disconnect();
                    _context.UserContext.LatestException = new Exception("could not authenticate web socket server");
                    _context.UserContext.OnDisconnect();
                }
            }
            else
            {
                int remaining = context.ReceivedByteCount;
                while (remaining > 0)
                {
                    // add bytes to existing or empty frame
                    int readCount = context.UserContext.DataFrame.Append(context.Buffer, remaining, true);
                    if (readCount <= 0)
                    {
                        break; // partial header
                    }
                    else if (context.UserContext.DataFrame.State == Handlers.WebSocket.DataFrame.DataState.Complete)
                    {
                        // pass frame to user code and start new frame
                        context.UserContext.OnReceive();
                        context.UserContext.DataFrame.Reset();
                    }

                    remaining -= readCount; // process rest of received bytes
                    if (remaining > 0)
                    {
                        // move remaining bytes to beginning of array
                        Array.Copy(context.Buffer, readCount, context.Buffer, 0, remaining);
                    }
                }
            }
        }