OLD | NEW |
(Empty) | |
| 1 #region Copyright notice and license |
| 2 // Copyright 2015-2016, Google Inc. |
| 3 // All rights reserved. |
| 4 // |
| 5 // Redistribution and use in source and binary forms, with or without |
| 6 // modification, are permitted provided that the following conditions are |
| 7 // met: |
| 8 // |
| 9 // * Redistributions of source code must retain the above copyright |
| 10 // notice, this list of conditions and the following disclaimer. |
| 11 // * Redistributions in binary form must reproduce the above |
| 12 // copyright notice, this list of conditions and the following disclaimer |
| 13 // in the documentation and/or other materials provided with the |
| 14 // distribution. |
| 15 // * Neither the name of Google Inc. nor the names of its |
| 16 // contributors may be used to endorse or promote products derived from |
| 17 // this software without specific prior written permission. |
| 18 // |
| 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 #endregion |
| 31 |
| 32 using System; |
| 33 using System.Collections; |
| 34 using System.Collections.Generic; |
| 35 using System.Collections.Specialized; |
| 36 using System.Globalization; |
| 37 using System.Runtime.InteropServices; |
| 38 using System.Text; |
| 39 using System.Text.RegularExpressions; |
| 40 |
| 41 using Grpc.Core.Utils; |
| 42 |
| 43 namespace Grpc.Core |
| 44 { |
| 45 /// <summary> |
| 46 /// A collection of metadata entries that can be exchanged during a call. |
| 47 /// gRPC supports these types of metadata: |
| 48 /// <list type="bullet"> |
| 49 /// <item><term>Request headers</term><description>are sent by the client at
the beginning of a remote call before any request messages are sent.</descripti
on></item> |
| 50 /// <item><term>Response headers</term><description>are sent by the server a
t the beginning of a remote call handler before any response messages are sent.<
/description></item> |
| 51 /// <item><term>Response trailers</term><description>are sent by the server
at the end of a remote call along with resulting call status.</description></ite
m> |
| 52 /// </list> |
| 53 /// </summary> |
| 54 public sealed class Metadata : IList<Metadata.Entry> |
| 55 { |
| 56 /// <summary> |
| 57 /// All binary headers should have this suffix. |
| 58 /// </summary> |
| 59 public const string BinaryHeaderSuffix = "-bin"; |
| 60 |
| 61 /// <summary> |
| 62 /// An read-only instance of metadata containing no entries. |
| 63 /// </summary> |
| 64 public static readonly Metadata Empty = new Metadata().Freeze(); |
| 65 |
| 66 readonly List<Entry> entries; |
| 67 bool readOnly; |
| 68 |
| 69 /// <summary> |
| 70 /// Initializes a new instance of <c>Metadata</c>. |
| 71 /// </summary> |
| 72 public Metadata() |
| 73 { |
| 74 this.entries = new List<Entry>(); |
| 75 } |
| 76 |
| 77 /// <summary> |
| 78 /// Makes this object read-only. |
| 79 /// </summary> |
| 80 /// <returns>this object</returns> |
| 81 internal Metadata Freeze() |
| 82 { |
| 83 this.readOnly = true; |
| 84 return this; |
| 85 } |
| 86 |
| 87 // TODO: add support for access by key |
| 88 |
| 89 #region IList members |
| 90 |
| 91 public int IndexOf(Metadata.Entry item) |
| 92 { |
| 93 return entries.IndexOf(item); |
| 94 } |
| 95 |
| 96 public void Insert(int index, Metadata.Entry item) |
| 97 { |
| 98 CheckWriteable(); |
| 99 entries.Insert(index, item); |
| 100 } |
| 101 |
| 102 public void RemoveAt(int index) |
| 103 { |
| 104 CheckWriteable(); |
| 105 entries.RemoveAt(index); |
| 106 } |
| 107 |
| 108 public Metadata.Entry this[int index] |
| 109 { |
| 110 get |
| 111 { |
| 112 return entries[index]; |
| 113 } |
| 114 |
| 115 set |
| 116 { |
| 117 CheckWriteable(); |
| 118 entries[index] = value; |
| 119 } |
| 120 } |
| 121 |
| 122 public void Add(Metadata.Entry item) |
| 123 { |
| 124 CheckWriteable(); |
| 125 entries.Add(item); |
| 126 } |
| 127 |
| 128 public void Add(string key, string value) |
| 129 { |
| 130 Add(new Entry(key, value)); |
| 131 } |
| 132 |
| 133 public void Add(string key, byte[] valueBytes) |
| 134 { |
| 135 Add(new Entry(key, valueBytes)); |
| 136 } |
| 137 |
| 138 public void Clear() |
| 139 { |
| 140 CheckWriteable(); |
| 141 entries.Clear(); |
| 142 } |
| 143 |
| 144 public bool Contains(Metadata.Entry item) |
| 145 { |
| 146 return entries.Contains(item); |
| 147 } |
| 148 |
| 149 public void CopyTo(Metadata.Entry[] array, int arrayIndex) |
| 150 { |
| 151 entries.CopyTo(array, arrayIndex); |
| 152 } |
| 153 |
| 154 public int Count |
| 155 { |
| 156 get { return entries.Count; } |
| 157 } |
| 158 |
| 159 public bool IsReadOnly |
| 160 { |
| 161 get { return readOnly; } |
| 162 } |
| 163 |
| 164 public bool Remove(Metadata.Entry item) |
| 165 { |
| 166 CheckWriteable(); |
| 167 return entries.Remove(item); |
| 168 } |
| 169 |
| 170 public IEnumerator<Metadata.Entry> GetEnumerator() |
| 171 { |
| 172 return entries.GetEnumerator(); |
| 173 } |
| 174 |
| 175 IEnumerator System.Collections.IEnumerable.GetEnumerator() |
| 176 { |
| 177 return entries.GetEnumerator(); |
| 178 } |
| 179 |
| 180 private void CheckWriteable() |
| 181 { |
| 182 GrpcPreconditions.CheckState(!readOnly, "Object is read only"); |
| 183 } |
| 184 |
| 185 #endregion |
| 186 |
| 187 /// <summary> |
| 188 /// Metadata entry |
| 189 /// </summary> |
| 190 public struct Entry |
| 191 { |
| 192 private static readonly Encoding Encoding = Encoding.ASCII; |
| 193 private static readonly Regex ValidKeyRegex = new Regex("^[a-z0-9_-]
+$"); |
| 194 |
| 195 readonly string key; |
| 196 readonly string value; |
| 197 readonly byte[] valueBytes; |
| 198 |
| 199 private Entry(string key, string value, byte[] valueBytes) |
| 200 { |
| 201 this.key = key; |
| 202 this.value = value; |
| 203 this.valueBytes = valueBytes; |
| 204 } |
| 205 |
| 206 /// <summary> |
| 207 /// Initializes a new instance of the <see cref="Grpc.Core.Metadata.
Entry"/> struct with a binary value. |
| 208 /// </summary> |
| 209 /// <param name="key">Metadata key, needs to have suffix indicating
a binary valued metadata entry.</param> |
| 210 /// <param name="valueBytes">Value bytes.</param> |
| 211 public Entry(string key, byte[] valueBytes) |
| 212 { |
| 213 this.key = NormalizeKey(key); |
| 214 GrpcPreconditions.CheckArgument(this.key.EndsWith(BinaryHeaderSu
ffix), |
| 215 "Key for binary valued metadata entry needs to have suffix i
ndicating binary value."); |
| 216 this.value = null; |
| 217 GrpcPreconditions.CheckNotNull(valueBytes, "valueBytes"); |
| 218 this.valueBytes = new byte[valueBytes.Length]; |
| 219 Buffer.BlockCopy(valueBytes, 0, this.valueBytes, 0, valueBytes.L
ength); // defensive copy to guarantee immutability |
| 220 } |
| 221 |
| 222 /// <summary> |
| 223 /// Initializes a new instance of the <see cref="Grpc.Core.Metadata.
Entry"/> struct holding an ASCII value. |
| 224 /// </summary> |
| 225 /// <param name="key">Metadata key, must not use suffix indicating a
binary valued metadata entry.</param> |
| 226 /// <param name="value">Value string. Only ASCII characters are allo
wed.</param> |
| 227 public Entry(string key, string value) |
| 228 { |
| 229 this.key = NormalizeKey(key); |
| 230 GrpcPreconditions.CheckArgument(!this.key.EndsWith(BinaryHeaderS
uffix), |
| 231 "Key for ASCII valued metadata entry cannot have suffix indi
cating binary value."); |
| 232 this.value = GrpcPreconditions.CheckNotNull(value, "value"); |
| 233 this.valueBytes = null; |
| 234 } |
| 235 |
| 236 /// <summary> |
| 237 /// Gets the metadata entry key. |
| 238 /// </summary> |
| 239 public string Key |
| 240 { |
| 241 get |
| 242 { |
| 243 return this.key; |
| 244 } |
| 245 } |
| 246 |
| 247 /// <summary> |
| 248 /// Gets the binary value of this metadata entry. |
| 249 /// </summary> |
| 250 public byte[] ValueBytes |
| 251 { |
| 252 get |
| 253 { |
| 254 if (valueBytes == null) |
| 255 { |
| 256 return Encoding.GetBytes(value); |
| 257 } |
| 258 |
| 259 // defensive copy to guarantee immutability |
| 260 var bytes = new byte[valueBytes.Length]; |
| 261 Buffer.BlockCopy(valueBytes, 0, bytes, 0, valueBytes.Length)
; |
| 262 return bytes; |
| 263 } |
| 264 } |
| 265 |
| 266 /// <summary> |
| 267 /// Gets the string value of this metadata entry. |
| 268 /// </summary> |
| 269 public string Value |
| 270 { |
| 271 get |
| 272 { |
| 273 GrpcPreconditions.CheckState(!IsBinary, "Cannot access strin
g value of a binary metadata entry"); |
| 274 return value ?? Encoding.GetString(valueBytes); |
| 275 } |
| 276 } |
| 277 |
| 278 /// <summary> |
| 279 /// Returns <c>true</c> if this entry is a binary-value entry. |
| 280 /// </summary> |
| 281 public bool IsBinary |
| 282 { |
| 283 get |
| 284 { |
| 285 return value == null; |
| 286 } |
| 287 } |
| 288 |
| 289 /// <summary> |
| 290 /// Returns a <see cref="System.String"/> that represents the curren
t <see cref="Grpc.Core.Metadata.Entry"/>. |
| 291 /// </summary> |
| 292 public override string ToString() |
| 293 { |
| 294 if (IsBinary) |
| 295 { |
| 296 return string.Format("[Entry: key={0}, valueBytes={1}]", key
, valueBytes); |
| 297 } |
| 298 |
| 299 return string.Format("[Entry: key={0}, value={1}]", key, value); |
| 300 } |
| 301 |
| 302 /// <summary> |
| 303 /// Gets the serialized value for this entry. For binary metadata en
tries, this leaks |
| 304 /// the internal <c>valueBytes</c> byte array and caller must not ch
ange contents of it. |
| 305 /// </summary> |
| 306 internal byte[] GetSerializedValueUnsafe() |
| 307 { |
| 308 return valueBytes ?? Encoding.GetBytes(value); |
| 309 } |
| 310 |
| 311 /// <summary> |
| 312 /// Creates a binary value or ascii value metadata entry from data r
eceived from the native layer. |
| 313 /// We trust C core to give us well-formed data, so we don't perform
any checks or defensive copying. |
| 314 /// </summary> |
| 315 internal static Entry CreateUnsafe(string key, byte[] valueBytes) |
| 316 { |
| 317 if (key.EndsWith(BinaryHeaderSuffix)) |
| 318 { |
| 319 return new Entry(key, null, valueBytes); |
| 320 } |
| 321 return new Entry(key, Encoding.GetString(valueBytes), null); |
| 322 } |
| 323 |
| 324 private static string NormalizeKey(string key) |
| 325 { |
| 326 var normalized = GrpcPreconditions.CheckNotNull(key, "key").ToLo
wer(CultureInfo.InvariantCulture); |
| 327 GrpcPreconditions.CheckArgument(ValidKeyRegex.IsMatch(normalized
), |
| 328 "Metadata entry key not valid. Keys can only contain lowerca
se alphanumeric characters, underscores and hyphens."); |
| 329 return normalized; |
| 330 } |
| 331 } |
| 332 } |
| 333 } |
OLD | NEW |