Пример #1
0
        /// <summary>
        /// Processes the packet within the context. Returns true whether the packet was processed or throttled.
        /// </summary>
        /// <param name="channel">The through which the packet is coming/going out.</param>
        /// <param name="context">The packet context for this operation.</param>
        /// <returns>True whether the packet was processed or throttled, false otherwise.</returns>
        public static ProcessingState Process(Emitter.Connection channel, ProcessingContext context)
        {
            // It can only process StringPacket objects
            var response = context.Packet as HttpResponse;

            if (response == null)
            {
                return(ProcessingState.Failure);
            }

            // If we explicitely disabled the response, don't send it
            if (!response.ShouldSend)
            {
                return(ProcessingState.Failure);
            }

            // Get the client
            var client = context.Client;
            var stream = response.GetUnderlyingStream();

            // Flush the writer, ensuring we have the correct compiled buffer here.
            response.Flush();

            // Compile the headers
            var headers = HttpHeaders.Compile(response);

            // Create the buffer
            var buffer = context.BufferReserve(headers.Length + (int)stream.Length);

            Memory.Copy(headers, 0, buffer.Array, buffer.Offset, headers.Length);
            Memory.Copy(stream.GetBuffer(), 0, buffer.Array, buffer.Offset + headers.Length, (int)stream.Length);

            // Trace HTTP request
            NetTrace.WriteLine(response.Status + " " + response.ContentType + " Length: " + stream.Length, channel, NetTraceCategory.Http);

            // Write to the buffer
            context.SwitchBuffer(buffer);

            return(ProcessingState.Success);
        }
Пример #2
0
        /// <summary>
        /// Builds a websocket handler for the incoming http request.
        /// </summary>
        /// <param name="request">There upgrade request that upgrades the connection to a websocket one.</param>
        /// <param name="onMessage">A delegate to be invoked on each incoming message.</param>
        /// <param name="onClose">A delecate to be invoked when a connection is closed.</param>
        /// <param name="channel">The underlying channel that requests the upgrade</param>
        /// <returns>The websocket protocol handler constructed.</returns>
        public static IWebSocketProtocol GetHandler(HttpRequest request, Emitter.Connection channel, Action <string> onMessage, Action onClose)
        {
            // Get the version number that is requested
            string version = GetVersion(request);

            // Trace a websocket event
            NetTrace.WriteLine("Requested upgrade to version " + version, channel, NetTraceCategory.WebSocket);

            // Get the handler
            switch (version)
            {
            case "76": return(WebSocketFactory.Draft76);

            case "7": return(WebSocketFactory.Hybi13);

            case "8": return(WebSocketFactory.Hybi13);

            case "13": return(WebSocketFactory.Hybi13);

            case "75": return(WebSocketFactory.Hybi13);    // not sure!
            }

            throw new WebSocketException("Unsupported websocket request. Protocol version requested: " + version);
        }
Пример #3
0
 /// <summary>
 /// Processes the packet within the context. Returns true whether the packet was processed or throttled.
 /// </summary>
 /// <param name="channel">The through which the packet is coming/going out.</param>
 /// <param name="context">The packet context for this operation.</param>
 /// <returns>True whether the packet was processed or throttled, false otherwise.</returns>
 public static ProcessingState Process(Emitter.Connection channel, ProcessingContext context)
 {
     return(ProcessingState.Failure);
 }
