Пример #1
0
 public void Remove(HeadersSequence item)
 {
     lock (_modificationLock)
     {
         _collection.Remove(item);
     }
 }
Пример #2
0
 public void Add(HeadersSequence item)
 {
     lock (_modificationLock)
     {
         _collection.Add(item);
     }
 }
        private void HandleContinuation(ContinuationFrame contFrame, out Http2Stream stream)
        {
            if (!(_lastFrame is ContinuationFrame || _lastFrame is HeadersFrame))
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "Last frame was not headers or continuation");

            Http2Logger.LogDebug("New continuation with id = " + contFrame.StreamId);

            if (contFrame.StreamId == 0)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "Incoming continuation frame with id = 0");
            }

            var serHeaders = new byte[contFrame.CompressedHeaders.Count];

            Buffer.BlockCopy(contFrame.CompressedHeaders.Array,
                                contFrame.CompressedHeaders.Offset,
                                serHeaders, 0, serHeaders.Length);

            var decomprHeaders = _comprProc.Decompress(serHeaders);
            var contHeaders = new HeadersList(decomprHeaders);
            foreach (var header in contHeaders)
            {
                Http2Logger.LogDebug("Stream {0} header: {1}={2}", contFrame.StreamId, header.Key, header.Value);
            }
            contFrame.Headers.AddRange(contHeaders);
            var sequence = _headersSequences.Find(seq => seq.StreamId == contFrame.StreamId);
            if (sequence == null)
            {
                sequence = new HeadersSequence(contFrame.StreamId, contFrame);
                _headersSequences.Add(sequence);
            }
            else
            {
                sequence.AddHeaders(contFrame);
            }

            if (!sequence.IsComplete)
            {
                stream = null;
                return;
            }

            stream = GetStream(contFrame.StreamId);
            if (stream == null)
            {
                stream = CreateStream(sequence.Headers, contFrame.StreamId, sequence.Priority);
            }
            else
            {
                stream.Headers.AddRange(sequence.Headers);
            }
        }
Пример #4
0
        /// <summary>
        /// Creates new http2 stream.
        /// </summary>
        /// <param name="priority">The stream priority.</param>
        /// <returns></returns>
        /// <exception cref="System.InvalidOperationException">Thrown when trying to create more streams than allowed by the remote side</exception>
        public Http2Stream CreateStream(int priority)
        {
            if (priority < 0 || priority > Constants.MaxPriority)
                throw new ArgumentOutOfRangeException("priority is not between 0 and MaxPriority");

            if (ActiveStreams.GetOpenedStreamsBy(_ourEnd) + 1 > RemoteMaxConcurrentStreams)
            {
                throw new MaxConcurrentStreamsLimitException();
            }

            var id = GetNextId();
            if (_usePriorities)
            {
                ActiveStreams[id] = new Http2Stream(id, _writeQueue, _flowControlManager, priority);
            }
            else
            {
                ActiveStreams[id] = new Http2Stream(id, _writeQueue, _flowControlManager);
            }

            var streamSequence = new HeadersSequence(id, (new HeadersFrame(id, priority)));
            _headersSequences.Add(streamSequence);

            ActiveStreams[id].OnFrameSent += (o, args) =>
            {
                if (!(args.Frame is IHeadersFrame))
                    return;

                var streamSeq = _headersSequences.Find(id);
                streamSeq.AddHeaders(args.Frame as IHeadersFrame);
            };

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

                    var streamSeq = _headersSequences.Find(id);

                    if (streamSeq != null)
                        _headersSequences.Remove(streamSeq);
                };

            return ActiveStreams[id];
        }
Пример #5
0
        internal Http2Stream CreateStream(HeadersSequence sequence)
        {

            if (sequence == null)
                throw new ArgumentNullException("sequence is null");

            if (sequence.Priority < 0 || sequence.Priority > Constants.MaxPriority)
                throw new ArgumentOutOfRangeException("priority is not between 0 and MaxPriority");

            if (ActiveStreams.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams)
            {
                throw new MaxConcurrentStreamsLimitException();
            }

            int id = sequence.StreamId;
            int priority = sequence.Priority;
            var headers = sequence.Headers;

            var stream = new Http2Stream(headers, id,
                                         _writeQueue, _flowControlManager, priority);

            if (sequence.WasEndStreamReceived)
                stream.EndStreamReceived = sequence.WasEndStreamReceived;

            ActiveStreams[stream.Id] = stream;

            stream.OnFrameSent += (o, args) =>
            {
                if (!(args.Frame is IHeadersFrame))
                    return;

                var streamSeq = _headersSequences.Find(stream.Id);
                streamSeq.AddHeaders(args.Frame as IHeadersFrame);
            };

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

                var streamSeq = _headersSequences.Find(stream.Id);

                if (streamSeq != null)
                    _headersSequences.Remove(streamSeq);
            };

            return stream;
        }
Пример #6
0
        /// <summary>
        /// Creates stream.
        /// </summary>
        /// <param name="headers"></param>
        /// <param name="streamId"></param>
        /// <param name="priority"></param>
        /// <returns></returns>
        public Http2Stream CreateStream(HeadersList headers, int streamId, int priority = -1)
        {

            if (headers == null)
                throw new ArgumentNullException("pairs is null");

            if (priority == -1)
                priority = Constants.DefaultStreamPriority;

            if (priority < 0 || priority > Constants.MaxPriority)
                throw new ArgumentOutOfRangeException("priority is not between 0 and MaxPriority");

            if (ActiveStreams.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams)
            {
                throw new MaxConcurrentStreamsLimitException();
            }

            var stream = new Http2Stream(headers, streamId,
                                         _writeQueue, _flowControlManager, priority);

            var streamSequence = new HeadersSequence(streamId, (new HeadersFrame(streamId, priority){Headers = headers}));
            _headersSequences.Add(streamSequence);

            ActiveStreams[stream.Id] = stream;

            stream.OnFrameSent += (o, args) =>
                {
                    if (!(args.Frame is IHeadersFrame))
                        return;

                    var streamSeq = _headersSequences.Find(stream.Id);
                    streamSeq.AddHeaders(args.Frame as IHeadersFrame);
                };

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

                    var streamSeq = _headersSequences.Find(stream.Id);

                    if (streamSeq != null)
                        _headersSequences.Remove(streamSeq);
                };

            return stream;
        }
        private void HandleHeaders(HeadersFrame headersFrame, out Http2Stream stream)
        {
            Http2Logger.LogDebug("New headers with id = " + headersFrame.StreamId);

            //spec 06:
            //If a HEADERS frame
            //is received whose stream identifier field is 0x0, the recipient MUST
            //respond with a connection error (Section 5.4.1) of type
            //PROTOCOL_ERROR [PROTOCOL_ERROR].
            if (headersFrame.StreamId == 0)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Incoming headers frame with id = 0");
            }

            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);
            foreach (var header in headers)
            {
                Http2Logger.LogDebug("Stream {0} header: {1}={2}", headersFrame.StreamId, header.Key, header.Value);
            }
            headersFrame.Headers.AddRange(headers);

            var sequence = _headersSequences.Find(seq => seq.StreamId == headersFrame.StreamId);
            if (sequence == null)
            {
                sequence = new HeadersSequence(headersFrame.StreamId, headersFrame);
                _headersSequences.Add(sequence);
            }
            else
            {
                sequence.AddHeaders(headersFrame);
            }

            if (headersFrame.HasPriority)
            {
                sequence.Priority = headersFrame.Priority;
            }

            if (!sequence.IsComplete)
            {
                stream = null;
                return;
            }

            stream = GetStream(headersFrame.StreamId);
            if (stream == null)
            {
                stream = CreateStream(sequence.Headers, headersFrame.StreamId, sequence.Priority);
            }
            else
            {
                stream.Headers.AddRange(sequence.Headers);
            }
        }
        private void HandleContinuation(ContinuationFrame contFrame, out Http2Stream stream)
        {
            if (!(_lastFrame is ContinuationFrame || _lastFrame is HeadersFrame))
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "Last frame was not headers or continuation");

            Http2Logger.LogDebug("New continuation with id = " + contFrame.StreamId);

            //09 -> 6.10.  CONTINUATION
            //CONTINUATION frames MUST be associated with a stream.  If a
            //CONTINUATION frame is received whose stream identifier field is 0x0,
            //the recipient MUST respond with a connection error (Section 5.4.1) of
            //type PROTOCOL_ERROR.
            if (contFrame.StreamId == 0)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "Incoming continuation frame with id = 0");
            }

            var serHeaders = new byte[contFrame.CompressedHeaders.Count];

            Buffer.BlockCopy(contFrame.CompressedHeaders.Array,
                                contFrame.CompressedHeaders.Offset,
                                serHeaders, 0, serHeaders.Length);

            var decomprHeaders = _comprProc.Decompress(serHeaders);
            var contHeaders = new HeadersList(decomprHeaders);
            foreach (var header in contHeaders)
            {
                Http2Logger.LogDebug("Stream {0} header: {1}={2}", contFrame.StreamId, header.Key, header.Value);
            }
            contFrame.Headers.AddRange(contHeaders);
            var sequence = _headersSequences.Find(contFrame.StreamId);
            if (sequence == null)
            {
                sequence = new HeadersSequence(contFrame.StreamId, contFrame);
                _headersSequences.Add(sequence);
            }
            else
            {
                sequence.AddHeaders(contFrame);
            }

            if (!sequence.IsComplete)
            {
                stream = null;
                return;
            }

            stream = GetStream(contFrame.StreamId);
            if (stream == null)
            {
                stream = CreateStream(sequence);

                ValidateHeaders(stream);
            }
            else
            {
                stream.Headers = sequence.Headers;

                ValidateHeaders(stream);
            }
        }
Пример #9
0
        /// <summary>
        /// Creates new http2 stream.
        /// </summary>
        /// <param name="priority">The stream priority.</param>
        /// <returns></returns>
        /// <exception cref="System.InvalidOperationException">Thrown when trying to create more streams than allowed by the remote side</exception>
        public Http2Stream CreateStream(int priority)
        {
            if (priority < 0 || priority > Constants.MaxPriority)
                throw new ArgumentOutOfRangeException("priority is not between 0 and MaxPriority");

            if (StreamDictionary.GetOpenedStreamsBy(_ourEnd) + 1 > RemoteMaxConcurrentStreams)
            {
                throw new MaxConcurrentStreamsLimitException();
            }
            int nextId = GetNextId();
            var stream = StreamDictionary[nextId];

            var streamSequence = new HeadersSequence(nextId, (new HeadersFrame(nextId, true)));
            _headersSequences.Add(streamSequence);

            stream.OnFrameSent += (o, args) =>
            {
                if (!(args.Frame is IHeadersFrame))
                    return;

                var streamSeq = _headersSequences.Find(nextId);
                streamSeq.AddHeaders(args.Frame as IHeadersFrame);
            };

            stream.OnClose += (o, args) =>
                {
                    var streamSeq = _headersSequences.Find(stream.Id);

                    if (streamSeq != null)
                        _headersSequences.Remove(streamSeq);
                };

            stream.Priority = priority;
            stream.Opened = true;

            return stream;
        }
        private void HandlePushPromiseFrame(PushPromiseFrame frame, out Http2Stream stream)
        {
            Http2Logger.LogDebug("New push_promise with id = " + frame.StreamId);
            Http2Logger.LogDebug("Promised id = " + frame.PromisedStreamId);

            //09 -> 6.6.  PUSH_PROMISE
            //PUSH_PROMISE frames MUST be associated with an existing, peer-
            //initiated stream.  If the stream identifier field specifies the value
            //0x0, a recipient MUST respond with a connection error (Section 5.4.1)
            //of type PROTOCOL_ERROR.

            //... a receiver MUST
            //treat the receipt of a PUSH_PROMISE that promises an illegal stream
            //identifier (Section 5.1.1) (that is, an identifier for a stream that
            //is not currently in the "idle" state) as a connection error
            //(Section 5.4.1) of type PROTOCOL_ERROR, unless the receiver recently
            //sent a RST_STREAM frame to cancel the associated stream (see
            //Section 5.1).

            if (frame.StreamId == 0 || frame.PromisedStreamId == 0 || (frame.PromisedStreamId % 2) != 0
                || ActiveStreams.Any(str => (str.Key % 2) == 0 && str.Key > frame.PromisedStreamId))
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Incorrect promised stream id");
            }

            var serializedHeaders = new byte[frame.CompressedHeaders.Count];

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

            var decompressedHeaders = _comprProc.Decompress(serializedHeaders);
            var headers = new HeadersList(decompressedHeaders);
            foreach (var header in headers)
            {
                Http2Logger.LogDebug("Stream {0} header: {1}={2}", frame.StreamId, header.Key, header.Value);

                frame.Headers.Add(header);
            }

            var sequence = _headersSequences.Find(frame.PromisedStreamId);
            if (sequence == null)
            {
                sequence = new HeadersSequence(frame.PromisedStreamId, frame);
                _headersSequences.Add(sequence);
            }
            else
            {
                //09 -> 6.6.  PUSH_PROMISE
                //A receiver MUST
                //treat the receipt of a PUSH_PROMISE on a stream that is neither
                //"open" nor "half-closed (local)" as a connection error
                //(Section 5.4.1) of type PROTOCOL_ERROR.

                //This means that we already got push_promise with the same PromisedId.
                //Hence Stream is in the reserved state.
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Got multiple push_promises with same Promised id's");
            }

            //09 -> 6.6.  PUSH_PROMISE
            //A PUSH_PROMISE frame without the END_PUSH_PROMISE flag set MUST be
            //followed by a CONTINUATION frame for the same stream.  A receiver
            //MUST treat the receipt of any other type of frame or a frame on a
            //different stream as a connection error (Section 5.4.1) of type
            //PROTOCOL_ERROR.
            if (!sequence.IsComplete)
            {
                stream = null;
                return;
            }

            //09 -> 8.2.1.  Push Requests
            //The server MUST include a method in the ":method"
            //header field that is safe (see [HTTP-p2], Section 4.2.1).  If a
            //client receives a PUSH_PROMISE that does not include a complete and
            // valid set of header fields, or the ":method" header field identifies
            //a method that is not safe, it MUST respond with a stream error
            //(Section 5.4.2) of type PROTOCOL_ERROR.

            //Lets think that only GET method is safe for now
            var method = sequence.Headers.GetValue(CommonHeaders.Method);
            if (method == null || !method.Equals(Verbs.Get, StringComparison.OrdinalIgnoreCase))
            {
                var frameReceiveStream = GetStream(frame.StreamId);
                frameReceiveStream.WriteRst(ResetStatusCode.ProtocolError);
                frameReceiveStream.Dispose(ResetStatusCode.None);

                stream = null;
                return;
            }

            stream = GetStream(frame.PromisedStreamId);
            if (stream == null)
            {
                stream = CreateStream(sequence);
                stream.EndStreamSent = true;

                ValidateHeaders(stream);
            }
            else
            {
                //09 -> 6.6.  PUSH_PROMISE
                //Similarly, a receiver MUST
                //treat the receipt of a PUSH_PROMISE that promises an illegal stream
                //identifier (Section 5.1.1) (that is, an identifier for a stream that
                //is not currently in the "idle" state) as a connection error
                //(Section 5.4.1) of type PROTOCOL_ERROR

                throw new ProtocolError(ResetStatusCode.ProtocolError, "Remote ep tried to promise incorrect stream id");
            }
        }
Пример #11
0
        internal Http2Stream CreateStream(HeadersSequence sequence)
        {

            if (sequence == null)
                throw new ArgumentNullException("sequence is null");

            if (sequence.Priority < 0 || sequence.Priority > Constants.MaxPriority)
                throw new ArgumentOutOfRangeException("priority is not between 0 and MaxPriority");

            if (StreamDictionary.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams)
            {
                throw new MaxConcurrentStreamsLimitException();
            }

            int id = sequence.StreamId;
            int priority = sequence.Priority;
            var headers = sequence.Headers;

            var stream = StreamDictionary[id];

            if (sequence.WasEndStreamReceived)
                stream.HalfClosedLocal = sequence.WasEndStreamReceived;

            stream.OnFrameSent += (o, args) =>
            {
                if (!(args.Frame is IHeadersFrame))
                    return;

                var streamSeq = _headersSequences.Find(stream.Id);
                streamSeq.AddHeaders(args.Frame as IHeadersFrame);
            };

            stream.OnClose += (o, args) =>
            {
                var streamSeq = _headersSequences.Find(stream.Id);

                if (streamSeq != null)
                    _headersSequences.Remove(streamSeq);
            };

            stream.Headers = headers;
            stream.Priority = priority;
            stream.Opened = true;

            return stream;
        }
Пример #12
0
        /// <summary>
        /// Creates stream.
        /// </summary>
        /// <param name="headers"></param>
        /// <param name="streamId"></param>
        /// <param name="priority"></param>
        /// <returns></returns>
        public Http2Stream CreateStream(HeadersList headers, int streamId, int priority = -1)
        {

            if (headers == null)
                throw new ArgumentNullException("pairs is null");

            if (priority == -1)
                priority = Constants.DefaultStreamPriority;

            if (priority < 0 || priority > Constants.MaxPriority)
                throw new ArgumentOutOfRangeException("priority is not between 0 and MaxPriority");

            if (StreamDictionary.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams)
            {
                throw new MaxConcurrentStreamsLimitException();
            }

            var streamSequence = new HeadersSequence(streamId, (new HeadersFrame(streamId, true){Headers = headers}));
            _headersSequences.Add(streamSequence);

            var stream = StreamDictionary[streamId];

            stream.OnFrameSent += (o, args) =>
                {
                    if (!(args.Frame is IHeadersFrame))
                        return;

                    var streamSeq = _headersSequences.Find(stream.Id);
                    streamSeq.AddHeaders(args.Frame as IHeadersFrame);
                };

            stream.OnClose += (o, args) =>
                {
                    var streamSeq = _headersSequences.Find(stream.Id);

                    if (streamSeq != null)
                        _headersSequences.Remove(streamSeq);
                };
            stream.Priority = priority;
            stream.Headers = headers;
            stream.Opened = true;

            return stream;
        }
        private void HandlePushPromiseFrame(PushPromiseFrame frame, out Http2Stream stream)
        {
            Http2Logger.LogDebug("PUSH_PROMISE frame: stream id={0}, payload len={1}, promised id={2}, " +
                                 "has pad={3}, pad high={4}, pad low={5}, end headers={6}",
                                 frame.StreamId, frame.PayloadLength, frame.PromisedStreamId, frame.HasPadding,
                                 frame.PadHigh, frame.PadLow, frame.IsEndHeaders);

            /* 12 -> 6.6. 
            PUSH_PROMISE frames MUST be associated with an existing, peer- initiated stream.
            If the stream identifier field specifies the value 0x0, a recipient MUST respond
            with a connection error of type PROTOCOL_ERROR. */
            if (frame.StreamId == 0)
                throw new ProtocolError(ResetStatusCode.ProtocolError, "push promise frame with stream id=0");

            /* 12 -> 5.1
            An endpoint that receives any frame after receiving a RST_STREAM MUST treat
            that as a stream error of type STREAM_CLOSED. */
            if (StreamDictionary[frame.StreamId].Closed)
                throw new Http2StreamNotFoundException(frame.StreamId);


            //... a receiver MUST
            //treat the receipt of a PUSH_PROMISE that promises an illegal stream
            //identifier (Section 5.1.1) (that is, an identifier for a stream that
            //is not currently in the "idle" state) as a connection error
            //(Section 5.4.1) of type PROTOCOL_ERROR, unless the receiver recently
            //sent a RST_STREAM frame to cancel the associated stream (see
            //Section 5.1).  
            if (frame.StreamId % 2 == 0
                || frame.PromisedStreamId == 0
                || (frame.PromisedStreamId % 2) != 0
                || frame.PromisedStreamId < _lastPromisedId
                || !((StreamDictionary[frame.StreamId].Opened || StreamDictionary[frame.StreamId].HalfClosedLocal)))
            { 
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Incorrect Promised Stream id");
            }         

            var serializedHeaders = new byte[frame.CompressedHeaders.Count];

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

            var decompressedHeaders = _comprProc.Decompress(serializedHeaders);
            var headers = new HeadersList(decompressedHeaders);
            foreach (var header in headers)
            {
                Http2Logger.LogDebug("Stream {0} header: {1}={2}", frame.StreamId, header.Key, header.Value);

                frame.Headers.Add(header);
            }

            var sequence = _headersSequences.Find(frame.PromisedStreamId);
            if (sequence == null)
            {
                sequence = new HeadersSequence(frame.PromisedStreamId, frame);
                _headersSequences.Add(sequence);
            }
            else
            {
                //09 -> 6.6.  PUSH_PROMISE
                //A receiver MUST
                //treat the receipt of a PUSH_PROMISE on a stream that is neither
                //"open" nor "half-closed (local)" as a connection error
                //(Section 5.4.1) of type PROTOCOL_ERROR.

                //This means that we already got push_promise with the same PromisedId.
                //Hence Stream is in the reserved state.
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "Got multiple push promises with same Promised Stream id's");
            }

            //09 -> 6.6.  PUSH_PROMISE
            //A PUSH_PROMISE frame without the END_PUSH_PROMISE flag set MUST be
            //followed by a CONTINUATION frame for the same stream.  A receiver
            //MUST treat the receipt of any other type of frame or a frame on a
            //different stream as a connection error (Section 5.4.1) of type
            //PROTOCOL_ERROR.
            if (!sequence.IsComplete)
            {
                stream = null;
                return;
            }

            //09 -> 8.2.1.  Push Requests
            //The server MUST include a method in the ":method"
            //header field that is safe (see [HTTP-p2], Section 4.2.1).  If a
            //client receives a PUSH_PROMISE that does not include a complete and
            // valid set of header fields, or the ":method" header field identifies
            //a method that is not safe, it MUST respond with a stream error
            //(Section 5.4.2) of type PROTOCOL_ERROR.

            //Lets think that only GET method is safe for now
            var method = sequence.Headers.GetValue(CommonHeaders.Method);
            if (method == null || !method.Equals(Verbs.Get, StringComparison.OrdinalIgnoreCase))
            {
                var frameReceiveStream = GetStream(frame.StreamId);
                frameReceiveStream.WriteRst(ResetStatusCode.ProtocolError);
                frameReceiveStream.Close(ResetStatusCode.None);

                stream = null;
                return;
            }

            stream = GetStream(frame.PromisedStreamId);

            if (stream.Idle)
            {
                stream = CreateStream(sequence);
                stream.ReservedRemote = true;

                ValidateHeaders(stream);
                _lastPromisedId = stream.Id;
            }
            else {
                /* 12 -> 6.6
                Similarly, a receiver MUST treat the receipt of a PUSH_PROMISE that
                promises an illegal stream identifier (that is, an identifier for a stream that
                is not currently in the "idle" state) as a connection error of type PROTOCOL_ERROR. */
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Remote endpoint tried to Promise incorrect Stream id");
            }
        }
        private void HandleHeaders(HeadersFrame headersFrame, out Http2Stream stream)
        {
            Http2Logger.LogDebug("HEADERS frame: stream id={0}, payload len={1}, has pad={2}, pad high={3}, pad low={4}, " +
                                 "end stream={5}, has priority={6}, exclusive={7}, dependency={8}, weight={9}", 
                                 headersFrame.StreamId, headersFrame.PayloadLength,
                                 headersFrame.HasPadding, headersFrame.PadHigh, headersFrame.PadLow, headersFrame.IsEndStream,
                                 headersFrame.HasPriority, headersFrame.Exclusive, headersFrame.StreamDependency, headersFrame.Weight);

            /* 12 -> 6.2 
            HEADERS frames MUST be associated with a stream.  If a HEADERS frame
            is received whose stream identifier field is 0x0, the recipient MUST
            respond with a connection error of type PROTOCOL_ERROR. */
            if (headersFrame.StreamId == 0)
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Incoming headers frame with stream id=0");

            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);
            foreach (var header in headers)
            {
                Http2Logger.LogDebug("{1}={2}", headersFrame.StreamId, header.Key, header.Value);
            }
            headersFrame.Headers.AddRange(headers);

            var sequence = _headersSequences.Find(headersFrame.StreamId);
            if (sequence == null)
            {
                sequence = new HeadersSequence(headersFrame.StreamId, headersFrame);
                _headersSequences.Add(sequence);
            }
            else
            {
                sequence.AddHeaders(headersFrame);
            }

            if (headersFrame.HasPriority)
            {
                //TODO: Priority was deprecated, now we need to use Dependency and Weight
                //sequence.Priority = headersFrame.Priority;
            }

            if (!sequence.IsComplete)
            {
                stream = null;
                return;
            }

            stream = GetStream(headersFrame.StreamId);
            if (stream.Idle)
            {
                stream = CreateStream(sequence);

                ValidateHeaders(stream);
            }
            else if (stream.ReservedRemote)
            {
                stream = CreateStream(sequence);
                stream.HalfClosedLocal = true;
                ValidateHeaders(stream);
            }
            else if(stream.Opened || stream.HalfClosedLocal)
            {
                stream.Headers = sequence.Headers;//Modify by the last accepted frame

                ValidateHeaders(stream);
            }
            else if (stream.HalfClosedRemote)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "headers for half closed remote stream");
            }
            else
            {
                throw new Http2StreamNotFoundException(headersFrame.StreamId);
            }
        }
        private void HandleContinuation(ContinuationFrame contFrame, out Http2Stream stream)
        {
            Http2Logger.LogDebug("CONTINUATION frame: stream id={0}, payload len={1}, has pad={2}, pad high={3}," +
                                 " pad low={4}, end headers={5}", contFrame.StreamId, contFrame.PayloadLength,
                                 contFrame.HasPadding, contFrame.PadHigh, contFrame.PadLow, contFrame.IsEndHeaders);

            if (!(_lastFrame is ContinuationFrame || _lastFrame is HeadersFrame))
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "Last frame was not headers or continuation");

            /* 12 -> 6.10
            CONTINUATION frames MUST be associated with a stream. If a CONTINUATION 
            frame is received whose stream identifier field is 0x0, the recipient MUST
            respond with a connection error of type PROTOCOL_ERROR. */
            if (contFrame.StreamId == 0)
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "Incoming continuation frame with stream id=0");

            var serHeaders = new byte[contFrame.CompressedHeaders.Count];

            Buffer.BlockCopy(contFrame.CompressedHeaders.Array,
                                contFrame.CompressedHeaders.Offset,
                                serHeaders, 0, serHeaders.Length);

            var decomprHeaders = _comprProc.Decompress(serHeaders);
            var contHeaders = new HeadersList(decomprHeaders);
            foreach (var header in contHeaders)
            {
                Http2Logger.LogDebug("Stream {0} header: {1}={2}", contFrame.StreamId, header.Key, header.Value);
            }
            contFrame.Headers.AddRange(contHeaders);
            var sequence = _headersSequences.Find(contFrame.StreamId);
            if (sequence == null)
            {
                sequence = new HeadersSequence(contFrame.StreamId, contFrame);
                _headersSequences.Add(sequence);
            }
            else
            {
                sequence.AddHeaders(contFrame);
            }

            if (!sequence.IsComplete)
            {
                stream = null;
                return;
            }

            stream = GetStream(contFrame.StreamId);
            if (stream.Idle || stream.ReservedRemote)
            {
                stream = CreateStream(sequence);

                ValidateHeaders(stream);
            }
            else if (stream.Opened || stream.HalfClosedLocal)
            {
                stream.Headers = sequence.Headers;//Modify by the last accepted frame

                ValidateHeaders(stream);
            }
            else if (stream.HalfClosedRemote)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "continuation for half closed remote stream");
            }
            else
            {
                throw new Http2StreamNotFoundException(contFrame.StreamId);
            }
        }