示例#1
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;
         }
     }
 }
 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;
         }
     }
 }
        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
        }
        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)
            {
                stream.WriteRst(ResetStatusCode.ProtocolError);
                stream.Dispose(ResetStatusCode.ProtocolError);
                return;
            }

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

            //got some king of error
            if (code != StatusCode.Code200Ok)
            {
                //Close server's stream
                stream.Dispose(ResetStatusCode.Cancel); //will dispose client's stream and close server's one.
            }
            //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);
                }
            });
        }
 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;
         }
     }
 }
        /// <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);
                });

        }
        private async Task SendResponse(Http2Stream stream, IOwinContext context)
        {
            try
            {
                var isFirstWrite = true;
                var response = context.Response;
                var respBody = response.Body as ResponseStream;
                HeadersList responseHeaders = null;

                if (respBody == null)
                {
                    stream.WriteRst(ResetStatusCode.InternalError);
                    return;
                }

                int contentLen = 0;
                int read = 0;

                respBody.OnDataWritten += (sender, args) =>
                    {
                        if (isFirstWrite)
                        {
                            Http2Logger.LogDebug("Transfer begin");
                            if (response.Headers != null)
                            {
                                responseHeaders = new HeadersList(response.Headers);
                                contentLen = int.Parse(responseHeaders.GetValue(CommonHeaders.ContentLength));
                            }
                            WriteStatus(stream, response.StatusCode, response.StatusCode != StatusCode.Code200Ok, responseHeaders);
                        }
                        isFirstWrite = false;

                        if (read < contentLen)
                        {
                            var temp = new byte[args.Count];

                            respBody.Seek(0, SeekOrigin.Begin);
                            int tmpRead = respBody.Read(temp, 0, temp.Length);
                            respBody.Seek(0, SeekOrigin.Begin);

                            Debug.Assert(tmpRead > 0);

                            var readBytes = new byte[tmpRead];
                            Buffer.BlockCopy(temp, 0, readBytes, 0, tmpRead);

                            read += tmpRead;
                            SendDataTo(stream, readBytes, read == contentLen);
                        }
                    };

                await _next(context.Environment);

                //Handle file not found case
                if (response.StatusCode == StatusCode.Code404NotFound)
                {
                    WriteStatus(stream, response.StatusCode, response.StatusCode != StatusCode.Code200Ok, responseHeaders);
                }

                Http2Logger.LogDebug("Transfer end");
            }
            catch (Exception ex)
            {   
                EndResponse(stream, ex);;
            }
        }
        /// <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);
                }
            });
            
        }