< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Dispatcher.FormatSelectingMessageInspector
Assembly: CoreWCF.WebHttp
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/Dispatcher/FormatSelectingMessageInspector.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 84
Coverable lines: 84
Total lines: 221
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 56
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
.ctor(...)0%440%
RegisterOperation(...)0%220%
AfterReceiveRequest(...)0%18180%
BeforeSendReply(...)100%110%
TrySetFormatFromCache(...)0%220%
TrySetFormatFromAcceptHeader(...)0%18180%
TrySetFormatFromContentType(...)0%10100%
SetFormatFromDefault(...)100%110%
SetFormatFromDefault(...)0%220%
SetFormatAndContentType(...)100%110%
.ctor(...)100%110%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/Dispatcher/FormatSelectingMessageInspector.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.Collections.Generic;
 5using System.Linq;
 6using System.Net.Mime;
 7using CoreWCF.Channels;
 8using CoreWCF.Description;
 9using CoreWCF.Runtime;
 10using CoreWCF.Web;
 11
 12namespace CoreWCF.Dispatcher
 13{
 14    internal class FormatSelectingMessageInspector : IDispatchMessageInspector
 15    {
 016        private static readonly IEnumerable<string> s_wildcardMediaTypes = new List<string>() { "application", "text" };
 17        private readonly List<MultiplexingFormatMapping> _mappings;
 18        private readonly Dictionary<string, MultiplexingDispatchMessageFormatter> _formatters;
 19        private readonly Dictionary<string, NameValueCache<FormatContentTypePair>> _caches;
 20        private readonly bool _automaticFormatSelectionEnabled;
 21
 22        // There are technically an infinite number of valid accept headers for just xml and json,
 23        // but to prevent DOS attacks, we need to set an upper limit. It is assumed that there would
 24        // never be more than two dozen valid accept headers actually used out in the wild.
 025        private static readonly int s_maxCachedAcceptHeaders = 25;
 26
 027        public FormatSelectingMessageInspector(WebHttpBehavior webHttpBehavior, List<MultiplexingFormatMapping> mappings
 28        {
 029            if (webHttpBehavior == null)
 30            {
 031                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(webHttpBehavior));
 32            }
 33
 034            _automaticFormatSelectionEnabled = webHttpBehavior.AutomaticFormatSelectionEnabled;
 35
 036            _formatters = new Dictionary<string, MultiplexingDispatchMessageFormatter>();
 37
 038            _caches = new Dictionary<string, NameValueCache<FormatContentTypePair>>();
 39
 040            _mappings = mappings ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(mappings));
 041        }
 42
 43        public void RegisterOperation(string operationName, MultiplexingDispatchMessageFormatter formatter)
 44        {
 045            if (formatter == null)
 46            {
 047                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(formatter));
 48            }
 49
 50            Fx.Assert(!_formatters.ContainsKey(operationName), "An operation should only be registered once.");
 51
 052            _formatters.Add(operationName, formatter);
 053            _caches.Add(operationName, new NameValueCache<FormatContentTypePair>(s_maxCachedAcceptHeaders));
 054        }
 55
 56        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
 57        {
 058            if (_automaticFormatSelectionEnabled)
 59            {
 060                MessageProperties messageProperties = OperationContext.Current.IncomingMessageProperties;
 061                if (messageProperties.ContainsKey(WebHttpDispatchOperationSelector.HttpOperationNamePropertyName))
 62                {
 063                    string operationName = messageProperties[WebHttpDispatchOperationSelector.HttpOperationNamePropertyN
 064                    if (!string.IsNullOrEmpty(operationName) && _formatters.ContainsKey(operationName))
 65                    {
 66
 067                        string acceptHeader = WebOperationContext.Current.IncomingRequest.Accept;
 068                        if (!string.IsNullOrEmpty(acceptHeader))
 69                        {
 070                            if (TrySetFormatFromCache(operationName, acceptHeader) ||
 071                                TrySetFormatFromAcceptHeader(operationName, acceptHeader, true /* matchCharSet */) ||
 072                                TrySetFormatFromAcceptHeader(operationName, acceptHeader, false /* matchCharSet */))
 73                            {
 074                                return null;
 75                            }
 76                        }
 77
 078                        if (TrySetFormatFromContentType(operationName))
 79                        {
 080                            return null;
 81                        }
 82
 083                        SetFormatFromDefault(operationName);
 84                    }
 85                }
 86            }
 87
 088            return null;
 89        }
 90
 91        public void BeforeSendReply(ref Message reply, object correlationState)
 92        {
 93            // do nothing
 094        }
 95
 96        private bool TrySetFormatFromCache(string operationName, string acceptHeader)
 97        {
 98            Fx.Assert(_caches.ContainsKey(operationName), "The calling method is responsible for ensuring that the 'oper
 99            Fx.Assert(acceptHeader != null, "The calling method is responsible for ensuring that 'acceptHeader' is not n
 100
 0101            FormatContentTypePair pair = _caches[operationName].Lookup(acceptHeader.ToUpperInvariant());
 0102            if (pair != null)
 103            {
 0104                SetFormatAndContentType(pair.Format, pair.ContentType);
 0105                return true;
 106            }
 107
 0108            return false;
 109        }
 110
 111        private bool TrySetFormatFromAcceptHeader(string operationName, string acceptHeader, bool matchCharSet)
 112        {
 113            Fx.Assert(_formatters.ContainsKey(operationName), "The calling method is responsible for ensuring that the '
 114
 0115            IList<ContentType> acceptHeaderElements = WebOperationContext.Current.IncomingRequest.GetAcceptHeaderElement
 116
 0117            for (int i = 0; i < acceptHeaderElements.Count; i++)
 118            {
 0119                string[] typeAndSubType = acceptHeaderElements[i].MediaType.Split('/');
 0120                string type = typeAndSubType[0].Trim().ToLowerInvariant();
 0121                string subType = typeAndSubType[1].Trim();
 122
 0123                if ((subType[0] == '*' && subType.Length == 1) &&
 0124                     ((type[0] == '*' && type.Length == 1) ||
 0125                      s_wildcardMediaTypes.Contains(type)))
 126                {
 0127                    SetFormatFromDefault(operationName, acceptHeader);
 0128                    return true;
 129                }
 130
 0131                foreach (MultiplexingFormatMapping mapping in _mappings)
 132                {
 0133                    WebMessageFormat format = mapping.MessageFormat;
 0134                    if (_formatters[operationName].SupportsMessageFormat(format) &&
 0135                        mapping.CanFormatResponse(acceptHeaderElements[i], matchCharSet, out ContentType contentType))
 136                    {
 0137                        string contentTypeStr = contentType.ToString();
 0138                        _caches[operationName].AddOrUpdate(acceptHeader.ToUpperInvariant(), new FormatContentTypePair(fo
 0139                        SetFormatAndContentType(format, contentTypeStr);
 0140                        return true;
 141                    }
 142                }
 143            }
 144
 0145            return false;
 0146        }
 147
 148        private bool TrySetFormatFromContentType(string operationName)
 149        {
 150            Fx.Assert(_formatters.ContainsKey(operationName), "The calling method is responsible for ensuring that the '
 151
 0152            string contentTypeStr = WebOperationContext.Current.IncomingRequest.ContentType;
 0153            if (contentTypeStr != null)
 154            {
 0155                ContentType contentType = Utility.GetContentType(contentTypeStr);
 0156                if (contentType != null)
 157                {
 0158                    foreach (MultiplexingFormatMapping mapping in _mappings)
 159                    {
 0160                        if (_formatters[operationName].SupportsMessageFormat(mapping.MessageFormat) &&
 0161                            mapping.CanFormatResponse(contentType, false, out ContentType responseContentType))
 162                        {
 0163                            SetFormatAndContentType(mapping.MessageFormat, responseContentType.ToString());
 0164                            return true;
 165                        }
 166                    }
 167                }
 168            }
 169
 0170            return false;
 0171        }
 172
 173        private void SetFormatFromDefault(string operationName)
 174        {
 0175            SetFormatFromDefault(operationName, null);
 0176        }
 177
 178        private  void SetFormatFromDefault(string operationName, string acceptHeader)
 179        {
 180            Fx.Assert(_formatters.ContainsKey(operationName), "The calling method is responsible for ensuring that the '
 0181            WebMessageFormat format = _formatters[operationName].DefaultFormat;
 182
 0183            if (!string.IsNullOrEmpty(acceptHeader))
 184            {
 0185                _caches[operationName].AddOrUpdate(acceptHeader.ToUpperInvariant(), new FormatContentTypePair(format, nu
 186            }
 187
 0188            WebOperationContext.Current.OutgoingResponse.Format = format;
 189
 190            //if (DiagnosticUtility.ShouldTraceInformation)
 191            //{
 192            //    TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.AutomaticFormatSelectedOperationDefault,
 193            //}
 0194        }
 195
 196        private void SetFormatAndContentType(WebMessageFormat format, string contentType)
 197        {
 0198            OutgoingWebResponseContext outgoingResponse = WebOperationContext.Current.OutgoingResponse;
 0199            outgoingResponse.Format = format;
 0200            outgoingResponse.AutomatedFormatSelectionContentType = contentType;
 201
 202            //if (DiagnosticUtility.ShouldTraceInformation)
 203            //{
 204            //    TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.AutomaticFormatSelectedRequestBased, SR2
 205            //}
 0206        }
 207
 208        internal class FormatContentTypePair
 209        {
 0210            public FormatContentTypePair(WebMessageFormat format, string contentType)
 211            {
 0212                Format = format;
 0213                ContentType = contentType;
 0214            }
 215
 0216            public WebMessageFormat Format { get; }
 217
 0218            public string ContentType { get; }
 219        }
 220    }
 221}