예제 #1
0
        private static Frame GetFrameType(Frame preamble)
        {
            switch (preamble.FrameType)
            {
                case FrameType.GoAway:
                    return new GoAwayFrame(preamble);

                case FrameType.Ping:
                    return new PingFrame(preamble);

                case FrameType.RstStream:
                    return new RstStreamFrame(preamble);

                case FrameType.Settings:
                    return new SettingsFrame(preamble);

                case FrameType.Headers:
                    return new HeadersFrame(preamble);

                case FrameType.WindowUpdate:
                    return new WindowUpdateFrame(preamble);

                case FrameType.Data:
                    return new DataFrame(preamble);

                case FrameType.PushPromise:
                    return new PushPromiseFrame(preamble);

                case FrameType.Priority:
                    return new PriorityFrame(preamble);

                default:
                    throw new NotImplementedException("Frame type: " + preamble.FrameType);
            }
        }
예제 #2
0
        public Frame ReadFrame()
        {
            if (_isDisposed)
                return null;

            var preamble = new Frame();
            if (!TryFill(preamble.Buffer, 0, preamble.Buffer.Length))
            {
                return null;
            }

            Frame wholeFrame;
            try
            {
                wholeFrame = GetFrameType(preamble);
            }
            //09 -> 4.1.  Frame Format 
            //Implementations MUST ignore frames of unsupported or unrecognized types
            catch (NotImplementedException)
            {
                return preamble;
            }
            
            if (!TryFill(wholeFrame.Buffer, Constants.FramePreambleSize, wholeFrame.Buffer.Length - Constants.FramePreambleSize))
            {
                return null;
            }

            return wholeFrame;
        }
        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);
        }
        /// <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);
                });

        }
예제 #6
0
        // Queue up a fully rendered frame to send
        public void WriteFrame(Frame frame)
        {
            if (frame == null)
                throw new ArgumentNullException("frame is null");

            var priority = frame.StreamId != 0 ? _streams[frame.StreamId].Priority : Constants.DefaultStreamPriority;

            IQueueItem entry;

            if (IsPriorityTurnedOn)
            {
                entry = new PriorityQueueEntry(frame, priority);
            }
            else
            {
                entry = new QueueEntry(frame);
            }

            _messageQueue.Enqueue(entry);
        }
예제 #7
0
 // Incoming
 public SettingsFrame(Frame preamble)
     : base(preamble)
 {
 }
        private void HandleAltSvcFrame(Frame altSvcFrame)
        {
            Http2Logger.LogDebug("ALTSVC frame: stream id={0}, payload len={1}", 
                altSvcFrame.StreamId, altSvcFrame.PayloadLength);

            /* 12 -> 6.11 
            The ALTSVC frame is intended for receipt by clients; a server that receives 
            an ALTSVC frame MUST treat it as a connection error of type PROTOCOL_ERROR. */
            if (_ourEnd == ConnectionEnd.Server)
                throw new ProtocolError(ResetStatusCode.ProtocolError, "Rst frame with stream id=0");

            /* 12 -> 6.11 
            The ALTSVC frame advertises the availability of an alternative service to the client. */

            // TODO: receiving ALTSVC frame by client is not implemented.
            // see https://tools.ietf.org/html/draft-ietf-httpbis-http2-12#section-6.11
            // and https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-01
        }
예제 #9
0
        // Queue up a fully rendered frame to send
        public void WriteFrame(Frame frame)
        {
            if (frame == null)
                throw new ArgumentNullException("frame is null");

            //Do not write to already closed stream
            if (frame.FrameType != FrameType.Settings
                && frame.FrameType != FrameType.GoAway
                && frame.FrameType != FrameType.Ping
                && _streams[frame.StreamId] == null)
            {
                return;
            }

            var priority = frame.StreamId != 0 ? _streams[frame.StreamId].Priority : Constants.DefaultStreamPriority;

            IQueueItem entry;

            if (IsPriorityTurnedOn)
            {
                entry = new PriorityQueueEntry(frame, priority);
            }
            else
            {
                entry = new QueueEntry(frame);
            }

            _messageQueue.Enqueue(entry);
        }
예제 #10
0
 public FrameSentArgs(Frame frame)
 {
     Frame = frame;
 }
 /// <summary>
 /// Processes the incoming data.
 /// </summary>
 /// <param name="stream">The stream.</param>
 /// <returns></returns>
 protected abstract void ProcessIncomingData(Http2Stream stream, Frame frame);
