/// <summary>
        ///     Are about to send a new message
        /// </summary>
        /// <param name="message">Message to send</param>
        /// <remarks>
        ///     Can be used to prepare the next message. for instance serialize it etc.
        /// </remarks>
        /// <exception cref="NotSupportedException">Message is of a type that the encoder cannot handle.</exception>
        public void Prepare(object message)
        {
            if (message is IWebSocketMessage)
            {
                _message = (IWebSocketMessage)message;
                _message.Payload.Position = 0;
                _totalAmountToSend        = (int)_message.Payload.Length;
            }
            else
            {
                try
                {
                    _httpMessageEncoder.Prepare(message);

                    var httpMessage = message as IHttpMessage;
                    if (WebSocketUtils.IsWebSocketUpgrade(httpMessage))
                    {
                        _handshake = httpMessage;
                    }
                }
                catch (Exception e)
                {
                    throw new InvalidOperationException("This encoder only supports messages deriving from 'HttpMessage' or 'WebSocketMessage'", e);
                }
            }
        }
Exemple #2
0
 public WebSocketFrame(
     WebSocketFin fin,
     WebSocketRsv rsv1,
     WebSocketRsv rsv2,
     WebSocketRsv rsv3,
     WebSocketOpcode opcode,
     WebSocketMask mask,
     byte[] maskingKey,
     Stream payload
     )
     : this(fin, rsv1, rsv2, rsv3, opcode, mask, maskingKey, 0, new byte[0], payload)
 {
     if (payload != null)
     {
         var len = payload.Length;
         if (len < 126)
         {
             _payloadLength    = (byte)len;
             _extPayloadLength = new byte[0];
         }
         else if (len < 0x010000)
         {
             _payloadLength    = (byte)126;
             _extPayloadLength = WebSocketUtils.GetBigEndianBytes((ushort)len);
         }
         else
         {
             _payloadLength    = (byte)127;
             _extPayloadLength = WebSocketUtils.GetBigEndianBytes((ulong)len);
         }
     }
 }
Exemple #3
0
 /// <summary>
 /// Create a new instance of <see cref="WebSocketUpgradeResponse"/>
 /// </summary>
 /// <param name="webSocketKey">Key from the HTTP request.</param>
 public WebSocketUpgradeResponse(string webSocketKey)
     : base(HttpStatusCode.SwitchingProtocols, "Switching Protocols", "HTTP/1.1")
 {
     Headers["Upgrade"]              = "websocket";
     Headers["Connection"]           = "Upgrade";
     Headers["Sec-WebSocket-Accept"] = WebSocketUtils.HashWebSocketKey(webSocketKey);
 }
        /// <summary>
        /// Intercept http messages and look for websocket upgrade requests
        /// </summary>
        /// <param name="message">message from http decoder</param>
        private void OnHttpMessage(object message)
        {
            var httpMessage = message as IHttpMessage;

            // TODO: is there a better way to detect WebSocket upgrade?
            if (WebSocketUtils.IsWebSocketUpgrade(httpMessage))
            {
                _handshake   = httpMessage;
                _isWebSocket = true;
            }

            _messageReceived(message);
        }
