private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame)
        {
            string originalPath = stream.Headers.GetValue(CommonHeaders.Path.ToLower());
            //If user sets the empty file in get command we return notFound webpage
            string fileName = string.IsNullOrEmpty(Path.GetFileName(originalPath)) ? Index : Path.GetFileName(originalPath);
            string path = Path.Combine(AssemblyPath, fileName);

            try
            {
               _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count,
                                    path, stream.ReceivedDataAmount != 0);
            }
            catch (IOException)
            {
                Http2Logger.LogError("File is still downloading. Repeat request later");
               
                stream.Close(ResetStatusCode.InternalError);
                return;
            }

            stream.ReceivedDataAmount += dataFrame.Data.Count;

            if (dataFrame.IsEndStream)
            {
                if (stream.HalfClosedRemote)
                {
                    //send terminator
                    stream.WriteDataFrame(new ArraySegment<byte>(new byte[0]), true);
                    Http2Logger.LogConsole("Terminator was sent");
                }
                _fileHelper.RemoveStream(path);
                Http2Logger.LogConsole("Bytes received: " + stream.ReceivedDataAmount);
            }
        }
        protected override void ProcessRequest(Http2Stream stream, Frame frame)
        {
            //spec 09 -> 8.1.3.2.  Response Header Fields
            //A single ":status" header field is defined that carries the HTTP
            //status code field (see [HTTP-p2], Section 6).  This header field MUST
            //be included in all responses, otherwise the response is malformed
            if (stream.Headers.GetValue(CommonHeaders.Status) == null)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "no status header in response. StreamId = " + stream.Id);
            }

            int code;
            if (!int.TryParse(stream.Headers.GetValue(CommonHeaders.Status), out code))
            {
                stream.WriteRst(ResetStatusCode.ProtocolError);  //Got something strange in the status field
                stream.Close(ResetStatusCode.ProtocolError);
            }

            //got some king of error
            if (code != StatusCode.Code200Ok)
            {
                //Close server's stream
                stream.Close(ResetStatusCode.Cancel); //will dispose client's stream and close server's one.
            }
            //Do nothing. Client may not process requests for now
        }
        protected override void ProcessIncomingData(Http2Stream stream, Frame frame)
        {
            //wont process incoming non data frames for now.
            if (!(frame is DataFrame))
                return;

            var dataFrame = frame as DataFrame;

            SaveDataFrame(stream, dataFrame);
        }
        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);
            }
        }
 private void ValidateHeaders(Http2Stream stream)
 {
     /* 12 -> 8.1.3
     Header field names MUST be converted to lowercase prior to their 
     encoding in HTTP/2.0. A request or response containing uppercase 
     header field names MUST be treated as malformed. */
     foreach (var header in stream.Headers)
     {
         string key = header.Key;
         if (!_matcher.IsMatch(key) || key == ":")
         {                    
             stream.WriteRst(ResetStatusCode.RefusedStream);
             stream.Close(ResetStatusCode.RefusedStream);
             break;
         }
     }
 }
        /// <summary>
        /// Overrides request processing logic.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="frame">The request header frame.</param>
        /// <returns></returns>
        protected override void ProcessRequest(Http2Stream stream, Frame frame)
        {
            //spec 06
            //8.1.2.1.  Request Header Fields
            //HTTP/2.0 defines a number of headers starting with a ':' character
            //that carry information about the request target:
 
            //o  The ":method" header field includes the HTTP method ([HTTP-p2],
            //      Section 4).
 
            //o  The ":scheme" header field includes the scheme portion of the
            //      target URI ([RFC3986], Section 3.1).
 
            //o  The ":host" header field includes the authority portion of the
            //      target URI ([RFC3986], Section 3.2).
 
            //o  The ":path" header field includes the path and query parts of the
                //target URI (the "path-absolute" production from [RFC3986] and
                 //optionally a '?' character followed by the "query" production, see
                 //[RFC3986], Section 3.3 and [RFC3986], Section 3.4).  This field
                 //MUST NOT be empty; URIs that do not contain a path component MUST
                 //include a value of '/', unless the request is an OPTIONS request
                 //for '*', in which case the ":path" header field MUST include '*'.
 
            //All HTTP/2.0 requests MUST include exactly one valid value for all of
            //these header fields.  An intermediary MUST ensure that requests that
            //it forwards are correct.  A server MUST treat the absence of any of
            //these header fields, presence of multiple values, or an invalid value
            //as a stream error (Section 5.4.2) of type PROTOCOL_ERROR.

            if (stream.Headers.GetValue(CommonHeaders.Method) == null
                || stream.Headers.GetValue(CommonHeaders.Path) == null
                || stream.Headers.GetValue(CommonHeaders.Scheme) == null
                || stream.Headers.GetValue(CommonHeaders.Host) == null)
            {
                stream.WriteRst(ResetStatusCode.ProtocolError);
                return;
            }

            Task.Factory.StartNew(async () =>
                {
                    var context = PopulateEnvironment(stream.Headers);
                    await SendResponse(stream, context);
                });

        }
