File: HttpHeaderCollection.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="HttpHeaderCollection.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 * Collection of headers with write through to IIS for Set, Add, and Remove
 * 
 * Copyright (c) 2000 Microsoft Corporation
 */
 
namespace System.Web {
    using System.Collections;
    using System.Collections.Specialized;
    using System.Globalization;
    using System.Security.Permissions;
    using System.Runtime.Serialization;
    using System.Web.Hosting;
    using System.Web.Util;
    
    [Serializable()]
    internal class HttpHeaderCollection : HttpValueCollection {
        private HttpRequest _request;
        private HttpResponse _response;
        private IIS7WorkerRequest _iis7WorkerRequest;
        
 
        // This constructor creates the header collection for request headers.
        // Try to preallocate the base collection with a size that should be sufficient
        // to store the headers for most requests.
        internal HttpHeaderCollection(HttpWorkerRequest wr, HttpRequest request, int capacity) : base(capacity) {
 
            // if this is an IIS7WorkerRequest, then the collection will be writeable and we will
            // call into IIS7 to update the header blocks when changes are made.
            _iis7WorkerRequest = wr as IIS7WorkerRequest;
 
            _request = request;
        }
 
        // This constructor creates the header collection for response headers.
        // Try to preallocate the base collection with a size that should be sufficient
        // to store the headers for most requests.
        internal HttpHeaderCollection(HttpWorkerRequest wr, HttpResponse response, int capacity) : base(capacity) {
 
            // if this is an IIS7WorkerRequest, then the collection will be writeable and we will
            // call into IIS7 to update the header blocks when changes are made.
            _iis7WorkerRequest = wr as IIS7WorkerRequest;
 
            _response = response;
        }
 
        // This copy constructor is used by the granular request validation feature. Since these collections are immutable
        // once created, it's ok for us to have two collections containing the same data.
        internal HttpHeaderCollection(HttpHeaderCollection col)
            : base(col) {
 
            _request = col._request;
            _response = col._response;
            _iis7WorkerRequest = col._iis7WorkerRequest;
        }
 
        [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
        public override void GetObjectData(SerializationInfo info, StreamingContext context) {
            // WOS 127340: Request.Headers and Response.Headers are no longer serializable
            base.GetObjectData(info, context);
            // create an instance of HttpValueCollection since HttpHeaderCollection is tied to the request
            info.SetType(typeof(HttpValueCollection));
        }
 
        public override void Add(String name, String value) {
            if (_iis7WorkerRequest == null) {
                throw new PlatformNotSupportedException();
            }
            // append to existing value
            SetHeader(name, value, false /*replace*/);
        }
 
        public override void Clear() {
            throw new NotSupportedException();
        }
 
        internal void ClearInternal() {
            // clear is only supported for response headers
            if (_request != null) {
                throw new NotSupportedException();
            }
            base.Clear();
        }
 
        public override void Set(String name, String value) {
            if (_iis7WorkerRequest == null) {
                throw new PlatformNotSupportedException();
            }
            // set new value
            SetHeader(name, value, true /*replace*/);
        }
 
        internal void SetHeader(String name, String value, bool replace) {
            Debug.Assert(_iis7WorkerRequest != null, "_iis7WorkerRequest != null");
 
            if (name == null) {
                throw new ArgumentNullException("name"); 
            }
            
            if (value == null) {
                throw new ArgumentNullException("value");
            }
 
            if (_request != null) {
                _iis7WorkerRequest.SetRequestHeader(name, value, replace);
            }
            else {
                if (_response.HeadersWritten) {
                    throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
                }
 
                // IIS7 integrated pipeline mode needs to call the header encoding routine explicitly since it
                // doesn't go through HttpResponse.WriteHeaders().
                string encodedName = name;
                string encodedValue = value;
                if (HttpRuntime.EnableHeaderChecking) {
                    HttpEncoder.Current.HeaderNameValueEncode(name, value, out encodedName, out encodedValue);
                }
                
                // set the header encoding to the selected encoding
                _iis7WorkerRequest.SetHeaderEncoding(_response.HeaderEncoding);
 
                _iis7WorkerRequest.SetResponseHeader(encodedName, encodedValue, replace);
 
                if (_response.HasCachePolicy && StringUtil.EqualsIgnoreCase("Set-Cookie", name)) {
                    _response.Cache.SetHasSetCookieHeader();
                }
            }
            
            // update managed copy of header
            if (replace) {
                base.Set(name, value);
            }
            else {
                base.Add(name, value);
            }
 
            if (_request != null) {
                // update managed copy of server variable
                string svValue = replace ? value : base.Get(name);
                HttpServerVarsCollection serverVars = _request.ServerVariables as HttpServerVarsCollection;
                if (serverVars != null) {
                    serverVars.SynchronizeServerVariable("HTTP_" + name.ToUpper(CultureInfo.InvariantCulture).Replace('-', '_'), svValue, ensurePopulated: false);
                }
 
                // invalidate Params collection
                _request.InvalidateParams();
            }
        }
 
        // updates managed copy of header with current value from native header block
        internal void SynchronizeHeader(String name, String value) {
            if (name == null) {
                throw new ArgumentNullException("name");
            }
            
            if (value != null) {
                base.Set(name, value);
            }
            else {
                base.Remove(name);
            }
 
            if (_request != null) {
                _request.InvalidateParams();
            }
        }
 
        public override void Remove(String name) {
            if (_iis7WorkerRequest == null) {
                throw new PlatformNotSupportedException();
            }
 
            if (name == null) {
                throw new ArgumentNullException("name");
            }           
 
            if (_request != null) {
                // delete by sending null value
                _iis7WorkerRequest.SetRequestHeader(name, null /*value*/, false /*replace*/);
            }
            else {
                _iis7WorkerRequest.SetResponseHeader(name, null /*value*/, false /*replace*/);
            }
 
            base.Remove(name);
            if (_request != null) {
                // update managed copy of server variable
                HttpServerVarsCollection serverVars = _request.ServerVariables as HttpServerVarsCollection;
                if (serverVars != null) {
                    serverVars.SynchronizeServerVariable("HTTP_" + name.ToUpper(CultureInfo.InvariantCulture).Replace('-', '_'), null, ensurePopulated: false);
                }
 
                // invalidate Params collection
                _request.InvalidateParams();
            }
        }
    }
}