Exemple #5
0
 public WebSocketFrame(
     WebSocketFin fin,
     WebSocketOpcode opcode,
     WebSocketMask mask,
     Stream payload
     )
     : this(fin, WebSocketRsv.Off, WebSocketRsv.Off, WebSocketRsv.Off, opcode, mask, mask == WebSocketMask.Mask ? WebSocketUtils.CreateMaskingKey() : new byte[0], payload)
 {
 }
        /// <summary>
        ///     Buffer structure used for socket send operations.
        /// </summary>
        /// <param name="buffer">
        ///     Do note that there are not buffer attached to the structure, you have to assign one yourself using
        ///     <see cref="ISocketBuffer.SetBuffer(int,int)" />. This choice was made
        ///     to prevent unnecessary copy operations.
        /// </param>
        public void Send(ISocketBuffer buffer)
        {
            if (_message == null)
            {
                _httpMessageEncoder.Send(buffer);
            }
            else
            {
                // last send operation did not send all bytes enqueued in the buffer
                // so let's just continue until doing next message
                if (_bytesToSend > 0)
                {
                    buffer.SetBuffer(_buffer, _offset, _bytesToSend);
                    return;
                }

                var offset      = (int)_message.Payload.Position;
                var length      = (int)_message.Payload.Length;
                var frameLength = length - offset;

                var fin = WebSocketFin.Final;
                if (frameLength > WebSocketFrame.FragmentLength)
                {
                    frameLength = WebSocketFrame.FragmentLength;
                    fin         = WebSocketFin.More;
                }
                var opcode = WebSocketOpcode.Continuation;
                if (offset == 0) // first frame
                {
                    opcode = _message.Opcode;
                }

                var buff = new byte[frameLength];
                _message.Payload.Read(buff, 0, buff.Length);
                var payload = new MemoryStream(buff);

                WebSocketFrame frame = new WebSocketFrame(fin, opcode, (_handshake is IHttpRequest) ? WebSocketMask.Mask : WebSocketMask.Unmask, payload);

                using (var stream = new MemoryStream())
                {
                    var header = (int)frame.Fin;
                    header = (header << 1) + (int)frame.Rsv1;
                    header = (header << 1) + (int)frame.Rsv2;
                    header = (header << 1) + (int)frame.Rsv3;
                    header = (header << 4) + (int)frame.Opcode;
                    header = (header << 1) + (int)frame.Mask;
                    header = (header << 7) + (int)frame.PayloadLength;

                    stream.Write(WebSocketUtils.GetBigEndianBytes((ushort)header), 0, 2);

                    if (frame.PayloadLength > 125)
                    {
                        stream.Write(frame.ExtPayloadLength, 0, frame.ExtPayloadLength.Length);
                    }

                    if (frame.Mask == WebSocketMask.Mask)
                    {
                        stream.Write(frame.MaskingKey, 0, frame.MaskingKey.Length);
                        frame.Unmask();
                    }

                    _totalAmountToSend += (int)stream.Length;

                    if (frame.PayloadLength > 0)
                    {
                        frame.Payload.CopyTo(stream);
                    }

                    buffer.UserToken = _message;

                    _buffer      = stream.ToArray();
                    _bytesToSend = _buffer.Length;

                    buffer.SetBuffer(_buffer, 0, _bytesToSend);
                }
            }
        }
        /// <summary>
        /// We've received bytes from the socket. Build a message out of them.
        /// </summary>
        /// <param name="buffer">Buffer</param>
        public void ProcessReadBytes(ISocketBuffer buffer)
        {
            if (!_isWebSocket)
            {
                _httpMessageDecoder.ProcessReadBytes(buffer);
            }
            else
            {
                var receiveBufferOffset      = buffer.Offset;
                var bytesLeftInReceiveBuffer = buffer.BytesTransferred;
                while (true)
                {
                    if (bytesLeftInReceiveBuffer <= 0)
                    {
                        break;
                    }

                    if (_frame == null)
                    {
                        var first  = buffer.Buffer[receiveBufferOffset + 0];
                        var second = buffer.Buffer[receiveBufferOffset + 1];

                        var fin        = (first & 0x80) == 0x80 ? WebSocketFin.Final : WebSocketFin.More;
                        var rsv1       = (first & 0x40) == 0x40 ? WebSocketRsv.On : WebSocketRsv.Off;
                        var rsv2       = (first & 0x20) == 0x20 ? WebSocketRsv.On : WebSocketRsv.Off;
                        var rsv3       = (first & 0x10) == 0x10 ? WebSocketRsv.On : WebSocketRsv.Off;
                        var opcode     = (WebSocketOpcode)(first & 0x0f);
                        var mask       = (second & 0x80) == 0x80 ? WebSocketMask.Mask : WebSocketMask.Unmask;
                        var payloadLen = (byte)(second & 0x7f);

                        receiveBufferOffset      += 2;
                        bytesLeftInReceiveBuffer -= 2;

                        // TODO:
                        // check if valid headers
                        // control frame && payloadLen > 125
                        // control frame && more
                        // not data && compressed

                        var size = payloadLen < 126
                                    ? 0
                                    : payloadLen == 126
                                        ? 2
                                        : 8;

                        var extPayloadLen = new byte[size];
                        for (var i = 0; i < size; i++)
                        {
                            extPayloadLen[i] = buffer.Buffer[receiveBufferOffset + i];
                        }
                        receiveBufferOffset      += size;
                        bytesLeftInReceiveBuffer -= size;

                        var maskingKey = new byte[0];
                        if (mask == WebSocketMask.Mask)
                        {
                            maskingKey = new byte[] {
                                buffer.Buffer[receiveBufferOffset + 0],
                                buffer.Buffer[receiveBufferOffset + 1],
                                buffer.Buffer[receiveBufferOffset + 2],
                                buffer.Buffer[receiveBufferOffset + 3],
                            };
                            receiveBufferOffset      += 4;
                            bytesLeftInReceiveBuffer -= 4;
                        }

                        ulong len = payloadLen < 126
                                    ? payloadLen
                                    : payloadLen == 126
                                        ? WebSocketUtils.ToBigEndianUInt16(extPayloadLen)
                                        : WebSocketUtils.ToBigEndianUInt64(extPayloadLen);

                        _frameContentBytesLeft = (int)len;

                        _frame = new WebSocketFrame(fin, rsv1, rsv2, rsv3, opcode, mask, maskingKey, payloadLen, extPayloadLen, new MemoryStream(_frameContentBytesLeft));

                        if (_frame.Fin == WebSocketFin.More || _frame.Opcode == WebSocketOpcode.Continuation)
                        {
                            _frames.Add(_frame);
                        }
                    }

                    if (_frameContentBytesLeft > 0)
                    {
                        var bytesRead    = BytesProcessed(buffer.Offset, receiveBufferOffset);
                        var bytesToWrite = Math.Min(_frameContentBytesLeft, buffer.BytesTransferred - bytesRead);
                        _frame.Payload.Write(buffer.Buffer, receiveBufferOffset, bytesToWrite);
                        _frameContentBytesLeft   -= bytesToWrite;
                        receiveBufferOffset      += bytesToWrite;
                        bytesLeftInReceiveBuffer -= bytesToWrite;
                    }

                    if (_frameContentBytesLeft == 0)
                    {
                        _frame.Payload.Position = 0;

                        if (_frame.Fin == WebSocketFin.Final)
                        {
                            if (_frame.Opcode == WebSocketOpcode.Continuation)
                            {
                                TriggerMessageReceived(_frames);
                                _frames = new List <WebSocketFrame>();
                            }
                            else
                            {
                                TriggerMessageReceived(new[] { _frame });
                            }
                        }
                        _frame = null;
                    }
                }
            }
        }
