private static bool CanHandle(BufferSegment buffer) { if (buffer.Length < 6) { return(false); } byte *pBuffer = buffer.AsBytePointer(); char first = (char)*pBuffer; if (first == 'P' || first == 'G' || first == 'H' || first == 'D' || first == 'O' || first == 'T' || first == 'C') { // Next, check if it contains a HTTP Verb if (*(pBuffer) == 'P' && *(pBuffer + 1) == 'O' && *(pBuffer + 2) == 'S' && *(pBuffer + 3) == 'T') { return(true); } if (*(pBuffer) == 'G' && *(pBuffer + 1) == 'E' && *(pBuffer + 2) == 'T') { return(true); } if (*(pBuffer) == 'P' && *(pBuffer + 1) == 'U' && *(pBuffer + 2) == 'T') { return(true); } if (*(pBuffer) == 'H' && *(pBuffer + 1) == 'E' && *(pBuffer + 2) == 'A' && *(pBuffer + 3) == 'D') { return(true); } if (*(pBuffer) == 'D' && *(pBuffer + 1) == 'E' && *(pBuffer + 2) == 'L' && *(pBuffer + 3) == 'E' && *(pBuffer + 4) == 'T' && *(pBuffer + 5) == 'E') { return(true); } if (*(pBuffer) == 'O' && *(pBuffer + 1) == 'P' && *(pBuffer + 2) == 'T' && *(pBuffer + 3) == 'I' && *(pBuffer + 4) == 'O' && *(pBuffer + 5) == 'N' && *(pBuffer + 6) == 'S') { return(true); } if (*(pBuffer) == 'T' && *(pBuffer + 1) == 'R' && *(pBuffer + 2) == 'A' && *(pBuffer + 3) == 'C' && *(pBuffer + 4) == 'E') { return(true); } if (*(pBuffer) == 'C' && *(pBuffer + 1) == 'O' && *(pBuffer + 2) == 'N' && *(pBuffer + 3) == 'N' && *(pBuffer + 4) == 'E' && *(pBuffer + 5) == 'C' && *(pBuffer + 6) == 'T') { return(true); } } return(false); }
/// <summary> /// Decodes a single hybi13 frame. /// </summary> /// <param name="context">The context for decoding.</param> /// <param name="pBuffer">The byte pointer to start decoding process.</param> /// <param name="length">The length of the buffer, starting at pBuffer offset.</param> /// <param name="index">The amount of procesed bytes.</param> /// <param name="isFinal">Whether the frame is final or not.</param> /// <param name="decoded">The frame decoded.</param> /// <returns>Processing state.</returns> private static ProcessingState DecodeFrame(ProcessingContext context, byte *pBuffer, int length, out int index, out bool isFinal, out BufferSegment decoded) { // Default values index = 0; decoded = null; // Check if we have a buffer for the mask if (MaskBuffer == null) { MaskBuffer = new byte[4]; } // Get the first part of the header var frameType = (WebSocketFrameType)(*pBuffer & 15); isFinal = (*pBuffer & 128) != 0; // Must have at least some bytes if (length == 0) { return(ProcessingState.InsufficientData); } // We only got one byte, kthx iOS if (length == 1 && (frameType == WebSocketFrameType.Text || frameType == WebSocketFrameType.Binary)) { return(ProcessingState.InsufficientData); } // Get the second part of the header var isMasked = (*(pBuffer + 1) & 128) != 0; var dataLength = (*(pBuffer + 1) & 127); // Close the connection if requested if (frameType == WebSocketFrameType.Close) { // Client sent us a close frame, we should close the connection context.Channel.Close(); return(ProcessingState.Stop); } // We got some weird shit if (!isMasked) { throw new WebSocketException("Incorrect incoming Hybi13 data format."); } // Validate the frame type switch (frameType) { // Frames with a payload case WebSocketFrameType.Text: case WebSocketFrameType.Binary: case WebSocketFrameType.Continuation: case WebSocketFrameType.Ping: case WebSocketFrameType.Pong: break; // Unrecognized frame type default: throw new WebSocketException("Unsupported Huby13 frame (" + frameType + ") received."); } index = 2; int payloadLength; if (dataLength == 127) { if (length < index + 8) { return(ProcessingState.InsufficientData); } // TODO: Support little endian too payloadLength = *(pBuffer + 2) << 56 | *(pBuffer + 3) << 48 | *(pBuffer + 4) << 40 | *(pBuffer + 5) << 32 | *(pBuffer + 6) << 24 | *(pBuffer + 7) << 16 | *(pBuffer + 8) << 8 | *(pBuffer + 9); index += 8; } else if (dataLength == 126) { if (length < index + 2) { return(ProcessingState.InsufficientData); } // TODO: Support little endian too payloadLength = *(pBuffer + 2) << 8 | *(pBuffer + 3); index += 2; } else { payloadLength = dataLength; } // Check if we have the whole frame here if (length < index + payloadLength + 4) { return(ProcessingState.InsufficientData); } // Get the mask bytes MaskBuffer[0] = *(pBuffer + index); MaskBuffer[1] = *(pBuffer + index + 1); MaskBuffer[2] = *(pBuffer + index + 2); MaskBuffer[3] = *(pBuffer + index + 3); index += 4; // Reserve a decode buffer to decode into decoded = context.BufferReserve(payloadLength); // Decode the body byte *pTo = decoded.AsBytePointer(); byte *pFrom = pBuffer + index; for (int i = 0; i < payloadLength; ++i, ++pTo, ++pFrom) { *pTo = (byte)((*pFrom) ^ MaskBuffer[i % 4]); } // Set the new index index += payloadLength; switch (frameType) { // We need to reply to the ping case WebSocketFrameType.Ping: { // Send the websocket with the copy of the body (as per RFC) context.Channel.Send(WebSocketPong.Acquire(decoded)); decoded.TryRelease(); return(ProcessingState.Stop); } // Pong is just used for the heartbeat case WebSocketFrameType.Pong: return(ProcessingState.Stop); } // Trace a websocket event return(ProcessingState.Success); }