Beispiel #7
0
 private void ValidateHeaders(Http2Stream stream)
 {
     /* 12 -> 8.1.3
      * Header field names MUST be converted to lowercase prior to their
      * encoding in HTTP/2.0. A request or response containing uppercase
      * header field names MUST be treated as malformed. */
     foreach (var header in stream.Headers)
     {
         string key = header.Key;
         if (!_matcher.IsMatch(key) || key == ":")
         {
             stream.WriteRst(ResetStatusCode.RefusedStream);
             stream.Close(ResetStatusCode.RefusedStream);
             break;
         }
     }
 }
Beispiel #8
0
        private void HandleDataFrame(DataFrame dataFrame, out Http2Stream stream)
        {
            Http2Logger.LogDebug("DATA frame: stream id={0}, payload len={1}, has pad={2}, pad high={3}, pad low={4}, " +
                                 "end stream={5}", dataFrame.StreamId, dataFrame.PayloadLength,
                                 dataFrame.HasPadding, dataFrame.PadHigh, dataFrame.PadLow, dataFrame.IsEndStream);

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

            /* 12 -> 6.1
             * An endpoint that has not enabled DATA frame compression MUST
             * treat the receipt of a DATA frame with the COMPRESSED flag set as a
             * connection error of type PROTOCOL_ERROR. */
            if (dataFrame.IsCompressed)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "GZIP compression is not enabled");
            }

            stream = GetStream(dataFrame.StreamId);

            if (stream.Closed)
            {
                throw new Http2StreamNotFoundException(dataFrame.StreamId);
            }

            if (!(stream.Opened || stream.HalfClosedLocal))
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "data in non opened or half closed local stream");
            }


            if (stream.IsFlowControlEnabled && !dataFrame.IsEndStream)
            {
                stream.WriteWindowUpdate(Constants.MaxFramePayloadSize);
            }
        }
        private void HandleDataFrame(DataFrame dataFrame, out Http2Stream stream)
        {
            stream = GetStream(dataFrame.StreamId);

            //Aggressive window update
            if (stream != null)
            {
                Http2Logger.LogDebug("Data frame. StreamId: {0} Length: {1}", dataFrame.StreamId,
                                     dataFrame.FrameLength);
                if (stream.IsFlowControlEnabled)
                {
                    stream.WriteWindowUpdate(Constants.MaxFrameContentSize);
                }
            }
            else
            {
                throw new Http2StreamNotFoundException(dataFrame.StreamId);
            }
        }
Beispiel #10
0
        private void HandlePriority(PriorityFrame priorityFrame, out Http2Stream stream)
        {
            Http2Logger.LogDebug("PRIORITY frame: stream id={0}, exclusive={1}, dependency={2}, weight={3}",
                                 priorityFrame.StreamId, priorityFrame.Exclusive, priorityFrame.StreamDependency,
                                 priorityFrame.Weight);

            /* 12 -> 6.3
             * The PRIORITY frame is associated with an existing stream. If a
             * PRIORITY frame is received with a stream identifier of 0x0, the
             * recipient MUST respond with a connection error of type PROTOCOL_ERROR. */
            if (priorityFrame.StreamId == 0)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Incoming priority frame with stream id=0");
            }

            stream = GetStream(priorityFrame.StreamId);

            /* 12 -> 6.3
             * The PRIORITY frame can be sent on a stream in any of the "reserved
             * (remote)", "open", "half-closed (local)", or "half closed (remote)"
             * states, though it cannot be sent between consecutive frames that
             * comprise a single header block. */

            if (stream.Closed)
            {
                throw new Http2StreamNotFoundException(priorityFrame.StreamId);
            }

            if (!(stream.Opened || stream.ReservedRemote || stream.HalfClosedLocal))
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "priority for non opened or reserved stream");
            }

            if (!_usePriorities)
            {
                return;
            }

            stream.Priority = priorityFrame.Weight;
        }
        /// <summary>
        /// Creates stream.
        /// </summary>
        /// <param name="headers"></param>
        /// <param name="streamId"></param>
        /// <param name="priority"></param>
        /// <returns></returns>
        private 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,
                                         _comprProc, priority);

            ActiveStreams[stream.Id] = stream;

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

            return stream;
        }
