< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Web.OutgoingWebResponseContext
Assembly: CoreWCF.WebHttp
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/Web/OutgoingWebResponseContext.cs
Line coverage
15%
Covered lines: 17
Uncovered lines: 93
Coverable lines: 110
Total lines: 320
Line coverage: 15.4%
Branch coverage
6%
Covered branches: 4
Total branches: 66
Branch coverage: 6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor(...)100%11100%
SetETag(...)100%110%
SetETag(...)100%110%
SetETag(...)100%110%
SetETag(...)100%110%
SetStatusAsCreated(...)0%220%
SetStatusAsNotFound()100%110%
SetStatusAsNotFound(...)100%110%
GenerateValidEtagFromString(...)0%40400%
GenerateValidEtag(...)100%110%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/Web/OutgoingWebResponseContext.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.Collections.Generic;
 6using System.Globalization;
 7using System.Net;
 8using System.Text;
 9using CoreWCF.Channels;
 10using CoreWCF.Description;
 11using CoreWCF.Runtime;
 12
 13namespace CoreWCF.Web
 14{
 15    public class OutgoingWebResponseContext
 16    {
 117        internal static readonly string s_webResponseFormatPropertyName = "WebResponseFormatProperty";
 118        internal static readonly string s_automatedFormatSelectionContentTypePropertyName = "AutomatedFormatSelectionCon
 19
 20        private Encoding _bindingWriteEncoding = null;
 21        private readonly OperationContext _operationContext;
 22
 4123        internal OutgoingWebResponseContext(OperationContext operationContext)
 24        {
 25            Fx.Assert(operationContext != null, "operationContext is null");
 26
 4127            _operationContext = operationContext;
 4128        }
 29
 30        public long ContentLength
 31        {
 032            get { return long.Parse(MessageProperty.Headers[HttpResponseHeader.ContentLength], CultureInfo.InvariantCult
 033            set { MessageProperty.Headers[HttpResponseHeader.ContentLength] = value.ToString(CultureInfo.InvariantCultur
 34        }
 35
 36        public string ContentType
 37        {
 3038            get { return MessageProperty.Headers[HttpResponseHeader.ContentType]; }
 5039            set { MessageProperty.Headers[HttpResponseHeader.ContentType] = value; }
 40        }
 41
 42        public string ETag
 43        {
 044            get { return MessageProperty.Headers[HttpResponseHeader.ETag]; }
 045            set { MessageProperty.Headers[HttpResponseHeader.ETag] = value; }
 46        }
 47
 148        public WebHeaderCollection Headers => MessageProperty.Headers;
 49
 50        public DateTime LastModified
 51        {
 52            get
 53            {
 054                string dateTime = MessageProperty.Headers[HttpRequestHeader.LastModified];
 055                if (!string.IsNullOrEmpty(dateTime))
 56                {
 057                    if (DateTime.TryParse(dateTime, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime pars
 58                    {
 059                        return parsedDateTime;
 60                    }
 61                }
 62
 063                return DateTime.MinValue;
 64            }
 65            set
 66            {
 067                MessageProperty.Headers[HttpResponseHeader.LastModified] =
 068                    (value.Kind == DateTimeKind.Utc ?
 069                    value.ToString("R", CultureInfo.InvariantCulture) :
 070                    value.ToUniversalTime().ToString("R", CultureInfo.InvariantCulture));
 071            }
 72        }
 73
 74        public string Location
 75        {
 076            get { return MessageProperty.Headers[HttpResponseHeader.Location]; }
 077            set { MessageProperty.Headers[HttpResponseHeader.Location] = value; }
 78        }
 79
 80        public HttpStatusCode StatusCode
 81        {
 082            get { return MessageProperty.StatusCode; }
 283            set { MessageProperty.StatusCode = value; }
 84        }
 85
 86        public string StatusDescription
 87        {
 088            get { return MessageProperty.StatusDescription; }
 089            set { MessageProperty.StatusDescription = value; }
 90        }
 91
 92        public bool SuppressEntityBody
 93        {
 094            get { return MessageProperty.SuppressEntityBody; }
 895            set { MessageProperty.SuppressEntityBody = value; }
 96        }
 97
 98        public WebMessageFormat? Format
 99        {
 100            get
 101            {
 30102                if (!_operationContext.OutgoingMessageProperties.ContainsKey(s_webResponseFormatPropertyName))
 103                {
 30104                    return null;
 105                }
 106
 0107                return _operationContext.OutgoingMessageProperties[s_webResponseFormatPropertyName] as WebMessageFormat?
 108            }
 109            set
 110            {
 0111                if (value.HasValue)
 112                {
 0113                    if (!WebMessageFormatHelper.IsDefined(value.Value))
 114                    {
 0115                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof
 116                    }
 117                    else
 118                    {
 0119                        _operationContext.OutgoingMessageProperties[s_webResponseFormatPropertyName] = value.Value;
 120                    }
 121                }
 122                else
 123                {
 0124                    _operationContext.OutgoingMessageProperties[s_webResponseFormatPropertyName] = null;
 125                }
 126
 0127                AutomatedFormatSelectionContentType = null;
 0128            }
 129        }
 130
 131        // This is an internal property because we need to carry the content-type that was selected by the FormatSelecti
 132        // forward so that the formatter has access to it. However, we dond't want to use the ContentType property on th
 133        // developers would have to clear the ContentType property manually when overriding the format set by the
 134        // FormatSelectingMessageInspector
 135        internal string AutomatedFormatSelectionContentType
 136        {
 137            get
 138            {
 29139                if (!_operationContext.OutgoingMessageProperties.ContainsKey(s_automatedFormatSelectionContentTypeProper
 140                {
 29141                    return null;
 142                }
 0143                return _operationContext.OutgoingMessageProperties[s_automatedFormatSelectionContentTypePropertyName] as
 144            }
 145            set
 146            {
 0147                _operationContext.OutgoingMessageProperties[s_automatedFormatSelectionContentTypePropertyName] = value;
 0148            }
 149        }
 150
 151        public Encoding BindingWriteEncoding
 152        {
 153            get
 154            {
 0155                if (_bindingWriteEncoding == null)
 156                {
 0157                    string endpointId = _operationContext.EndpointDispatcher.Id;
 158                    Fx.Assert(endpointId != null, "There should always be an valid EndpointDispatcher.Id");
 0159                    foreach (ServiceEndpoint endpoint in _operationContext.Host.Description.Endpoints)
 160                    {
 0161                        if (endpoint.Id == endpointId)
 162                        {
 0163                            if (endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>() is Web
 164                            {
 0165                                _bindingWriteEncoding = encodingElement.WriteEncoding;
 166                            }
 167                        }
 168                    }
 169                }
 170
 0171                return _bindingWriteEncoding;
 172            }
 173        }
 174
 175        internal HttpResponseMessageProperty MessageProperty
 176        {
 177            get
 178            {
 65179                if (!_operationContext.OutgoingMessageProperties.ContainsKey(HttpResponseMessageProperty.Name))
 180                {
 34181                    _operationContext.OutgoingMessageProperties.Add(HttpResponseMessageProperty.Name, new HttpResponseMe
 182                }
 183
 65184                return _operationContext.OutgoingMessageProperties[HttpResponseMessageProperty.Name] as HttpResponseMess
 185            }
 186        }
 187
 188        public void SetETag(string entityTag)
 189        {
 0190            ETag = GenerateValidEtagFromString(entityTag);
 0191        }
 192
 193        public void SetETag(int entityTag)
 194        {
 0195            ETag = GenerateValidEtag(entityTag);
 0196        }
 197
 198        public void SetETag(long entityTag)
 199        {
 0200            ETag = GenerateValidEtag(entityTag);
 0201        }
 202
 203        public void SetETag(Guid entityTag)
 204        {
 0205            ETag = GenerateValidEtag(entityTag);
 0206        }
 207
 208        public void SetStatusAsCreated(Uri locationUri)
 209        {
 0210            if (locationUri == null)
 211            {
 0212                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(locationUri));
 213            }
 214
 0215            StatusCode = HttpStatusCode.Created;
 0216            Location = locationUri.ToString();
 0217        }
 218
 219        public void SetStatusAsNotFound()
 220        {
 0221            StatusCode = HttpStatusCode.NotFound;
 0222        }
 223
 224        public void SetStatusAsNotFound(string description)
 225        {
 0226            StatusCode = HttpStatusCode.NotFound;
 0227            StatusDescription = description;
 0228        }
 229
 230        internal static string GenerateValidEtagFromString(string entityTag)
 231        {
 232            // This method will generate a valid entityTag from a string by doing the following:
 233            //   1) Adding surrounding double quotes if the string doesn't already start and end with them
 234            //   2) Escaping any internal double quotes that aren't already escaped (preceded with a backslash)
 235            //   3) If a string starts with a double quote but doesn't end with one, or vice-versa, then the
 236            //      double quote is considered internal and is escaped.
 237
 0238            if (string.IsNullOrEmpty(entityTag))
 239            {
 0240                return null;
 241            }
 242
 0243            if (entityTag.StartsWith("W/\"", StringComparison.OrdinalIgnoreCase) &&
 0244                entityTag.EndsWith("\"", StringComparison.OrdinalIgnoreCase))
 245            {
 0246                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 0247                    SR.Format(SR.WeakEntityTagsNotSupported, entityTag)));
 248            }
 249
 0250            List<int> escapeCharacterInsertIndices = null;
 0251            int lastEtagIndex = entityTag.Length - 1;
 0252            bool startsWithQuote = entityTag[0] == '\"';
 0253            bool endsWithQuote = entityTag[lastEtagIndex] == '\"';
 254
 255            // special case where the entityTag is a single character, a double quote, '"'
 0256            if (lastEtagIndex == 0 && startsWithQuote)
 257            {
 0258                endsWithQuote = false;
 259            }
 260
 0261            bool needsSurroundingQuotes = !startsWithQuote || !endsWithQuote;
 262
 0263            if (startsWithQuote && !endsWithQuote)
 264            {
 0265                if (escapeCharacterInsertIndices == null)
 266                {
 0267                    escapeCharacterInsertIndices = new List<int>();
 268                }
 269
 0270                escapeCharacterInsertIndices.Add(0);
 271            }
 272
 0273            for (int x = 1; x < lastEtagIndex; x++)
 274            {
 0275                if (entityTag[x] == '\"' && entityTag[x - 1] != '\\')
 276                {
 0277                    if (escapeCharacterInsertIndices == null)
 278                    {
 0279                        escapeCharacterInsertIndices = new List<int>();
 280                    }
 281
 0282                    escapeCharacterInsertIndices.Add(x + escapeCharacterInsertIndices.Count);
 283                }
 284            }
 285
 286            // Possible that the ending internal quote is already escaped so must check the character before it
 0287            if (!startsWithQuote && endsWithQuote && entityTag[lastEtagIndex - 1] != '\\')
 288            {
 0289                if (escapeCharacterInsertIndices == null)
 290                {
 0291                    escapeCharacterInsertIndices = new List<int>();
 292                }
 293
 0294                escapeCharacterInsertIndices.Add(lastEtagIndex + escapeCharacterInsertIndices.Count);
 295            }
 296
 0297            if (needsSurroundingQuotes || escapeCharacterInsertIndices != null)
 298            {
 0299                int escapeCharacterInsertIndicesCount = (escapeCharacterInsertIndices == null) ? 0 : escapeCharacterInse
 0300                StringBuilder editedEtag = new StringBuilder(entityTag, entityTag.Length + escapeCharacterInsertIndicesC
 0301                for (int x = 0; x < escapeCharacterInsertIndicesCount; x++)
 302                {
 0303                    editedEtag.Insert(escapeCharacterInsertIndices[x], '\\');
 304                }
 305
 0306                if (needsSurroundingQuotes)
 307                {
 0308                    editedEtag.Insert(entityTag.Length + escapeCharacterInsertIndicesCount, '\"');
 0309                    editedEtag.Insert(0, '\"');
 310                }
 311
 0312                entityTag = editedEtag.ToString();
 313            }
 314
 0315            return entityTag;
 316        }
 317
 0318        internal static string GenerateValidEtag(object entityTag) => string.Format(CultureInfo.InvariantCulture, "\"{0}
 319    }
 320}