Пример #4
0
        /// <summary>
        /// Processes the packet within the context. Returns true whether the packet was processed or throttled.
        /// </summary>
        /// <param name="channel">The through which the packet is coming/going out.</param>
        /// <param name="context">The packet context for this operation.</param>
        /// <returns>True whether the packet was processed or throttled, false otherwise.</returns>
        public static ProcessingState Process(Emitter.Connection channel, ProcessingContext context)
        {
            // Only process precompiled buffer
            var inputBuffer = context.Buffer;

            if (inputBuffer == null)
            {
                return(ProcessingState.Failure);
            }

            // Get the length & the final length
            var length      = inputBuffer.Length;
            var finalLength = length > UInt16.MaxValue
                ? length + 10
                : length > 125
                    ? length + 4
                    : length + 2;

            // Reserve the buffer
            var buffer = context.BufferReserve(finalLength);

            // Get the frame type

            /*var frameType = channel.TransportEncoding == TransportEncoding.Binary
             *      ? WebSocketFrameType.Binary
             *      : WebSocketFrameType.Text;*/
            var frameType = WebSocketFrameType.Binary;

            // Is it a pong request? change the frame type
            if (context.Packet is WebSocketPong)
            {
                frameType = WebSocketFrameType.Pong;
            }

            // Get the operation byte
            var op      = (byte)((byte)frameType + 128);
            var segment = buffer.AsSegment();
            var offset  = segment.Offset;

            // Write the operation first
            segment.Array[offset] = op;
            ++offset;

            if (length > UInt16.MaxValue)
            {
                // Length flag
                segment.Array[offset] = (byte)127;
                ++offset;

                // Length value
                var lengthBytes = length.ToBigEndianBytes <ulong>();
                Memory.Copy(lengthBytes, 0, segment.Array, offset, lengthBytes.Length);
                offset += lengthBytes.Length;
            }
            else if (length > 125)
            {
                // Length flag
                segment.Array[offset] = (byte)126;
                ++offset;

                // Length value
                var lengthBytes = length.ToBigEndianBytes <ushort>();
                Memory.Copy(lengthBytes, 0, segment.Array, offset, lengthBytes.Length);
                offset += lengthBytes.Length;
            }
            else
            {
                // Length value
                segment.Array[offset] = (byte)length;
                ++offset;
            }

            // Write the payload
            Memory.Copy(inputBuffer.Array, inputBuffer.Offset, segment.Array, offset, inputBuffer.Length);
            offset += length;

            // Switch to the new buffer
            context.SwitchBuffer(buffer);

            // Trace a websocket event
            NetTrace.WriteLine("Hybi13 frame [" + buffer.Length + "] encoded", channel, NetTraceCategory.WebSocket);

            return(ProcessingState.Success);
        }
Пример #5
0
        /// <summary>
        /// Processes the packet within the context. Returns true whether the packet was processed or throttled.
        /// </summary>
        /// <param name="channel">The through which the packet is coming/going out.</param>
        /// <param name="context">The packet context for this operation.</param>
        /// <returns>True whether the packet was processed or throttled, false otherwise.</returns>
        public static ProcessingState Process(Emitter.Connection channel, ProcessingContext context)
        {
            // Only handle stuff with HTTP Context
            var httpContext = context.GetSession <HttpContext>();

            if (httpContext == null)
            {
                return(ProcessingState.Failure);
            }

            // Must have Upgrade: websocket
            string upgradeType;

            if (!httpContext.Request.TryGetHeader("Upgrade", out upgradeType))
            {
                return(ProcessingState.Failure);
            }
            if (String.IsNullOrWhiteSpace(upgradeType))
            {
                return(ProcessingState.Failure);
            }
            if (upgradeType.ToLowerInvariant() != "websocket")
            {
                return(ProcessingState.Failure);
            }

            // This should not be tested with Fiddler2 or another proxy
            NetTrace.WriteLine("Websocket transport requested", channel, NetTraceCategory.WebSocket);

            // Get the websocket handler
            var handler = WebSocketFactory.GetHandler(httpContext.Request, channel, null, null);

            if (handler == null)
            {
                throw new WebSocketException("Requested websocket version is not supported");
            }

            // Upgrade to websocket (handshake)
            var upgrade = handler.Upgrade(context, httpContext);

            // Trace the websocket upgrade
            NetTrace.WriteLine("WebSocket upgrade done", channel, NetTraceCategory.WebSocket);

            // If it's socket.io 1.x, setup the transport

            /*if (httpContext.Request.IsFabric)
             * {
             *  // Set the pipeline
             *  channel.Encoding.PipelineAddBeforeOrLast(
             *      upgrade.Encoder,
             *      Encode.WebSocket);
             *  channel.Decoding.PipelineAddAfterOrFirst(
             *      upgrade.Decoder,
             *      Decode.WebSocket);
             *
             *  // Make sure the session is set
             *  var session = FabricRegistry.Get(httpContext);
             *  if (session != null)
             *  {
             *      // Unbind the old one
             *      channel.Client.UnbindChannel(channel);
             *
             *      // Set the session
             *      channel.Client = session;
             *      channel.FabricSession = session.Handle;
             *      channel.FabricEncoding = httpContext.GetFabricEncoding();
             *  }
             * }*/

            // Trace the channel upgrade
            NetTrace.WriteLine(channel + " was upgraded to websocket transport", channel, NetTraceCategory.Channel);

            // There still might be SSL encoding in the pipeline
            return(ProcessingState.Stop);
        }
