| File: System\Net\WebHeaderCollection.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.WebHeaderCollection\src\System.Net.WebHeaderCollection.csproj (System.Net.WebHeaderCollection) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.Serialization; using System.Text; namespace System.Net { internal enum WebHeaderCollectionType : byte { Unknown, WebRequest, WebResponse } public class WebHeaderCollection : NameValueCollection, ISerializable { private const int ApproxAveHeaderLineSize = 30; private const int ApproxHighAvgNumHeaders = 16; private WebHeaderCollectionType _type; private NameValueCollection? _innerCollection; [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] protected WebHeaderCollection(SerializationInfo serializationInfo, StreamingContext streamingContext) { throw new PlatformNotSupportedException(); } private bool AllowHttpRequestHeader { get { if (_type == WebHeaderCollectionType.Unknown) { _type = WebHeaderCollectionType.WebRequest; } return _type == WebHeaderCollectionType.WebRequest; } } private static HeaderInfoTable HeaderInfo => field ??= new HeaderInfoTable(); private NameValueCollection InnerCollection => _innerCollection ??= new NameValueCollection(ApproxHighAvgNumHeaders, StringComparer.OrdinalIgnoreCase); private bool AllowHttpResponseHeader { get { if (_type == WebHeaderCollectionType.Unknown) { _type = WebHeaderCollectionType.WebResponse; } return _type == WebHeaderCollectionType.WebResponse; } } public string? this[HttpRequestHeader header] { get { if (!AllowHttpRequestHeader) { throw new InvalidOperationException(SR.net_headers_req); } return this[header.GetName()]; } set { if (!AllowHttpRequestHeader) { throw new InvalidOperationException(SR.net_headers_req); } this[header.GetName()] = value; } } public string? this[HttpResponseHeader header] { get { if (!AllowHttpResponseHeader) { throw new InvalidOperationException(SR.net_headers_rsp); } return this[header.GetName()]; } set { if (!AllowHttpResponseHeader) { throw new InvalidOperationException(SR.net_headers_rsp); } this[header.GetName()] = value; } } #pragma warning disable CS8765 // Nullability of parameter 'name' doesn't match overridden member public override void Set(string name, string? value) #pragma warning restore CS8765 { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } name = HttpValidationHelpers.CheckBadHeaderNameChars(name); value = HttpValidationHelpers.CheckBadHeaderValueChars(value); InvalidateCachedArrays(); InnerCollection.Set(name, value); } public void Set(HttpRequestHeader header, string? value) { if (!AllowHttpRequestHeader) { throw new InvalidOperationException(SR.net_headers_req); } this.Set(header.GetName(), value); } public void Set(HttpResponseHeader header, string? value) { if (!AllowHttpResponseHeader) { throw new InvalidOperationException(SR.net_headers_rsp); } this.Set(header.GetName(), value); } [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { throw new PlatformNotSupportedException(); } void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { throw new PlatformNotSupportedException(); } public void Remove(HttpRequestHeader header) { if (!AllowHttpRequestHeader) { throw new InvalidOperationException(SR.net_headers_req); } this.Remove(header.GetName()); } public void Remove(HttpResponseHeader header) { if (!AllowHttpResponseHeader) { throw new InvalidOperationException(SR.net_headers_rsp); } this.Remove(header.GetName()); } public override void OnDeserialization(object? sender) { // Nop in desktop } public static bool IsRestricted(string headerName) { return IsRestricted(headerName, false); } public static bool IsRestricted(string headerName, bool response) { headerName = HttpValidationHelpers.CheckBadHeaderNameChars(headerName); return response ? HeaderInfo[headerName].IsResponseRestricted : HeaderInfo[headerName].IsRequestRestricted; } public override string[]? GetValues(int index) { return InnerCollection.GetValues(index); } // GetValues // Routine Description: // This method takes a header name and returns a string array representing // the individual values for that headers. For example, if the headers // contained the line Accept: text/plain, text/html then // GetValues("Accept") would return an array of two strings: "text/plain" // and "text/html". // Arguments: // header - Name of the header. // Return Value: // string[] - array of parsed string objects #pragma warning disable CS8765 // Nullability of parameter 'header' doesn't match overridden member public override string[]? GetValues(string header) #pragma warning restore CS8765 { // First get the information about the header and the values for // the header. HeaderInfo info = HeaderInfo[header!]; string[]? values = InnerCollection.GetValues(header); // If we have no information about the header or it doesn't allow // multiple values, just return the values. if (info == null || values == null || !info.AllowMultiValues) { return values; } // Here we have a multi value header. We need to go through // each entry in the multi values array, and if an entry itself // has multiple values we'll need to combine those in. // // We do some optimazation here, where we try not to copy the // values unless there really is one that have multiple values. string[] tempValues; List<string>? valueList = null; for (int i = 0; i < values.Length; i++) { // Parse this value header. tempValues = info.Parser(values[i]); // If we don't have an array list yet, see if this // value has multiple values. if (valueList == null) { // If it's not empty, replace valueList. // Because for invalid WebRequest headers, we will return empty // valueList instead of the default NameValueCollection.GetValues(). if (tempValues != null) { // It does, so we need to create an array list that // represents the Values, then trim out this one and // the ones after it that haven't been parsed yet. valueList = new List<string>(values); valueList.RemoveRange(i, values.Length - i); valueList.AddRange(tempValues); } } else { // We already have an List, so just add the values. valueList.AddRange(tempValues); } } // See if we have an List. If we don't, just return the values. // Otherwise convert the List to a string array and return that. if (valueList != null) { return valueList.ToArray(); } return values; } public override string GetKey(int index) { return InnerCollection.GetKey(index)!; } public override void Clear() { InvalidateCachedArrays(); _innerCollection?.Clear(); } public override string? Get(int index) { if (_innerCollection == null) { return null; } return _innerCollection.Get(index); } public override string? Get(string? name) { if (_innerCollection == null) { return null; } return _innerCollection.Get(name); } public void Add(HttpRequestHeader header, string? value) { if (!AllowHttpRequestHeader) { throw new InvalidOperationException(SR.net_headers_req); } this.Add(header.GetName(), value); } public void Add(HttpResponseHeader header, string? value) { if (!AllowHttpResponseHeader) { throw new InvalidOperationException(SR.net_headers_rsp); } this.Add(header.GetName(), value); } public void Add(string header) { if (string.IsNullOrEmpty(header)) { throw new ArgumentNullException(nameof(header)); } int colpos = header.IndexOf(':'); // check for badly formed header passed in if (colpos < 0) { throw new ArgumentException(SR.net_WebHeaderMissingColon, nameof(header)); } string name = header.Substring(0, colpos); string value = header.Substring(colpos + 1); name = HttpValidationHelpers.CheckBadHeaderNameChars(name); value = HttpValidationHelpers.CheckBadHeaderValueChars(value); InvalidateCachedArrays(); InnerCollection.Add(name, value); } #pragma warning disable CS8765 // Nullability of parameter 'name' doesn't match overridden member public override void Add(string name, string? value) #pragma warning restore CS8765 { ArgumentException.ThrowIfNullOrEmpty(name); name = HttpValidationHelpers.CheckBadHeaderNameChars(name); value = HttpValidationHelpers.CheckBadHeaderValueChars(value); InvalidateCachedArrays(); InnerCollection.Add(name, value); } protected void AddWithoutValidate(string headerName, string? headerValue) { headerName = HttpValidationHelpers.CheckBadHeaderNameChars(headerName); headerValue = HttpValidationHelpers.CheckBadHeaderValueChars(headerValue); InvalidateCachedArrays(); InnerCollection.Add(headerName, headerValue); } // Remove - // Routine Description: // Removes give header with validation to see if they are "proper" headers. // If the header is a special header, listed in RestrictedHeaders object, // then this call will cause an exception indicating as such. // Arguments: // name - header-name to remove // Return Value: // None /// <devdoc> /// <para>Removes the specified header.</para> /// </devdoc> #pragma warning disable CS8765 // Nullability of parameter 'name' doesn't match overridden member public override void Remove(string name) #pragma warning restore CS8765 { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } name = HttpValidationHelpers.CheckBadHeaderNameChars(name); if (_innerCollection != null) { InvalidateCachedArrays(); _innerCollection.Remove(name); } } // ToString() - // Routine Description: // Generates a string representation of the headers, that is ready to be sent except for it being in string format: // the format looks like: // // Header-Name: Header-Value\r\n // Header-Name2: Header-Value2\r\n // ... // Header-NameN: Header-ValueN\r\n // \r\n // // Uses the string builder class to Append the elements together. // Arguments: // None. // Return Value: // string public override string ToString() { if (Count == 0) { return "\r\n"; } var sb = new StringBuilder(ApproxAveHeaderLineSize * Count); foreach (string? key in InnerCollection) { string? val = InnerCollection.Get(key); sb.Append(key) .Append(": ") .Append(val) .Append("\r\n"); } sb.Append("\r\n"); return sb.ToString(); } public byte[] ToByteArray() { string tempString = this.ToString(); return System.Text.Encoding.ASCII.GetBytes(tempString); } public WebHeaderCollection() { } public override int Count { get { return (_innerCollection == null ? 0 : _innerCollection.Count); } } public override KeysCollection Keys { get { return InnerCollection.Keys; } } public override string[] AllKeys { get { return InnerCollection.AllKeys!; } } public override IEnumerator GetEnumerator() { return InnerCollection.Keys.GetEnumerator(); } } }