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));
            }
        }
Beispiel #2
0
        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");
            }
        }
Beispiel #3
0
 /// <summary>
 /// Process a push promise frame request.
 /// </summary>
 /// <param name="httpContext">The current http context.</param>
 /// <param name="pushPromiseFrame">The push promise frame.</param>
 /// <param name="stream">The selected stream context.</param>
 private static void ProcessPushPromiseFrameRequest(Nequeo.Net.Http2.HttpContext httpContext, PushPromiseFrame pushPromiseFrame, out ContextStream stream)
 {
     // Attempt to get the sepcific stream.
     stream = httpContext.GetStream(pushPromiseFrame.StreamId);
     if (stream == null)
     {
         throw new MaxConcurrentStreamsLimitException();
     }
 }