Beispiel #12
0
        public Http2Session(Stream stream, ConnectionEnd end,
                            bool usePriorities, bool useFlowControl, bool isSecure,
                            CancellationToken cancel,
                            int initialWindowSize    = Constants.InitialFlowControlWindowSize,
                            int maxConcurrentStreams = Constants.DefaultMaxConcurrentStreams)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream is null");
            }

            if (cancel == null)
            {
                throw new ArgumentNullException("cancellation token is null");
            }

            if (maxConcurrentStreams <= 0)
            {
                throw new ArgumentOutOfRangeException("maxConcurrentStreams cant be less or equal then 0");
            }

            if (initialWindowSize <= 0 && useFlowControl)
            {
                throw new ArgumentOutOfRangeException("initialWindowSize cant be less or equal then 0");
            }

            _ourEnd         = end;
            _usePriorities  = usePriorities;
            _useFlowControl = useFlowControl;
            _isSecure       = isSecure;

            _cancelSessionToken = cancel;

            if (_ourEnd == ConnectionEnd.Client)
            {
                _remoteEnd = ConnectionEnd.Server;
                _lastId    = -1; // Streams opened by client are odd

                //if we got unsecure connection then server will respond with id == 1. We cant initiate
                //new stream with id == 1.
                if (!(stream is SslStream))
                {
                    _lastId = 3;
                }
            }
            else
            {
                _remoteEnd = ConnectionEnd.Client;
                _lastId    = 0; // Streams opened by server are even
            }

            _goAwayReceived = false;
            _comprProc      = new CompressionProcessor();
            _ioStream       = stream;

            _frameReader = new FrameReader(_ioStream);

            _writeQueue                = new WriteQueue(_ioStream, _comprProc, _usePriorities);
            OurMaxConcurrentStreams    = maxConcurrentStreams;
            RemoteMaxConcurrentStreams = maxConcurrentStreams;
            InitialWindowSize          = initialWindowSize;

            _flowControlManager = new FlowControlManager(this);

            if (!_useFlowControl)
            {
                _flowControlManager.Options = (byte)FlowControlOptions.DontUseFlowControl;
            }

            SessionWindowSize  = 0;
            _headersSequences  = new HeadersSequenceList();
            _promisedResources = new Dictionary <int, string>();

            StreamDictionary = new StreamDictionary();
            for (byte i = 0; i < OurMaxConcurrentStreams; i++)
            {
                var http2Stream = new Http2Stream(new HeadersList(), i + 1, _writeQueue, _flowControlManager)
                {
                    Idle = true
                };
                StreamDictionary.Add(new KeyValuePair <int, Http2Stream>(i + 1, http2Stream));
            }

            _flowControlManager.SetStreamDictionary(StreamDictionary);
            _writeQueue.SetStreamDictionary(StreamDictionary);
        }
        private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame)
        {
            string originalPath = stream.Headers.GetValue(CommonHeaders.Path.ToLower());
            //If user sets the empty file in get command we return notFound webpage
            string fileName = string.IsNullOrEmpty(Path.GetFileName(originalPath)) ? Index : Path.GetFileName(originalPath);
            string path = Path.Combine(AssemblyPath, fileName);

            try
            {
               _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count,
                                    path, stream.ReceivedDataAmount != 0);
            }
            catch (IOException)
            {
                Http2Logger.LogError("File is still downloading. Repeat request later");
                //stream.WriteDataFrame(new byte[0], true);

                //RST always has endstream flag
                //_fileHelper.RemoveStream(path);
                stream.Dispose(ResetStatusCode.InternalError);
                return;
            }

            stream.ReceivedDataAmount += dataFrame.FrameLength;

            if (dataFrame.IsEndStream)
            {
                if (!stream.EndStreamSent)
                {
                    //send terminator
                    stream.WriteDataFrame(new byte[0], true);
                    Http2Logger.LogConsole("Terminator was sent");
                }
                _fileHelper.RemoveStream(path);
                Http2Logger.LogConsole("Bytes received " + stream.ReceivedDataAmount);
            #if DEBUG
                const string wayToServerRoot1 = @"..\..\..\..\Drop\Root";
                const string wayToServerRoot2 = @".\Root";
                var areFilesEqual = _fileHelper.CompareFiles(path, wayToServerRoot1 + originalPath) ||
                                    _fileHelper.CompareFiles(path, wayToServerRoot2 + originalPath);
                if (!areFilesEqual)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Http2Logger.LogError("Files are NOT EQUAL!");
                }
                else
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Http2Logger.LogConsole("Files are EQUAL!");
                }
                Console.ForegroundColor = ConsoleColor.Gray;
            #endif
            }
        }
 /// <summary>
 /// Processes the request.
 /// </summary>
 /// <param name="stream">The stream.</param>
 /// <param name="frame">The request header frame.</param>
 /// <returns></returns>
 protected abstract void ProcessRequest(Http2Stream stream, Frame frame);
        /// <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];
        }
 private void ValidateHeaders(Http2Stream stream)
 {
     //spec 09 -> 8.1.3.  HTTP Header Fields
     //Header field names MUST be
     //converted to lowercase prior to their encoding in HTTP/2.0.  A
     //request or response containing uppercase header field names MUST be
     //treated as malformed (Section 8.1.3.5).
     foreach (var header in stream.Headers)
     {
         if (!_matcher.IsMatch(header.Key))
         {
             stream.WriteRst(ResetStatusCode.RefusedStream);
             stream.Dispose(ResetStatusCode.RefusedStream);
             break;
         }
     }
 }
