Welcome to mirror list, hosted at ThFree Co, Russian Federation.

HttpCookieCollection.cs « System.Web « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 8858577a9a78813da90a56e84198e774070ec760 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
//------------------------------------------------------------------------------
// <copyright file="HttpCookieCollection.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------

/*
 * Collection of Http cookies for request and response intrinsics
 * 
 * Copyright (c) 1998 Microsoft Corporation
 */

namespace System.Web {
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Linq;
    using System.Web.Util;

    /// <devdoc>
    ///    <para>
    ///       Provides a type-safe
    ///       way to manipulate HTTP cookies.
    ///    </para>
    /// </devdoc>
    public sealed class HttpCookieCollection : NameObjectCollectionBase {
        // Response object to notify about changes in collection
        private HttpResponse _response;

        // cached All[] arrays
        private HttpCookie[] _all;
        private String[] _allKeys;
        private bool    _changed;

        // for implementing granular request validation
        private ValidateStringCallback _validationCallback;
        private HashSet<string> _keysAwaitingValidation;

        internal HttpCookieCollection(HttpResponse response, bool readOnly)
            : base(StringComparer.OrdinalIgnoreCase)  {
            _response = response;
            IsReadOnly = readOnly;
        }


        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the HttpCookieCollection
        ///       class.
        ///    </para>
        /// </devdoc>
        public HttpCookieCollection(): base(StringComparer.OrdinalIgnoreCase)  {
        }

        // This copy constructor is used by the granular request validation feature. The collections are mutable once
        // created, but nobody should ever be mutating them, so it's ok for these to be out of [....]. Additionally,
        // we don't copy _response since this should only ever be called for the request cookies.
        internal HttpCookieCollection(HttpCookieCollection col)
            : base(StringComparer.OrdinalIgnoreCase) {

            // We explicitly don't copy validation-related fields, as we want the copy to "reset" validation state.

            // Copy the file references from the original collection into this instance
            for (int i = 0; i < col.Count; i++) {
                ThrowIfMaxHttpCollectionKeysExceeded();
                string key = col.BaseGetKey(i);
                object value = col.BaseGet(i);
                BaseAdd(key, value);
            }

            IsReadOnly = col.IsReadOnly;
        }

        internal bool Changed {
            get { return _changed; }
            set { _changed = value; }
        }
        internal void AddCookie(HttpCookie cookie, bool append) {
            ThrowIfMaxHttpCollectionKeysExceeded();

            _all = null;
            _allKeys = null;

            if (append) {
                // DevID 251951	Cookie is getting duplicated by ASP.NET when they are added via a native module
                // Need to not double add response cookies from native modules
                if (!cookie.FromHeader) {
                    // mark cookie as new
                    cookie.Added = true;
                }
                BaseAdd(cookie.Name, cookie);
            }
            else {
                if (BaseGet(cookie.Name) != null) {                   
                    // mark the cookie as changed because we are overriding the existing one
                    cookie.Changed = true;
                }
                BaseSet(cookie.Name, cookie);
            }
        }

        // MSRC 12038: limit the maximum number of items that can be added to the collection,
        // as a large number of items potentially can result in too many hash collisions that may cause DoS
        private void ThrowIfMaxHttpCollectionKeysExceeded() {
            if (Count >= AppSettings.MaxHttpCollectionKeys) {
                throw new InvalidOperationException(SR.GetString(SR.CollectionCountExceeded_HttpValueCollection, AppSettings.MaxHttpCollectionKeys));
            }
        }

        internal void EnableGranularValidation(ValidateStringCallback validationCallback) {
            // Iterate over all the keys, adding each to the set containing the keys awaiting validation.
            // Unlike dictionaries, HashSet<T> can contain null keys, so don't need to special-case them.
            _keysAwaitingValidation = new HashSet<string>(Keys.Cast<string>(), StringComparer.OrdinalIgnoreCase);
            _validationCallback = validationCallback;
        }

        private void EnsureKeyValidated(string key, string value) {
            if (_keysAwaitingValidation == null) {
                // If dynamic validation hasn't been enabled, no-op.
                return;
            }

            if (!_keysAwaitingValidation.Contains(key)) {
                // If this key has already been validated (or is excluded), no-op.
                return;
            }

            // If validation fails, the callback will throw an exception. If validation succeeds,
            // we can remove it from the candidates list. A note:
            // - Eager validation skips null/empty values, so we should, also.
            if (!String.IsNullOrEmpty(value)) {
                _validationCallback(key, value);
            }
            _keysAwaitingValidation.Remove(key);
        }