Exemple #8
0
        /// <summary>
        /// Handles the upgrade
        /// </summary>
        /// <param name="source">Channel that we've received a request from</param>
        /// <param name="msg">Message received.</param>
        protected override void OnMessage(ITcpChannel source, object msg)
        {
            var httpMessage = msg as IHttpMessage;

            if (WebSocketUtils.IsWebSocketUpgrade(httpMessage))
            {
                if (httpMessage is IHttpRequest) // server mode
                {
                    var args = new WebSocketClientConnectEventArgs(source, (IHttpRequest)httpMessage);
                    WebSocketClientConnect(this, args);

                    if (args.MayConnect)
                    {
                        var webSocketKey = httpMessage.Headers["Sec-WebSocket-Key"];

                        // TODO: why not provide the response in the WebSocketClientConnectEventArgs event?
                        var response = new WebSocketUpgradeResponse(webSocketKey);

                        source.Send(response);

                        WebSocketClientConnected(this,
                                                 new WebSocketClientConnectedEventArgs(source, (IHttpRequest)httpMessage, response));
                    }
                    else
                    {
                        var response = new HttpResponseBase(HttpStatusCode.NotImplemented, "Not Implemented", "HTTP/1.1");
                        if (args.Response != null)
                        {
                            response.Body = args.Response;
                        }

                        source.Send(response);
                    }
                    return;
                }

                if (httpMessage is IHttpResponse) // client mode
                {
                    WebSocketClientConnected(this,
                                             new WebSocketClientConnectedEventArgs(source, null, (IHttpResponse)httpMessage));
                }
            }

            var webSocketMessage = msg as IWebSocketMessage;

            if (webSocketMessage != null)
            {
                // standard message responses handled by listener
                switch (webSocketMessage.Opcode)
                {
                case WebSocketOpcode.Ping:
                    source.Send(new WebSocketMessage(WebSocketOpcode.Pong, webSocketMessage.Payload));
                    return;

                case WebSocketOpcode.Close:
                    source.Send(new WebSocketMessage(WebSocketOpcode.Close));
                    source.Close();

                    WebSocketClientDisconnected(this,
                                                new ClientDisconnectedEventArgs(source, new Exception("WebSocket closed")));
                    return;
                }

                _webSocketMessageReceived(source, webSocketMessage);
                return;
            }

            base.OnMessage(source, msg);
        }