Beispiel #17
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;

            try
            {
                if (frame.PayloadLength > Constants.MaxFramePayloadSize)
                {
                    throw new ProtocolError(ResetStatusCode.FrameSizeError,
                                            String.Format("Frame too large: Type: {0} {1}", frame.FrameType,
                                                          frame.PayloadLength));
                }

                /* 12 -> 6.5
                 * A SETTINGS frame MUST be sent by both endpoints at the start of a
                 * connection, and MAY be sent at any other time by either endpoint over
                 * the lifetime of the connection.*/
                if (frame.FrameType != FrameType.Settings && !_wasSettingsReceived)
                {
                    throw new ProtocolError(ResetStatusCode.ProtocolError,
                                            "Settings frame was not the first frame in the session");
                }

                Http2Logger.LogDebug("Incoming frame: " + frame.FrameType);

                switch (frame.FrameType)
                {
                case FrameType.Headers:
                    HandleHeaders(frame as HeadersFrame, out stream);
                    break;

                case FrameType.Continuation:
                    HandleContinuation(frame as ContinuationFrame, out stream);
                    break;

                case FrameType.Priority:
                    HandlePriority(frame as PriorityFrame, out stream);
                    break;

                case FrameType.RstStream:
                    HandleRstFrame(frame as RstStreamFrame, out stream);
                    break;

                case FrameType.Data:
                    HandleDataFrame(frame as DataFrame, out stream);
                    break;

                case FrameType.Ping:
                    HandlePingFrame(frame as PingFrame);
                    break;

                case FrameType.Settings:
                    HandleSettingsFrame(frame as SettingsFrame);
                    if (!(frame as SettingsFrame).IsAck)
                    {
                        // sending ACK settings
                        WriteSettings(new SettingsPair[0], true);
                    }
                    break;

                case FrameType.WindowUpdate:
                    HandleWindowUpdateFrame(frame as WindowUpdateFrame, out stream);
                    break;

                case FrameType.GoAway:
                    HandleGoAwayFrame(frame as GoAwayFrame);
                    break;

                case FrameType.PushPromise:
                    HandlePushPromiseFrame(frame as PushPromiseFrame, out stream);
                    if (stream != null)     //This means that sequence is complete
                    {
                        _promisedResources.Add(stream.Id, stream.Headers.GetValue(CommonHeaders.Path));
                    }
                    break;

                case FrameType.AltSvc:
                    HandleAltSvcFrame(frame);
                    break;

                case FrameType.Blocked:
                    HandleBlockedFrame(frame);
                    break;

                default:
                    /* 12 -> 4.1
                     * Implementations MUST treat the receipt of an unknown frame type
                     * (any frame types not defined in this document) as a connection
                     * error of type PROTOCOL_ERROR. */
                    throw new ProtocolError(ResetStatusCode.ProtocolError, "Unknown frame type detected");
                }

                _lastFrame = frame;

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

                    //Promised resource has been pushed
                    if (_promisedResources.ContainsKey(stream.Id))
                    {
                        _promisedResources.Remove(stream.Id);
                    }
                }

                if (stream == null || OnFrameReceived == null)
                {
                    return;
                }

                OnFrameReceived(this, new FrameReceivedEventArgs(stream, frame));
                stream.FramesReceived++;
            }

            //09
            //5.1.  Stream States
            //An endpoint MUST NOT send frames on a closed stream.  An endpoint
            //that receives a frame after receiving a RST_STREAM [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 STREAM_CLOSED
            //[STREAM_CLOSED].
            catch (Http2StreamNotFoundException ex)
            {
                Http2Logger.LogDebug("Frame for already Closed stream with stream id={0}", ex.Id);
                var rstFrame = new RstStreamFrame(ex.Id, ResetStatusCode.StreamClosed);

                Http2Logger.LogDebug("Sending RST_STREAM frame: stream id={0}, status code={1}",
                                     rstFrame.StreamId, rstFrame.StatusCode);

                _writeQueue.WriteFrame(rstFrame);
                stream.WasRstSent = true;
            }
            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);
            }
            catch (MaxConcurrentStreamsLimitException)
            {
                //Remote side tries to open more streams than allowed
                Dispose();
            }
            catch (Exception ex)
            {
                Http2Logger.LogError("Unknown error occurred: " + ex.Message);
                Close(ResetStatusCode.InternalError);
            }
        }
        private void HandleRstFrame(RstStreamFrame resetFrame, out Http2Stream stream)
        {
            stream = GetStream(resetFrame.StreamId);

            //Spec 06 tells that impl MUST not answer with rst on rst to avoid loop.
            if (stream != null)
            {
                Http2Logger.LogDebug("RST frame with code {0} for id {1}", resetFrame.StatusCode,
                                        resetFrame.StreamId);
                stream.Dispose(ResetStatusCode.None);
            }
            //Do not signal an error because (06)
            //WINDOW_UPDATE [WINDOW_UPDATE], PRIORITY [PRIORITY], or RST_STREAM
            //[RST_STREAM] frames can be received in this state for a short
            //period after a frame containing an END_STREAM flag is sent.
        }
        private void HandleRstFrame(RstStreamFrame resetFrame, out Http2Stream stream)
        {
            //spec 09
            //6.4.  RST_STREAM
            //RST_STREAM frames MUST be associated with a stream.  If a RST_STREAM
            //frame is received with a stream identifier of 0x0, the recipient MUST
            //treat this as a connection error (Section 5.4.1) of type
            //PROTOCOL_ERROR.
            if (resetFrame.StreamId == 0)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "resetFrame with id = 0");
            }

            stream = GetStream(resetFrame.StreamId);

            //09 -> 5.4.2.  Stream Error Handling
            //An endpoint MUST NOT send a RST_STREAM in response to an RST_STREAM
            //frame, to avoid looping.
            if (stream == null)
                return;

            Http2Logger.LogDebug("RST frame with code {0} for id {1}", resetFrame.StatusCode,
                                 resetFrame.StreamId);
            stream.Dispose(ResetStatusCode.None);
            //09 -> 5.1.  Stream States
            //WINDOW_UPDATE, PRIORITY, or RST_STREAM frames can be received in
            //this state for a short period after a DATA or HEADERS frame
            //containing an END_STREAM flag is sent.  Until the remote peer
            //receives and processes the frame bearing the END_STREAM flag, it
            //might send frame of any of these types.  Endpoints MUST ignore
            //WINDOW_UPDATE, PRIORITY, or RST_STREAM frames received in this
            //state, though endpoints MAY choose to treat frames that arrive a
            //significant time after sending END_STREAM as a connection error
            //(Section 5.4.1) of type PROTOCOL_ERROR.
        }
