/// <summary> /// Builds the headers frame. /// </summary> /// <param name="stream">The stream.</param> /// <param name="headers">The headers.</param> /// <param name="final">if set to <c>true</c> than this frame is final for the stream.</param> /// <returns>Headers frame.</returns> public ControlFrame BuildHeadersFrame(Http2Stream stream, ProtocolHeaders headers, bool final) { ControlFrame frame = BuildControlFrame(FrameType.Headers, stream, headers); frame.IsFinal = final; return(frame); }
/// <summary> /// Builds the SYN_STREAM frame. /// </summary> /// <param name="streamId">The stream id.</param> /// <param name="headers">The headers.</param> /// <returns>SYN_STREAM frame.</returns> public ControlFrame BuildSynStreamFrame(int streamId, ProtocolHeaders headers) { ControlFrame frame = BuildControlFrame(FrameType.SynStream, streamId, headers); frame.Priority = SYNStreamPriority; return(frame); }
/// <summary> /// Builds the SYN_STREAM frame. /// </summary> /// <param name="stream">The stream.</param> /// <param name="headers">The headers.</param> /// <param name="final">Indicates that stream is not going to send any more data.</param> /// <returns>SYN_STREAM frame.</returns> public ControlFrame BuildSynStreamFrame(Http2Stream stream, ProtocolHeaders headers, bool final) { ControlFrame frame = BuildControlFrame(FrameType.SynStream, stream, headers); frame.Priority = SYNStreamPriority; frame.IsFinal = final; return(frame); }
/// <summary> /// Builds the control frame. /// </summary> /// <param name="type">The frame type.</param> /// <param name="streamId">The Http2 stream id.</param> /// <param name="headers">The headers.</param> /// <returns>Returns Control frame object.</returns> private static ControlFrame BuildControlFrame(FrameType type, int streamId, ProtocolHeaders headers) { ControlFrame frame = new ControlFrame(headers); frame.StreamId = streamId; frame.Type = type; frame.Priority = ControlPriority; return(frame); }
/// <summary> /// Builds the Ping frame. /// </summary> /// <param name="lastSeenGoodStreamId">The stream id.</param> /// <returns>Builded Ping Frame</returns> public ControlFrame BuildPingFrame(int streamId) { ControlFrame frame = new ControlFrame { StreamId = streamId }; frame.Flags = 0; frame.Length = 4; return(frame); }
/// <summary> /// Builds the RST Frame. /// </summary> /// <param name="streamId">The stream id.</param> /// <param name="reason">The reason for RST.</param> /// <returns>RST frame.</returns> public ControlFrame BuildRSTFrame(int streamId, StatusCode reason) { ControlFrame frame = new ControlFrame(); frame.StreamId = streamId; frame.Type = FrameType.RTS; frame.StatusCode = reason; frame.Priority = RSTPriority; return(frame); }
/// <summary> /// Initializes a new instance of the <see cref="WindowUpdateEventArgs" /> class. /// </summary> /// <param name="deltaWindowSize">Size of the delta window.</param> public WindowUpdateEventArgs(ControlFrame frame) : base(frame) { this.DeltaWindowSize = frame.DeltaWindowSize; }
/// <summary> /// Serializes the control frame. /// </summary> /// <param name="frame">The frame.</param> /// <returns>Serialized control frame.</returns> private byte[] SerializeControlFrame(ControlFrame frame) { var byteList = new List<byte>(); byteList.Add((byte)(((frame.Version & 0xFF00) >> 8) | 0x80)); byteList.Add((byte)(frame.Version & 0x00FF)); byteList.Add((byte)(((Int16)frame.Type & 0xFF00) >> 8)); byteList.Add((byte)((Int16)frame.Type & 0x00FF)); var headersArray = new byte[0]; switch (frame.Type) { case FrameType.SynStream: byteList.Add(Convert.ToByte(frame.Flags | (frame.IsFinal ? 0x01 : 0x00))); headersArray = SerializeControlFrameHeaders(frame.Headers); if (headersArray.Length > 0) { ProcessorRun(ref headersArray, DirectionProcessType.Outbound, frame.Flags); } byteList.AddRange(BinaryHelper.Int32ToBytes(headersArray.Length + 10, 3)); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.StreamId)); byteList.AddRange(BinaryHelper.Int32ToBytes(0)); byteList.Add(Convert.ToByte(frame.Priority >> 5)); byteList.Add(Unused); break; case FrameType.RTS: byteList.Add(0); byteList.AddRange(BinaryHelper.Int32ToBytes(headersArray.Length + 8, 3)); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.StreamId)); byteList.AddRange(BinaryHelper.Int32ToBytes((int)frame.StatusCode)); break; case FrameType.SynReply: byteList.Add(frame.Flags); headersArray = SerializeControlFrameHeaders(frame.Headers); if (headersArray.Length > 0) { ProcessorRun(ref headersArray, DirectionProcessType.Outbound, frame.Flags); } byteList.AddRange(BinaryHelper.Int32ToBytes(headersArray.Length + 4, 3)); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.StreamId)); break; case FrameType.GoAway: byteList.Add(frame.Flags); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.Length,3)); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.StreamId)); byteList.AddRange(BinaryHelper.Int32ToBytes((int)frame.StatusCode)); break; case FrameType.Ping: byteList.Add(frame.Flags); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.Length,3)); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.StreamId)); break; case FrameType.WindowUpdate: byteList.Add(frame.Flags); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.Length,3)); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.StreamId)); byteList.AddRange(BinaryHelper.Int64ToBytes(((WindowUpdateFrame)frame).DeltaWindowSize)); break; case FrameType.Settings: byteList.Add(frame.Flags); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.Length,3)); byteList.AddRange(BinaryHelper.Int32ToBytes(frame.NumberOfEntries)); break; } byteList.AddRange(headersArray); return byteList.ToArray(); }
/// <summary> /// Parses HTTP headers of Http2 control frame. /// </summary> /// <param name="frame">The frame.</param> /// <param name="data">The data.</param> /// <param name="offset">Offset of HTTP headers in the data.</param> private void ParseControlFrameHeaders(ref ControlFrame frame, byte[] data, int offset) { var headers = new byte[data.Length - offset]; Array.Copy(data, offset, headers, 0, headers.Length); if (headers.Length > 0) ProcessorRun(ref headers, DirectionProcessType.Inbound, frame.Flags); int headersOffset = 0; int numberOfKeyValue = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(headers, headersOffset, 4)); headersOffset += 4; for (int i = 0; i < numberOfKeyValue; ++i) { int nameLength = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(headers, headersOffset, 4)); headersOffset += 4; string name = Encoding.UTF8.GetString(headers, headersOffset, nameLength); headersOffset += nameLength; int valLength = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(headers, headersOffset, 4)); headersOffset += 4; string val = Encoding.UTF8.GetString(headers, headersOffset, valLength); headersOffset += valLength; // Ensure no duplicates. if (frame.Headers.ContainsKey(name)) { throw new ProtocolExeption(StatusCode.InternalError); } frame.Headers.Add(name, val); } }
/// <summary> /// Deserializes the data into control frame. /// </summary> /// <param name="data">The data.</param> /// <returns>Deserialized frame.</returns> private BaseFrame DeserializeControlFrame(byte[] data) { FrameType type = GetFrameType(data); ControlFrame frame = new ControlFrame(); switch (type) { case FrameType.RTS: frame.StreamId = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 8, 4)); frame.StatusCode = (StatusCode)BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 12, 4)); break; case FrameType.Headers: case FrameType.SynReply: ParseControlFrameHeader(ref frame, data); frame.StreamId = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 8, 4)); ParseControlFrameHeaders(ref frame, data, 12); break; case FrameType.SynStream: ParseControlFrameHeader(ref frame, data); frame.StreamId = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 8, 4)); frame.AssociatedToStreamId = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 12, 4)); frame.Priority = (byte)(data[16] >> 5); frame.Slot = data[17]; ParseControlFrameHeaders(ref frame, data, 18); break; case FrameType.Settings: int numberOfEntries = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 8, 4)); frame = new SettingsFrame(numberOfEntries); int headersOffset = 12; for (int i = 0; i < numberOfEntries; i++) { int key = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, headersOffset, 4)); headersOffset += 4; int value = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, headersOffset, 4)); headersOffset += 4; frame.SettingsHeaders.Add(key, value); } ParseControlFrameHeader(ref frame, data); break; case FrameType.GoAway: int lastSeenGoodStreamId = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 8, 4)); StatusCode status = StatusCode.Success; if (data.Length > 12) status = (StatusCode)BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 12, 4)); frame = new GoAwayFrame(lastSeenGoodStreamId, status); ParseControlFrameHeader(ref frame, data); break; case FrameType.Ping: int streamID = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 4, 4)); frame = new ControlFrame { StreamId = streamID }; ParseControlFrameHeader(ref frame, data); break; case FrameType.WindowUpdate: int streamId = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 4, 4)); int deltaWindowSize = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 8, 4)); frame = new WindowUpdateFrame(streamId, deltaWindowSize); ParseControlFrameHeader(ref frame, data); break; } frame.Type = type; return frame; }
/// <summary> /// Parses HTTP header of Http2 control frame. /// </summary> /// <param name="frame">The frame.</param> /// <param name="data">The data.</param> private static void ParseControlFrameHeader(ref ControlFrame frame, byte[] data) { frame.Version = BinaryHelper.Int16FromBytes(data[0], data[1], 1); frame.Type = (FrameType)BinaryHelper.Int16FromBytes(data[2], data[3]); frame.Flags = data[4]; // it would be always 4 th byte for flags in spec. frame.Length = BinaryHelper.Int32FromBytes(new ArraySegment<byte>(data, 5, 3)); frame.IsFinal = (frame.Flags & 0x01) != 0; }
/// <summary> /// Builds the Ping frame. /// </summary> /// <param name="lastSeenGoodStreamId">The stream id.</param> /// <returns>Builded Ping Frame</returns> public ControlFrame BuildPingFrame(int streamId) { ControlFrame frame = new ControlFrame { StreamId = streamId }; frame.Flags = 0; frame.Length = 4; return frame; }
/// <summary> /// Builds the control frame. /// </summary> /// <param name="type">The frame type.</param> /// <param name="streamId">The Http2 stream id.</param> /// <param name="headers">The headers.</param> /// <returns>Returns Control frame object.</returns> private static ControlFrame BuildControlFrame(FrameType type, int streamId, ProtocolHeaders headers) { ControlFrame frame = new ControlFrame(headers); frame.StreamId = streamId; frame.Type = type; frame.Priority = ControlPriority; return frame; }
/// <summary> /// Builds the RST Frame. /// </summary> /// <param name="streamId">The stream id.</param> /// <param name="reason">The reason for RST.</param> /// <returns>RST frame.</returns> public ControlFrame BuildRSTFrame(int streamId, StatusCode reason) { ControlFrame frame = new ControlFrame(); frame.StreamId = streamId; frame.Type = FrameType.RTS; frame.StatusCode = reason; frame.Priority = RSTPriority; return frame; }
/// <summary> /// Process Protocol control frame. /// </summary> /// <param name="frame">The control frame.</param> private void ProcessControlFrame(ControlFrame frame) { if (frame.Version != Version) { throw new ProtocolExeption(StatusCode.UnsupportedVersion); } if (frame.Type == FrameType.GoAway) { GoAwayFrame goAway = (GoAwayFrame)frame; CloseInternal(goAway.Status, goAway.LastSeenGoodStreamId, false); } else { Http2Stream stream = this.streamsStore.GetStreamById(frame.StreamId); // if this is rst frame - don't send error or it will go in rst loop if (stream == null && frame.Type != FrameType.RTS && frame.Type != FrameType.Settings && frame.Type != FrameType.SynStream) { this.SendRST(frame.StreamId, StatusCode.InvalidStream); return; } switch (frame.Type) { case FrameType.SynStream: //TODO validate syn_stream and send syn_reply or rst //this.OnSessionFrame(this, new ControlFrameEventArgs(frame)); this.SendRST(frame.StreamId, StatusCode.RefusedStream); break; case FrameType.SynReply: this.OnSessionFrame(this, new ControlFrameEventArgs(frame)); break; case FrameType.Headers: this.OnStreamFrame(this, new HeadersEventArgs(this.streamsStore.GetStreamById(frame.StreamId), frame.Headers)); break; case FrameType.RTS: this.OnStreamFrame(this, new RSTEventArgs(this.streamsStore.GetStreamById(frame.StreamId), frame.StatusCode)); break; case FrameType.Ping: this.OnSocketPing(this, new PingEventArgs(frame.StreamId)); break; case FrameType.Settings: OnSessionFrame(this, new SettingsEventArgs(frame)); break; case FrameType.WindowUpdate: Http2Stream http2Stream = this.streamsStore.GetStreamById(frame.StreamId); http2Stream.UpdateWindowBalance(frame.DeltaWindowSize); break; } } }
/// <summary> /// Sends the frame. /// </summary> /// <param name="frame">The control frame.</param> private void SendFrame(ControlFrame frame) { byte[] frameBinary = this.serializer.Serialize(frame); frame.Length = frameBinary.Length; SendMessage(frameBinary); if (this.OnFrameSent != null) { this.OnFrameSent(this, new FrameEventArgs(frame)); } }
/// <summary> /// Initializes a new instance of the <see cref="SettingsEventArgs"/> class. /// </summary> /// <param name="stream">The settings name/value pairs.</param> public SettingsEventArgs(ControlFrame settingsFrame) : base(settingsFrame) { this.SettingsHeaders = settingsFrame.SettingsHeaders; }