Example #1
0
        private Frame GetFrameType(Frame preamble)
        {
            switch (preamble.FrameType)
            {
                case FrameType.GoAway:
                    return new GoAwayFrame(preamble);

                case FrameType.Ping:
                    return new PingFrame(preamble);

                case FrameType.RstStream:
                    return new RstStreamFrame(preamble);

                case FrameType.Settings:
                    return new SettingsFrame(preamble);

                case FrameType.Headers:
                    return new HeadersFrame(preamble);

                case FrameType.WindowUpdate:
                    return new WindowUpdateFrame(preamble);

                case FrameType.Data:
                    return new DataFrame(preamble);
                default:
                    throw new NotImplementedException("Frame type: " + preamble.FrameType);
            }
        }
 // Queue up a fully rendered frame to send
 public Task WriteFrameAsync(Frame frame, Priority priority)
 {
     PriorityQueueEntry entry = new PriorityQueueEntry(frame, priority);
     Enqueue(entry);
     SignalDataAvailable();
     return entry.Task;
 }
        public async Task<Frame> ReadFrameAsync()
        {
            Frame preamble = new Frame();
            if (!await TryFillAsync(preamble.Buffer, 0, preamble.Buffer.Length, _cancel))
            {
                return null;
            }

            if (_validateFirstFrameIsControl)
            {
                if (!preamble.IsControl)
                {
                    // Probably a HTTP/1.1 text formatted request.  We could check if it starts with 'GET'
                    // Is it sane to send a response here?  What kind of response? 1.1 text, or 2.0 binary?
                    throw new ProtocolViolationException("First frame is not a control frame.");
                }
                _validateFirstFrameIsControl = false;
            }
            // TODO: If this is the first frame, verify that it is in fact a control frame, and that it is not a HTTP/1.1 text request.
            // Not applicable after an HTTP/1.1->HTTP-01/2.0 upgrade handshake.

            if (preamble.IsControl && preamble.Version != Constants.CurrentProtocolVersion)
            {
                throw new NotSupportedException("This control frame uses an unsupported version: " + preamble.Version);
            }

            Frame wholeFrame = GetFrameType(preamble);
            if (!await TryFillAsync(wholeFrame.Buffer, Constants.FramePreambleSize, wholeFrame.Buffer.Length - Constants.FramePreambleSize, _cancel))
            {
                return null;
            }

            return wholeFrame;
        }
Example #4
0
        // Queue up a fully rendered frame to send
        public void WriteFrame(Frame frame)
        {
            //Do not write to already closed stream
            if (frame.FrameType != FrameType.Settings
                && frame.FrameType != FrameType.GoAway
                && frame.FrameType != FrameType.Ping
                && _streams[frame.StreamId] == null)
            {
                return;
            }

            var priority = frame.StreamId != 0 ? _streams[frame.StreamId].Priority : Priority.Pri7;

            IQueueItem entry = null;

            if (IsPriorityTurnedOn)
            {
                entry = new PriorityQueueEntry(frame, priority);
            }
            else
            {
                entry = new QueueEntry(frame);
            }

            _messageQueue.Enqueue(entry);
        }
Example #5
0
        public Frame ReadFrame()
        {
            var preamble = new Frame();
            if (!TryFill(preamble.Buffer, 0, preamble.Buffer.Length))
            {
                return null;
            }

            var wholeFrame = GetFrameType(preamble);
            if (!TryFill(wholeFrame.Buffer, Constants.FramePreambleSize, wholeFrame.Buffer.Length - Constants.FramePreambleSize))
            {
                return null;
            }

            return wholeFrame;
        }
        private Frame GetFrameType(Frame preamble)
        {
            if (!preamble.IsControl)
            {
                return new DataFrame(preamble);
            }
            switch (preamble.FrameType)
            {
                case ControlFrameType.Credential:
                    return new CredentialFrame(preamble);

                case ControlFrameType.GoAway:
                    return new GoAwayFrame(preamble);

                case ControlFrameType.Headers:
                    return new HeadersFrame(preamble);

                case ControlFrameType.Ping:
                    return new PingFrame(preamble);

                case ControlFrameType.RstStream:
                    return new RstStreamFrame(preamble);

                case ControlFrameType.Settings:
                    return new SettingsFrame(preamble);

                case ControlFrameType.SynReply:
                    return new SynReplyFrame(preamble);

                case ControlFrameType.SynStream:
                    return new SynStreamFrame(preamble);

                case ControlFrameType.WindowUpdate:
                    return new WindowUpdateFrame(preamble);

                default:
                    throw new NotImplementedException("Frame type: " + preamble.FrameType);
            }
        }
 public FrameReceivedEventArgs(Http2Stream stream, Frame frame)
 {
     Stream = stream;
     Frame = frame;
 }