Beispiel #20
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);
            }
        }
        private void HandleDataFrame(DataFrame dataFrame, out Http2Stream stream)
        {
            //09 -> 6.1.  DATA
            //DATA frames MUST be associated with a stream.  If a DATA 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 (dataFrame.StreamId == 0)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError,
                                        "Incoming continuation frame with id = 0");
            }

            stream = GetStream(dataFrame.StreamId);

            //Aggressive window update
            if (stream != null)
            {
                Http2Logger.LogDebug("Data frame. StreamId: {0} Length: {1}", dataFrame.StreamId,
                                     dataFrame.FrameLength);
                if (stream.IsFlowControlEnabled)
                {
                    stream.WriteWindowUpdate(Constants.MaxFrameContentSize);
                }
            }
            else
            {
                throw new Http2StreamNotFoundException(dataFrame.StreamId);
            }
        }
        /// <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>
        private 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, _comprProc, priority);
            }
            else
            {
                ActiveStreams[id] = new Http2Stream(id, _writeQueue, _flowControlManager, _comprProc);
            }

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

                    var streamSequence = _headersSequences.Find(seq => seq.StreamId == args.Id);

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

            ActiveStreams[id].OnFrameSent += (o, args) =>
                {
                    if (OnFrameSent != null)
                    {
                        OnFrameSent(o, args);
                    }
                };

            return ActiveStreams[id];
        }
        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);
            }
        }
        /// <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;
        }
