/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }