Beispiel #1
0
        //TODO Think about: writing push_promise is available in any time now. Need to handle it.
        public void WritePushPromise(IDictionary<string, string[]> pairs, Int32 promisedId)
        {
            if (Id % 2 != 0 && promisedId % 2 != 0)
                throw new InvalidOperationException("Client cant send push_promise frames");

            if (Closed)
                return;

            var headers = new HeadersList(pairs);
            var frame = new PushPromiseFrame(Id, promisedId, true, true, headers);

            ReservedLocal = true;

            _writeQueue.WriteFrame(frame);

            if (OnFrameSent != null)
            {
                OnFrameSent(this, new FrameSentEventArgs(frame));
            }
        }
        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");
            }
        }
Beispiel #3
0
        //TODO Think about: writing push_promise is available in any time now. Need to handle it.
        public void WritePushPromise(IDictionary<string, string[]> pairs, Int32 promisedId)
        {
            if (Id % 2 != 0 && promisedId % 2 != 0)
                throw new InvalidOperationException("Client cant send push_promise frames");

            if (Disposed)
                return;

            var headers = new HeadersList(pairs);

            //TODO IsEndPushPromise should be computationable
            var frame = new PushPromiseFrame(Id, promisedId, true, headers);

            _writeQueue.WriteFrame(frame);

            if (OnFrameSent != null)
            {
                OnFrameSent(this, new FrameSentEventArgs(frame));
            }
        }
        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");
            }
        }