Beispiel #25
0
        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 HandleWindowUpdateFrame(WindowUpdateFrame windowUpdateFrame, out Http2Stream stream)
        {
            if (_useFlowControl)
            {
                Http2Logger.LogDebug("WindowUpdate frame. Delta: {0} StreamId: {1}", windowUpdateFrame.Delta,
                                     windowUpdateFrame.StreamId);
                stream = GetStream(windowUpdateFrame.StreamId);

                //09 -> 6.9.  WINDOW_UPDATE
                //The payload of a WINDOW_UPDATE frame is one reserved bit, plus an
                //unsigned 31-bit integer indicating the number of bytes that the
                //sender can transmit in addition to the existing flow control window.
                //The legal range for the increment to the flow control window is 1 to
                //2^31 - 1 (0x7fffffff) bytes.
                if (!(0 < windowUpdateFrame.Delta && windowUpdateFrame.Delta <= Constants.MaxPriority))
                {
                    Http2Logger.LogDebug("Incorrect window update delta : {0}", windowUpdateFrame.Delta);
                    throw new ProtocolError(ResetStatusCode.FlowControlError, String.Format("Incorrect window update delta : {0}", windowUpdateFrame.Delta));
                }

                //TODO Remove this hack
                //Connection window size
                if (windowUpdateFrame.StreamId == 0)
                {
                    _flowControlManager.StreamsInitialWindowSize += windowUpdateFrame.Delta;
                }

                //09 -> 5.1.  Stream States
                //A receiver can ignore WINDOW_UPDATE [WINDOW_UPDATE] or PRIORITY
                //[PRIORITY] frames in this state.
                if (stream != null)
                {
                    stream.UpdateWindowSize(windowUpdateFrame.Delta);
                    stream.PumpUnshippedFrames();
                }
                //09 -> 5.1.  Stream States
                //WINDOW_UPDATE, PRIORITY, or RST_STREAM frames can be received in
                //this state for a short period after a DATA or HEADERS frame
                //containing an END_STREAM flag is sent.  Until the remote peer
                //receives and processes the frame bearing the END_STREAM flag, it
                //might send frame of any of these types.  Endpoints MUST ignore
                //WINDOW_UPDATE, PRIORITY, or RST_STREAM frames received in this
                //state, though endpoints MAY choose to treat frames that arrive a
                //significant time after sending END_STREAM as a connection error
                //(Section 5.4.1) of type PROTOCOL_ERROR.
            }
            else
            {
                stream = null;
            }
        }
        private void HandlePriority(PriorityFrame priorityFrame, out Http2Stream stream)
        {
            //spec 06:
            //The PRIORITY frame is associated with an existing stream.  If a
            //PRIORITY frame is received with a stream identifier of 0x0, the
            //recipient MUST respond with a connection error (Section 5.4.1) of
            //type PROTOCOL_ERROR [PROTOCOL_ERROR].
            if (priorityFrame.StreamId == 0)
            {
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Incoming priority frame with id = 0");
            }

            Http2Logger.LogDebug("Priority frame. StreamId: {0} Priority: {1}", priorityFrame.StreamId,
                                 priorityFrame.Priority);

            stream = GetStream(priorityFrame.StreamId);

            if (!_usePriorities)
                return;

            //06
            //A receiver can ignore WINDOW_UPDATE [WINDOW_UPDATE] or PRIORITY
            //[PRIORITY] frames in this state.
            if (stream != null && !stream.EndStreamReceived)
            {
                stream.Priority = priorityFrame.Priority;
            }
            //Do not signal an error because (06)
            //WINDOW_UPDATE [WINDOW_UPDATE], PRIORITY [PRIORITY], or RST_STREAM
            //[RST_STREAM] frames can be received in this state for a short
            //period after a frame containing an END_STREAM flag is sent.
        }