Пример #6
0
        /// <summary>
        /// Processes the packet within the context. Returns true whether the packet was processed or throttled.
        /// </summary>
        /// <param name="channel">The through which the packet is coming/going out.</param>
        /// <param name="context">The packet context for this operation.</param>
        /// <returns>True whether the packet was processed or throttled, false otherwise.</returns>
        public static ProcessingState Process(Emitter.Connection channel, ProcessingContext context)
        {
            // Get the buffer
            if (context.Buffer == null || context.Buffer.Length < PacketHeader.TotalSize)
            {
                return(ProcessingState.Failure);
            }

            // Check for 'MSH' prefix
            var pBuffer = context.Buffer.AsBytePointer();

            if (*(pBuffer) != 'M' ||
                *(pBuffer + 1) != 'S' ||
                *(pBuffer + 2) != 'H')
            {
                return(ProcessingState.Failure);
            }

            // Get the type of the mesh frame
            var type = (char)(*(pBuffer + 3));

            if (type != 'F' && type != 'C')
            {
                return(ProcessingState.Failure);
            }

            // Must be a TCP channel
            var tcpChannel = channel as Connection;

            if (tcpChannel == null)
            {
                return(ProcessingState.Failure);
            }

            // Get the packet length
            int length = PeekPacketLength(pBuffer + 4);

            if (length < 0 || length > context.MaxMemory)
            {
                return(ProcessingState.Failure);
            }

            // Check whether we have all the bytes
            if (context.Buffer.Length < length + PacketHeader.TotalSize)
            {
                return(ProcessingState.InsufficientData);
            }

            // There might be several packets in the same segment. We need to specify
            // that one is decoded and forward only that one to the next decoder.
            // However, we must not discard the segment completely as we might loose data!
            context.Throttle(PacketHeader.TotalSize + length);
            context.SwitchBuffer(
                context.Buffer.Split(PacketHeader.TotalSize)
                );

            // Decompress with Snappy
            if (Decode.Snappy(channel, context) != ProcessingState.Success)
            {
                return(ProcessingState.Failure);
            }

            try
            {
                // Update the last seen, if we have a mesh identifier
                var server = Service.Mesh.Members.Get(tcpChannel.MeshIdentifier);
                if (server != null)
                {
                    server.LastTouchUtc = Timer.UtcNow;
                }

                // Something went wrong?
                if (context.Buffer == null)
                {
                    return(ProcessingState.Stop);
                }

                switch (type)
                {
                // This represents a mesh 'Command' type including commands that need to establish mesh
                // handshake between the nodes in the cluster. In that case, we don't have an ID set
                // on the connection and must let everything pass.
                case 'C':

                    // Acquire a command packet and the reader
                    using (var reader = PacketReaderPool.Default.Acquire(context.Buffer))
                        using (var packet = Create((MeshEventType)reader.PeekByte()))
                        {
                            // Deserialize the packet
                            packet.Read(reader);

                            // Process the command and exit
                            MeshHandler.Process(channel as Connection, packet);
                            break;
                        }

                // This represents a mesh 'Frame', which is a simple array segment forwarded to a set
                // of handlers, in the way in which the Fabric is done as well.
                case 'F':

                    // Process the frame and exit
                    Service.Mesh.OnFrame(server, context.Buffer);
                    break;

                // We've received some weird data
                default:
                    Service.Logger.Log("Mesh: Unknown frame type '" + type + "'");
                    break;
                }
            }
            catch (Exception ex)
            {
                Service.Logger.Log(ex);
            }

            // We're done here, stop the processing. The buffer will be freed right after
            // we've returned here, in the ProcessingContext's code.
            return(ProcessingState.Stop);
        }
