예제 #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)
        {
            // Only process precompiled buffer
            var inputBuffer = context.Buffer;

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

            // Get the length & reserve new buffer
            var length = inputBuffer.Length;
            var buffer = context.BufferReserve(length + 2);

            // Wrap in between 0 and 255
            buffer.Array[buffer.Offset] = Start;
            Memory.Copy(inputBuffer.Array, inputBuffer.Offset, buffer.Array, buffer.Offset + 1, length);
            buffer.Array[buffer.Offset + length + 1] = End;

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

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

            return(ProcessingState.Success);
        }
예제 #2
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)
        {
            var buffer = context.Buffer;

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

            // Get the buffer pointer
            var pBuffer = buffer.AsBytePointer();
            var offset  = 0;
            var length  = buffer.Length;

            // Decode the frame
            var queue = new Queue <BufferSegment>();
            int index; bool isFinal; BufferSegment decoded;

            do
            {
                //var trace = (context.Buffer.ViewAsHybi13 == "(Invalid: Hybi13)" ? context.Buffer.ViewAsBytes : context.Buffer.ViewAsHybi13);
                var result = DecodeFrame(context, pBuffer, length, out index, out isFinal, out decoded);
                //Console.WriteLine(trace + " -> " + result);
                if (result != ProcessingState.Success)
                {
                    return(result);
                }

                pBuffer += index;
                offset  += index;
                length  -= index;

                queue.Enqueue(decoded);
            } while (!isFinal);

            // Merge the queue
            decoded = queue.Dequeue();
            while (queue.Count > 0)
            {
                decoded = decoded.Join(queue.Dequeue());
            }

            // Throttle the rest
            if (offset < buffer.Length)
            {
                context.Throttle(offset);
            }

            // Swap the buffer
            context.SwitchBuffer(decoded);

            NetTrace.WriteLine("Hybi13 frame decoded", channel, NetTraceCategory.WebSocket);
            return(ProcessingState.Success);
        }
예제 #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)
        {
            // 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);
        }
예제 #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)
        {
            // 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);
        }
예제 #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
            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);
        }
예제 #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);
            }

            // 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);
        }
예제 #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)
        {
            // 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);
        }