Beispiel #28
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");
            }
        }
        private void HandleWindowUpdateFrame(WindowUpdateFrame windowUpdateFrame, out Http2Stream stream)
        {
            if (_useFlowControl)
            {
                Http2Logger.LogDebug("WindowUpdate frame. Delta: {0} StreamId: {1}", windowUpdateFrame.Delta,
                                     windowUpdateFrame.StreamId);
                stream = GetStream(windowUpdateFrame.StreamId);

                //06
                //The legal range for the increment to the flow control window is 1 to
                //2^31 - 1 (0x7fffffff) bytes.
                if (!(0 < windowUpdateFrame.Delta && windowUpdateFrame.Delta <= Constants.MaxPriority))
                {
                    Http2Logger.LogDebug("Incorrect window update delta : {0}", windowUpdateFrame.Delta);
                    throw new ProtocolError(ResetStatusCode.FlowControlError, String.Format("Incorrect window update delta : {0}", windowUpdateFrame.Delta));
                }

                //06
                //A receiver can ignore WINDOW_UPDATE [WINDOW_UPDATE] or PRIORITY
                //[PRIORITY] frames in this state.
                if (stream != null)
                {
                    stream.UpdateWindowSize(windowUpdateFrame.Delta);
                    stream.PumpUnshippedFrames();
                }
                //Do not signal an error because (06)
                //WINDOW_UPDATE [WINDOW_UPDATE], PRIORITY [PRIORITY], or RST_STREAM
                //[RST_STREAM] frames can be received in this state for a short
                //period after a frame containing an END_STREAM flag is sent.
            }
            else
            {
                stream = null;
            }
        }
Beispiel #30
0
 /// <summary>
 /// Processes the request.
 /// </summary>
 /// <param name="stream">The stream.</param>
 /// <param name="frame">The request header frame.</param>
 /// <returns></returns>
 protected abstract void ProcessRequest(Http2Stream stream, Frame frame);
        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);
            }
        }
