1 //------------------------------------------------------------------------------
2 // <copyright file="HttpServerVarsCollection.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 /*
8  * Collection of server variables with callback to HttpRequest for 'dynamic' ones
9  *
10  * Copyright (c) 2000 Microsoft Corporation
11  */
12 
13 namespace System.Web {
14     using System;
15     using System.Collections;
16     using System.Collections.Generic;
17     using System.Diagnostics.CodeAnalysis;
18     using System.Runtime.Serialization;
19     using System.Security.Permissions;
20     using System.Text;
21     using System.Web.Hosting;
22     using System.Web.Util;
23 
24     internal class HttpServerVarsCollection : HttpValueCollection {
25         private bool _populated;
26         private HttpRequest _request;
27         private IIS7WorkerRequest _iis7workerRequest;
28         private List<HttpServerVarsCollectionEntry> _unsyncedEntries;
29 
30         // We preallocate the base collection with a size that should be sufficient
31         // to store all server variables w/o having to expand
HttpServerVarsCollection(HttpWorkerRequest wr, HttpRequest request)32         internal HttpServerVarsCollection(HttpWorkerRequest wr, HttpRequest request) : base(59) {
33             // if this is an IIS7WorkerRequest, then the collection will be writeable and we will
34             // call into IIS7 to update the server var block when changes are made.
35             _iis7workerRequest = wr as IIS7WorkerRequest;
36             _request = request;
37             _populated = false;
38 
39             Debug.Assert( _request != null );
40         }
41 
42         [SuppressMessage("Microsoft.Usage", "CA2236:CallBaseClassMethodsOnISerializableTypes",
43             Justification = "this class, while derived from class implementing ISerializable, is not serializable")]
GetObjectData(SerializationInfo info, StreamingContext context)44         public override void GetObjectData(SerializationInfo info, StreamingContext context) {
45             throw new SerializationException();
46         }
47 
Dispose()48         internal void Dispose() {
49             _request = null;
50         }
51 
AddStatic(String name, String value)52         internal void AddStatic(String name, String value) {
53             if (value == null)
54                 value = String.Empty;
55 
56             InvalidateCachedArrays();
57             BaseAdd(name, new HttpServerVarsCollectionEntry(name, value));
58         }
59 
AddDynamic(String name, DynamicServerVariable var)60         internal void AddDynamic(String name, DynamicServerVariable var) {
61             InvalidateCachedArrays();
62             BaseAdd(name, new HttpServerVarsCollectionEntry(name, var));
63         }
64 
GetServerVar(Object e)65         private String GetServerVar(Object e) {
66             HttpServerVarsCollectionEntry entry = (HttpServerVarsCollectionEntry)e;
67             return (entry != null) ? entry.GetValue(_request) : null;
68         }
69 
70         //
71         //  Support for deferred population of the collection
72         //
73 
Populate()74         private void Populate() {
75             if (!_populated) {
76                 if (_request != null) {
77                     MakeReadWrite();
78                     _request.FillInServerVariablesCollection();
79 
80                     // Add all unsynchronized entries, if any
81                     if (_unsyncedEntries != null) {
82                         foreach (var entry in _unsyncedEntries) {
83                             var existingEntry = (HttpServerVarsCollectionEntry)BaseGet(entry.Name);
84                             if (existingEntry != null && existingEntry.IsDynamic) {
85                                 // Exisiting dynamic server variables cannot be modified - ignore the new value
86                                 continue;
87                             }
88 
89                             InvalidateCachedArrays();
90                             BaseSet(entry.Name, entry); // Update an existing entry, or create one if it's new
91                         }
92 
93                         _unsyncedEntries.Clear();
94                     }
95 
96                     if (_iis7workerRequest == null) {
97                         MakeReadOnly();
98                     }
99                 }
100                 _populated = true;
101             }
102         }
103 
GetSimpleServerVar(String name)104         private String GetSimpleServerVar(String name) {
105             // get server var without population of the collection
106             // only most popular are included
107 
108             if (name != null && name.Length > 1 && _request != null) {
109                 switch (name[0]) {
110                     case 'A':
111                     case 'a':
112                         if (StringUtil.EqualsIgnoreCase(name, "AUTH_TYPE"))
113                             return _request.CalcDynamicServerVariable(DynamicServerVariable.AUTH_TYPE);
114                         else if (StringUtil.EqualsIgnoreCase(name, "AUTH_USER"))
115                             return _request.CalcDynamicServerVariable(DynamicServerVariable.AUTH_USER);
116                         break;
117                     case 'H':
118                     case 'h':
119                         if (StringUtil.EqualsIgnoreCase(name, "HTTP_USER_AGENT"))
120                             return _request.UserAgent;
121                         break;
122                     case 'Q':
123                     case 'q':
124                         if (StringUtil.EqualsIgnoreCase(name, "QUERY_STRING"))
125                             return _request.QueryStringText;
126                         break;
127                     case 'P':
128                     case 'p':
129                         if (StringUtil.EqualsIgnoreCase(name, "PATH_INFO"))
130                             return _request.Path;
131                         else if (StringUtil.EqualsIgnoreCase(name, "PATH_TRANSLATED"))
132                             return _request.PhysicalPath;
133                         break;
134                     case 'R':
135                     case 'r':
136                         if (StringUtil.EqualsIgnoreCase(name, "REQUEST_METHOD"))
137                             return _request.HttpMethod;
138                         else if (StringUtil.EqualsIgnoreCase(name, "REMOTE_USER"))
139                             return _request.CalcDynamicServerVariable(DynamicServerVariable.AUTH_USER);
140                         else if (StringUtil.EqualsIgnoreCase(name, "REMOTE_HOST"))
141                             return _request.UserHostName;
142                         else if (StringUtil.EqualsIgnoreCase(name, "REMOTE_ADDRESS"))
143                             return _request.UserHostAddress;
144                         break;
145                     case 'S':
146                     case 's':
147                         if (StringUtil.EqualsIgnoreCase(name, "SCRIPT_NAME"))
148                             return _request.FilePath;
149                         break;
150                 }
151             }
152 
153             // do the default processing (populate the collection)
154             return null;
155         }
156 
157         //
158         //  Enumerator must pre-populate the collection
159         //
160 
GetEnumerator()161         public override IEnumerator GetEnumerator() {
162             Populate();
163             return base.GetEnumerator();
164         }
165 
166         //
167         //  NameValueCollection overrides
168         //
169 
170         public override int Count {
171             get {
172                 Populate();
173                 return base.Count;
174             }
175         }
176 
Add(String name, String value)177         public override void Add(String name, String value) {
178             // not supported because it appends the value to a comma separated list
179             throw new NotSupportedException();
180         }
181 
Clear()182         public override void Clear() {
183             throw new NotSupportedException();
184         }
185 
Get(String name)186         public override String Get(String name) {
187             if (!_populated) {
188                 String value = GetSimpleServerVar(name);
189 
190                 if (value != null)
191                     return value;
192 
193                 Populate();
194             }
195 
196             if (_iis7workerRequest != null) {
197                 string var = GetServerVar(BaseGet(name));
198 
199                 if (String.IsNullOrEmpty(var)) {
200                     var = _request.FetchServerVariable(name);
201                 }
202 
203                 return var;
204             }
205             else {
206                 return GetServerVar(BaseGet(name));
207             }
208         }
209 
GetValues(String name)210         public override String[] GetValues(String name) {
211             String s = Get(name);
212             return(s != null) ? new String[1] { s} : null;
213         }
214 
215         [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.High)]
Set(String name, String value)216         public override void Set(String name, String value) {
217             if (_iis7workerRequest == null) {
218                 throw new PlatformNotSupportedException();
219             }
220 
221             if (name == null) {
222                 throw new ArgumentNullException("name");
223             }
224 
225             SetNoDemand(name, value);
226         }
227 
SetNoDemand(String name, String value)228         internal void SetNoDemand(String name, String value) {
229             if (value == null) {
230                 value = String.Empty;
231             }
232 
233             _iis7workerRequest.SetServerVariable(name, value);
234             SetServerVariableManagedOnly(name, value);
235             SynchronizeHeader(name, value);
236             _request.InvalidateParams();
237         }
238 
SynchronizeHeader(String name, String value)239         private void SynchronizeHeader(String name, String value) {
240             if (StringUtil.StringStartsWith(name, "HTTP_"))
241             {
242                 // update managed copy of header
243                 string headerName = name.Substring("HTTP_".Length);
244                 headerName = headerName.Replace('_', '-');
245                 int knownIndex = HttpWorkerRequest.GetKnownRequestHeaderIndex(headerName);
246                 if (knownIndex > -1) {
247                     headerName = HttpWorkerRequest.GetKnownRequestHeaderName(knownIndex);
248                 }
249 
250                 HttpHeaderCollection headers = _request.Headers as HttpHeaderCollection;
251                 if (headers != null) {
252                     headers.SynchronizeHeader(headerName, value);
253                 }
254             }
255         }
256 
257         // updates managed copy of server variable with current value from native header block
SynchronizeServerVariable(String name, String value, bool ensurePopulated = true)258         internal void SynchronizeServerVariable(String name, String value, bool ensurePopulated = true) {
259             if (name == null) {
260                 throw new ArgumentNullException("name");
261             }
262 
263             if (value != null) {
264                 if (this._populated || ensurePopulated) {
265                     SetServerVariableManagedOnly(name, value);
266                 }
267                 else {
268                     // Lazy synchronization - when populate is indeed required
269                     if (_unsyncedEntries == null) {
270                         _unsyncedEntries = new List<HttpServerVarsCollectionEntry>();
271                     }
272 
273                     _unsyncedEntries.Add(new HttpServerVarsCollectionEntry(name, value));
274                 }
275             }
276             else {
277                 base.Remove(name);
278             }
279 
280             _request.InvalidateParams();
281         }
282 
283         // updates managed copy of server variable with current value from native header block
SetServerVariableManagedOnly(String name, String value)284         private void SetServerVariableManagedOnly(String name, String value) {
285             Debug.Assert(name != null);
286             Debug.Assert(value != null);
287 
288             // populate in order to identify dynamic variables
289             Populate();
290 
291             // dynamic server variables cannot be modified
292             HttpServerVarsCollectionEntry entry = (HttpServerVarsCollectionEntry) BaseGet(name);
293             if (entry != null && entry.IsDynamic) {
294                 throw new HttpException(SR.GetString(SR.Server_variable_cannot_be_modified));
295             }
296 
297             InvalidateCachedArrays();
298             // this will update an existing entry, or create one if it's new
299             BaseSet(name, new HttpServerVarsCollectionEntry(name, value));
300         }
301 
302         [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.High)]
Remove(String name)303         public override void Remove(String name) {
304             if (_iis7workerRequest == null) {
305                 throw new PlatformNotSupportedException();
306             }
307 
308             if (name == null) {
309                 throw new ArgumentNullException("name");
310             }
311 
312             RemoveNoDemand(name);
313         }
314 
RemoveNoDemand(String name)315         internal void RemoveNoDemand(String name) {
316             // delete by sending null value
317             _iis7workerRequest.SetServerVariable(name, null /*value*/);
318 
319             base.Remove(name);
320             SynchronizeHeader(name, null);
321             _request.InvalidateParams();
322         }
323 
Get(int index)324         public override String Get(int index)  {
325             Populate();
326             return GetServerVar(BaseGet(index));
327         }
328 
GetValues(int index)329         public override String[] GetValues(int index) {
330             String s = Get(index);
331             return(s != null) ? new String[1] { s} : null;
332         }
333 
GetKey(int index)334         public override String GetKey(int index) {
335             Populate();
336             return base.GetKey(index);
337         }
338 
339         public override string[] AllKeys {
340             get {
341                 Populate();
342                 return base.AllKeys;
343             }
344         }
345 
346         //
347         //  HttpValueCollection overrides
348         //
349 
ToString(bool urlencoded)350         internal override string ToString(bool urlencoded) {
351             Populate();
352 
353             StringBuilder s = new StringBuilder();
354             int n = Count;
355             String key, value;
356 
357             for (int i = 0; i < n; i++) {
358                 if (i > 0)
359                     s.Append('&');
360 
361                 key = GetKey(i);
362                 if (urlencoded)
363                     key = UrlEncodeForToString(key);
364                 s.Append(key);
365 
366                 s.Append('=');
367 
368                 value = Get(i);
369                 if (urlencoded)
370                     value = UrlEncodeForToString(value);
371                 s.Append(value);
372             }
373 
374             return s.ToString();
375         }
376     }
377 
378 /*
379  *  Entry in a server vars colleciton
380  */
381     internal class HttpServerVarsCollectionEntry {
382         internal readonly String Name;
383         internal readonly bool   IsDynamic;
384         internal readonly String Value;
385         internal readonly DynamicServerVariable Var;
386 
HttpServerVarsCollectionEntry(String name, String value)387         internal HttpServerVarsCollectionEntry(String name, String value) {
388             Name = name;
389             Value = value;
390             IsDynamic = false;
391         }
392 
HttpServerVarsCollectionEntry(String name, DynamicServerVariable var)393         internal HttpServerVarsCollectionEntry(String name, DynamicServerVariable var) {
394             Name = name;
395             Var = var;
396             IsDynamic = true;
397         }
398 
GetValue(HttpRequest request)399         internal String GetValue(HttpRequest request) {
400             String v = null;
401 
402             if (IsDynamic) {
403                 if (request != null)
404                     v = request.CalcDynamicServerVariable(Var);
405             }
406             else {
407                 v = Value;
408             }
409 
410             return v;
411         }
412     }
413 
414 
415 }
416