< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Channels.ServerFramingDuplexSessionChannel
Assembly: CoreWCF.NetFramingBase
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetFramingBase/src/CoreWCF/Channels/ServerFramingDuplexSessionChannel.cs
Line coverage
84%
Covered lines: 118
Uncovered lines: 21
Coverable lines: 139
Total lines: 436
Line coverage: 84.8%
Branch coverage
72%
Covered branches: 42
Total branches: 58
Branch coverage: 72.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
ReturnConnectionIfNecessary(...)100%22100%
GetProperty()100%22100%
OnOpenAsync(...)100%11100%
OnOpened()100%1196.42%
OnClosing()100%11100%
OnCloseAsync()50%4471.42%
.ctor(...)100%11100%
ReceiveAsync()50%141466.66%
WaitForMessageAsync(...)100%110%
EnsureDecoderAtEof()0%440%
DecodeMessageAsync()100%181891.42%
CopyBuffer(...)70%101082.35%
PrepareMessage(...)100%44100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetFramingBase/src/CoreWCF/Channels/ServerFramingDuplexSessionChannel.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.Net;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using System.Xml;
 10using CoreWCF.Channels.Framing;
 11using CoreWCF.Runtime;
 12using CoreWCF.Security;
 13using Microsoft.Extensions.Hosting;
 14using Microsoft.Extensions.DependencyInjection;
 15
 16namespace CoreWCF.Channels
 17{
 18    internal class ServerFramingDuplexSessionChannel : FramingDuplexSessionChannel
 19    {
 20        private IServiceProvider _serviceProvider;
 21        private CancellationTokenRegistration _applicationStoppingRegistration;
 22
 23        public ServerFramingDuplexSessionChannel(FramingConnection connection, ITransportFactorySettings settings,
 24            bool exposeConnectionProperty, IServiceProvider serviceProvider)
 6425            : base(connection, settings, exposeConnectionProperty)
 26        {
 6427            Connection = connection;
 6428            _serviceProvider = serviceProvider;
 6429            SetMessageSource(new ServerSessionConnectionMessageSource(connection));
 6430        }
 31
 32        protected override void ReturnConnectionIfNecessary(bool abort, CancellationToken token)
 33        {
 11934            if (abort)
 35            {
 36                // Need to use an overload of Connection.Abort which takes a parameter as
 37                // ConnectionContext.Abort() does a clean socket shutdown and we need
 38                // to abort the socket and send a RST as we are working with the assumption
 39                // the client is no longer reachable.
 740                Connection.Abort(string.Format(SR.ReceiveTimedOut2, DefaultReceiveTimeout));
 41            }
 11942        }
 43
 44        public override T GetProperty<T>()
 45        {
 26246            T service = _serviceProvider.GetService<T>();
 26247            if (service != null)
 48            {
 12649                return service;
 50            }
 51
 13652            return base.GetProperty<T>();
 53        }
 54
 55        protected override Task OnOpenAsync(CancellationToken token)
 56        {
 6457            return Task.CompletedTask; // NO-OP
 58        }
 59
 60        protected override void OnOpened()
 61        {
 6462            base.OnOpened();
 63
 6464            IApplicationLifetime appLifetime = _serviceProvider.GetRequiredService<IApplicationLifetime>();
 6465            _applicationStoppingRegistration = appLifetime.ApplicationStopping.Register(() =>
 6466            {
 6467                // Use a discard and don't wait on the task so that multiple clients can close their channels
 6468                // simultaneously without waiting for each other. This prevents serializing of closing connected
 6469                // clients. As we're discarding the task results, we must make sure to handle any exceptions inside
 6470                // the task to avoid unhandled exceptions.
 571                _ = Task.Run(async () =>
 572                {
 573                    // If the application is stopping, we should close the channel gracefully.
 574                    // This will ensure that any pending messages are processed before closing.
 575                    if (State == CommunicationState.Opened)
 576                    {
 577                        try
 578                        {
 479                            await CloseAsync();
 080                        }
 481                        catch (Exception)
 582                        {
 583                            // Catch any exceptions that occur during close to avoid unhandled exceptions
 584                            // As we're shutting down, exceptions closing the channel have no consequences
 585                            // TODO: When bringing back EventSource logging, log the exception here
 486                        }
 587                    }
 1088                });
 6989            });
 6490        }
 91
 92        protected override void OnClosing()
 93        {
 6894            base.OnClosing();
 6895            _applicationStoppingRegistration.Dispose();
 6896        }
 97
 98        protected override async Task OnCloseAsync(CancellationToken token)
 99        {
 62100            await base.OnCloseAsync(token);
 101
 57102            if (_serviceProvider is IAsyncDisposable asyncDisposable)
 103            {
 57104                await asyncDisposable.DisposeAsync();
 105            }
 0106            else if (_serviceProvider is IDisposable disposable)
 107            {
 0108                disposable.Dispose();
 109            }
 110
 57111            _serviceProvider = null;
 57112        }
 113
 114        internal class ServerSessionConnectionMessageSource : IMessageSource
 115        {
 116            private readonly FramingConnection _connection;
 117
 64118            public ServerSessionConnectionMessageSource(FramingConnection connection)
 119            {
 64120                _connection = connection;
 64121            }
 122
 123            public async Task<Message> ReceiveAsync(CancellationToken token)
 124            {
 125                // TODO: Apply timeouts
 126                Message message;
 144127                ReadOnlySequence<byte> buffer = ReadOnlySequence<byte>.Empty;
 128                for (; ; )
 129                {
 144130                    System.IO.Pipelines.ReadResult readResult = await _connection.Input.ReadAsync(token);
 134131                    if (readResult.IsCompleted || readResult.Buffer.Length == 0)
 132                    {
 0133                        if (!readResult.IsCompleted)
 134                        {
 0135                            _connection.Input.AdvanceTo(readResult.Buffer.Start);
 136                        }
 137
 0138                        EnsureDecoderAtEof();
 0139                        _connection.EOF = true;
 140                    }
 141
 134142                    if (_connection.EOF)
 143                    {
 0144                        return null;
 145                    }
 146
 134147                    buffer = readResult.Buffer;
 134148                    (message, buffer) = await DecodeMessageAsync(buffer);
 133149                    _connection.Input.AdvanceTo(buffer.Start);
 150
 133151                    _connection.Logger.ReceivedMessage(message);
 133152                    if (message != null)
 153                    {
 76154                        PrepareMessage(message);
 76155                        return message;
 156                    }
 57157                    else if (_connection.EOF) // could have read the END record under DecodeMessage
 158                    {
 57159                        return null;
 160                    }
 161
 0162                    if (buffer.Length != 0)
 163                    {
 0164                        throw Fx.AssertAndThrow("Receive: DecodeMessage() should consume the outstanding buffer or retur
 165                    }
 166                }
 133167            }
 168
 169            public Task<bool> WaitForMessageAsync(CancellationToken token)
 170            {
 0171                throw new NotImplementedException();
 172            }
 173
 174            private void EnsureDecoderAtEof()
 175            {
 0176                var decoder = _connection.FramingDecoder as ServerSessionDecoder;
 0177                if (!(decoder.CurrentState == ServerSessionDecoder.State.End || decoder.CurrentState == ServerSessionDec
 178                {
 0179                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException());
 180                }
 0181            }
 182
 183            private async ValueTask<(Message, ReadOnlySequence<byte>)> DecodeMessageAsync(ReadOnlySequence<byte> buffer)
 184            {
 134185                int maxBufferSize = _connection.MaxBufferSize;
 134186                var decoder = (ServerSessionDecoder)_connection.FramingDecoder;
 631187                while (!_connection.EOF && buffer.Length > 0)
 188                {
 574189                    int bytesRead = decoder.Decode(buffer);
 574190                    if (bytesRead > 0)
 191                    {
 287192                        if (!_connection.EnvelopeBuffer.IsEmpty)
 193                        {
 76194                            Memory<byte> remainingEnvelopeBuffer = _connection.EnvelopeBuffer.Slice(_connection.Envelope
 76195                            CopyBuffer(buffer, remainingEnvelopeBuffer, bytesRead);
 76196                            _connection.EnvelopeOffset += bytesRead;
 197                        }
 198
 287199                        buffer = buffer.Slice(bytesRead);
 200                    }
 201
 574202                    switch (decoder.CurrentState)
 203                    {
 204                        case ServerSessionDecoder.State.EnvelopeStart:
 77205                            int envelopeSize = decoder.EnvelopeSize;
 77206                            if (envelopeSize > maxBufferSize)
 207                            {
 1208                                _connection.Input.AdvanceTo(buffer.Start); // Advance the input pipe so that SendFaultAs
 1209                                await _connection.SendFaultAsync(FramingEncodingString.MaxMessageSizeExceededFault, Tran
 210
 1211                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
 1212                                    MaxMessageSizeStream.CreateMaxReceivedMessageSizeExceededException(maxBufferSize));
 213                            }
 214
 76215                            _connection.EnvelopeBuffer = _connection.BufferManager.TakeBuffer(envelopeSize);
 76216                            _connection.EnvelopeSize = envelopeSize;
 76217                            _connection.EnvelopeOffset = 0;
 76218                            break;
 219
 220                        case ServerSessionDecoder.State.EnvelopeEnd:
 76221                            if (!_connection.EnvelopeBuffer.IsEmpty)
 222                            {
 223                                Message message;
 224                                try
 225                                {
 76226                                    message = _connection.MessageEncoder.ReadMessage(
 76227                                        new ArraySegment<byte>(_connection.EnvelopeBuffer.ToArray(), 0, _connection.Enve
 76228                                        _connection.BufferManager,
 76229                                        _connection.FramingDecoder.ContentType);
 76230                                }
 0231                                catch (XmlException xmlException)
 232                                {
 0233                                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
 0234                                        new ProtocolException(SR.MessageXmlProtocolError, xmlException));
 235                                }
 236
 76237                                _connection.EnvelopeBuffer = null;
 76238                                return (message, buffer);
 239                            }
 240                            break;
 241
 242                        case ServerSessionDecoder.State.End:
 57243                            _connection.EOF = true;
 244                            break;
 245                    }
 246                }
 247
 57248                return (null, buffer);
 133249            }
 250
 251            private void CopyBuffer(ReadOnlySequence<byte> src, Memory<byte> dest, int bytesToCopy)
 252            {
 253                Fx.Assert(src.Length >= bytesToCopy, "Trying to copy more bytes than exist in src");
 254                // Grab only the number of bytes that we want to copy from the source sequence
 76255                src = src.Slice(0, bytesToCopy);
 76256                if (dest.Length < bytesToCopy)
 257                {
 0258                    throw new ArgumentOutOfRangeException(nameof(bytesToCopy));
 259                }
 260
 76261                Span<byte> destSpan = dest.Span;
 76262                if (src.IsSingleSegment)
 263                {
 74264                    ReadOnlySpan<byte> srcSpan = src.First.Span;
 74265                    srcSpan.CopyTo(destSpan);
 266                }
 267                else
 268                {
 10269                    foreach (ReadOnlyMemory<byte> segment in src)
 270                    {
 4271                        ReadOnlySpan<byte> srcSpan = segment.Span;
 4272                        if (srcSpan.Length > bytesToCopy)
 273                        {
 0274                            srcSpan = srcSpan.Slice(0, bytesToCopy);
 275                        }
 4276                        srcSpan.CopyTo(destSpan);
 4277                        bytesToCopy -= srcSpan.Length;
 4278                        if (bytesToCopy == 0)
 279                        {
 2280                            return;
 281                        }
 282
 2283                        destSpan = destSpan.Slice(srcSpan.Length);
 284                    }
 285                }
 0286            }
 287
 288            private void PrepareMessage(Message message)
 289            {
 76290                if (_connection.SecurityMessageProperty != null)
 291                {
 9292                    message.Properties.Security = (SecurityMessageProperty)_connection.SecurityMessageProperty.CreateCop
 293                }
 294
 76295                IPEndPoint remoteEndPoint = _connection.RemoteEndpoint;
 296
 297                // pipes will return null
 76298                if (remoteEndPoint != null)
 299                {
 73300                    var remoteEndpointProperty = new RemoteEndpointMessageProperty(remoteEndPoint);
 73301                    message.Properties.Add(RemoteEndpointMessageProperty.Name, remoteEndpointProperty);
 302                }
 76303            }
 304        }
 305    }
 306
 307    internal abstract class FramingDuplexSessionChannel : TransportDuplexSessionChannel
 308    {
 309        private readonly bool _exposeConnectionProperty;
 310
 311        private FramingDuplexSessionChannel(ITransportFactorySettings settings,
 312            EndpointAddress localAddress, Uri localVia, EndpointAddress remoteAddress, Uri via, bool exposeConnectionPro
 313            : base(settings, localAddress, localVia, remoteAddress, via)
 314        {
 315            _exposeConnectionProperty = exposeConnectionProperty;
 316        }
 317
 318        protected FramingDuplexSessionChannel(FramingConnection connection, ITransportFactorySettings settings, bool exp
 319    : this(settings, new EndpointAddress(connection.ServiceDispatcher.BaseAddress), connection.Via,
 320    EndpointAddress.AnonymousAddress, connection.MessageEncoder.MessageVersion.Addressing.AnonymousUri, exposeConnection
 321        {
 322            Session = FramingConnectionDuplexSession.CreateSession(this, connection.StreamUpgradeAcceptor);
 323        }
 324
 325        protected FramingConnection Connection { get; set; }
 326
 327        protected override bool IsStreamedOutput
 328        {
 329            get { return false; }
 330        }
 331
 332        protected override async Task CloseOutputSessionCoreAsync(CancellationToken token)
 333        {
 334            await Connection.Output.WriteAsync(SessionEncoder.EndBytes, token);
 335            await Connection.Output.FlushAsync();
 336        }
 337
 338        protected override async Task CompleteCloseAsync(CancellationToken token)
 339        {
 340            if (Connection.RawStream != null)
 341            {
 342                Connection.Logger.UnwrappingRawStream();
 343                Connection.RawStream = null;
 344                await Connection.Output.CompleteAsync();
 345                await Connection.Input.CompleteAsync();
 346            }
 347            ReturnConnectionIfNecessary(false, token);
 348        }
 349
 350        protected override async Task OnSendCoreAsync(Message message, CancellationToken token)
 351        {
 352            bool allowOutputBatching;
 353            ArraySegment<byte> messageData;
 354            allowOutputBatching = message.Properties.AllowOutputBatching;
 355            Connection.Logger.SendMessage(message);
 356            messageData = EncodeMessage(message);
 357            try
 358            {
 359                await Connection.Output.WriteAsync(messageData, token);
 360                await Connection.Output.FlushAsync();
 361            }
 362            finally
 363            {
 364                BufferManager.ReturnBuffer(messageData.Array);
 365            }
 366        }
 367
 368        protected override async Task CloseOutputAsync(CancellationToken token)
 369        {
 370            await Connection.Output.WriteAsync(SessionEncoder.EndBytes, token);
 371            await Connection.Output.FlushAsync();
 372        }
 373
 374        protected override ArraySegment<byte> EncodeMessage(Message message)
 375        {
 376            ArraySegment<byte> messageData = MessageEncoder.WriteMessage(message,
 377                int.MaxValue, BufferManager, SessionEncoder.MaxMessageFrameSize);
 378
 379            messageData = SessionEncoder.EncodeMessageFrame(messageData);
 380
 381            return messageData;
 382        }
 383
 384        private class FramingConnectionDuplexSession : ConnectionDuplexSession
 385        {
 386            private FramingConnectionDuplexSession(FramingDuplexSessionChannel channel)
 387                : base(channel)
 388            {
 389            }
 390
 391            public static FramingConnectionDuplexSession CreateSession(FramingDuplexSessionChannel channel,
 392                StreamUpgradeAcceptor upgradeAcceptor)
 393            {
 394                if (!(upgradeAcceptor is StreamSecurityUpgradeAcceptor security))
 395                {
 396                    return new FramingConnectionDuplexSession(channel);
 397                }
 398                else
 399                {
 400                    return new SecureConnectionDuplexSession(channel);
 401                }
 402            }
 403
 404            private class SecureConnectionDuplexSession : FramingConnectionDuplexSession, ISecuritySession
 405            {
 406                private EndpointIdentity _remoteIdentity;
 407
 408                public SecureConnectionDuplexSession(FramingDuplexSessionChannel channel)
 409                    : base(channel)
 410                {
 411                    // empty
 412                }
 413
 414                EndpointIdentity ISecuritySession.RemoteIdentity
 415                {
 416                    get
 417                    {
 418                        if (_remoteIdentity == null)
 419                        {
 420                            SecurityMessageProperty security = Channel.RemoteSecurity;
 421                            if (security != null && security.ServiceSecurityContext != null &&
 422                                security.ServiceSecurityContext.IdentityClaim != null &&
 423                                security.ServiceSecurityContext.PrimaryIdentity != null)
 424                            {
 425                                _remoteIdentity = EndpointIdentity.CreateIdentity(
 426                                    security.ServiceSecurityContext.IdentityClaim);
 427                            }
 428                        }
 429
 430                        return _remoteIdentity;
 431                    }
 432                }
 433            }
 434        }
 435    }
 436}