Beispiel #32
0
 /// <summary>
 /// Processes the incoming data.
 /// </summary>
 /// <param name="stream">The stream.</param>
 /// <param name="frame"></param>
 /// <returns></returns>
 protected abstract void ProcessIncomingData(Http2Stream stream, Frame frame);
        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;
        }
 /// <summary>
 /// Ends the response in case of error.
 /// </summary>
 /// <param name="stream">The stream.</param>
 /// <param name="ex">The caught exception.</param>
 private void EndResponse(Http2Stream stream, Exception ex)
 {
     Http2Logger.LogDebug("Error processing request:\r\n" + ex);
     // TODO: What if the response has already started?
     WriteStatus(stream, StatusCode.Code500InternalServerError, true);
 }
 /// <summary>
 /// Processes the incoming data.
 /// </summary>
 /// <param name="stream">The stream.</param>
 /// <returns></returns>
 protected abstract void ProcessIncomingData(Http2Stream stream, Frame frame);
        /// <summary>
        /// Writes the status header to the stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="statusCode">The status code.</param>
        /// <param name="final">if set to <c>true</c> then marks headers frame as final.</param>
        /// <param name="headers">Additional headers</param>
        private void WriteStatus(Http2Stream stream, int statusCode, bool final, HeadersList headers = null)
        {
            if (headers == null)
                {
                headers = new HeadersList();
            }
            headers.Add(new KeyValuePair<string, string>(CommonHeaders.Status, statusCode.ToString(CultureInfo.InvariantCulture)));

            stream.WriteHeadersFrame(headers, final, true);
        }
 internal Http2OwinMessageContext(Http2Stream protocolStream)
 {
     _protocolStream = protocolStream;
     PopulateEnvironment();
 }
 /// <summary>
 /// Overrides data processing logic.
 /// </summary>
 /// <param name="stream">The stream.</param>
 /// <param name="frame"></param>
 /// <returns></returns>
 protected override void ProcessIncomingData(Http2Stream stream, Frame frame)
 {
     //Do nothing... handling data is not supported by the server yet
 }
        protected override void ProcessRequest(Http2Stream stream, Frame frame)
        {
            //spec 06
            //A client
            //MUST treat the absence of the ":status" header field, the presence of
            //multiple values, or an invalid value as a stream error
            //(Section 5.4.2) of type PROTOCOL_ERROR [PROTOCOL_ERROR].

            if (stream.Headers.GetValue(CommonHeaders.Status) == null)
            {
                stream.WriteRst(ResetStatusCode.ProtocolError);
            }
            //Do nothing. Client may not process requests for now
        }
        /// <summary>
        /// Overrides request processing logic.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="frame">The request header frame.</param>
        /// <returns></returns>
        protected override void ProcessRequest(Http2Stream stream, Frame frame)
        {
            //spec 09
            //8.1.3.1.  Request Header Fields

            //HTTP/2.0 defines a number of header fields starting with a colon ':'
            //character that carry information about the request target:

            //o  The ":method" header field includes the HTTP method ([HTTP-p2],
            //Section 4).

            //o  The ":scheme" header field includes the scheme portion of the
            //target URI ([RFC3986], Section 3.1).

            //o  The ":authority" header field includes the authority portion of
            //the target URI ([RFC3986], Section 3.2).

            //To ensure that the HTTP/1.1 request line can be reproduced
            //accurately, this header field MUST be omitted when translating
            //from an HTTP/1.1 request that has a request target in origin or
            //asterisk form (see [HTTP-p1], Section 5.3).  Clients that generate
            //HTTP/2.0 requests directly SHOULD instead omit the "Host" header
            //field.  An intermediary that converts a request to HTTP/1.1 MUST
            //create a "Host" header field if one is not present in a request by
            //copying the value of the ":authority" header field.

            //o  The ":path" header field includes the path and query parts of the
            //target URI (the "path-absolute" production from [RFC3986] and
            //optionally a '?' character followed by the "query" production, see
            //[RFC3986], Section 3.3 and [RFC3986], Section 3.4).  This field
            //MUST NOT be empty; URIs that do not contain a path component MUST
            //include a value of '/', unless the request is an OPTIONS in
            //asterisk form, in which case the ":path" header field MUST include
            //'*'.

            //All HTTP/2.0 requests MUST include exactly one valid value for all of
            //these header fields, unless this is a CONNECT request (Section 8.3).
            //An HTTP request that omits mandatory header fields is malformed
            //(Section 8.1.3.5).

            //Header field names that contain a colon are only valid in the
            //HTTP/2.0 context.  These are not HTTP header fields.  Implementations
            //MUST NOT generate header fields that start with a colon, but they
            //MUST ignore any header field that starts with a colon.  In
            //particular, header fields with names starting with a colon MUST NOT
            //be exposed as HTTP header fields.

            if (stream.Headers.GetValue(CommonHeaders.Method) == null
                || stream.Headers.GetValue(CommonHeaders.Path) == null
                || stream.Headers.GetValue(CommonHeaders.Scheme) == null
                || stream.Headers.GetValue(CommonHeaders.Authority) == null)
            {
                stream.WriteRst(ResetStatusCode.ProtocolError);
                stream.Dispose(ResetStatusCode.ProtocolError);
                return;
            }

            Task.Factory.StartNew(async () =>
            {
                try
                {
                    var context = new Http2OwinMessageContext(stream);
                    var contextEnv = context.OwinContext.Environment;

                    PushFunc pushDelegate = null;
                    pushDelegate = async pairs =>
                        {
                            var promisedStream = CreateStream();
                            //assume that we have already received endStream
                            promisedStream.EndStreamReceived = true;
                            stream.WritePushPromise(pairs, promisedStream.Id);

                            var headers = new HeadersList(pairs);
                            promisedStream.Headers.AddRange(headers);

                            var http2MsgCtx = new Http2OwinMessageContext(promisedStream);
                            var http2PushCtx = http2MsgCtx.OwinContext;

                            http2PushCtx.Set(CommonOwinKeys.ServerPushFunc, pushDelegate);

                            //pass add info from parent to child context. This info can store
                            //reference table for example or something els that should be passed from
                            //client request into child push requests.
                            if (contextEnv.ContainsKey(CommonOwinKeys.AdditionalInfo))
                                http2PushCtx.Set(CommonOwinKeys.AdditionalInfo, contextEnv[CommonOwinKeys.AdditionalInfo]);

                            await _next(http2PushCtx);

                            http2MsgCtx.FinishResponse();
                        };

                    context.OwinContext.Set(CommonOwinKeys.ServerPushFunc, pushDelegate);
                    context.OwinContext.Set(CommonOwinKeys.EnableServerPush, _isPushEnabled);

                    await _next(context.OwinContext);
                    context.FinishResponse();
                }
                catch (Exception ex)
                {
                    EndResponse(stream, ex);
                }
            });
        }