Пример #7
0
        /// <summary>
        /// Processes the packet within the context. Returns true whether the packet was processed or throttled.
        /// </summary>
        /// <param name="channel">The through which the packet is coming/going out.</param>
        /// <param name="context">The packet context for this operation.</param>
        /// <returns>True whether the packet was processed or throttled, false otherwise.</returns>
        public static ProcessingState Process(Emitter.Connection channel, ProcessingContext context)
        {
            // Get the buffer
            var buffer = context.Buffer;

            if (buffer == null)
            {
                return(ProcessingState.Failure);
            }

            // Check if it looks like an http request but only with a single byte
            var pBuffer = buffer.AsBytePointer();
            var length  = context.Buffer.Length;
            var first   = (char)*pBuffer;

            if (length == 1 &&
                (first == 'P' ||
                 first == 'G' ||
                 first == 'H' ||
                 first == 'D' ||
                 first == 'O' ||
                 first == 'T' ||
                 first == 'C'))
            {
                return(ProcessingState.InsufficientData);
            }

            // Check if we can handle it
            if (!CanHandle(buffer))
            {
                return(ProcessingState.Failure);
            }

            // Acquire a HTTP context and save it in the session
            HttpContext httpContext = HttpContextPool.Default.Acquire(channel);

            context.Session = httpContext;

            // Make sure we have HTTP timeout set
            if (channel.Timeout != httpContext.ScriptTimeout)
            {
                channel.Timeout = httpContext.ScriptTimeout;
            }

            // Check if we have a http request
            if (buffer.Length < 14) // "GET / HTTP/1.1".Length;
            {
                return(ProcessingState.InsufficientData);
            }

            // Parse http request
            var errorOccured     = false;
            var insufficientData = false;
            var bytesParsed      = httpContext.ParseRequest(context.Buffer.AsBytePointer(), length,
                                                            out errorOccured, out insufficientData);

            // If there was en error during the parse, fail it
            if (errorOccured)
            {
                return(ProcessingState.Failure);
            }

            // Parsing of http headers was not complete, need to read more bytes
            if (insufficientData)
            {
                return(ProcessingState.InsufficientData);
            }

            // Trace HTTP request
            var request = httpContext.Request;

            NetTrace.WriteLine(request.HttpVerb.ToString().ToUpper() + " " + request.Path, channel, NetTraceCategory.Http);

            // Attempt to read the body
            if (buffer.Length < bytesParsed + request.ContentLength)
            {
                return(ProcessingState.InsufficientData);
            }

            // We got enough data, map the read data to the request
            if (request.ContentLength > 0)
            {
                // Switch to the current buffer, release the old one
                context.Throttle(bytesParsed + request.ContentLength);
                context.SwitchBuffer(
                    buffer.Split(bytesParsed)
                    );

                // Parse the body to the request
                request.Body = context.Buffer.AsArray();
            }
            else
            {
                // Make a special case for draft websockets, as they send a body with a
                // GET request. Bastards!
                if (bytesParsed + 8 <= buffer.Length &&
                    request.ContainsHeader("Sec-WebSocket-Key1"))
                {
                    // Get the 8 bytes of websocket challenge
                    context.Throttle(bytesParsed + 8);
                    context.SwitchBuffer(
                        buffer.Split(bytesParsed)
                        );

                    // Parse the body to the request
                    request.Body = context.Buffer.AsArray();
                }
                else
                {
                    // Switch to the current buffer, release the old one
                    context.Throttle(bytesParsed);
                    context.SwitchBuffer(null);
                }
            }

            // Redirect to the various built-in handlers
            context.Redirect(
                Handle.WebSocketUpgrade,
                Handle.Http
                );

            return(ProcessingState.Success);
        }
