Ejemplo n.º 1
0
        /// <summary>
        /// Process a continuation frame request.
        /// </summary>
        /// <param name="httpContext">The current http context.</param>
        /// <param name="continuationFrame">The continuation frame.</param>
        /// <param name="stream">The selected stream context.</param>
        /// <param name="canPassContext">Can the data be sent to the client context.</param>
        private static void ProcessContinuationFrameRequest(Nequeo.Net.Http2.HttpContext httpContext,
                                                            ContinuationFrame continuationFrame, out ContextStream stream, out bool canPassContext)
        {
            // The data can be sent to the client
            // through the http context session.
            canPassContext = false;

            /*  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 (continuationFrame.StreamId == 0)
            {
                throw new ProtocolError(ErrorCodeRegistry.Protocol_Error, "Incoming continuation frame with stream id is equal to 0.");
            }

            // Attempt to get the sepcific stream.
            stream = httpContext.GetStream(continuationFrame.StreamId);
            if (stream == null)
            {
                throw new MaxConcurrentStreamsLimitException();
            }

            // Get the number of compressed headers.
            var serializedHeaders = new byte[continuationFrame.CompressedHeaders.Count];

            // Copy the compressed frame.
            Buffer.BlockCopy(continuationFrame.CompressedHeaders.Array, continuationFrame.CompressedHeaders.Offset, serializedHeaders, 0, serializedHeaders.Length);

            // Decompress the compressed headers.
            HeadersList decompressedHeaders = new HeaderCompression().Decompress(serializedHeaders);
            HeadersList headers             = new HeadersList(decompressedHeaders);

            // Add the list of headers.
            foreach (KeyValuePair <string, string> header in headers)
            {
                stream.HttpRequest.OriginalHeaders.Add(new NameValue()
                {
                    Name = header.Key, Value = header.Value
                });
            }

            // Determine if all headers have been found.
            if (continuationFrame.IsEndHeaders)
            {
                // The end of the headers has been found.
                stream.HttpRequest.HeadersFound = true;
            }

            bool wasValidated = false;

            // Check the current stream state.
            if (stream.Idle || stream.ReservedRemote)
            {
                // Validate all the headers.
                httpContext.ValidateHeaders(stream);
                wasValidated = true;
            }
            else if (stream.Opened || stream.HalfClosedLocal)
            {
                // Validate all the headers.
                httpContext.ValidateHeaders(stream);
                wasValidated = true;
            }
            else if (stream.HalfClosedRemote)
            {
                // Half closed remote stream.
                throw new ProtocolError(ErrorCodeRegistry.Protocol_Error, "Continuation for half closed remote stream.");
            }
            else
            {
                // Stream has no state error.
                throw new StreamNotFoundException(continuationFrame.StreamId);
            }

            // If the headers where validated.
            if (wasValidated)
            {
                // If headers have been found.
                if (stream.HttpRequest.HeadersFound)
                {
                    // Set the current stream id
                    httpContext.StreamId           = stream.StreamId;
                    stream.HttpRequest.IsEndOfData = continuationFrame.IsEndStream;

                    // Get the request resources.
                    RequestResource resource = Nequeo.Net.Http2.Utility.GetRequestResource(stream.HttpRequest.OriginalHeaders);

                    // Assign the http request content.
                    stream.HttpRequest.ReadRequestHeaders(stream.HttpRequest.OriginalHeaders, resource);

                    // If this is the last bit of data
                    // that has been sent then sent
                    // the data to the client context.
                    if (continuationFrame.IsEndStream)
                    {
                        canPassContext = true;
                    }
                }
            }
        }
Ejemplo n.º 2
0
        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);
            }
        }
 public ContinuationFrameList(ContinuationFrame first, ContinuationFrameList rest)
 {
     this.first = first;
     this.rest = rest;
 }