< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Channels.Framing.ServerFramingSingletonMiddleware
Assembly: CoreWCF.NetFramingBase
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetFramingBase/src/CoreWCF/Channels/Framing/ServerFramingSingletonMiddleware.cs
Line coverage
74%
Covered lines: 82
Uncovered lines: 28
Coverable lines: 110
Total lines: 298
Line coverage: 74.5%
Branch coverage
75%
Covered branches: 57
Total branches: 76
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
OnConnectedAsync()82.5%404076.92%
CanReadAndDecode(...)100%22100%
ChangeUpgradeState(...)63.33%303054.54%
UpgradeConnectionAsync()100%11100%
CreatePipelineFromStream(...)100%11100%
SetupSecurityIfNecessary(...)75%4462.5%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetFramingBase/src/CoreWCF/Channels/Framing/ServerFramingSingletonMiddleware.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Buffers;
 6using System.IO;
 7using System.Threading.Tasks;
 8using CoreWCF.Configuration;
 9using CoreWCF.Runtime;
 10
 11namespace CoreWCF.Channels.Framing
 12{
 13    internal class ServerFramingSingletonMiddleware
 14    {
 15        private readonly HandshakeDelegate _next;
 16
 8317        public ServerFramingSingletonMiddleware(HandshakeDelegate next)
 18        {
 8319            _next = next;
 8320        }
 21
 22        public async Task OnConnectedAsync(FramingConnection connection)
 23        {
 5024            TimeSpan receiveTimeout = connection.ServiceDispatcher.Binding.ReceiveTimeout;
 5025            var timeoutHelper = new TimeoutHelper(receiveTimeout);
 5026            bool success = false;
 27            try
 28            {
 5029                var decoder = connection.FramingDecoder as ServerSingletonDecoder;
 30                Fx.Assert(decoder != null, "FramingDecoder must be non-null and an instance of ServerSessionDecoder");
 31
 32                // first validate our content type
 33                //ValidateContentType(connection, decoder);
 5034                UpgradeState upgradeState = UpgradeState.None;
 35                // next read any potential upgrades and finish consuming the preamble
 5036                ReadOnlySequence<byte> buffer = ReadOnlySequence<byte>.Empty;
 37                while (true)
 38                {
 5539                    if (buffer.Length == 0 && CanReadAndDecode(upgradeState))
 40                    {
 5141                        System.IO.Pipelines.ReadResult readResult = await connection.Input.ReadAsync();
 5142                        buffer = readResult.Buffer;
 5143                        if (readResult.IsCompleted)
 44                        {
 045                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFExceptio
 46                        }
 47                    }
 48
 49                    while (true)
 50                    {
 15851                        if (CanReadAndDecode(upgradeState))
 52                        {
 53                            Fx.Assert(buffer.Length > 0, "There must be something in the buffer to decode");
 15454                            int bytesDecoded = decoder.Decode(buffer);
 15455                            if (bytesDecoded > 0)
 56                            {
 5357                                buffer = buffer.Slice(bytesDecoded);
 5358                                if (buffer.Length == 0)
 59                                {
 5160                                    connection.Input.AdvanceTo(buffer.Start);
 61                                }
 62                            }
 63                        }
 64
 15865                        switch (decoder.CurrentState)
 66                        {
 67                            case ServerSingletonDecoder.State.UpgradeRequest:
 68                                switch (upgradeState)
 69                                {
 70                                    case UpgradeState.None:
 71                                        //change the state so that we don't read/decode until it is safe
 172                                        ChangeUpgradeState(ref upgradeState, UpgradeState.VerifyingUpgradeRequest);
 173                                        break;
 74                                    case UpgradeState.VerifyingUpgradeRequest:
 175                                        if (connection.StreamUpgradeAcceptor == null)
 76                                        {
 077                                            connection.Input.AdvanceTo(buffer.Start); // Make sure that input pipe is ab
 078                                            await connection.SendFaultAsync(FramingEncodingString.UpgradeInvalidFault, T
 079                                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
 080                                                new ProtocolException(SR.Format(SR.UpgradeRequestToNonupgradableService,
 81                                        }
 82
 183                                        if (!connection.StreamUpgradeAcceptor.CanUpgrade(decoder.Upgrade))
 84                                        {
 085                                            connection.Input.AdvanceTo(buffer.Start); // Make sure that input pipe is ab
 086                                            await connection.SendFaultAsync(FramingEncodingString.UpgradeInvalidFault, T
 087                                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolExcept
 88                                        }
 89
 190                                        ChangeUpgradeState(ref upgradeState, UpgradeState.WritingUpgradeAck);
 91                                        // accept upgrade
 192                                        await connection.Output.WriteAsync(ServerSingletonEncoder.UpgradeResponseBytes, 
 193                                        await connection.Output.FlushAsync(timeoutHelper.GetCancellationToken());
 194                                        ChangeUpgradeState(ref upgradeState, UpgradeState.UpgradeAckSent);
 195                                        break;
 96                                    case UpgradeState.UpgradeAckSent:
 97                                        // This state was used to capture any extra read bytes into PreReadConnection bu
 98                                        // This extra state transition has been left here to maintain the same state tra
 199                                        ChangeUpgradeState(ref upgradeState, UpgradeState.BeginUpgrade);
 1100                                        break;
 101                                    case UpgradeState.BeginUpgrade:
 102                                        // Set input pipe so that the next read will return all the unconsumed bytes.
 103                                        // If all bytes have already been consumed so the buffer has 0 length, AdvanceTo
 104                                        // as it's already been called.
 1105                                        if (buffer.Length > 0)
 106                                        {
 0107                                            connection.Input.AdvanceTo(buffer.Start);
 108                                        }
 109
 1110                                        buffer = ReadOnlySequence<byte>.Empty;
 111                                        try
 112                                        {
 1113                                            await UpgradeConnectionAsync(connection, decoder.Upgrade);
 1114                                            ChangeUpgradeState(ref upgradeState, UpgradeState.EndUpgrade);
 1115                                        }
 116                                        catch (Exception exception)
 117                                        {
 0118                                            if (Fx.IsFatal(exception))
 119                                            {
 0120                                                throw;
 121                                            }
 122
 123                                            throw;
 124                                        }
 125                                        break;
 126                                    case UpgradeState.EndUpgrade:
 127                                        //Must be a different state here than UpgradeComplete so that we don't try to re
 1128                                        ChangeUpgradeState(ref upgradeState, UpgradeState.UpgradeComplete);
 1129                                        break;
 130                                    case UpgradeState.UpgradeComplete:
 131                                        //Client is doing more than one upgrade, reset the state
 0132                                        ChangeUpgradeState(ref upgradeState, UpgradeState.VerifyingUpgradeRequest);
 0133                                        break;
 134                                }
 135                                break;
 136                            case ServerSingletonDecoder.State.Start:
 50137                                SetupSecurityIfNecessary(connection);
 50138                                if (upgradeState == UpgradeState.UpgradeComplete //We have done at least one upgrade, bu
 50139                                    || upgradeState == UpgradeState.None)//no upgrade, just send the preample end bytes
 140                                {
 50141                                    ChangeUpgradeState(ref upgradeState, UpgradeState.WritingPreambleEnd);
 142                                    // we've finished the preamble. Ack and return.
 50143                                    await connection.Output.WriteAsync(ServerSessionEncoder.AckResponseBytes);
 50144                                    await connection.Output.FlushAsync();
 145                                    //terminal state
 50146                                    ChangeUpgradeState(ref upgradeState, UpgradeState.PreambleEndSent);
 147                                }
 148                                // If all bytes have already been consumed so the buffer has 0 length, AdvanceTo would t
 149                                // as it's already been called.
 50150                                if (buffer.Length > 0)
 151                                {
 0152                                    connection.Input.AdvanceTo(buffer.Start);
 153                                }
 154
 50155                                success = true;
 50156                                await _next(connection);
 50157                                return;
 158                        }
 159
 108160                        if (buffer.Length == 0)
 161                        {
 5162                            break;
 163                        }
 164                    }
 165                }
 166            }
 167            finally
 168            {
 50169                if (!success)
 170                {
 0171                    connection.Abort();
 172                }
 173            }
 50174        }
 175
 176        private bool CanReadAndDecode(UpgradeState upgradeState)
 177        {
 178            //ok to read/decode before we start the upgrade
 179            //and between UpgradeComplete/WritingPreambleAck
 213180            return upgradeState == UpgradeState.None
 213181                || upgradeState == UpgradeState.UpgradeComplete;
 182        }
 183
 184        private void ChangeUpgradeState(ref UpgradeState currentState, UpgradeState newState)
 185        {
 186            switch (newState)
 187            {
 188                case UpgradeState.None:
 0189                    throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + currentState + ", newState=" + n
 190                case UpgradeState.VerifyingUpgradeRequest:
 1191                    if (currentState != UpgradeState.None //starting first upgrade
 1192                        && currentState != UpgradeState.UpgradeComplete)//completing one upgrade and starting another
 193                    {
 0194                        throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + currentState + ", newState="
 195                    }
 196                    break;
 197                case UpgradeState.WritingUpgradeAck:
 1198                    if (currentState != UpgradeState.VerifyingUpgradeRequest)
 199                    {
 0200                        throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + currentState + ", newState="
 201                    }
 202                    break;
 203                case UpgradeState.UpgradeAckSent:
 1204                    if (currentState != UpgradeState.WritingUpgradeAck)
 205                    {
 0206                        throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + currentState + ", newState="
 207                    }
 208                    break;
 209                case UpgradeState.BeginUpgrade:
 1210                    if (currentState != UpgradeState.UpgradeAckSent)
 211                    {
 0212                        throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + currentState + ", newState="
 213                    }
 214                    break;
 215                case UpgradeState.EndUpgrade:
 1216                    if (currentState != UpgradeState.BeginUpgrade)
 217                    {
 0218                        throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + currentState + ", newState="
 219                    }
 220                    break;
 221                case UpgradeState.UpgradeComplete:
 1222                    if (currentState != UpgradeState.EndUpgrade)
 223                    {
 0224                        throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + currentState + ", newState="
 225                    }
 226                    break;
 227                case UpgradeState.WritingPreambleEnd:
 50228                    if (currentState != UpgradeState.None //no upgrade being used
 50229                        && currentState != UpgradeState.UpgradeComplete)//upgrades are now complete, end the preamble ha
 230                    {
 0231                        throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + currentState + ", newState="
 232                    }
 233                    break;
 234                case UpgradeState.PreambleEndSent:
 50235                    if (currentState != UpgradeState.WritingPreambleEnd)
 236                    {
 0237                        throw Fx.AssertAndThrow("Invalid State Transition: currentState=" + currentState + ", newState="
 238                    }
 239                    break;
 240                default:
 0241                    throw Fx.AssertAndThrow("Unexpected Upgrade State: " + newState);
 242            }
 243
 106244            currentState = newState;
 106245        }
 246
 247        public static async Task UpgradeConnectionAsync(FramingConnection connection, string contentType)
 248        {
 1249            var duplexPipeStream = new DuplexPipeStream(connection.Input, connection.Output);
 1250            connection.RawStream = duplexPipeStream;
 1251            StreamUpgradeAcceptor upgradeAcceptor = connection.StreamUpgradeAcceptor;
 1252            Stream stream = await upgradeAcceptor.AcceptUpgradeAsync(connection.RawStream);
 1253            duplexPipeStream.SetContentType(contentType);
 1254            CreatePipelineFromStream(connection, stream);
 1255        }
 256
 257        private static void CreatePipelineFromStream(FramingConnection connection, Stream stream)
 258        {
 1259            var wrappedPipeline = new StreamDuplexPipe(connection.Transport, stream);
 1260            connection.Transport = wrappedPipeline;
 1261        }
 262
 263        private static void SetupSecurityIfNecessary(FramingConnection connection)
 264        {
 50265            if (connection.StreamUpgradeAcceptor is StreamSecurityUpgradeAcceptor securityUpgradeAcceptor)
 266            {
 1267                Security.SecurityMessageProperty remoteSecurity = securityUpgradeAcceptor.GetRemoteSecurity();
 268
 1269                if (remoteSecurity == null)
 270                {
 0271                    Exception securityFailedException = new ProtocolException(
 0272                        SR.Format(SR.RemoteSecurityNotNegotiatedOnStreamUpgrade, connection.Via));
 273                    //WriteAuditFailure(securityUpgradeAcceptor, securityFailedException);
 0274                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(securityFailedException);
 275                }
 276                else
 277                {
 1278                    connection.SecurityMessageProperty = remoteSecurity;
 279                    // Audit Authentication Success
 280                    //WriteAuditEvent(securityUpgradeAcceptor, AuditLevel.Success, null);
 281                }
 282            }
 50283        }
 284
 285        private enum UpgradeState
 286        {
 287            None,
 288            VerifyingUpgradeRequest,
 289            WritingUpgradeAck,
 290            UpgradeAckSent,
 291            BeginUpgrade,
 292            EndUpgrade,
 293            UpgradeComplete,
 294            WritingPreambleEnd,
 295            PreambleEndSent,
 296        }
 297    }
 298}