Esempio n. 1
0
        public void WriteHeadersFrame(HeadersList headers, bool isEndStream, bool isEndHeaders)
        {
            if (headers == null)
            {
                throw new ArgumentNullException("headers is null");
            }

            if (Closed)
            {
                return;
            }

            var frame = new HeadersFrame(_id, true)
            {
                IsEndHeaders = isEndHeaders,
                IsEndStream  = isEndStream,
                Headers      = headers,
            };

            _writeQueue.WriteFrame(frame);

            if (frame.IsEndStream)
            {
                HalfClosedLocal = true;
            }
            else if (ReservedLocal)
            {
                HalfClosedRemote = true;
            }

            if (OnFrameSent != null)
            {
                OnFrameSent(this, new FrameSentEventArgs(frame));
            }
        }
Esempio n. 2
0
        public void WriteHeadersFrame(HeadersList headers, bool isEndStream)
        {
            Headers = headers;

            byte[] headerBytes = _compressionProc.Compress(headers);

            var frame = new HeadersFrame(_id, headerBytes, Priority)
            {
                IsEndHeaders = true,
                IsEndStream  = isEndStream,
            };

            if (frame.IsEndStream)
            {
                EndStreamSent = true;
            }

            _writeQueue.WriteFrame(frame);

            if (OnFrameSent != null)
            {
                OnFrameSent(this, new FrameSentArgs(frame));
            }
        }
