Merge pull request #413 from AerisG222/http_capability_provider
[mono.git] / mcs / class / System.Web / System.Web / HttpRequest.cs
1 //
2 // System.Web.HttpRequest.cs 
3 //
4 // 
5 // Author:
6 //      Miguel de Icaza (miguel@novell.com)
7 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
8 //      Marek Habersack <mhabersack@novell.com>
9 //
10
11 //
12 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
13 // Copyright (C) 2011-2012 Xamarin, Inc (http://xamarin.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34 using System.Text;
35 using System.Collections;
36 using System.Collections.Specialized;
37 using System.IO;
38 using System.Runtime.InteropServices;
39 using System.Security;
40 using System.Security.Permissions;
41 using System.Security.Principal;
42 using System.Web.Configuration;
43 using System.Web.Management;
44 using System.Web.UI;
45 using System.Web.Util;
46 using System.Globalization;
47
48 #if NET_4_0
49 using System.Security.Authentication.ExtendedProtection;
50 using System.Web.Routing;
51 #endif
52
53 namespace System.Web
54 {       
55         // CAS - no InheritanceDemand here as the class is sealed
56         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
57         public sealed partial class HttpRequest
58         {
59                 HttpWorkerRequest worker_request;
60                 HttpContext context;
61                 WebROCollection query_string_nvc;
62
63                 //
64                 //string filename;
65                 string orig_url = null;
66                 UriBuilder url_components;
67
68                 string client_target;
69
70                 //
71                 // On-demand computed values
72                 //
73                 HttpBrowserCapabilities browser_capabilities;
74                 string file_path, base_virtual_dir, root_virtual_dir, client_file_path;
75                 string content_type;
76                 int content_length = -1;
77                 Encoding encoding;
78                 string current_exe_path;
79                 string physical_path;
80                 string unescaped_path;
81                 string original_path;
82                 string path_info;
83                 string raw_url;
84                 WebROCollection all_params;
85                 WebROCollection headers;
86                 Stream input_stream;
87                 InputFilterStream input_filter;
88                 Stream filter;
89                 HttpCookieCollection cookies;
90                 string http_method;
91
92                 WebROCollection form;
93                 HttpFileCollection files;
94                 
95                 ServerVariablesCollection server_variables;
96                 HttpClientCertificate client_cert;
97                 
98                 string request_type;
99                 string [] accept_types;
100                 string [] user_languages;
101                 Uri cached_url;
102                 TempFileStream request_file;
103
104                 readonly static System.Net.IPAddress [] host_addresses;
105                 
106                 // Validations
107                 bool validate_cookies, validate_query_string, validate_form;
108                 bool checked_cookies, checked_query_string, checked_form;
109                 static readonly UrlMappingCollection urlMappings;
110                 readonly static char [] queryTrimChars = {'?'};
111 #if NET_4_0
112                 bool lazyFormValidation;
113                 bool lazyQueryStringValidation;
114                 bool inputValidationEnabled;
115                 RequestContext requestContext;
116                 
117                 static bool validateRequestNewMode;
118                 internal static bool ValidateRequestNewMode {
119                         get { return validateRequestNewMode; }
120                 }
121
122                 internal bool InputValidationEnabled {
123                         get { return inputValidationEnabled; }
124                 }
125                 
126                 private static char[] RequestPathInvalidCharacters {
127                         get; set;
128                 }
129
130                 private static char[] CharsFromList (string list)
131                 {
132                         // List format is very strict and enforced by the Configuration 
133                         // there must be a single char separated by commas with no trailing comma
134                         // whitespace is allowed though and should be trimmed.
135                         
136                         string [] pieces = list.Split (',');
137
138                         char [] chars = new char [pieces.Length];
139                         for (int i = 0; i < chars.Length; i++) {
140                                 string trimmed = pieces [i].Trim ();
141                                 if (trimmed.Length != 1) {
142                                         // This should have been caught by System.Web.Configuration
143                                         // and throw a configuration error. This is just here for sanity
144                                         throw new System.Configuration.ConfigurationErrorsException ();
145                                 }
146
147                                 chars [i] = trimmed [0];
148                         }
149
150                         return chars;
151                 }
152 #endif
153
154                 static HttpRequest ()
155                 {
156                         try {
157                                 UrlMappingsSection ums = WebConfigurationManager.GetWebApplicationSection ("system.web/urlMappings") as UrlMappingsSection;
158                                 if (ums != null && ums.IsEnabled) {
159                                         urlMappings = ums.UrlMappings;
160                                         if (urlMappings.Count == 0)
161                                                 urlMappings = null;
162                                 }
163
164 #if NET_4_0
165                                 Version validationMode = HttpRuntime.Section.RequestValidationMode;
166
167                                 if (validationMode >= new Version (4, 0)) {
168                                         validateRequestNewMode = true;
169                                         string invalidChars = HttpRuntime.Section.RequestPathInvalidCharacters;
170                                         if (!String.IsNullOrEmpty (invalidChars))
171                                                 RequestPathInvalidCharacters = CharsFromList (invalidChars);
172                                 }
173 #endif
174                         } catch {
175                                 // unlikely to happen
176                         }
177                         
178                         host_addresses = GetLocalHostAddresses ();
179                 }
180                 
181                 public HttpRequest (string filename, string url, string queryString)
182                 {
183                         // warning 169: what are we supposed to do with filename?
184                         
185                         //this.filename = filename;
186
187                         orig_url = url;
188                         url_components = new UriBuilder (url);
189                         url_components.Query = queryString;
190                         
191                         query_string_nvc = new WebROCollection ();
192                         if (queryString != null)
193                                 HttpUtility.ParseQueryString (queryString, Encoding.Default, query_string_nvc);
194                         query_string_nvc.Protect ();
195                 }
196
197                 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
198                 {
199                         this.worker_request = worker_request;
200                         this.context = context;
201                 }
202                 
203                 internal UriBuilder UrlComponents {
204                         get {
205                                 if (url_components == null) {
206                                         string query;
207                                         byte[] queryStringRaw = worker_request.GetQueryStringRawBytes();
208                                         if(queryStringRaw != null)
209                                                 query = ContentEncoding.GetString(queryStringRaw);
210                                         else
211                                                 query = worker_request.GetQueryString();
212                                         
213                                         BuildUrlComponents (ApplyUrlMapping (worker_request.GetUriPath ()), query);
214                                 }
215                                 return url_components;
216                         }
217                 }
218
219                 void BuildUrlComponents (string path, string query)
220                 {
221                         if (url_components != null)
222                                 return;
223                         url_components = new UriBuilder ();
224                         url_components.Scheme = worker_request.GetProtocol ();
225                         url_components.Host = worker_request.GetServerName ();
226                         url_components.Port = worker_request.GetLocalPort ();
227                         url_components.Path = path;
228                         if (query != null && query.Length > 0)
229                                 url_components.Query = query.TrimStart (queryTrimChars);
230                 }
231
232                 internal string ApplyUrlMapping (string url)
233                 {
234                         if (urlMappings == null)
235                                 return url;
236
237                         string relUrl = VirtualPathUtility.ToAppRelative (url);
238                         UrlMapping um = null;
239                         
240                         foreach (UrlMapping u in urlMappings) {
241                                 if (u == null)
242                                         continue;
243                                 if (String.Compare (relUrl, u.Url, StringComparison.Ordinal) == 0) {
244                                         um = u;
245                                         break;
246                                 }
247                         }
248
249                         if (um == null)
250                                 return url;
251
252                         string rawUrl = VirtualPathUtility.ToAbsolute (um.MappedUrl.Trim ());
253                         Uri newUrl = new Uri ("http://host.com" + rawUrl);
254
255                         if (url_components != null) {
256                                 url_components.Path = newUrl.AbsolutePath;
257                                 url_components.Query = newUrl.Query.TrimStart (queryTrimChars);
258                                 query_string_nvc = new WebROCollection ();
259                                 HttpUtility.ParseQueryString (newUrl.Query, Encoding.Default, query_string_nvc);
260                                 query_string_nvc.Protect ();
261                         } else
262                                 BuildUrlComponents (newUrl.AbsolutePath, newUrl.Query);
263
264                         return url_components.Path;
265                 }
266
267                 string [] SplitHeader (int header_index)
268                 {
269                         string [] result = null;
270                         string header = worker_request.GetKnownRequestHeader (header_index);
271                         if (header != null && header != "" && header.Trim () != "") {
272                                 result = header.Split (',');
273                                 for (int i = result.Length - 1; i >= 0; i--)
274                                         result [i] = result [i].Trim ();
275                         }
276                         return result;
277                 }
278
279                 public string [] AcceptTypes {
280                         get {
281                                 if (worker_request == null)
282                                         return null;
283
284                                 if (accept_types == null)
285                                         accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
286
287                                 return accept_types;
288                         }
289                 }
290
291 #if !TARGET_JVM
292                 public WindowsIdentity LogonUserIdentity {
293                         get { throw new NotImplementedException (); }
294                 }
295 #endif
296                 
297                 string anonymous_id;
298                 public string AnonymousID {
299                         get {
300                                 return anonymous_id;
301                         }
302                         internal set {
303                                 anonymous_id = value;
304                         }
305                 }
306
307                 public string ApplicationPath {
308                         get {
309                                 if (worker_request == null)
310                                         return null;
311                                 return worker_request.GetAppPath ();
312                         }
313                 }
314
315                 public HttpBrowserCapabilities Browser {
316                         get {
317                                 if (browser_capabilities == null)
318 #if NET_4_0
319                                         browser_capabilities = HttpCapabilitiesBase.BrowserCapabilitiesProvider.GetBrowserCapabilities (this);
320 #else
321                                         browser_capabilities = (HttpBrowserCapabilities)
322                                                 HttpCapabilitiesBase.GetConfigCapabilities (null, this);
323 #endif
324
325                                 return browser_capabilities;
326                         }
327
328                         set {
329                                 browser_capabilities = value;
330                         }
331                 }
332
333                 internal bool BrowserMightHaveSpecialWriter {
334                         get {
335                                 return (browser_capabilities != null 
336                                         || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
337                         }
338                 }
339
340                 internal bool BrowserMightHaveAdapters {
341                         get {
342                                 return (browser_capabilities != null 
343                                         || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
344                         }
345                 }
346
347                 public HttpClientCertificate ClientCertificate {
348                         get {
349                                 if (client_cert == null)
350                                         client_cert = new HttpClientCertificate (worker_request);
351                                 return client_cert;
352                         }
353                 }
354
355                 static internal string GetParameter (string header, string attr)
356                 {
357                         int ap = header.IndexOf (attr);
358                         if (ap == -1)
359                                 return null;
360
361                         ap += attr.Length;
362                         if (ap >= header.Length)
363                                 return null;
364                         
365                         char ending = header [ap];
366                         if (ending != '"')
367                                 ending = ' ';
368                         
369                         int end = header.IndexOf (ending, ap+1);
370                         if (end == -1)
371                                 return (ending == '"') ? null : header.Substring (ap);
372
373                         return header.Substring (ap+1, end-ap-1);
374                 }
375
376                 public Encoding ContentEncoding {
377                         get {
378                                 if (encoding == null){
379                                         if (worker_request == null)
380                                                 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
381                                         
382                                         string content_type = ContentType;
383                                         string parameter = GetParameter (content_type, "; charset=");
384                                         if (parameter == null) {
385                                                 encoding = WebEncoding.RequestEncoding;
386                                         } else {
387                                                 try {
388                                                         // Do what the #1 web server does
389                                                         encoding = Encoding.GetEncoding (parameter);
390                                                 } catch {
391                                                         encoding = WebEncoding.RequestEncoding;
392                                                 }
393                                         }
394                                 }
395                                 return encoding;
396                         }
397
398                         set {
399                                 encoding = value;
400                         }
401                 }
402
403                 public int ContentLength {
404                         get {
405                                 if (content_length == -1){
406                                         if (worker_request == null)
407                                                 return 0;
408
409                                         string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
410
411                                         if (cl != null) {
412                                                 try {
413                                                         content_length = Int32.Parse (cl);
414                                                 } catch { }
415                                         }
416                                 }
417
418                                 // content_length will still be < 0, but we know we gotta read from the client
419                                 if (content_length < 0)
420                                         return 0;
421
422                                 return content_length;
423                         }
424                 }
425
426                 public string ContentType {
427                         get {
428                                 if (content_type == null){
429                                         if (worker_request != null)
430                                                 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
431
432                                         if (content_type == null)
433                                                 content_type = String.Empty;
434                                 }
435                                 
436                                 return content_type;
437                         }
438
439                         set {
440                                 content_type = value;
441                         }
442                 }
443
444                 public HttpCookieCollection Cookies {
445                         get {
446                                 if (cookies == null) {
447                                         if (worker_request == null) {
448                                                 cookies = new HttpCookieCollection ();
449                                         } else {
450                                                 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
451                                                 cookies = new HttpCookieCollection (cookie_hv);
452                                         }
453                                 }
454
455 #if TARGET_J2EE
456                                 // For J2EE portal support we emulate cookies using the session.
457                                 GetSessionCookiesForPortal (cookies);
458 #endif
459                                 bool needValidation = validate_cookies;
460 #if NET_4_0
461                                 needValidation |= validateRequestNewMode;
462 #endif
463                                 if (needValidation && !checked_cookies) {
464                                         // Setting this before calling the validator prevents
465                                         // possible endless recursion
466                                         checked_cookies = true;
467                                         ValidateCookieCollection (cookies);
468                                 }
469
470                                 return cookies;
471                         }
472
473                 }
474
475                 public string CurrentExecutionFilePath {
476                         get {
477                                 if (current_exe_path != null)
478                                         return current_exe_path;
479
480                                 return FilePath;
481                         }
482                 }
483 #if NET_4_0
484                 public string CurrentExecutionFilePathExtension {
485                         get { return global::System.IO.Path.GetExtension (CurrentExecutionFilePath); }
486                 }
487 #endif
488                 public string AppRelativeCurrentExecutionFilePath {
489                         get {
490                                 return VirtualPathUtility.ToAppRelative (CurrentExecutionFilePath);
491                         }
492                 }
493
494                 public string FilePath {
495                         get {
496                                 if (worker_request == null)
497                                         return "/"; // required for 2.0
498
499                                 if (file_path == null)
500                                         file_path = UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
501
502                                 return file_path;
503                         }
504                 }
505
506                 internal string ClientFilePath {
507                         get {
508                                 if (client_file_path == null) {
509                                         if (worker_request == null)
510                                                 return "/";
511                                         
512                                         return UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
513                                 }
514                                 
515                                 return client_file_path;
516                         }
517
518                         set {
519                                 if (value == null || value.Length == 0)
520                                         client_file_path = null;
521                                 else
522                                         client_file_path = value;
523                         }
524                 }
525                 
526                 internal string BaseVirtualDir {
527                         get {
528                                 if (base_virtual_dir == null){
529                                         base_virtual_dir = FilePath;
530                                         if (UrlUtils.HasSessionId (base_virtual_dir))
531                                                 base_virtual_dir = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (base_virtual_dir), base_virtual_dir);
532                                         
533                                         int p = base_virtual_dir.LastIndexOf ('/');
534                                         if (p != -1) {
535                                                 if (p == 0)
536                                                         p = 1;
537                                                 base_virtual_dir = base_virtual_dir.Substring (0, p);
538                                         } else
539                                                 base_virtual_dir = "/";
540                                 }
541                                 return base_virtual_dir;
542                         }
543                 }
544                 
545                 public HttpFileCollection Files {
546                         get {
547                                 if (files == null) {
548                                         files = new HttpFileCollection ();
549                                         if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
550                                                 form = new WebROCollection ();
551                                                 LoadMultiPart ();
552                                                 form.Protect ();
553                                         }
554                                 }
555                                 return files;
556                         }
557                 }
558
559                 public Stream Filter {
560                         get {
561                                 if (filter != null)
562                                         return filter;
563
564                                 if (input_filter == null)
565                                         input_filter = new InputFilterStream ();
566
567                                 return input_filter;
568                         }
569
570                         set {
571                                 // This checks that get_ was called before.
572                                 if (input_filter == null)
573                                         throw new HttpException ("Invalid filter");
574
575                                 filter = value;
576                         }
577                 }
578
579                 // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
580                 static Stream GetSubStream (Stream stream)
581                 {
582 #if !TARGET_JVM
583                         if (stream is IntPtrStream)
584                                 return new IntPtrStream (stream);
585 #endif
586
587                         if (stream is MemoryStream) {
588                                 MemoryStream other = (MemoryStream) stream;
589                                 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
590                         }
591
592                         if (stream is TempFileStream) {
593                                 ((TempFileStream) stream).SavePosition ();
594                                 return stream;
595                         }
596
597                         throw new NotSupportedException ("The stream is " + stream.GetType ());
598                 }
599
600                 static void EndSubStream (Stream stream)
601                 {
602                         if (stream is TempFileStream) {
603                                 ((TempFileStream) stream).RestorePosition ();
604                         }
605                 }
606
607                 //
608                 // Loads the data on the form for multipart/form-data
609                 //
610                 void LoadMultiPart ()
611                 {
612                         string boundary = GetParameter (ContentType, "; boundary=");
613                         if (boundary == null)
614                                 return;
615
616                         Stream input = GetSubStream (InputStream);
617                         HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
618
619                         HttpMultipart.Element e;
620                         while ((e = multi_part.ReadNextElement ()) != null) {
621                                 if (e.Filename == null){
622                                         byte [] copy = new byte [e.Length];
623                                 
624                                         input.Position = e.Start;
625                                         input.Read (copy, 0, (int) e.Length);
626
627                                         form.Add (e.Name, ContentEncoding.GetString (copy));
628                                 } else {
629                                         //
630                                         // We use a substream, as in 2.x we will support large uploads streamed to disk,
631                                         //
632                                         HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
633                                         files.AddFile (e.Name, sub);
634                                 }
635                         }
636                         EndSubStream (input);
637                 }
638
639                 //
640                 // Adds the key/value to the form, and sets the argumets to empty
641                 //
642                 void AddRawKeyValue (StringBuilder key, StringBuilder value)
643                 {
644                         string decodedKey = HttpUtility.UrlDecode (key.ToString (), ContentEncoding);
645                         form.Add (decodedKey,
646                                   HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
647
648                         key.Length = 0;
649                         value.Length = 0;
650                 }
651
652                 //
653                 // Loads the form data from on a application/x-www-form-urlencoded post
654                 // 
655 #if TARGET_J2EE
656                 void RawLoadWwwForm ()
657 #else
658                 void LoadWwwForm ()
659 #endif
660                 {
661                         using (Stream input = GetSubStream (InputStream)) {
662                                 using (StreamReader s = new StreamReader (input, ContentEncoding)) {
663                                         StringBuilder key = new StringBuilder ();
664                                         StringBuilder value = new StringBuilder ();
665                                         int c;
666
667                                         while ((c = s.Read ()) != -1){
668                                                 if (c == '='){
669                                                         value.Length = 0;
670                                                         while ((c = s.Read ()) != -1){
671                                                                 if (c == '&'){
672                                                                         AddRawKeyValue (key, value);
673                                                                         break;
674                                                                 } else
675                                                                         value.Append ((char) c);
676                                                         }
677                                                         if (c == -1){
678                                                                 AddRawKeyValue (key, value);
679                                                                 return;
680                                                         }
681                                                 } else if (c == '&')
682                                                         AddRawKeyValue (key, value);
683                                                 else
684                                                         key.Append ((char) c);
685                                         }
686                                         if (c == -1)
687                                                 AddRawKeyValue (key, value);
688
689                                         EndSubStream (input);
690                                 }
691                         }
692                 }
693
694                 bool IsContentType (string ct, bool starts_with)
695                 {
696                         if (starts_with)
697                                 return StrUtils.StartsWith (ContentType, ct, true);
698
699                         return String.Compare (ContentType, ct, true, Helpers.InvariantCulture) == 0;
700                 }
701
702                 internal WebROCollection FormUnvalidated {
703                         get {
704                                 if (form == null){
705                                         form = new WebROCollection ();
706                                         files = new HttpFileCollection ();
707
708                                         if (IsContentType ("multipart/form-data", true))
709                                                 LoadMultiPart ();
710                                         else if (
711                                                 IsContentType ("application/x-www-form-urlencoded", true))
712                                                 LoadWwwForm ();
713
714                                         form.Protect ();
715                                 }
716
717                                 return form;
718                         }
719                 }
720                 
721                 public NameValueCollection Form {
722                         get {
723                                 NameValueCollection form = FormUnvalidated;
724 #if NET_4_0
725                                 if (validateRequestNewMode && !checked_form) {
726                                         if (!lazyFormValidation) {
727                                                 // Setting this before calling the validator prevents
728                                                 // possible endless recursion
729                                                 checked_form = true;
730                                                 ValidateNameValueCollection ("Form", form, RequestValidationSource.Form);
731                                         }
732                                 } else
733 #endif
734                                         if (validate_form && !checked_form){
735                                                 checked_form = true;
736                                                 ValidateNameValueCollection ("Form", form);
737                                         }
738                                 
739                                 return form;
740                         }
741                 }
742
743                 public NameValueCollection Headers {
744                         get {
745                                 if (headers == null) {
746                                         headers = new HeadersCollection (this);
747 #if NET_4_0
748                                         if (validateRequestNewMode) {
749                                                 RequestValidator validator = RequestValidator.Current;
750                                                 int validationFailureIndex;
751
752                                                 foreach (string hkey in headers.AllKeys) {
753                                                         string value = headers [hkey];
754                                                         
755                                                         if (!validator.IsValidRequestString (HttpContext.Current, value, RequestValidationSource.Headers, hkey, out validationFailureIndex))
756                                                                 ThrowValidationException ("Headers", hkey, value);
757                                                 }
758                                         }
759 #endif
760                                 }
761                                 
762                                 return headers;
763                         }
764                 }
765
766                 public string HttpMethod {
767                         get {
768                                 if (http_method == null){
769                                         if (worker_request != null)
770                                                 http_method = worker_request.GetHttpVerbName ();
771                                         else
772                                                 http_method = "GET";
773                                 }
774                                 return http_method;
775                         }
776                 }
777
778                 void DoFilter (byte [] buffer)
779                 {
780                         if (input_filter == null || filter == null)
781                                 return;
782
783                         if (buffer.Length < 1024)
784                                 buffer = new byte [1024];
785
786                         // Replace the input with the filtered input
787                         input_filter.BaseStream = input_stream;
788                         MemoryStream ms = new MemoryStream ();
789                         while (true) {
790                                 int n = filter.Read (buffer, 0, buffer.Length);
791                                 if (n <= 0)
792                                         break;
793                                 ms.Write (buffer, 0, n);
794                         }
795                         // From now on input_stream has the filtered input
796                         input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
797                 }
798
799 #if !TARGET_JVM
800                 const int INPUT_BUFFER_SIZE = 32*1024;
801
802                 TempFileStream GetTempStream ()
803                 {
804                         string tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
805                         TempFileStream f = null;
806                         string path;
807                         Random rnd = new Random ();
808                         int num;
809                         do {
810                                 num = rnd.Next ();
811                                 num++;
812                                 path = System.IO.Path.Combine (tempdir, "tmp" + num.ToString("x") + ".req");
813
814                                 try {
815                                         f = new TempFileStream (path);
816                                 } catch (SecurityException) {
817                                         // avoid an endless loop
818                                         throw;
819                                 } catch { }
820                         } while (f == null);
821
822                         return f;
823                 }
824
825                 void MakeInputStream ()
826                 {
827                         if (input_stream != null)
828                                 return;
829
830                         if (worker_request == null) {
831                                 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
832                                 DoFilter (new byte [1024]);
833                                 return;
834                         }
835
836                         //
837                         // Use an unmanaged memory block as this might be a large
838                         // upload
839                         //
840                         int content_length = ContentLength;
841                         int content_length_kb = content_length / 1024;
842                         HttpRuntimeSection config = HttpRuntime.Section;
843                         if (content_length_kb > config.MaxRequestLength)
844                                 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
845
846                         int total = 0;
847                         byte [] buffer;
848                         buffer = worker_request.GetPreloadedEntityBody ();
849                         // we check the instance field 'content_length' here, not the local var.
850                         if (this.content_length <= 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
851                                 if (buffer == null || content_length == 0) {
852                                         input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
853                                 } else {
854                                         input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
855                                 }
856                                 DoFilter (new byte [1024]);
857                                 return;
858                         }
859
860                         if (buffer != null)
861                                 total = buffer.Length;
862
863                         if (content_length > 0 && content_length_kb >= config.RequestLengthDiskThreshold) {
864                                 // Writes the request to disk
865                                 total = Math.Min (content_length, total);
866                                 request_file = GetTempStream ();
867                                 Stream output = request_file;
868                                 if (total > 0)
869                                         output.Write (buffer, 0, total);
870
871                                 if (total < content_length) {
872                                         buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
873                                         do {
874                                                 int n;
875                                                 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
876                                                 n = worker_request.ReadEntityBody (buffer, min);
877                                                 if (n <= 0)
878                                                         break;
879                                                 output.Write (buffer, 0, n);
880                                                 total += n;
881                                         } while (total < content_length);
882                                 }
883
884                                 request_file.SetReadOnly ();
885                                 input_stream = request_file;
886                         } else if (content_length > 0) {
887                                 // Buffers the request in an IntPtrStream
888                                 total = Math.Min (content_length, total);
889                                 IntPtr content = Marshal.AllocHGlobal (content_length);
890                                 if (content == (IntPtr) 0)
891                                         throw HttpException.NewWithCode (
892                                                 String.Format ("Not enough memory to allocate {0} bytes.", content_length),
893                                                 WebEventCodes.WebErrorOtherError);
894
895                                 if (total > 0)
896                                         Marshal.Copy (buffer, 0, content, total);
897
898                                 if (total < content_length) {
899                                         buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
900                                         do {
901                                                 int n;
902                                                 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
903                                                 n = worker_request.ReadEntityBody (buffer, min);
904                                                 if (n <= 0)
905                                                         break;
906                                                 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
907                                                 total += n;
908                                         } while (total < content_length);
909                                 }
910
911                                 input_stream = new IntPtrStream (content, total);
912                         } else {
913                                 // Buffers the request in a MemoryStream or writes to disk if threshold exceeded
914                                 MemoryStream ms = new MemoryStream ();
915                                 Stream output = ms;
916                                 if (total > 0)
917                                         ms.Write (buffer, 0, total);
918
919                                 buffer = new byte [INPUT_BUFFER_SIZE];
920                                 long maxlength = config.MaxRequestLength * 1024L;
921                                 long disk_th = config.RequestLengthDiskThreshold * 1024L;
922                                 int n;
923                                 while (true) {
924                                         n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
925                                         if (n <= 0)
926                                                 break;
927                                         total += n;
928                                         if (total < 0 || total > maxlength)
929                                                 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
930
931                                         if (ms != null && total > disk_th) {
932                                                 // Swith to on-disk file.
933                                                 request_file = GetTempStream ();
934                                                 ms.WriteTo (request_file);
935                                                 ms = null;
936                                                 output = request_file;
937                                         }
938                                         output.Write (buffer, 0, n);
939                                 }
940
941                                 if (ms != null) {
942                                         input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
943                                 } else {
944                                         request_file.SetReadOnly ();
945                                         input_stream = request_file;
946                                 }
947                         }
948                         DoFilter (buffer);
949
950                         if (total < content_length)
951                                 throw HttpException.NewWithCode (411, "The request body is incomplete.", WebEventCodes.WebErrorOtherError);
952                 }
953 #endif
954
955                 internal void ReleaseResources ()
956                 {
957                         Stream stream;
958                         if (input_stream != null){
959                                 stream = input_stream;
960                                 input_stream = null;
961                                 try {
962                                         stream.Close ();
963                                 } catch {}
964                         }
965
966                         if (request_file != null) {
967                                 stream = request_file;
968                                 request_file = null;
969                                 try {
970                                         stream.Close ();
971                                 } catch {}
972                         }
973                 }
974 #if NET_4_0
975                 public RequestContext RequestContext {
976                         get {
977                                 if (requestContext == null)
978                                         requestContext = new RequestContext (new HttpContextWrapper (this.context ?? HttpContext.Current), new RouteData ());
979
980                                 return requestContext;
981                         }
982                         
983                         internal set { requestContext = value; }        
984                 }
985
986                 public ChannelBinding HttpChannelBinding {
987                         get {
988                                 throw new PlatformNotSupportedException ("This property is not supported.");
989                         }
990                 }
991 #endif
992                 public Stream InputStream {
993                         get {
994                                 if (input_stream == null)
995                                         MakeInputStream ();
996
997                                 return input_stream;
998                         }
999                 }
1000
1001                 public bool IsAuthenticated {
1002                         get {
1003                                 if (context.User == null || context.User.Identity == null)
1004                                         return false;
1005                                 return context.User.Identity.IsAuthenticated;
1006                         }
1007                 }
1008
1009                 public bool IsSecureConnection {
1010                         get {
1011                                 if (worker_request == null)
1012                                         return false;
1013                                 return worker_request.IsSecure ();
1014                         }
1015                 }
1016
1017                 public string this [string key] {
1018                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1019                         get {
1020                                 // "The QueryString, Form, Cookies, or ServerVariables collection member
1021                                 // specified in the key parameter."
1022                                 string val = QueryString [key];
1023                                 if (val == null)
1024                                         val = Form [key];
1025                                 if (val == null) {
1026                                         HttpCookie cookie = Cookies [key];
1027                                         if (cookie != null)
1028                                                 val = cookie.Value;
1029                                 }
1030                                 if (val == null)
1031                                         val = ServerVariables [key];
1032
1033                                 return val;
1034                         }
1035                 }
1036
1037                 public NameValueCollection Params {
1038                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1039                         get {
1040                                 if (all_params == null)
1041                                         all_params = new HttpParamsCollection (QueryString, Form, ServerVariables, Cookies);
1042
1043                                 return all_params;
1044                         }
1045                 }
1046
1047                 internal string PathNoValidation {
1048                         get {
1049                                 if (original_path == null) {
1050                                         if (url_components != null)
1051                                                 // use only if it's already been instantiated, so that we can't go into endless
1052                                                 // recursion in some scenarios
1053                                                 original_path = UrlComponents.Path;
1054                                         else
1055                                                 original_path = ApplyUrlMapping (worker_request.GetUriPath ());
1056                                 }
1057
1058                                 return original_path;
1059                         }
1060                 }
1061                 
1062                 public string Path {
1063                         get {
1064                                 if (unescaped_path == null) {
1065                                         unescaped_path = PathNoValidation;
1066 #if NET_4_0
1067                                         if (validateRequestNewMode) {
1068                                                 RequestValidator validator = RequestValidator.Current;
1069                                                 int validationFailureIndex;
1070                                                 
1071                                                 if (!validator.IsValidRequestString (HttpContext.Current, unescaped_path, RequestValidationSource.Path, null, out validationFailureIndex))
1072                                                         ThrowValidationException ("Path", "Path", unescaped_path);
1073                                         }
1074 #endif
1075                                 }
1076                                 
1077                                 return unescaped_path;
1078                         }
1079                 }
1080
1081                 public string PathInfo {
1082                         get {
1083                                 if (path_info == null) {
1084                                         if (worker_request == null)
1085                                                 return String.Empty;
1086                                         path_info = worker_request.GetPathInfo () ?? String.Empty;
1087 #if NET_4_0
1088                                         if (validateRequestNewMode) {
1089                                                 RequestValidator validator = RequestValidator.Current;
1090                                                 int validationFailureIndex;
1091                                                 
1092                                                 if (!validator.IsValidRequestString (HttpContext.Current, path_info, RequestValidationSource.PathInfo, null, out validationFailureIndex))
1093                                                         ThrowValidationException ("PathInfo", "PathInfo", path_info);
1094                                         }
1095 #endif
1096                                 }
1097
1098                                 return path_info;
1099                         }
1100                 }
1101
1102                 public string PhysicalApplicationPath {
1103                         get {
1104                                 if (worker_request == null)
1105                                         throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
1106
1107                                 string path = HttpRuntime.AppDomainAppPath;
1108                                 if (SecurityManager.SecurityEnabled) {
1109                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
1110                                 }
1111                                 return path;
1112                         }
1113                 }
1114
1115                 public string PhysicalPath {
1116                         get {
1117                                 if (worker_request == null)
1118                                         return String.Empty; // don't check security with an empty string!
1119
1120                                 if (physical_path == null) {
1121                                         // Don't call HttpRequest.MapPath here, as that one *trims* the input
1122                                         physical_path = worker_request.MapPath (FilePath);
1123                                 }
1124
1125                                 if (SecurityManager.SecurityEnabled) {
1126                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
1127                                 }
1128                                 return physical_path;
1129                         }
1130                 }
1131
1132                 internal string RootVirtualDir {
1133                         get {
1134                                 if (root_virtual_dir == null){
1135                                         string fp = FilePath;
1136                                         int p = fp.LastIndexOf ('/');
1137
1138                                         if (p < 1)
1139                                                 root_virtual_dir = "/";
1140                                         else
1141                                                 root_virtual_dir = fp.Substring (0, p);
1142                                 }
1143                                 return root_virtual_dir;
1144                         }
1145                 }
1146
1147                 internal WebROCollection QueryStringUnvalidated {
1148                         get {
1149                                 if (query_string_nvc == null) {
1150                                         query_string_nvc = new WebROCollection ();
1151                                         string q = UrlComponents.Query;
1152                                         if (q != null) {
1153                                                 if (q.Length != 0)
1154                                                         q = q.Remove(0, 1);
1155                                         
1156                                                 HttpUtility.ParseQueryString (q, ContentEncoding, query_string_nvc);
1157                                         }
1158                                         
1159                                         query_string_nvc.Protect();
1160                                 }
1161
1162                                 return query_string_nvc;
1163                         }
1164                 }
1165                 
1166                 public NameValueCollection QueryString {
1167                         get {
1168                                 NameValueCollection query_string_nvc = QueryStringUnvalidated;
1169 #if NET_4_0
1170                                 if (validateRequestNewMode && !checked_query_string) {
1171                                         if (!lazyQueryStringValidation) {
1172                                                 // Setting this before calling the validator prevents
1173                                                 // possible endless recursion
1174                                                 checked_query_string = true;
1175                                                 ValidateNameValueCollection ("QueryString", query_string_nvc, RequestValidationSource.QueryString);
1176                                         }
1177                                 } else
1178 #endif
1179                                         if (validate_query_string && !checked_query_string) {
1180                                                 // Setting this before calling the validator prevents
1181                                                 // possible endless recursion
1182                                                 checked_query_string = true;
1183                                                 ValidateNameValueCollection ("QueryString", query_string_nvc);
1184                                         }
1185                                 
1186                                 return query_string_nvc;
1187                         }
1188                 }
1189
1190                 public string RawUrl {
1191                         get {
1192                                 if (raw_url == null) {
1193                                         if (worker_request != null)
1194                                                 raw_url = worker_request.GetRawUrl ();
1195                                         else
1196                                                 raw_url = UrlComponents.Path + UrlComponents.Query;
1197                                         
1198                                         if (raw_url == null)
1199                                                 raw_url = String.Empty;
1200 #if NET_4_0
1201                                         if (validateRequestNewMode) {
1202                                                 RequestValidator validator = RequestValidator.Current;
1203                                                 int validationFailureIndex;
1204
1205                                                 if (!validator.IsValidRequestString (HttpContext.Current, raw_url, RequestValidationSource.RawUrl, null, out validationFailureIndex))
1206                                                         ThrowValidationException ("RawUrl", "RawUrl", raw_url);
1207                                         }
1208 #endif
1209                                 }
1210                                 
1211                                 return raw_url;
1212                         }
1213                 }
1214
1215                 //
1216                 // "GET" or "SET"
1217                 //
1218                 public string RequestType {
1219                         get {
1220                                 if (request_type == null){
1221                                         if (worker_request != null) {
1222                                                 request_type = worker_request.GetHttpVerbName ();
1223                                                 http_method = request_type;
1224                                         } else {
1225                                                 request_type = "GET";
1226                                         }
1227                                 }
1228                                 return request_type;
1229                         }
1230
1231                         set {
1232                                 request_type = value;
1233                         }
1234                 }
1235
1236                 public NameValueCollection ServerVariables {
1237                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1238                         get {
1239                                 if (server_variables == null)
1240                                         server_variables = new ServerVariablesCollection (this);
1241
1242                                 return server_variables;
1243                         }
1244                 }
1245
1246                 public int TotalBytes {
1247                         get {
1248                                 Stream ins = InputStream;
1249                                 return (int) ins.Length;
1250                         }
1251                 }
1252
1253                 public Uri Url {
1254                         get {
1255                                 if (cached_url == null) {
1256                                         if (orig_url == null)
1257                                                 cached_url = UrlComponents.Uri;
1258                                         else
1259                                                 cached_url = new Uri (orig_url);
1260                                 }
1261
1262                                 return cached_url;                      
1263                         }
1264                 }
1265
1266                 public Uri UrlReferrer {
1267                         get {
1268                                 if (worker_request == null)
1269                                         return null;
1270
1271                                 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
1272                                 if (hr == null)
1273                                         return null;
1274
1275                                 Uri uri = null;
1276                                 try {
1277                                         uri = new Uri (hr);
1278                                 } catch (UriFormatException) {}
1279                                 return uri;
1280                         }
1281                 }
1282
1283                 public string UserAgent {
1284                         get {
1285                                 if (worker_request == null)
1286                                         return null;
1287
1288                                 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
1289                         }
1290                 }
1291
1292                 public string UserHostAddress {
1293                         get {
1294                                 if (worker_request == null)
1295                                         return null;
1296
1297                                 return worker_request.GetRemoteAddress ();
1298                         }
1299                 }
1300
1301                 public string UserHostName {
1302                         get {
1303                                 if (worker_request == null)
1304                                         return null;
1305
1306                                 return worker_request.GetRemoteName ();
1307                         }
1308                 }
1309
1310                 public string [] UserLanguages {
1311                         get {
1312                                 if (worker_request == null)
1313                                         return null;
1314
1315                                 if (user_languages == null)
1316                                         user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
1317
1318                                 return user_languages;
1319                         }
1320                 }
1321
1322                 public byte [] BinaryRead (int count)
1323                 {
1324                         if (count < 0)
1325                                 throw new ArgumentException ("count is < 0");
1326
1327                         Stream s = InputStream;
1328                         byte [] ret = new byte [count];
1329                         if (s.Read (ret, 0, count) != count)
1330                                 throw new ArgumentException (
1331                                         String.Format ("count {0} exceeds length of available input {1}",
1332                                                 count, s.Length - s.Position));
1333                         return ret;
1334                 }
1335
1336                 public int [] MapImageCoordinates (string imageFieldName)
1337                 {
1338                         string method = HttpMethod;
1339                         NameValueCollection coll = null;
1340                         if (method == "HEAD" || method == "GET")
1341                                 coll = QueryString;
1342                         else if (method == "POST")
1343                                 coll = Form;
1344
1345                         if (coll == null)
1346                                 return null;
1347
1348                         string x = coll [imageFieldName + ".x"];
1349                         if (x == null || x == "")
1350                                 return null;
1351
1352                         string y = coll [imageFieldName + ".y"];
1353                         if (y == null || y == "")
1354                                 return null;
1355
1356                         int [] result = new int [2];
1357                         try {
1358                                 result [0] = Int32.Parse (x);
1359                                 result [1] = Int32.Parse (y);
1360                         } catch {
1361                                 return null;
1362                         }
1363
1364                         return result;
1365                 }
1366
1367                 public string MapPath (string virtualPath)
1368                 {
1369                         if (worker_request == null)
1370                                 return null;
1371
1372                         return MapPath (virtualPath, BaseVirtualDir, true);
1373                 }
1374
1375                 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1376                 {
1377                         if (worker_request == null)
1378                                 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
1379
1380                         if (virtualPath == null)
1381                                 virtualPath = "~";
1382                         else {
1383                                 virtualPath = virtualPath.Trim ();
1384                                 if (virtualPath.Length == 0)
1385                                         virtualPath = "~";
1386                         }
1387
1388                         if (!VirtualPathUtility.IsValidVirtualPath (virtualPath))
1389                                 throw HttpException.NewWithCode (String.Format ("'{0}' is not a valid virtual path.", virtualPath), WebEventCodes.RuntimeErrorRequestAbort);
1390
1391                         string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
1392                         if (!VirtualPathUtility.IsRooted (virtualPath)) {
1393                                 if (StrUtils.IsNullOrEmpty (baseVirtualDir))
1394                                         baseVirtualDir = appVirtualPath;
1395                                 virtualPath = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (baseVirtualDir), virtualPath);
1396                                 if (!VirtualPathUtility.IsAbsolute (virtualPath))
1397                                         virtualPath = VirtualPathUtility.ToAbsolute (virtualPath, false);
1398                         } else if (!VirtualPathUtility.IsAbsolute (virtualPath))
1399                                 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath, false);
1400
1401                         bool isAppVirtualPath = String.Compare (virtualPath, appVirtualPath, RuntimeHelpers.StringComparison) == 0;
1402                         appVirtualPath = VirtualPathUtility.AppendTrailingSlash (appVirtualPath);
1403                         if (!allowCrossAppMapping){
1404                                 if (!StrUtils.StartsWith (virtualPath, appVirtualPath, true))
1405                                         throw new ArgumentException ("MapPath: Mapping across applications not allowed");
1406                                 if (appVirtualPath.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1407                                         throw HttpException.NewWithCode ("MapPath: Mapping across applications not allowed", WebEventCodes.RuntimeErrorRequestAbort);
1408                         }
1409                         
1410                         if (!isAppVirtualPath && !virtualPath.StartsWith (appVirtualPath, RuntimeHelpers.StringComparison))
1411                                 throw new InvalidOperationException (String.Format ("Failed to map path '{0}'", virtualPath));
1412 #if TARGET_JVM
1413                         return worker_request.MapPath (virtualPath);
1414 #else
1415                         string path = worker_request.MapPath (virtualPath);
1416                         if (virtualPath [virtualPath.Length - 1] != '/' && path [path.Length - 1] == System.IO.Path.DirectorySeparatorChar)
1417                                 path = path.TrimEnd (System.IO.Path.DirectorySeparatorChar);
1418                         return path;
1419 #endif
1420                 }
1421
1422                 public void SaveAs (string filename, bool includeHeaders)
1423                 {
1424                         Stream output = new FileStream (filename, FileMode.Create);
1425                         if (includeHeaders) {
1426                                 StringBuilder sb = new StringBuilder ();
1427                                 string version = String.Empty;
1428                                 string path = "/";
1429                                 if (worker_request != null) {
1430                                         version = worker_request.GetHttpVersion ();
1431                                         path = UrlComponents.Path;
1432                                 }
1433                                 string qs = UrlComponents.Query;
1434
1435                                 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1436                                 NameValueCollection coll = Headers;
1437                                 foreach (string k in coll.AllKeys) {
1438                                         sb.Append (k);
1439                                         sb.Append (':');
1440                                         sb.Append (coll [k]);
1441                                         sb.Append ("\r\n");
1442                                 }
1443                                 sb.Append ("\r\n");
1444                                 // latin1
1445                                 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1446                                 output.Write (bytes, 0, bytes.Length);
1447                         }
1448
1449                         // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1450                         // to keep InputStream in its state.
1451                         Stream input = GetSubStream (InputStream);
1452                         try {
1453                                 long len = input.Length;
1454                                 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1455                                 byte [] data = new byte [buf_size];
1456                                 int count = 0;
1457                                 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1458                                         output.Write (data, 0, count);
1459                                         len -= count;
1460                                 }
1461                         } finally {
1462                                 output.Flush ();
1463                                 output.Close ();
1464                                 EndSubStream (input);
1465                         }
1466                 }
1467
1468                 public void ValidateInput ()
1469                 {
1470                         validate_cookies = true;
1471                         validate_query_string = true;
1472                         validate_form = true;
1473 #if NET_4_0
1474                         inputValidationEnabled = true;
1475 #endif
1476                 }
1477 #if NET_4_0
1478                 internal void Validate ()
1479                 {
1480                         var cfg = HttpRuntime.Section;
1481                         string query = UrlComponents.Query;
1482                         
1483                         if (query != null && query.Length > cfg.MaxQueryStringLength)
1484                                 throw new HttpException (400, "The length of the query string for this request exceeds the configured maxQueryStringLength value.");
1485                         
1486                         string path = PathNoValidation;
1487                         if (path != null) {
1488                                 if (path.Length > cfg.MaxUrlLength)
1489                                         throw new HttpException (400, "The length of the URL for this request exceeds the configured maxUrlLength value.");
1490                                 
1491                                 char[] invalidChars = RequestPathInvalidCharacters;
1492                                 if (invalidChars != null) {
1493                                         int idx = path.IndexOfAny (invalidChars);
1494                                         if (idx != -1)
1495                                                 throw HttpException.NewWithCode (
1496                                                         String.Format ("A potentially dangerous Request.Path value was detected from the client ({0}).", path [idx]),
1497                                                         WebEventCodes.RuntimeErrorValidationFailure
1498                                                 );
1499                                 }
1500                         }
1501
1502                         if (validateRequestNewMode)
1503                                 ValidateInput ();
1504                 }
1505 #endif
1506 #region internal routines
1507                 internal string ClientTarget {
1508                         get {
1509                                 return client_target;
1510                         }
1511
1512                         set {
1513                                 client_target = value;
1514                         }
1515                 }
1516                 
1517                 public bool IsLocal {
1518                         get {
1519                                 string address = worker_request.GetRemoteAddress ();
1520
1521                                 if (StrUtils.IsNullOrEmpty (address))
1522                                         return false;
1523
1524                                 if (address == "127.0.0.1")
1525                                         return true;
1526
1527                                 System.Net.IPAddress remoteAddr = System.Net.IPAddress.Parse (address);
1528                                 if (System.Net.IPAddress.IsLoopback (remoteAddr))
1529                                         return true;
1530
1531                                 for (int i = 0; i < host_addresses.Length; i++)
1532                                         if (remoteAddr.Equals (host_addresses [i]))
1533                                                 return true;
1534
1535                                 return false;
1536                         }
1537                 }
1538
1539                 internal void SetFilePath (string path)
1540                 {
1541                         file_path = path;
1542                         physical_path = null;
1543                         original_path = null;
1544                 }
1545
1546                 internal void SetCurrentExePath (string path)
1547                 {
1548                         cached_url = null;
1549                         current_exe_path = path;
1550                         UrlComponents.Path = path + PathInfo;
1551                         // recreated on demand
1552                         root_virtual_dir = null;
1553                         base_virtual_dir = null;
1554                         physical_path = null;
1555                         unescaped_path = null;
1556                         original_path = null;
1557                 }
1558
1559                 internal void SetPathInfo (string pi)
1560                 {
1561                         cached_url = null;
1562                         path_info = pi;
1563                         original_path = null;
1564
1565                         string path = UrlComponents.Path;
1566                         UrlComponents.Path = path + PathInfo;
1567                 }
1568 #if NET_4_0
1569                 internal void SetFormCollection (WebROCollection coll, bool lazyValidation)
1570                 {
1571                         if (coll == null)
1572                                 return;
1573                         form = coll;
1574                         lazyFormValidation = lazyValidation;
1575                 }
1576
1577                 internal void SetQueryStringCollection (WebROCollection coll, bool lazyValidation)
1578                 {
1579                         if (coll == null)
1580                                 return;
1581                         query_string_nvc = coll;
1582                         lazyQueryStringValidation = lazyValidation;
1583                 }
1584 #endif
1585                 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1586                 internal void SetHeader (string name, string value)
1587                 {
1588                         WebROCollection h = (WebROCollection) Headers;
1589                         h.Unprotect ();
1590                         h [name] = value;
1591                         h.Protect ();
1592                 }
1593
1594                 // Notice: there is nothing raw about this querystring.
1595                 internal string QueryStringRaw {
1596                         get {
1597                                 UriBuilder urlComponents = UrlComponents;
1598
1599                                 if (urlComponents == null) {
1600                                         string ret = worker_request.GetQueryString ();
1601
1602                                         if (ret == null || ret.Length == 0)
1603                                                 return String.Empty;
1604
1605                                         if (ret [0] == '?')
1606                                                 return ret;
1607
1608                                         return "?" + ret;
1609                                 }
1610                                 
1611                                 return UrlComponents.Query;
1612                         }
1613
1614                         set {
1615                                 UrlComponents.Query = value;
1616                                 cached_url = null;
1617                                 query_string_nvc = null;
1618                         }
1619                 }
1620
1621                 // Internal, dont know what it does, so flagged as public so we can see it.
1622                 internal void SetForm (WebROCollection coll)
1623                 {
1624                         form = coll;
1625                 }
1626
1627                 internal HttpWorkerRequest WorkerRequest {
1628                         get {
1629                                 return worker_request;
1630                         }
1631                 }
1632
1633                 internal HttpContext Context {
1634                         get { return context; }
1635                         set { context = value; }
1636                 }
1637
1638                 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1639                 {
1640                         if (coll == null)
1641                                 return;
1642                 
1643                         foreach (string key in coll.Keys) {
1644                                 string val = coll [key];
1645                                 if (val != null && val.Length > 0 && IsInvalidString (val))
1646                                         ThrowValidationException (name, key, val);
1647                         }
1648                 }
1649 #if NET_4_0
1650                 static void ValidateNameValueCollection (string name, NameValueCollection coll, RequestValidationSource source)
1651                 {
1652                         if (coll == null)
1653                                 return;
1654
1655                         RequestValidator validator = RequestValidator.Current;
1656                         int validationFailureIndex;
1657                         HttpContext context = HttpContext.Current;
1658
1659                         foreach (string key in coll.Keys) {
1660                                 string val = coll [key];
1661                                 if (val != null && val.Length > 0 && !validator.IsValidRequestString (context, val, source, key, out validationFailureIndex))
1662                                         ThrowValidationException (name, key, val);
1663                         }
1664                 }
1665
1666                 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1667                 public void InsertEntityBody ()
1668                 {
1669                         throw new PlatformNotSupportedException ("This method is not supported.");
1670                 }
1671
1672                 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1673                 public void InsertEntityBody (byte[] buffer, int offset, int count)
1674                 {
1675                         throw new PlatformNotSupportedException ("This method is not supported.");
1676                 }
1677 #endif
1678                 static void ValidateCookieCollection (HttpCookieCollection cookies)
1679                 {
1680                         if (cookies == null)
1681                                 return;
1682                 
1683                         int size = cookies.Count;
1684                         HttpCookie cookie;
1685 #if NET_4_0
1686                         RequestValidator validator = RequestValidator.Current;
1687                         int validationFailureIndex;
1688                         HttpContext context = HttpContext.Current;
1689 #endif
1690                         bool invalid;
1691                         
1692                         for (int i = 0 ; i < size ; i++) {
1693                                 cookie = cookies[i];
1694                                 if (cookie == null)
1695                                         continue;
1696                                 
1697                                 string value = cookie.Value;
1698                                 string name = cookie.Name;
1699
1700                                 if (!String.IsNullOrEmpty (value)) {
1701 #if NET_4_0
1702                                         if (validateRequestNewMode)
1703                                                 invalid = !validator.IsValidRequestString (context, value, RequestValidationSource.Cookies, name, out validationFailureIndex);
1704                                         else
1705 #endif
1706                                                 invalid = IsInvalidString (value);
1707
1708                                         if (invalid)
1709                                                 ThrowValidationException ("Cookies", name, value);
1710                                 }
1711                         }
1712                 }
1713
1714                 static void ThrowValidationException (string name, string key, string value)
1715                 {
1716                         string v = "\"" + value + "\"";
1717                         if (v.Length > 20)
1718                                 v = v.Substring (0, 16) + "...\"";
1719                 
1720                         string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1721                                                     "detected from the client ({1}={2}).", name, key, v);
1722                 
1723                         throw new HttpRequestValidationException (msg);
1724                 }
1725 #if NET_4_0
1726                 internal static void ValidateString (string key, string value, RequestValidationSource source)
1727                 {
1728                         if (String.IsNullOrEmpty (value))
1729                                 return;
1730 #pragma warning disable 219
1731                         int ignore;
1732 #pragma warning restore 219
1733                         if (IsInvalidString (value, out ignore))
1734                                 ThrowValidationException (source.ToString (), key, value);
1735                 }
1736 #endif
1737                 internal static bool IsInvalidString (string val)
1738                 {
1739 #pragma warning disable 219
1740                         int validationFailureIndex;
1741 #pragma warning restore 219
1742                         return IsInvalidString (val, out validationFailureIndex);
1743                 }
1744
1745                 internal static bool IsInvalidString (string val, out int validationFailureIndex)
1746                 {
1747                         validationFailureIndex = 0;
1748
1749                         int len = val.Length;
1750                         if (len < 2)
1751                                 return false;
1752
1753                         char current = val [0];
1754                         for (int idx = 1; idx < len; idx++) {
1755                                 char next = val [idx];
1756                                 // See http://secunia.com/advisories/14325
1757                                 if (current == '<' || current == '\xff1c') {
1758                                         if (next == '!' || next < ' '
1759                                             || (next >= 'a' && next <= 'z')
1760                                             || (next >= 'A' && next <= 'Z')) {
1761                                                 validationFailureIndex = idx - 1;
1762                                                 return true;
1763                                         }
1764                                 } else if (current == '&' && next == '#') {
1765                                         validationFailureIndex = idx - 1;
1766                                         return true;
1767                                 }
1768
1769                                 current = next;
1770                         }
1771
1772                         return false;
1773                 }
1774                 
1775                 static System.Net.IPAddress [] GetLocalHostAddresses ()
1776                 {
1777                         try {
1778                                 string hostName = System.Net.Dns.GetHostName ();
1779                                 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostAddresses (hostName);
1780                                 return ipaddr;
1781                         } catch {
1782                                 return new System.Net.IPAddress[0];
1783                         }
1784                 }
1785         }
1786 #endregion
1787
1788 #region Helper classes
1789         
1790         //
1791         // Stream-based multipart handling.
1792         //
1793         // In this incarnation deals with an HttpInputStream as we are now using
1794         // IntPtr-based streams instead of byte [].   In the future, we will also
1795         // send uploads above a certain threshold into the disk (to implement
1796         // limit-less HttpInputFiles). 
1797         //
1798         
1799         class HttpMultipart {
1800
1801                 public class Element {
1802                         public string ContentType;
1803                         public string Name;
1804                         public string Filename;
1805                         public long Start;
1806                         public long Length;
1807                         
1808                         public override string ToString ()
1809                         {
1810                                 return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
1811                                         Start.ToString () + ", Length " + Length.ToString ();
1812                         }
1813                 }
1814                 
1815                 Stream data;
1816                 string boundary;
1817                 byte [] boundary_bytes;
1818                 byte [] buffer;
1819                 bool at_eof;
1820                 Encoding encoding;
1821                 StringBuilder sb;
1822                 
1823                 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
1824                 
1825                 // See RFC 2046 
1826                 // In the case of multipart entities, in which one or more different
1827                 // sets of data are combined in a single body, a "multipart" media type
1828                 // field must appear in the entity's header.  The body must then contain
1829                 // one or more body parts, each preceded by a boundary delimiter line,
1830                 // and the last one followed by a closing boundary delimiter line.
1831                 // After its boundary delimiter line, each body part then consists of a
1832                 // header area, a blank line, and a body area.  Thus a body part is
1833                 // similar to an RFC 822 message in syntax, but different in meaning.
1834                 
1835                 public HttpMultipart (Stream data, string b, Encoding encoding)
1836                 {
1837                         this.data = data;
1838                         boundary = b;
1839                         boundary_bytes = encoding.GetBytes (b);
1840                         buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1841                         this.encoding = encoding;
1842                         sb = new StringBuilder ();
1843                 }
1844
1845                 string ReadLine ()
1846                 {
1847                         // CRLF or LF are ok as line endings.
1848                         bool got_cr = false;
1849                         int b = 0;
1850                         sb.Length = 0;
1851                         while (true) {
1852                                 b = data.ReadByte ();
1853                                 if (b == -1) {
1854                                         return null;
1855                                 }
1856
1857                                 if (b == LF) {
1858                                         break;
1859                                 }
1860                                 got_cr = (b == CR);
1861                                 sb.Append ((char) b);
1862                         }
1863
1864                         if (got_cr)
1865                                 sb.Length--;
1866
1867                         return sb.ToString ();
1868
1869                 }
1870
1871                 static string GetContentDispositionAttribute (string l, string name)
1872                 {
1873                         int idx = l.IndexOf (name + "=\"");
1874                         if (idx < 0)
1875                                 return null;
1876                         int begin = idx + name.Length + "=\"".Length;
1877                         int end = l.IndexOf ('"', begin);
1878                         if (end < 0)
1879                                 return null;
1880                         if (begin == end)
1881                                 return "";
1882                         return l.Substring (begin, end - begin);
1883                 }
1884
1885                 string GetContentDispositionAttributeWithEncoding (string l, string name)
1886                 {
1887                         int idx = l.IndexOf (name + "=\"");
1888                         if (idx < 0)
1889                                 return null;
1890                         int begin = idx + name.Length + "=\"".Length;
1891                         int end = l.IndexOf ('"', begin);
1892                         if (end < 0)
1893                                 return null;
1894                         if (begin == end)
1895                                 return "";
1896
1897                         string temp = l.Substring (begin, end - begin);
1898                         byte [] source = new byte [temp.Length];
1899                         for (int i = temp.Length - 1; i >= 0; i--)
1900                                 source [i] = (byte) temp [i];
1901
1902                         return encoding.GetString (source);
1903                 }
1904
1905                 bool ReadBoundary ()
1906                 {
1907                         try {
1908                                 string line = ReadLine ();
1909                                 while (line == "")
1910                                         line = ReadLine ();
1911                                 if (line [0] != '-' || line [1] != '-')
1912                                         return false;
1913
1914                                 if (!StrUtils.EndsWith (line, boundary, false))
1915                                         return true;
1916                         } catch {
1917                         }
1918
1919                         return false;
1920                 }
1921
1922                 string ReadHeaders ()
1923                 {
1924                         string s = ReadLine ();
1925                         if (s == "")
1926                                 return null;
1927
1928                         return s;
1929                 }
1930
1931                 bool CompareBytes (byte [] orig, byte [] other)
1932                 {
1933                         for (int i = orig.Length - 1; i >= 0; i--)
1934                                 if (orig [i] != other [i])
1935                                         return false;
1936
1937                         return true;
1938                 }
1939
1940                 long MoveToNextBoundary ()
1941                 {
1942                         long retval = 0;
1943                         bool got_cr = false;
1944
1945                         int state = 0;
1946                         int c = data.ReadByte ();
1947                         while (true) {
1948                                 if (c == -1)
1949                                         return -1;
1950
1951                                 if (state == 0 && c == LF) {
1952                                         retval = data.Position - 1;
1953                                         if (got_cr)
1954                                                 retval--;
1955                                         state = 1;
1956                                         c = data.ReadByte ();
1957                                 } else if (state == 0) {
1958                                         got_cr = (c == CR);
1959                                         c = data.ReadByte ();
1960                                 } else if (state == 1 && c == '-') {
1961                                         c = data.ReadByte ();
1962                                         if (c == -1)
1963                                                 return -1;
1964
1965                                         if (c != '-') {
1966                                                 state = 0;
1967                                                 got_cr = false;
1968                                                 continue; // no ReadByte() here
1969                                         }
1970
1971                                         int nread = data.Read (buffer, 0, buffer.Length);
1972                                         int bl = buffer.Length;
1973                                         if (nread != bl)
1974                                                 return -1;
1975
1976                                         if (!CompareBytes (boundary_bytes, buffer)) {
1977                                                 state = 0;
1978                                                 data.Position = retval + 2;
1979                                                 if (got_cr) {
1980                                                         data.Position++;
1981                                                         got_cr = false;
1982                                                 }
1983                                                 c = data.ReadByte ();
1984                                                 continue;
1985                                         }
1986
1987                                         if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
1988                                                 at_eof = true;
1989                                         } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
1990                                                 state = 0;
1991                                                 data.Position = retval + 2;
1992                                                 if (got_cr) {
1993                                                         data.Position++;
1994                                                         got_cr = false;
1995                                                 }
1996                                                 c = data.ReadByte ();
1997                                                 continue;
1998                                         }
1999                                         data.Position = retval + 2;
2000                                         if (got_cr)
2001                                                 data.Position++;
2002                                         break;
2003                                 } else {
2004                                         // state == 1
2005                                         state = 0; // no ReadByte() here
2006                                 }
2007                         }
2008
2009                         return retval;
2010                 }
2011
2012                 public Element ReadNextElement ()
2013                 {
2014                         if (at_eof || ReadBoundary ())
2015                                 return null;
2016
2017                         Element elem = new Element ();
2018                         string header;
2019                         while ((header = ReadHeaders ()) != null) {
2020                                 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
2021                                         elem.Name = GetContentDispositionAttribute (header, "name");
2022                                         elem.Filename = StripPath (GetContentDispositionAttributeWithEncoding (header, "filename"));
2023                                 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
2024                                         elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
2025                                 }
2026                         }
2027
2028                         long start = data.Position;
2029                         elem.Start = start;
2030                         long pos = MoveToNextBoundary ();
2031                         if (pos == -1)
2032                                 return null;
2033
2034                         elem.Length = pos - start;
2035                         return elem;
2036                 }
2037
2038                 static string StripPath (string path)
2039                 {
2040                         if (path == null || path.Length == 0)
2041                                 return path;
2042                         
2043                         if (path.IndexOf (":\\") != 1 && !path.StartsWith ("\\\\"))
2044                                 return path;
2045                         return path.Substring (path.LastIndexOf ('\\') + 1);
2046                 }
2047         }
2048 #endregion
2049 }
2050