| | | 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 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Collections.Generic; |
| | | 6 | | using System.Net.Mime; |
| | | 7 | | using System.Text; |
| | | 8 | | using CoreWCF.Runtime; |
| | | 9 | | |
| | | 10 | | namespace CoreWCF.Web |
| | | 11 | | { |
| | | 12 | | internal static class Utility |
| | | 13 | | { |
| | | 14 | | public const string ApplicationXml = "application/xml"; |
| | | 15 | | public const string TextXml = "text/xml"; |
| | | 16 | | public const string ApplicationJson = "application/json"; |
| | | 17 | | public const string TextJson = "text/json"; |
| | | 18 | | public const string GET = "GET"; |
| | | 19 | | |
| | | 20 | | public static bool IsXmlContent(this string contentType) |
| | | 21 | | { |
| | 0 | 22 | | if (contentType == null) |
| | | 23 | | { |
| | 0 | 24 | | return true; |
| | | 25 | | } |
| | | 26 | | |
| | 0 | 27 | | string contentTypeProcessed = contentType.Trim(); |
| | | 28 | | |
| | 0 | 29 | | return contentTypeProcessed.StartsWith(ApplicationXml, StringComparison.OrdinalIgnoreCase) |
| | 0 | 30 | | || contentTypeProcessed.StartsWith(TextXml, StringComparison.OrdinalIgnoreCase); |
| | | 31 | | } |
| | | 32 | | |
| | | 33 | | public static bool IsJsonContent(this string contentType) |
| | | 34 | | { |
| | 0 | 35 | | if (contentType == null) |
| | | 36 | | { |
| | 0 | 37 | | return true; |
| | | 38 | | } |
| | | 39 | | |
| | 0 | 40 | | string contentTypeProcessed = contentType.Trim(); |
| | | 41 | | |
| | 0 | 42 | | return contentTypeProcessed.StartsWith(ApplicationJson, StringComparison.OrdinalIgnoreCase) |
| | 0 | 43 | | || contentTypeProcessed.StartsWith(TextJson, StringComparison.OrdinalIgnoreCase); |
| | | 44 | | } |
| | | 45 | | |
| | | 46 | | public static string CombineUri(string former, string latter) |
| | | 47 | | { |
| | | 48 | | // Appending the latter string to the form string, |
| | | 49 | | // while making sure there is a single slash char seperating the latter and the former. |
| | | 50 | | // This method behaves differently than new Uri(baseUri, relativeUri) |
| | | 51 | | // as CombineUri simply appends, whereas new Uri() actually replaces the last segment |
| | | 52 | | // of the its base path with the relative uri. |
| | | 53 | | |
| | 0 | 54 | | var builder = new StringBuilder(); |
| | 0 | 55 | | if (former.Length > 0 && latter.Length > 0) |
| | | 56 | | { |
| | 0 | 57 | | if (former[former.Length - 1] == '/' && latter[0] == '/') |
| | | 58 | | { |
| | 0 | 59 | | builder.Append(former, 0, former.Length - 1); |
| | 0 | 60 | | builder.Append(latter); |
| | 0 | 61 | | return builder.ToString(); |
| | | 62 | | } |
| | | 63 | | |
| | 0 | 64 | | if (former[former.Length - 1] != '/' && latter[0] != '/') |
| | | 65 | | { |
| | 0 | 66 | | builder.Append(former); |
| | 0 | 67 | | builder.Append('/'); |
| | 0 | 68 | | builder.Append(latter); |
| | 0 | 69 | | return builder.ToString(); |
| | | 70 | | } |
| | | 71 | | } |
| | | 72 | | |
| | 0 | 73 | | return former + latter; |
| | | 74 | | } |
| | | 75 | | |
| | | 76 | | public static List<string> QuoteAwareStringSplit(string str) |
| | | 77 | | { |
| | 0 | 78 | | List<string> subStrings = new List<string>(); |
| | 0 | 79 | | int offset = 0; |
| | 0 | 80 | | while (true) |
| | | 81 | | { |
| | 0 | 82 | | string subString = QuoteAwareSubString(str, ref offset); |
| | 0 | 83 | | if (subString == null) |
| | | 84 | | { |
| | | 85 | | break; |
| | | 86 | | } |
| | 0 | 87 | | subStrings.Add(subString); |
| | | 88 | | } |
| | | 89 | | |
| | 0 | 90 | | return subStrings; |
| | | 91 | | } |
| | | 92 | | |
| | | 93 | | // This method extracts substrings from a string starting at the offset |
| | | 94 | | // and up until the next comma in the string. The sub string extraction is |
| | | 95 | | // quote aware such that commas inside quoted-strings are ignored. On return, |
| | | 96 | | // offset points to the next char beyond the comma of the substring returned |
| | | 97 | | // and may point beyond the length of the header. |
| | | 98 | | public static string QuoteAwareSubString(string str, ref int offset) |
| | | 99 | | { |
| | | 100 | | // this method will filter out empty-string and white-space-only items in |
| | | 101 | | // the header. For example "x,,y" and "x, ,y" would result in just "x" and "y" |
| | | 102 | | // substrings being returned. |
| | | 103 | | |
| | 0 | 104 | | if (string.IsNullOrEmpty(str) || offset >= str.Length) |
| | | 105 | | { |
| | 0 | 106 | | return null; |
| | | 107 | | } |
| | | 108 | | |
| | 0 | 109 | | int startIndex = (offset > 0) ? offset : 0; |
| | | 110 | | |
| | | 111 | | // trim whitespace and commas from the begining of the item |
| | 0 | 112 | | while (char.IsWhiteSpace(str[startIndex]) || str[startIndex] == ',') |
| | | 113 | | { |
| | 0 | 114 | | startIndex++; |
| | 0 | 115 | | if (startIndex >= str.Length) |
| | | 116 | | { |
| | 0 | 117 | | return null; |
| | | 118 | | } |
| | | 119 | | } |
| | | 120 | | |
| | 0 | 121 | | int endIndex = startIndex; |
| | 0 | 122 | | bool insideQuotes = false; |
| | | 123 | | |
| | 0 | 124 | | while (endIndex < str.Length) |
| | | 125 | | { |
| | 0 | 126 | | if (str[endIndex] == '\"' && |
| | 0 | 127 | | (!insideQuotes || endIndex == 0 || str[endIndex - 1] != '\\')) |
| | | 128 | | { |
| | 0 | 129 | | insideQuotes = !insideQuotes; |
| | | 130 | | } |
| | 0 | 131 | | else if (str[endIndex] == ',' && !insideQuotes) |
| | | 132 | | { |
| | | 133 | | break; |
| | | 134 | | } |
| | 0 | 135 | | endIndex++; |
| | | 136 | | } |
| | 0 | 137 | | offset = endIndex + 1; |
| | | 138 | | |
| | | 139 | | // trim whitespace from the end of the item; the substring is guaranteed to |
| | | 140 | | // have at least one non-whitespace character |
| | 0 | 141 | | while (char.IsWhiteSpace(str[endIndex - 1])) |
| | | 142 | | { |
| | 0 | 143 | | endIndex--; |
| | | 144 | | } |
| | | 145 | | |
| | 0 | 146 | | return str.Substring(startIndex, endIndex - startIndex); |
| | | 147 | | } |
| | | 148 | | |
| | | 149 | | public static ContentType GetContentType(string contentType) |
| | | 150 | | { |
| | 0 | 151 | | string contentTypeTrimmed = contentType.Trim(); |
| | 0 | 152 | | if (!string.IsNullOrEmpty(contentTypeTrimmed)) |
| | | 153 | | { |
| | 0 | 154 | | return GetContentTypeOrNull(contentTypeTrimmed); |
| | | 155 | | } |
| | | 156 | | |
| | 0 | 157 | | return null; |
| | | 158 | | } |
| | | 159 | | |
| | | 160 | | public static ContentType GetContentTypeOrNull(string contentType) |
| | | 161 | | { |
| | | 162 | | try |
| | | 163 | | { |
| | | 164 | | Fx.Assert(contentType == contentType.Trim(), "The ContentType input argument should already be trimmed." |
| | | 165 | | Fx.Assert(!string.IsNullOrEmpty(contentType), "The ContentType input argument should not be null or empt |
| | | 166 | | |
| | 0 | 167 | | ContentType contentTypeToReturn = new ContentType(contentType); |
| | | 168 | | |
| | | 169 | | // Need to check for "*/<Something-other-than-*>" because the ContentType constructor doesn't catch this |
| | 0 | 170 | | string[] typeAndSubType = contentTypeToReturn.MediaType.Split('/'); |
| | | 171 | | Fx.Assert(typeAndSubType.Length == 2, "The creation of the ContentType would have failed if there wasn't |
| | 0 | 172 | | if (typeAndSubType[0][0] == '*' && typeAndSubType[0].Length == 1 && |
| | 0 | 173 | | !(typeAndSubType[1][0] == '*' && typeAndSubType[1].Length == 1)) |
| | | 174 | | { |
| | | 175 | | // |
| | | 176 | | |
| | | 177 | | |
| | | 178 | | |
| | | 179 | | // throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new FormatException( |
| | | 180 | | // SR2.GetString(SR2.InvalidContentType, contentType))); |
| | 0 | 181 | | return null; |
| | | 182 | | } |
| | 0 | 183 | | return contentTypeToReturn; |
| | | 184 | | } |
| | 0 | 185 | | catch (FormatException) |
| | | 186 | | { |
| | | 187 | | // Return null to indicate that the content type creation failed |
| | | 188 | | //System.ServiceModel.DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); |
| | 0 | 189 | | } |
| | | 190 | | |
| | 0 | 191 | | return null; |
| | 0 | 192 | | } |
| | | 193 | | |
| | | 194 | | public static string IEnumerableToCommaSeparatedString(IEnumerable<string> items) |
| | | 195 | | { |
| | | 196 | | Fx.Assert(items != null, "The 'items' argument should never be null."); |
| | | 197 | | |
| | 0 | 198 | | return string.Join(", ", items); |
| | | 199 | | } |
| | | 200 | | |
| | | 201 | | public static void AddRange<T>(ICollection<T> list, IEnumerable<T> itemsToAdd) |
| | | 202 | | { |
| | | 203 | | Fx.Assert(list != null, "The 'list' argument should never be null."); |
| | | 204 | | Fx.Assert(itemsToAdd != null, "The 'itemsToAdd' argument should never be null."); |
| | | 205 | | |
| | 0 | 206 | | foreach (T item in itemsToAdd) |
| | | 207 | | { |
| | 0 | 208 | | list.Add(item); |
| | | 209 | | } |
| | 0 | 210 | | } |
| | | 211 | | } |
| | | 212 | | } |