Пример #8
0
        /// <summary>
        /// Processes the packet within the context. Returns true whether the packet was processed or throttled.
        /// </summary>
        /// <param name="channel">The through which the packet is coming/going out.</param>
        /// <param name="context">The packet context for this operation.</param>
        /// <returns>True whether the packet was processed or throttled, false otherwise.</returns>
        public static ProcessingState Process(Emitter.Connection channel, ProcessingContext context)
        {
            // Get the buffer
            var buffer = context.Buffer;

            if (buffer == null)
            {
                return(ProcessingState.Failure);
            }

            // Get the info
            var pBuffer = buffer.AsBytePointer();
            int length  = buffer.Length;

            // To close the connection cleanly, a frame consisting of just a 0xFF
            // byte followed by a 0x00 byte is sent from one peer to ask that the
            // other peer close the connection.
            if (*pBuffer == 0xFF && *(pBuffer + 1) == 0x00)
            {
                // Trace a websocket event
                NetTrace.WriteLine("Draft76 connection termination requested", channel, NetTraceCategory.WebSocket);

                length = 0;
                channel.Close();
                return(ProcessingState.Stop);
            }

            if (*pBuffer != Start)
            {
                throw new WebSocketException("WebSocket frame is invalid.");
            }

            // Search for the next end byte
            int endIndex = -1;

            for (int i = 0; i < length; ++i)
            {
                if (*(pBuffer + i) == End)
                {
                    endIndex = i;
                    break;
                }
            }

            if (endIndex == -1)
            {
                // Trace a websocket event
                NetTrace.WriteLine("Draft76 frame is not complete", channel, NetTraceCategory.WebSocket);
                return(ProcessingState.InsufficientData);
            }

            if (endIndex > MaxSize)
            {
                throw new WebSocketException("WebSocket frame is too large.");
            }

            // Throttle the rest
            context.Throttle(endIndex + 1);

            // Decode the body
            var decoded = context.BufferWrite(buffer.Array, buffer.Offset + 1, endIndex - 1);

            context.SwitchBuffer(decoded);

            // Trace a websocket event
            NetTrace.WriteLine("Draft76 frame decoded", channel, NetTraceCategory.WebSocket);
            return(ProcessingState.Success);
        }
Пример #9
0
 /// <summary>
 /// Recycles (resets) the object to the original state.
 /// </summary>
 public override void Recycle()
 {
     // Reset all other members
     Origin   = null;
     Lifetime = PacketLifetime.Automatic;
 }
Пример #10
0
        /// <summary>
        /// Processes the packet within the context. Returns true whether the packet was processed or throttled.
        /// </summary>
        /// <param name="channel">The through which the packet is coming/going out.</param>
        /// <param name="context">The packet context for this operation.</param>
        /// <returns>True whether the packet was processed or throttled, false otherwise.</returns>
        public static ProcessingState Process(Emitter.Connection channel, ProcessingContext context)
        {
            // It can only process packets
            var packet = context.Packet as MeshPacket;

            if (packet == null)
            {
                return(ProcessingState.Failure);
            }

            if (channel.Client.TransportType != TransportType.Tcp)
            {
                return(ProcessingState.Failure);
            }

            var command = packet as MeshEvent;

            if (command != null && command.Type == MeshEventType.Custom)
            {
                NetTrace.WriteLine("Sending " + command.Value, channel, NetTraceCategory.Mesh);
            }

            // Prepare a stream variable
            var stream = ByteStreamPool.Default.Acquire();
            var writer = PacketWriterPool.Default.Acquire(stream);

            try
            {
                // Write and compile the packet
                packet.Write(writer);

                // Compress the packet
                var encoded       = SnappyEncoder.Process(stream, PacketHeader.TotalSize, context);
                int messageLength = encoded.Size - PacketHeader.TotalSize;

                // Add a sample to the average
                NetStat.Compression.Sample(1 - ((double)messageLength / stream.Length));

                // Write mesh prefix
                encoded.Array[encoded.Offset + 0] = 77; // 'M'
                encoded.Array[encoded.Offset + 1] = 83; // 'S'
                encoded.Array[encoded.Offset + 2] = 72; // 'H'

                // Write message type
                if (packet is MeshFrame)
                {
                    encoded.Array[encoded.Offset + 3] = 70; // 'F'
                }
                else if (packet is MeshEvent)
                {
                    encoded.Array[encoded.Offset + 3] = 67; // 'C'
                }
                // Write message length
                encoded.Array[encoded.Offset + 4] = (byte)(messageLength >> 24);
                encoded.Array[encoded.Offset + 5] = (byte)(messageLength >> 16);
                encoded.Array[encoded.Offset + 6] = (byte)(messageLength >> 8);
                encoded.Array[encoded.Offset + 7] = (byte)messageLength;

                // Copy to a buffer segment
                context.SwitchBuffer(encoded);
            }
            finally
            {
                // Make sure we release the stream and the writer
                stream.TryRelease();
                writer.TryRelease();
            }

            return(ProcessingState.Success);
        }