Esempio n. 3
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;

            //Spec 03 tells that frame with continues flag MUST be followed by a frame with the same type
            //and the same stread id.
            if (_toBeContinuedHeaders != null)
            {
                if (_toBeContinuedFrame.FrameType != frame.FrameType ||
                    _toBeContinuedFrame.StreamId != frame.StreamId)
                {
                    //If not, we must close the session.
                    Dispose();
                    return;
                }
            }

            try
            {
                switch (frame.FrameType)
                {
                case FrameType.Headers:
                    Http2Logger.LogDebug("New headers with id = " + frame.StreamId);
                    var headersFrame      = (HeadersFrame)frame;
                    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);

                    if (!headersFrame.IsEndHeaders)
                    {
                        _toBeContinuedHeaders = decompressedHeaders;
                        _toBeContinuedFrame   = headersFrame;
                        break;
                    }

                    if (_toBeContinuedHeaders != null)
                    {
                        headers.AddRange(_toBeContinuedHeaders);
                    }

                    headersFrame.Headers.AddRange(headers);
                    foreach (var header in headers)
                    {
                        Http2Logger.LogDebug("Stream {0} header: {1}={2}", frame.StreamId, header.Key, header.Value);
                    }

                    stream = GetStream(headersFrame.StreamId);

                    if (stream == null)
                    {
                        // new stream
                        if (ActiveStreams.GetOpenedStreamsBy(_remoteEnd) + 1 > OurMaxConcurrentStreams)
                        {
                            //Remote side tries to open more streams than allowed
                            Dispose();
                            return;
                        }

                        string path = headers.GetValue(":path");

                        if (path == null)
                        {
                            path = _handshakeHeaders.ContainsKey(":path") ? _handshakeHeaders[":path"] : @"\index.html";
                            headers.Add(new KeyValuePair <string, string>(":path", path));
                        }

                        stream = new Http2Stream(headers, headersFrame.StreamId,
                                                 _writeQueue, _flowControlManager,
                                                 _comprProc);

                        ActiveStreams[stream.Id] = stream;

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

                        _toBeContinuedFrame   = null;
                        _toBeContinuedHeaders = null;
                    }

                    break;

                case FrameType.Priority:
                    var priorityFrame = (PriorityFrame)frame;
                    Http2Logger.LogDebug("Priority frame. StreamId: {0} Priority: {1}", priorityFrame.StreamId, priorityFrame.Priority);
                    stream = GetStream(priorityFrame.StreamId);
                    if (_usePriorities)
                    {
                        stream.Priority = priorityFrame.Priority;
                    }
                    break;

                case FrameType.RstStream:
                    var resetFrame = (RstStreamFrame)frame;
                    stream = GetStream(resetFrame.StreamId);

                    if (stream != null)
                    {
                        Http2Logger.LogDebug("RST frame with code " + resetFrame.StatusCode);
                        stream.Dispose();
                    }
                    break;

                case FrameType.Data:
                    var dataFrame = (DataFrame)frame;
                    Http2Logger.LogDebug("Data frame. StreamId:{0} Length:{1}", dataFrame.StreamId, dataFrame.FrameLength);
                    stream = GetStream(dataFrame.StreamId);

                    //Aggressive window update
                    if (stream.IsFlowControlEnabled)
                    {
                        stream.WriteWindowUpdate(2000000);
                    }
                    break;

                case FrameType.Ping:
                    var pingFrame = (PingFrame)frame;
                    Http2Logger.LogDebug("Ping frame with StreamId:{0} Payload:{1}", pingFrame.StreamId, pingFrame.Payload.Count);

                    if (pingFrame.FrameLength != PingFrame.PayloadLength)
                    {
                        throw new ProtocolError(ResetStatusCode.ProtocolError, "Ping payload size is not equal to 8");
                    }

                    if (pingFrame.IsPong)
                    {
                        _wasPingReceived = true;
                        _pingReceived.Set();
                    }
                    else
                    {
                        var pongFrame = new PingFrame(true, pingFrame.Payload.ToArray());
                        _writeQueue.WriteFrame(pongFrame);
                    }
                    break;

                case FrameType.Settings:
                    //Not first frame in the session.
                    //Client initiates connection and sends settings before request.
                    //It means that if server sent settings before it will not be a first frame,
                    //because client initiates connection.
                    if (_ourEnd == ConnectionEnd.Server && !_wasSettingsReceived && ActiveStreams.Count != 0)
                    {
                        Dispose();
                        return;
                    }

                    var settingFrame = (SettingsFrame)frame;
                    Http2Logger.LogDebug("Settings frame. Entry count: {0} StreamId: {1}", settingFrame.EntryCount, settingFrame.StreamId);
                    _wasSettingsReceived = true;
                    _settingsManager.ProcessSettings(settingFrame, this, _flowControlManager);
                    break;

                case FrameType.WindowUpdate:
                    if (_useFlowControl)
                    {
                        var windowFrame = (WindowUpdateFrame)frame;
                        Http2Logger.LogDebug("WindowUpdate frame. Delta: {0} StreamId: {1}", windowFrame.Delta, windowFrame.StreamId);
                        stream = GetStream(windowFrame.StreamId);

                        if (stream != null)
                        {
                            stream.UpdateWindowSize(windowFrame.Delta);
                            stream.PumpUnshippedFrames();
                        }
                    }
                    break;

                case FrameType.GoAway:
                    _goAwayReceived = true;
                    Http2Logger.LogDebug("GoAway frame received");
                    Dispose();
                    break;

                default:
                    throw new NotImplementedException(frame.FrameType.ToString());
                }

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

                if (stream != null && OnFrameReceived != null)
                {
                    OnFrameReceived(this, new FrameReceivedEventArgs(stream, frame));
                }
            }

            //Frame came for already closed stream. Ignore it.
            //Spec:
            //An endpoint that sends RST_STREAM MUST ignore
            //frames that it receives on closed streams if it sends RST_STREAM.
            //
            //An endpoint MUST NOT send frames on a closed stream.  An endpoint
            //that receives a frame after receiving a 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 PROTOCOL_ERROR.
            catch (Http2StreamNotFoundException)
            {
                if (stream != null)
                {
                    stream.WriteRst(ResetStatusCode.ProtocolError);
                }
                else
                {
                    //GoAway?
                }
            }
            catch (CompressionError ex)
            {
                //The endpoint is unable to maintain the compression context for the connection.
                Http2Logger.LogError("Compression error occured: " + ex.Message);
                Close(ResetStatusCode.CompressionError);
            }
            catch (ProtocolError pEx)
            {
                Http2Logger.LogError("Protocol error occured: " + pEx.Message);
                Close(pEx.Code);
            }
        }
Esempio n. 4
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);
            }
        }