예제 #12
0
 public QueueEntry(Frame frame)
 {
     _frame = frame;
 }
        private void HandleBlockedFrame(Frame blockedFrame)
        {
            Http2Logger.LogDebug("BLOCKED frame: stream id={0}, payload len={1}",
                blockedFrame.StreamId, blockedFrame.PayloadLength);

            /* 12 -> 6.12 
            The BLOCKED frame defines no flags and contains no payload. A
            receiver MUST treat the receipt of a BLOCKED frame with a payload as
            a connection error of type FRAME_SIZE_ERROR. */
            if (blockedFrame.PayloadLength > 0)
                throw new ProtocolError(ResetStatusCode.FrameSizeError, "Blocked frame with non-zero payload");

            // TODO: receiving BLCOKED frame is not implemented.
            // see https://tools.ietf.org/html/draft-ietf-httpbis-http2-12#section-6.12
        }
 public FrameReceivedEventArgs(Http2Stream stream, Frame frame)
 {
     Stream = stream;
     Frame = frame;
 }
        /// <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);
                }
            });
        }
 /// <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
 }
예제 #17
0
 public DataFrameSentEventArgs(Frame frame)
 {
     Id = frame.StreamId;
     DataAmount = frame.Buffer.Length - Constants.FramePreambleSize;
 }
예제 #18
0
 public PushPromiseFrame(Frame preamble)
     : base(preamble)
 {
     Headers = new HeadersList();
 }
예제 #19
0
 // for incoming
 public PushPromiseFrame(Frame preamble)
     : base(preamble)
 {
 }
예제 #20
0
 /// <summary>
 /// Create an incoming frame
 /// </summary>
 /// <param name="preamble">Frame preamble</param>
 public HeadersFrame(Frame preamble)
     : base(preamble)
 {
 }
예제 #21
0
 // For incoming frames
 protected Frame(Frame preamble)
     : this(new byte[Constants.FramePreambleSize + preamble.PayloadLength])
 {
     System.Buffer.BlockCopy(preamble.Buffer, 0, Buffer, 0, Constants.FramePreambleSize);
 }
예제 #22
0
 //outgoing
 public ContinuationFrame(Frame preamble)
     : base(preamble)
 {
 }
예제 #23
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.FrameLength > Constants.MaxFrameContentSize)
                {
                    throw new ProtocolError(ResetStatusCode.FrameSizeError,
                                            String.Format("Frame too large: Type: {0} {1}", frame.FrameType,
                                                          frame.FrameLength));
                }

                //Settings MUST be first frame in the session from server and 
                //client MUST send settings immediately after connection header.
                //This means that settings ALWAYS first frame in the session.
                //This block checks if it doesnt.
                if (frame.FrameType != FrameType.Settings && !_wasSettingsReceived)
                {
                    throw new ProtocolError(ResetStatusCode.ProtocolError,
                                            "Settings was not the first frame in the session");
                }

                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)
                        {
                            //Send ack
                            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;
                    default:
                        //09 -> 4.1.  Frame Format
                        //Implementations MUST ignore frames of unsupported or unrecognized types.
                        Http2Logger.LogDebug("Unknown frame received. Ignoring it");
                        break;
                }

                _lastFrame = frame;

                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;

                    //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 Id = {0}", ex.Id);
                GetStream(ex.Id).WriteRst(ResetStatusCode.StreamClosed);
            }
            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);
            }
        }
예제 #24
0
 // For incoming
 public DataFrame(Frame preamble)
     : base(preamble)
 {
 }
 /// <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);
 public PriorityQueueEntry(Frame frame, int priority)
 {
     _frame = frame;
     _priority = priority;
 }
예제 #27
0
 // for incoming
 public PriorityFrame(Frame preamble)
     : base(preamble)
 {
 }
예제 #28
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);
            }
        }
        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
        }
예제 #30
0
        /// <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)
        {
            /* 12 -> 8.1.3.1
            All HTTP/2 requests MUST include exactly one valid value for the
            ":method", ":scheme", and ":path" 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). */

            if (stream.Headers.GetValue(CommonHeaders.Method) == null
                || stream.Headers.GetValue(CommonHeaders.Path) == null
                || stream.Headers.GetValue(CommonHeaders.Scheme) == null)
            {
                stream.WriteRst(ResetStatusCode.ProtocolError);
                stream.Close(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.HalfClosedLocal = 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);
                }
            });
            
        }