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