Example #1
0
        public static string VersionToString(ProtoVersion httpVersion)
        {
            switch (httpVersion)
            {
            case ProtoVersion.Proto10:
                return(Proto10Version);

            case ProtoVersion.Proto11:
                return(Proto11Version);

            default:
                return(null);
            }
        }
Example #2
0
        public static unsafe bool GetKnownVersion(this Span <byte> span, out ProtoVersion knownVersion, out byte length)
        {
            fixed(byte *data = span)
            {
                knownVersion = GetKnownVersion(data, span.Length);
                if (knownVersion != ProtoVersion.Unknown)
                {
                    length = sizeof(ulong);
                    return(true);
                }

                length = 0;
                return(false);
            }
        }
Example #3
0
        public void OnStartLine(ProtoMethod method, ProtoVersion version, Span <byte> target, Span <byte> path, Span <byte> query, Span <byte> customMethod, bool pathEncoded)
        {
            Debug.Assert(target.Length != 0, "Request target must be non-zero length");

            var ch = target[0];

            if (ch == ByteForwardSlash)
            {
                // origin-form.
                // The most common form of request-target.
                // https://tools.ietf.org/html/rfc7230#section-5.3.1
                OnOriginFormTarget(method, version, target, path, query, customMethod, pathEncoded);
            }
            else if (ch == ByteAsterisk && target.Length == 1)
            {
                OnAsteriskFormTarget(method);
            }
            else if (target.GetKnownProtoScheme(out var scheme))
            {
                OnAbsoluteFormTarget(target, query);
            }
            else
            {
                // Assume anything else is considered authority form.
                // FYI: this should be an edge case. This should only happen when
                // a client mistakenly thinks this server is a proxy server.
                OnAuthorityFormTarget(method, target);
            }

            Method = method;
            if (method == ProtoMethod.Custom)
            {
                _methodText = customMethod.GetAsciiStringNonNullCharacters();
            }

            _httpVersion = version;

            Debug.Assert(RawTarget != null, "RawTarget was not set");
            Debug.Assert(((IProtoRequestFeature)this).Method != null, "Method was not set");
            Debug.Assert(Path != null, "Path was not set");
            Debug.Assert(QueryString != null, "QueryString was not set");
            Debug.Assert(ProtoVersion != null, "ProtoVersion was not set");
        }
        public void Start(int port, CryptoAlgoId_Values cryptoAlgoId, ProtoVersion protoVersion, byte[] content, EventQueue eventQueue)
        {
            this.cryptoAlgoId = cryptoAlgoId;
            this.protoVersion = protoVersion;
            this.content      = content;
            this.eventQueue   = eventQueue;
            this.aes          = PccrrUtitlity.CreateAes(cryptoAlgoId);
            this.iv           = new byte[16];
            for (int i = 0; i < iv.Length; i++)
            {
                this.iv[i] = (byte)i;
            }

            pccrrServer = new PccrrServer(port);

            pccrrServer.MessageArrived += new MessageArrivedEventArgs(pccrrServer_MessageArrived);

            pccrrServer.StartListening();
        }