        internal void MakeReadOnly() {
            IsReadOnly = true;
        }

        internal void RemoveCookie(String name) {
            _all = null;
            _allKeys = null;

            BaseRemove(name);

            _changed = true;
        }

        internal void Reset() {
            _all = null;
            _allKeys = null;

            BaseClear();
            _changed = true;
            _keysAwaitingValidation = null;
        }

        //
        //  Public APIs to add / remove
        //


        /// <devdoc>
        ///    <para>
        ///       Adds a cookie to the collection.
        ///    </para>
        /// </devdoc>
        public void Add(HttpCookie cookie) {
            if (_response != null)
                _response.BeforeCookieCollectionChange();

            AddCookie(cookie, true);

            if (_response != null)
                _response.OnCookieAdd(cookie);
        }
        

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public void CopyTo(Array dest, int index) {
            if (_all == null) {
                int n = Count;
                HttpCookie[] all = new HttpCookie[n];

                for (int i = 0; i < n; i++)
                    all[i] = Get(i);

                _all = all; // wait until end of loop to set _all reference in case Get throws
            }
            _all.CopyTo(dest, index);
        }


        /// <devdoc>
        ///    <para> Updates the value of a cookie.</para>
        /// </devdoc>
        public void Set(HttpCookie cookie) {
            if (_response != null)
                _response.BeforeCookieCollectionChange();

            AddCookie(cookie, false);

            if (_response != null)
                _response.OnCookieCollectionChange();
        }


        /// <devdoc>
        ///    <para>
        ///       Removes a cookie from the collection.
        ///    </para>
        /// </devdoc>
        public void Remove(String name) {
            if (_response != null)
                _response.BeforeCookieCollectionChange();

            RemoveCookie(name);

            if (_response != null)
                _response.OnCookieCollectionChange();
        }


        /// <devdoc>
        ///    <para>
        ///       Clears all cookies from the collection.
        ///    </para>
        /// </devdoc>
        public void Clear() {
            Reset();
        }

        //
        //  Access by name
        //


        /// <devdoc>
        /// <para>Returns an <see cref='System.Web.HttpCookie'/> item from the collection.</para>
        /// </devdoc>
        public HttpCookie Get(String name) {
            HttpCookie cookie = (HttpCookie)BaseGet(name);

            if (cookie == null && _response != null) {
                // response cookies are created on demand
                cookie = new HttpCookie(name);
                AddCookie(cookie, true);
                _response.OnCookieAdd(cookie);
            }

            if (cookie != null) {
                EnsureKeyValidated(name, cookie.Value);
            }

            return cookie;
        }


        /// <devdoc>
        ///    <para>Indexed value that enables access to a cookie in the collection.</para>
        /// </devdoc>
        public HttpCookie this[String name]
        {
            get { return Get(name);}
        }

        //
        // Indexed access
        //


        /// <devdoc>
        ///    <para>
        ///       Returns an <see cref='System.Web.HttpCookie'/>
        ///       item from the collection.
        ///    </para>
        /// </devdoc>
        public HttpCookie Get(int index) {
            HttpCookie cookie = (HttpCookie)BaseGet(index);

            // Call GetKey so that we can pass the key to the validation routine.
            if (cookie != null) {
                EnsureKeyValidated(GetKey(index), cookie.Value);
            }
            return cookie;
        }


        /// <devdoc>
        ///    <para>
        ///       Returns key name from collection.
        ///    </para>
        /// </devdoc>
        public String GetKey(int index) {
            return BaseGetKey(index);
        }


        /// <devdoc>
        ///    <para>
        ///       Default property.
        ///       Indexed property that enables access to a cookie in the collection.
        ///    </para>
        /// </devdoc>
        public HttpCookie this[int index]
        {
            get { return Get(index);}
        }

        //
        // Access to keys and values as arrays
        //
        
        /*
         * All keys
         */

        /// <devdoc>
        ///    <para>
        ///       Returns
        ///       an array of all cookie keys in the cookie collection.
        ///    </para>
        /// </devdoc>
        public String[] AllKeys {
            get {
                if (_allKeys == null)
                    _allKeys = BaseGetAllKeys();

                return _allKeys;
            }
        }
    }
}