Example #8
0
 // Incoming
 public SettingsFrame(Frame preamble)
     : base(preamble)
 {
 }
 public PriorityQueueEntry(Frame frame, Priority priority)
 {
     _frame = frame;
     _priority = priority;
     _tcs = new TaskCompletionSource<object>();
 }
Example #10
0
 public QueueEntry(Frame frame)
 {
     _frame = frame;
 }
 // Create an incoming frame
 public SynStreamFrame(Frame preamble)
     : base(preamble)
 {
 }
Example #12
0
 /// <summary>
 /// Create an incoming frame
 /// </summary>
 /// <param name="preamble">Frame preamble</param>
 public HeadersFrame(Frame preamble)
     : base(preamble)
 {
 }
Example #13
0
 public PriorityQueueEntry(Frame frame, Priority priority)
 {
     _frame = frame;
     _priority = priority;
 }
 // Incoming
 public RstStreamFrame(Frame preamble)
     : base(preamble)
 {
 }
Example #15
0
 // For incoming frames
 protected Frame(Frame preamble)
     : this(new byte[Constants.FramePreambleSize + preamble.FrameLength])
 {
     System.Buffer.BlockCopy(preamble.Buffer, 0, Buffer, 0, Constants.FramePreambleSize);
 }
 // Create an incoming frame
 public SynReplyFrame(Frame preamble)
     : base(preamble)
 {
 }
 // For incoming
 public DataFrame(Frame preamble)
     : base(preamble)
 {
 }
 public DataFrameSentEventArgs(Frame frame)
 {
     this.Id = frame.StreamId;
     this.DataAmount = frame.Buffer.Length - Constants.FramePreambleSize;
 }
 // Incoming
 protected StreamControlFrame(Frame preamble)
     : base(preamble)
 {
 }