Example #5
0
        public void HostedCacheServer_PccrrServer_MessageHeader_VersionIncompatible()
        {
            CheckApplicability();

            ProtoVersion protoVersion = new ProtoVersion {
                MajorVersion = 1, MinorVersion = 0
            };

            PccrrClient pccrrClient = new PccrrClient(testConfig.HostedCacheServerComputerName, testConfig.HostedCacheServerHTTPListenPort);

            BaseTestSite.Log.Add(
                LogEntryKind.Debug,
                "Send negoRequest with incompatible proto version to hosted cache server");

            var negoRequest = pccrrClient.CreateMsgNegoRequest(
                protoVersion,
                protoVersion,
                CryptoAlgoId_Values.AES_128,
                MsgType_Values.MSG_NEGO_REQ);

            var header = negoRequest.MessageHeader;

            header.ProtVer = new ProtoVersion {
                MajorVersion = 0xFEFE, MinorVersion = 0xFEFE
            };
            negoRequest.MessageHeader = header;

            bool passed = false;

            try
            {
                pccrrClient.ExpectPacket();
            }
            catch
            {
                passed = true;
            }

            BaseTestSite.Assert.IsTrue(passed, "The pccrr server should silently discard the message with incompatible proto version");
        }
        /// <summary>
        /// Capture MESSAGE_HEADER structure releated requirements.
        /// </summary>
        /// <param name="messageHeader">Message header object</param>
        /// <param name="uiPayload">The length of http content body.</param>
        public static void CaptureMessageHeaderRequirements(MESSAGE_HEADER messageHeader, int uiPayload)
        {
            // The protocol version of the server-peer, which is configured in ptfconfig file.
            ushort uiMajor = ushort.Parse(
                site.Properties.Get(
                    "PCCRR.Protocol.MSG_NEGO_REQ.MinSupportedProtocolVersion.MajorVer"));
            ushort uiMinor = ushort.Parse(
                site.Properties.Get(
                    "PCCRR.Protocol.MSG_NEGO_REQ.MinSupportedProtocolVersion.MinorVer"));

            ProtoVersion protVer = messageHeader.ProtVer;

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R47");

            // Verify MS-PCCRR requirement: MS-PCCRR_R47
            // If the following types are verified, means those fields are the layout of message header.
            bool isVerifyR47 = messageHeader.GetType() == typeof(MESSAGE_HEADER) &&
                               messageHeader.ProtVer.GetType() == typeof(ProtoVersion) &&
                               messageHeader.MsgType.GetType() == typeof(MsgType_Values) &&
                               messageHeader.MsgSize.GetType() == typeof(uint) &&
                               messageHeader.CryptoAlgoId.GetType() == typeof(CryptoAlgoId_Values);

            site.CaptureRequirementIfIsTrue(
                isVerifyR47,
                47,
                @"[In MESSAGE_HEADER]The layout of the message header is as follows
                [ProtVer,MsgType,MsgSize,CryptoAlgoId].");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R48");

            bool isVerifyR48 = messageHeader.ProtVer.GetType() == typeof(ProtoVersion) &&
                               messageHeader.ProtVer.MajorVersion.GetType() == typeof(ushort) &&
                               messageHeader.ProtVer.MinorVersion.GetType() == typeof(ushort);

            // Verify MS-PCCRR requirement: MS-PCCRR_R48
            site.CaptureRequirementIfIsTrue(
                isVerifyR48,
                48,
                @"[MESSAGE_HEADER]ProtVer (4 bytes):  Protocol version number, formed by concatenating the
                protocol major version number and protocol minor version number, encoded as follows (where MSB
                is Most Significant Byte and LSB is Least Significant Byte): 1st Byte (Addr: X) is Minor version
                MSB;2nd Byte (Addr: X+1) is Minor version LSB;3rd Byte (Addr: X+2) is Major version MSB;4th Byte
                (Addr: X+3) is Major version LSB.");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R49");

            // Verify MS-PCCRR requirement: MS-PCCRR_R49
            // The protVer field will be set in HttpTransport_Receive method when receive a response message
            // Verify its low byte
            site.CaptureRequirementIfAreEqual <ushort>(
                uiMajor,
                protVer.MajorVersion,
                49,
                @"[In MESSAGE_HEADER][ProtVer (4 bytes)]The major version number is encoded in the least
                significant word of the protocol version's DWORD.");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R50");

            // Verify MS-PCCRR requirement: MS-PCCRR_R50
            // Verify its high byte
            site.CaptureRequirementIfAreEqual <ushort>(
                uiMinor,
                protVer.MinorVersion,
                50,
                @"[In MESSAGE_HEADER][ProtVer (4 bytes)]The minor version number is encoded in the most significant
                word of the protocol version's DWORD.");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R51");

            // Verify MS-PCCRR requirement: MS-PCCRR_R51
            // This is a bug, Major  version number is 2 bytes.
            bool isVerifyR51 = uiMajor >= 0x0000 && uiMajor <= 0xFFFF;

            site.CaptureRequirementIfIsTrue(
                isVerifyR51,
                51,
                @"[In MESSAGE_HEADER][ProtVer (4 bytes)]Major  version number can express the version range of
                0x00000000 to 0xFFFFFFFF.");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R5100");

            // Verify MS-PCCRR requirement: MS-PCCRR_R5100
            // This is a bug, Minor version number is 2 bytes.
            bool isVerifyR5100 = uiMinor >= 0x0000 && uiMinor <= 0xFFFF;

            site.CaptureRequirementIfIsTrue(
                isVerifyR5100,
                5100,
                @"[In MESSAGE_HEADER][ProtVer (4 bytes)]Minor version number can express the version range of
                0x00000000 to 0xFFFFFFFF.");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R52");

            // Verify MS-PCCRR requirement: MS-PCCRR_R52
            bool isVerifyR52 = ((uint)protVer.MajorVersion & 0x0000FFFF) == 0x00000001 &&
                               ((uint)protVer.MinorVersion & 0xFFFF0000) == 0x00000000;

            site.CaptureRequirementIfIsTrue(
                isVerifyR52,
                52,
                @"[In MESSAGE_HEADER][ProtVer (4 bytes)]Currently, the protocol[MS-PCCRR] version number MUST be
                set to {major=1 (0x0001), minor=0 (0x0000)}.");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R54");

            // Verify MS-PCCRR requirement: MS-PCCRR_R54
            bool isVerifyR54 = messageHeader.MsgType == MsgType_Values.MSG_NEGO_REQ ||
                               messageHeader.MsgType == MsgType_Values.MSG_NEGO_RESP ||
                               messageHeader.MsgType == MsgType_Values.MSG_GETBLKLIST ||
                               messageHeader.MsgType == MsgType_Values.MSG_GETBLKS ||
                               messageHeader.MsgType == MsgType_Values.MSG_BLKLIST ||
                               messageHeader.MsgType == MsgType_Values.MSG_BLK;

            site.CaptureRequirementIfIsTrue(
                isVerifyR54,
                54,
                @"[In MESSAGE_HEADER][MsgType (4 bytes)]MUST be set to one of the following values
                [MSG_NEGO_REQ 0x00000000,MSG_NEGO_RESP 0x00000001,MSG_GETBLKLIST 0x00000002,MSG_GETBLKS 0x00000003,
                MSG_BLKLIST0x00000004,MSG_BLK 0x00000005].");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R318");

            // Verify MS-PCCRR requirement: MS-PCCRR_R318
            bool isVerifyR318 = messageHeader.CryptoAlgoId == CryptoAlgoId_Values.V1 ||
                                messageHeader.CryptoAlgoId == CryptoAlgoId_Values.AES_128 ||
                                messageHeader.CryptoAlgoId == CryptoAlgoId_Values.AES_192 ||
                                messageHeader.CryptoAlgoId == CryptoAlgoId_Values.AES_256;

            site.CaptureRequirementIfIsTrue(
                isVerifyR318,
                318,
                @"[In Appendix A: Product Behavior]<3> Section 2.2.3: Windows supports: no encryption, AES_128,
                    AES_ 192, and AES_256.");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R64");

            // Verify MS-PCCRR requirement: MS-PCCRR_R64
            site.CaptureRequirementIfAreEqual <uint>(
                (uint)uiPayload,
                messageHeader.MsgSize,
                64,
                @"[In MESSAGE_HEADER]MsgSize (4 bytes):  Protocol message total size including MESSAGE_HEADER,
                but not including the Transport Header.");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R65");

            // Verify MS-PCCRR requirement: MS-PCCRR_R65
            bool isVerifyR65 = messageHeader.MsgSize >= 16 && messageHeader.MsgSize <= 98304;

            site.CaptureRequirementIfIsTrue(
                isVerifyR65,
                65,
                @"[In MESSAGE_HEADER][MsgSize (4 bytes)]The valid range of the total message size MUST be from
                16 bytes to 98,304 bytes (or 96 KB).");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R66");

            // Verify MS-PCCRR requirement: MS-PCCRR_R66
            site.CaptureRequirementIfAreEqual <int>(
                4,
                sizeof(CryptoAlgoId_Values),
                66,
                @"[In MESSAGE_HEADER]CryptoAlgoId (4 bytes):  Encryption algorithm used by the server-role peer
                to encrypt data.");

            //
            // Add the debug information
            //
            site.Log.Add(LogEntryKind.Debug, "Verify MS-PCCRR_R317");

            if (messageHeader.MsgType == MsgType_Values.MSG_GETBLKS)
            {
                bool isVerifyR317 = messageHeader.CryptoAlgoId == CryptoAlgoId_Values.AES_128;

                // Verify MS-PCCRR requirement: MS-PCCRR_R317
                site.CaptureRequirementIfIsTrue(
                    isVerifyR317,
                    317,
                    @"[In Appendix A: Product Behavior]<3> Section 2.2.3: Windows uses AES_128 as the default encryption
                    algorithm.");

                // Verify MS-PCCRR requirement: MS-PCCRR_R19
                bool isFourBytesAlign = messageHeader.MsgSize % 4 == 0;
                CaptureFieldAlignRequirements(isFourBytesAlign);
            }
        }
Example #7
0
        // Compare with Proto2Stream.TryValidatePseudoHeaders
        private void OnOriginFormTarget(ProtoMethod method, ProtoVersion version, Span <byte> target, Span <byte> path, Span <byte> query, Span <byte> customMethod, bool pathEncoded)
        {
            Debug.Assert(target[0] == ByteForwardSlash, "Should only be called when path starts with /");

            _requestTargetForm = ProtoRequestTarget.OriginForm;

            if (target.Length == 1)
            {
                // If target.Length == 1 it can only be a forward slash (e.g. home page)
                // and we know RawTarget and Path are the same and QueryString is Empty
                RawTarget   = ForwardSlash;
                Path        = ForwardSlash;
                QueryString = string.Empty;
                // Clear parsedData as we won't check it if we come via this path again,
                // an setting to null is fast as it doesn't need to use a GC write barrier.
                _parsedRawTarget = _parsedPath = _parsedQueryString = null;
                return;
            }

            // URIs are always encoded/escaped to ASCII https://tools.ietf.org/html/rfc3986#page-11
            // Multibyte Internationalized Resource Identifiers (IRIs) are first converted to utf8;
            // then encoded/escaped to ASCII  https://www.ietf.org/rfc/rfc3987.txt "Mapping of IRIs to URIs"

            try
            {
                var disableStringReuse = ServerOptions.DisableStringReuse;
                // Read raw target before mutating memory.
                var previousValue = _parsedRawTarget;
                if (disableStringReuse ||
                    previousValue == null || previousValue.Length != target.Length ||
                    !StringUtilities.BytesOrdinalEqualsStringAndAscii(previousValue, target))
                {
                    // The previous string does not match what the bytes would convert to,
                    // so we will need to generate a new string.
                    RawTarget = _parsedRawTarget = target.GetAsciiStringNonNullCharacters();

                    previousValue = _parsedQueryString;
                    if (disableStringReuse ||
                        previousValue == null || previousValue.Length != query.Length ||
                        !StringUtilities.BytesOrdinalEqualsStringAndAscii(previousValue, query))
                    {
                        // The previous string does not match what the bytes would convert to,
                        // so we will need to generate a new string.
                        QueryString = _parsedQueryString = query.GetAsciiStringNonNullCharacters();
                    }
                    else
                    {
                        // Same as previous
                        QueryString = _parsedQueryString;
                    }

                    if (path.Length == 1)
                    {
                        // If path.Length == 1 it can only be a forward slash (e.g. home page)
                        Path = _parsedPath = ForwardSlash;
                    }
                    else
                    {
                        Path = _parsedPath = PathNormalizer.DecodePath(path, pathEncoded, RawTarget, query.Length);
                    }
                }
                else
                {
                    // As RawTarget is the same we can reuse the previous parsed values.
                    RawTarget   = _parsedRawTarget;
                    Path        = _parsedPath;
                    QueryString = _parsedQueryString;
                }
            }
            catch (InvalidOperationException)
            {
                ThrowRequestTargetRejected(target);
            }
        }
        public void HostedCacheServer_PccrrClient_MessageHeader_CryptoAlgoIdV2(CryptoAlgoId_Values algoId)
        {
            CheckApplicability();

            EventQueue eventQueue = new EventQueue(BaseTestSite);

            eventQueue.Timeout = testConfig.Timeout;

            byte[] content = TestUtility.GenerateRandomArray(ContentInformationUtility.DefaultBlockSize);
            Content_Information_Data_Structure_V2 contentInformationV2 = contentInformationUtility.CreateContentInformationV2();

            ProtoVersion protoVersion = new ProtoVersion {
                MajorVersion = 2, MinorVersion = ushort.MaxValue
            };

            BaseTestSite.Log.Add(
                LogEntryKind.Debug,
                "Start PCCRR server to be ready to serve content to hosted cache server");

            using (PccrrTestServerV2 pccrrTestServerV2 = new PccrrTestServerV2())
            {
                pccrrTestServerV2.Start(
                    testConfig.ClientContentRetrievalListenPort,
                    algoId,
                    contentInformationV2,
                    content,
                    eventQueue);

                PCHCClient pchcClient = new PCHCClient(
                    TransferProtocol.HTTP,
                    testConfig.HostedCacheServerComputerName,
                    testConfig.HostedCacheServerHTTPListenPort,
                    PchcConsts.HttpUrl,
                    testConfig.DomainName,
                    testConfig.UserName,
                    testConfig.UserPassword);

                var batchedOfferMessage = pchcClient.CreateBatchedOfferMessage(
                    testConfig.ClientContentRetrievalListenPort,
                    contentInformationV2);
                pchcClient.SendBatchedOfferMessage(batchedOfferMessage);

                BaseTestSite.Log.Add(
                    LogEntryKind.Debug,
                    "Offer content segment 0 of chunk 0 to hosted cache server");

                int segmentCount = 0;
                TestUtility.DoUntilSucceed(delegate()
                {
                    eventQueue.Expect <MessageArrivedEventArgs>(typeof(PccrrServer).GetEvent("MessageArrived"), delegate(System.Net.IPEndPoint sender, PccrrPacket pccrrPacket)
                    {
                        var pccrrGetBlksRequest = pccrrPacket as PccrrGETBLKSRequestPacket;

                        if (pccrrGetBlksRequest != null)
                        {
                            segmentCount++;
                        }
                    });
                    return(segmentCount == 1);
                }, TimeSpan.MaxValue, TimeSpan.Zero);

                BaseTestSite.Log.Add(
                    LogEntryKind.Debug,
                    "Wait until cache is available on hosted cache server");

                TestUtility.DoUntilSucceed(() => sutControlAdapter.IsLocalCacheExisted(testConfig.HostedCacheServerComputerFQDNOrNetBiosName), testConfig.Timeout, testConfig.RetryInterval);
            }

            PccrrClient pccrrClient = new PccrrClient(testConfig.HostedCacheServerComputerName, testConfig.HostedCacheServerHTTPListenPort);
            Aes         aes         = PccrrUtitlity.CreateAes(algoId);

            BaseTestSite.Log.Add(
                LogEntryKind.Debug,
                "Retrieve segment 0 of chunk 0 from hosted cache server",
                0);
            PccrrGETBLKSRequestPacket pccrrBlkRequest = pccrrClient.CreateMsgGetBlksRequest(
                contentInformationV2.GetSegmentId(0, 0),
                algoId,
                MsgType_Values.MSG_GETBLKS,
                (uint)0,
                1);

            pccrrClient.SendPacket(
                pccrrBlkRequest,
                testConfig.Timeout);
            PccrrBLKResponsePacket pccrrBlkResponse
                = (PccrrBLKResponsePacket)pccrrClient.ExpectPacket();

            byte[] data = pccrrBlkResponse.MsgBLK.Block;

            if (algoId != CryptoAlgoId_Values.NoEncryption)
            {
                data = PccrrUtitlity.Decrypt(aes, data, contentInformationV2.chunks[0].chunkData[0].SegmentSecret, pccrrBlkResponse.MsgBLK.IVBlock);
            }

            BaseTestSite.Assert.IsTrue(
                Enumerable.SequenceEqual(content.Take((int)contentInformationV2.chunks[0].chunkData[0].cbSegment), data),
                "The retrieved cached data should be the same as server data.");
        }
        public void HostedCacheServer_PccrrClient_MessageHeader_ProtoVerIncompatible()
        {
            CheckApplicability();

            EventQueue eventQueue = new EventQueue(BaseTestSite);

            eventQueue.Timeout = testConfig.Timeout;

            Content_Information_Data_Structure contentInformation = contentInformationUtility.CreateContentInformationV1();

            CryptoAlgoId_Values cryptoAlgoId = CryptoAlgoId_Values.AES_128;
            ProtoVersion        protoVersion = new ProtoVersion {
                MajorVersion = 1, MinorVersion = 0
            };

            BaseTestSite.Log.Add(
                LogEntryKind.Debug,
                "Start PCCRR server to be ready to serve content to hosted cache server");
            using (PccrrTestIncompatibleProtoVerServer pccrrTestServer = new PccrrTestIncompatibleProtoVerServer())
            {
                pccrrTestServer.Start(
                    testConfig.ClientContentRetrievalListenPort,
                    cryptoAlgoId,
                    protoVersion,
                    contentInformation,
                    new byte[0],
                    eventQueue);

                PCHCClient pchcClient = new PCHCClient(
                    TransferProtocol.HTTPS,
                    testConfig.HostedCacheServerComputerName,
                    testConfig.HostedCacheServerHTTPSListenPort,
                    PchcConsts.HttpsUrl,
                    testConfig.DomainName,
                    testConfig.UserName,
                    testConfig.UserPassword);

                SEGMENT_INFO_MESSAGE segmentInfoMessage = pchcClient.CreateSegmentInfoMessage(
                    testConfig.ClientContentRetrievalListenPort,
                    contentInformation,
                    0);
                pchcClient.SendSegmentInfoMessage(segmentInfoMessage);


                BaseTestSite.Log.Add(
                    LogEntryKind.Debug,
                    "Offer PccrrBLKSResponse with incompatible proto version to hosted cache server");
                int blockCount = 0;
                TestUtility.DoUntilSucceed(delegate()
                {
                    eventQueue.Expect <MessageArrivedEventArgs>(typeof(PccrrServer).GetEvent("MessageArrived"), delegate(System.Net.IPEndPoint sender, PccrrPacket pccrrPacket)
                    {
                        var pccrrGetBlksRequest = pccrrPacket as PccrrGETBLKSRequestPacket;

                        if (pccrrGetBlksRequest != null)
                        {
                            blockCount++;
                        }
                    });
                    return(blockCount == 1);
                }, TimeSpan.MaxValue, TimeSpan.Zero);

                TestUtility.DoUntilSucceed(() => sutControlAdapter.IsLocalCacheExisted(testConfig.HostedCacheServerComputerFQDNOrNetBiosName), testConfig.NegativeTestTimeout, testConfig.RetryInterval);
            }
        }
Example #10
0
 public void OnStartLine(ProtoMethod method, ProtoVersion version, Span <byte> target, Span <byte> path, Span <byte> query, Span <byte> customMethod, bool pathEncoded)
 => Connection.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
Example #11
0
        public static MessageBody For(
            ProtoVersion httpVersion,
            ProtoRequestHeaders headers,
            Proto1Connection context)
        {
            // see also http://tools.ietf.org/html/rfc2616#section-4.4
            var keepAlive = httpVersion != ProtoVersion.Proto10;

            var upgrade = false;

            if (headers.HasConnection)
            {
                var connectionOptions = ProtoHeaders.ParseConnection(headers.HeaderConnection);

                upgrade   = (connectionOptions & ConnectionOptions.Upgrade) == ConnectionOptions.Upgrade;
                keepAlive = (connectionOptions & ConnectionOptions.KeepAlive) == ConnectionOptions.KeepAlive;
            }

            if (upgrade)
            {
                if (headers.HeaderTransferEncoding.Count > 0 || (headers.ContentLength.HasValue && headers.ContentLength.Value != 0))
                {
                    BadProtoRequestException.Throw(RequestRejectionReason.UpgradeRequestCannotHavePayload);
                }

                return(new Proto1UpgradeMessageBody(context));
            }

            if (headers.HasTransferEncoding)
            {
                var transferEncoding = headers.HeaderTransferEncoding;
                var transferCoding   = ProtoHeaders.GetFinalTransferCoding(transferEncoding);

                // https://tools.ietf.org/html/rfc7230#section-3.3.3
                // If a Transfer-Encoding header field
                // is present in a request and the chunked transfer coding is not
                // the final encoding, the message body length cannot be determined
                // reliably; the server MUST respond with the 400 (Bad Request)
                // status code and then close the connection.
                if (transferCoding != TransferCoding.Chunked)
                {
                    BadProtoRequestException.Throw(RequestRejectionReason.FinalTransferCodingNotChunked, transferEncoding);
                }

                // TODO may push more into the wrapper rather than just calling into the message body
                // NBD for now.
                return(new Proto1ChunkedEncodingMessageBody(keepAlive, context));
            }

            if (headers.ContentLength.HasValue)
            {
                var contentLength = headers.ContentLength.Value;

                if (contentLength == 0)
                {
                    return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose);
                }

                return(new Proto1ContentLengthMessageBody(keepAlive, contentLength, context));
            }

            // If we got here, request contains no Content-Length or Transfer-Encoding header.
            // Reject with 411 Length Required.
            if (context.Method == ProtoMethod.Post || context.Method == ProtoMethod.Put)
            {
                var requestRejectionReason = httpVersion == ProtoVersion.Proto11 ? RequestRejectionReason.LengthRequired : RequestRejectionReason.LengthRequiredProto10;
                BadProtoRequestException.Throw(requestRejectionReason, context.Method);
            }

            return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose);
        }
        public void Start(int port, CryptoAlgoId_Values cryptoAlgoId, ProtoVersion protoVersion, Content_Information_Data_Structure contentInformation, byte[] content, EventQueue eventQueue)
        {
            this.contentInformation = contentInformation;

            base.Start(port, cryptoAlgoId, protoVersion, content, eventQueue);
        }