Example #20
0
        /// <summary>
        /// Dispatches the incoming frame.
        /// </summary>
        /// <param name="frame">The frame.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        private void DispatchIncomingFrame(Frame frame)
        {
            Http2Stream stream = null;

            //Spec 03 tells that frame with continues flag MUST be followed by a frame with the same type
            //and the same stread id.
            if (_toBeContinuedHeaders != null)
            {
                if (_toBeContinuedFrame.FrameType != frame.FrameType
                    || _toBeContinuedFrame.StreamId != frame.StreamId)
                {
                    //If not, we must close the session.
                    Dispose();
                    return;
                }
            }

            try
            {
                switch (frame.FrameType)
                {
                    case FrameType.Headers:
                        Console.WriteLine("New headers with id = " + frame.StreamId);
                        var headersFrame = (Headers) frame;
                        var serializedHeaders = new byte[headersFrame.CompressedHeaders.Count];

                        Buffer.BlockCopy(headersFrame.CompressedHeaders.Array,
                                         headersFrame.CompressedHeaders.Offset,
                                         serializedHeaders, 0, serializedHeaders.Length);

                        var decompressedHeaders = _comprProc.Decompress(serializedHeaders, frame.StreamId % 2 != 0);
                        var headers = decompressedHeaders;

                        if (!headersFrame.IsEndHeaders)
                        {
                            _toBeContinuedHeaders = decompressedHeaders;
                            _toBeContinuedFrame = headersFrame;
                            break;
                        }

                        if (_toBeContinuedHeaders != null)
                        {
                            headers.AddRange(_toBeContinuedHeaders);
                        }

                        //Remote side tries to open more streams than allowed
                        if (ActiveStreams.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams)
                        {
                            Dispose();
                            return;
                        }

                        string path;
                        try
                        {
                            path = headers.GetValue(":path");
                        }
                        catch (KeyNotFoundException)
                        {
                            path = !_handshakeHeaders.ContainsKey(":path") ? _handshakeHeaders[":path"] : @"\index.html";
                            headers.Add(new Tuple<string, string, IAdditionalHeaderInfo>(":path", path, null));
                        }

                        stream = new Http2Stream(headers, headersFrame.StreamId,
                                                  _writeQueue, _flowControlManager,
                                                  _comprProc);

                        ActiveStreams[stream.Id] = stream;

                        stream.OnClose += (o, args) =>
                        {
                            if (!ActiveStreams.Remove(ActiveStreams[args.Id]))
                            {
                                throw new ArgumentException("Cant remove stream from ActiveStreams");
                            }
                        };

                        _toBeContinuedFrame = null;
                        _toBeContinuedHeaders = null;
                        break;

                    case FrameType.Priority:
                        var priorityFrame = (PriorityFrame) frame;
                        stream = GetStream(priorityFrame.StreamId);
                        if (_usePriorities)
                        {
                            stream.Priority = priorityFrame.Priority;
                        }
                        break;

                    case FrameType.RstStream:
                        var resetFrame = (RstStreamFrame) frame;
                        stream = GetStream(resetFrame.StreamId);

                        Console.WriteLine("Got rst with code {0}", resetFrame.StatusCode);
                        stream.Dispose();
                        break;
                    case FrameType.Data:
                        var dataFrame = (DataFrame) frame;
                        stream = GetStream(dataFrame.StreamId);

                        //Aggressive window update
                        if (stream.IsFlowControlEnabled)
                        {
                            stream.WriteWindowUpdate(2000000);
                        }
                        break;
                    case FrameType.Ping:
                        var pingFrame = (PingFrame) frame;
                        if (pingFrame.IsPong)
                        {
                              _wasPingReceived = true;
                              _pingReceived.Set();
                        }
                        else
                        {
                            var pingResonseFrame = new PingFrame(true);
                            _writeQueue.WriteFrame(pingResonseFrame);
                        }
                        break;
                    case FrameType.Settings:
                        //Not first frame in the session.
                        //Client initiates connection and sends settings before request.
                        //It means that if server sent settings before it will not be a first frame,
                        //because client initiates connection.
                        if (_ourEnd == ConnectionEnd.Server && !_wasSettingsReceived && ActiveStreams.Count != 0)
                        {
                            Dispose();
                            return;
                        }

                        _wasSettingsReceived = true;
                        _settingsManager.ProcessSettings((SettingsFrame) frame, this, _flowControlManager);
                        break;
                    case FrameType.WindowUpdate:
                        if (_useFlowControl)
                        {
                            var windowFrame = (WindowUpdateFrame) frame;

                            stream = GetStream(windowFrame.StreamId);

                            stream.UpdateWindowSize(windowFrame.Delta);
                            stream.PumpUnshippedFrames();
                        }
                        break;

                    case FrameType.GoAway:
                        _goAwayReceived = true;
                        Dispose();
                        break;
                    default:
                        throw new NotImplementedException(frame.FrameType.ToString());
                }

                if (stream != null && frame is IEndStreamFrame && ((IEndStreamFrame)frame).IsEndStream)
                {
                    //Tell the stream that it was the last frame
                    Console.WriteLine("Final frame received for stream with id = " + stream.Id);
                    stream.EndStreamReceived = true;
                }

                if (stream != null && OnFrameReceived != null)
                {
                    OnFrameReceived(this, new FrameReceivedEventArgs(stream, frame));
                }
            }

            //Frame came for already closed stream. Ignore it.
            //Spec:
            //An endpoint that sends RST_STREAM MUST ignore
            //frames that it receives on closed streams if it sends RST_STREAM.
            //
            //An endpoint MUST NOT send frames on a closed stream.  An endpoint
            //that receives a frame after receiving a RST_STREAM or a frame
            //containing a END_STREAM flag on that stream MUST treat that as a
            //stream error (Section 5.4.2) of type PROTOCOL_ERROR.
            catch (Http2StreamNotFoundException)
            {
                if (stream != null)
                {
                    stream.WriteRst(ResetStatusCode.ProtocolError);
                }
                else
                {
                    //GoAway?
                }
            }
        }
Example #21
0
 public FrameSentArgs(Frame frame)
 {
     Frame = frame;
 }
        /// <summary>
        /// Returns the StreamId from the given frame, or -1 if this frame type does not have one.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static int GetStreamId(Frame frame)
        {
            if (frame.IsControl)
            {
                ControlFrame controlFrame = (ControlFrame)frame;
                switch (controlFrame.FrameType)
                {
                    case ControlFrameType.Headers:
                    case ControlFrameType.RstStream:
                    case ControlFrameType.SynReply:
                    case ControlFrameType.SynStream:
                    case ControlFrameType.WindowUpdate:
                        StreamControlFrame streamFrame = (StreamControlFrame)controlFrame;
                        return streamFrame.StreamId;

                    case ControlFrameType.Credential:
                    case ControlFrameType.GoAway:
                    case ControlFrameType.Settings:
                    case ControlFrameType.Ping:
                        return -1;

                    default:
                        throw new NotImplementedException(controlFrame.FrameType.ToString());
                }
            }
            else
            {
                DataFrame dataFrame = (DataFrame)frame;
                return dataFrame.StreamId;
            }
        }
Example #23
0
        /// <summary>
        /// Dispatches the incoming frame.
        /// </summary>
        /// <param name="frame">The frame.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        private void DispatchIncomingFrame(Frame frame)
        {
            Http2Stream stream = null;

            //Spec 03 tells that frame with continues flag MUST be followed by a frame with the same type
            //and the same stread id.
            if (_toBeContinuedHeaders.Count != 0)
            {
                if (_toBeContinuedFrame.FrameType != frame.FrameType
                    || _toBeContinuedFrame.StreamId != frame.StreamId)
                {
                    //If not, we must close the session.
                    Dispose();
                    return;
                }
            }

            try
            {
                switch (frame.FrameType)
                {
                    case FrameType.Headers:
                        Http2Logger.LogDebug("New headers with id = " + frame.StreamId);
                        var headersFrame = (HeadersFrame)frame;
                        var serializedHeaders = new byte[headersFrame.CompressedHeaders.Count];

                        Buffer.BlockCopy(headersFrame.CompressedHeaders.Array,
                                         headersFrame.CompressedHeaders.Offset,
                                         serializedHeaders, 0, serializedHeaders.Length);

                        var decompressedHeaders = _comprProc.Decompress(serializedHeaders);
                        var headers = new HeadersList(decompressedHeaders);

                        if (!headersFrame.IsEndHeaders)
                        {
                            _toBeContinuedHeaders.AddRange(decompressedHeaders);
                            _toBeContinuedFrame = headersFrame;
                            break;
                        }

                        headers.AddRange(_toBeContinuedHeaders);
                        _toBeContinuedHeaders.Clear();
                        _toBeContinuedFrame = null;
                        headersFrame.Headers.AddRange(headers);
                        foreach (var header in headers)
                        {
                            Http2Logger.LogDebug("Stream {0} header: {1}={2}", frame.StreamId, header.Key, header.Value);
                        }

                        stream = GetStream(headersFrame.StreamId);

                        if (stream == null)
                        {
                            if (_ourEnd == ConnectionEnd.Server)
                            {
                                string path = headers.GetValue(":path");
                                if (path == null)
                                {
                                    path = _handshakeHeaders.ContainsKey(":path")
                                               ? _handshakeHeaders[":path"]
                                               : @"\index.html";
                                    headers.Add(new KeyValuePair<string, string>(":path", path));
                                }
                            }
                            else
                            {
                                headers.AddRange(_handshakeHeaders);
                            }
                            stream = CreateStream(headers, frame.StreamId);
                        }

                        break;

                    case FrameType.Priority:
                        var priorityFrame = (PriorityFrame)frame;
                        Http2Logger.LogDebug("Priority frame. StreamId: {0} Priority: {1}", priorityFrame.StreamId, priorityFrame.Priority);
                        stream = GetStream(priorityFrame.StreamId);
                        if (_usePriorities)
                        {
                            stream.Priority = priorityFrame.Priority;
                        }
                        break;

                    case FrameType.RstStream:
                        var resetFrame = (RstStreamFrame)frame;
                        stream = GetStream(resetFrame.StreamId);

                        if (stream != null)
                        {
                            Http2Logger.LogDebug("RST frame with code " + resetFrame.StatusCode);
                            stream.Dispose();
                        }
                        break;
                    case FrameType.Data:
                        var dataFrame = (DataFrame)frame;
                        Http2Logger.LogDebug("Data frame. StreamId:{0} Length:{1}", dataFrame.StreamId, dataFrame.FrameLength);
                        stream = GetStream(dataFrame.StreamId);

                        //Aggressive window update
                        if (stream != null && stream.IsFlowControlEnabled)
                        {
                            stream.WriteWindowUpdate(InitialWindowSize);
                        }
                        break;
                    case FrameType.Ping:
                        var pingFrame = (PingFrame)frame;
                        Http2Logger.LogDebug("Ping frame with StreamId:{0} Payload:{1}", pingFrame.StreamId, pingFrame.Payload.Count);

                        if (pingFrame.FrameLength != PingFrame.PayloadLength)
                        {
                            throw new ProtocolError(ResetStatusCode.ProtocolError, "Ping payload size is not equal to 8");
                        }

                        if (pingFrame.IsPong)
                        {
                            _wasPingReceived = true;
                            _pingReceived.Set();
                        }
                        else
                        {
                            var pongFrame = new PingFrame(true, pingFrame.Payload.ToArray());
                            _writeQueue.WriteFrame(pongFrame);
                        }
                        break;
                    case FrameType.Settings:
                        //Not first frame in the session.
                        //Client initiates connection and sends settings before request.
                        //It means that if server sent settings before it will not be a first frame,
                        //because client initiates connection.
                        if (_ourEnd == ConnectionEnd.Server && !_wasSettingsReceived
                            && (ActiveStreams.Count > 0))
                        {
                            Dispose();
                            return;
                        }

                        var settingFrame = (SettingsFrame)frame;
                        Http2Logger.LogDebug("Settings frame. Entry count: {0} StreamId: {1}", settingFrame.EntryCount, settingFrame.StreamId);
                        _wasSettingsReceived = true;
                        _settingsManager.ProcessSettings(settingFrame, this, _flowControlManager);

                        if (_ourEnd == ConnectionEnd.Server && _sessionSocket.SecureProtocol == SecureProtocol.None)
                        {
                            //The HTTP/1.1 request that is sent prior to upgrade is associated with
                            //stream 1 and is assigned the highest possible priority.  Stream 1 is
                            //implicitly half closed from the client toward the server, since the
                            //request is completed as an HTTP/1.1 request.  After commencing the
                            //HTTP/2.0 connection, stream 1 is used for the response.
                            stream = CreateStream(Priority.Pri0);
                            stream.EndStreamReceived = true;
                            stream.Headers.Add(new KeyValuePair<string, string>(":method", _handshakeHeaders[":method"]));
                            stream.Headers.Add(new KeyValuePair<string, string>(":path", _handshakeHeaders[":path"]));
                            OnFrameReceived(this, new FrameReceivedEventArgs(stream, new HeadersFrame(stream.Id, false)));
                        }

                        break;
                    case FrameType.WindowUpdate:
                        if (_useFlowControl)
                        {
                            var windowFrame = (WindowUpdateFrame)frame;
                            Http2Logger.LogDebug("WindowUpdate frame. Delta: {0} StreamId: {1}", windowFrame.Delta, windowFrame.StreamId);
                            stream = GetStream(windowFrame.StreamId);

                            if (stream != null)
                            {
                                stream.UpdateWindowSize(windowFrame.Delta);
                                stream.PumpUnshippedFrames();
                            }
                        }
                        break;

                    case FrameType.GoAway:
                        _goAwayReceived = true;
                        Http2Logger.LogDebug("GoAway frame received");
                        Dispose();
                        break;
                    default:
                        throw new NotImplementedException(frame.FrameType.ToString());
                }

                if (stream != null && frame is IEndStreamFrame && ((IEndStreamFrame)frame).IsEndStream)
                {
                    //Tell the stream that it was the last frame
                    Http2Logger.LogDebug("Final frame received for stream with id = " + stream.Id);
                    stream.EndStreamReceived = true;
                }

                if (stream != null && OnFrameReceived != null)
                {
                    OnFrameReceived(this, new FrameReceivedEventArgs(stream, frame));
                }
            }

            //Frame came for already closed stream. Ignore it.
            //Spec:
            //An endpoint that sends RST_STREAM MUST ignore
            //frames that it receives on closed streams if it sends RST_STREAM.
            //
            //An endpoint MUST NOT send frames on a closed stream.  An endpoint
            //that receives a frame after receiving a RST_STREAM or a frame
            //containing a END_STREAM flag on that stream MUST treat that as a
            //stream error (Section 5.4.2) of type PROTOCOL_ERROR.
            catch (Http2StreamNotFoundException)
            {
                if (stream != null)
                {
                    stream.WriteRst(ResetStatusCode.ProtocolError);
                }
                else
                {
                    //GoAway?
                }
            }
            catch (CompressionError ex)
            {
                //The endpoint is unable to maintain the compression context for the connection.
                Http2Logger.LogError("Compression error occurred: " + ex.Message);
                Close(ResetStatusCode.CompressionError);
            }
            catch (ProtocolError pEx)
            {
                Http2Logger.LogError("Protocol error occurred: " + pEx.Message);
                Close(pEx.Code);
            }
        }
 // Incoming
 public CredentialFrame(Frame preamble)
     : base(preamble)
 {
 }