2 // System.Web.HttpRequest.cs
6 // Miguel de Icaza (miguel@novell.com)
7 // Gonzalo Paniagua Javier (gonzalo@novell.com)
8 // Marek Habersack <mhabersack@novell.com>
12 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
13 // Copyright (C) 2011-2012 Xamarin, Inc (http://xamarin.com)
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:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
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.
35 using System.Collections;
36 using System.Collections.Specialized;
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;
45 using System.Web.Util;
46 using System.Globalization;
49 using System.Security.Authentication.ExtendedProtection;
50 using System.Web.Routing;
55 // CAS - no InheritanceDemand here as the class is sealed
56 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
57 public sealed partial class HttpRequest
59 HttpWorkerRequest worker_request;
61 WebROCollection query_string_nvc;
65 string orig_url = null;
66 UriBuilder url_components;
71 // On-demand computed values
73 HttpBrowserCapabilities browser_capabilities;
74 string file_path, base_virtual_dir, root_virtual_dir, client_file_path;
76 int content_length = -1;
78 string current_exe_path;
80 string unescaped_path;
84 WebROCollection all_params;
85 WebROCollection headers;
87 InputFilterStream input_filter;
89 HttpCookieCollection cookies;
93 HttpFileCollection files;
95 ServerVariablesCollection server_variables;
96 HttpClientCertificate client_cert;
99 string [] accept_types;
100 string [] user_languages;
102 TempFileStream request_file;
104 readonly static System.Net.IPAddress [] host_addresses;
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 = {'?'};
112 bool lazyFormValidation;
113 bool lazyQueryStringValidation;
114 bool inputValidationEnabled;
115 RequestContext requestContext;
117 static bool validateRequestNewMode;
118 internal static bool ValidateRequestNewMode {
119 get { return validateRequestNewMode; }
122 internal bool InputValidationEnabled {
123 get { return inputValidationEnabled; }
126 private static char[] RequestPathInvalidCharacters {
130 private static char[] CharsFromList (string list)
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.
136 string [] pieces = list.Split (',');
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 ();
147 chars [i] = trimmed [0];
154 static HttpRequest ()
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)
165 Version validationMode = HttpRuntime.Section.RequestValidationMode;
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);
175 // unlikely to happen
178 host_addresses = GetLocalHostAddresses ();
181 public HttpRequest (string filename, string url, string queryString)
183 // warning 169: what are we supposed to do with filename?
185 //this.filename = filename;
188 url_components = new UriBuilder (url);
189 url_components.Query = queryString;
191 query_string_nvc = new WebROCollection ();
192 if (queryString != null)
193 HttpUtility.ParseQueryString (queryString, Encoding.Default, query_string_nvc);
194 query_string_nvc.Protect ();
197 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
199 this.worker_request = worker_request;
200 this.context = context;
203 internal UriBuilder UrlComponents {
205 if (url_components == null) {
207 byte[] queryStringRaw = worker_request.GetQueryStringRawBytes();
208 if(queryStringRaw != null)
209 query = ContentEncoding.GetString(queryStringRaw);
211 query = worker_request.GetQueryString();
213 BuildUrlComponents (ApplyUrlMapping (worker_request.GetUriPath ()), query);
215 return url_components;
219 void BuildUrlComponents (string path, string query)
221 if (url_components != null)
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);
232 internal string ApplyUrlMapping (string url)
234 if (urlMappings == null)
237 string relUrl = VirtualPathUtility.ToAppRelative (url);
238 UrlMapping um = null;
240 foreach (UrlMapping u in urlMappings) {
243 if (String.Compare (relUrl, u.Url, StringComparison.Ordinal) == 0) {
252 string rawUrl = VirtualPathUtility.ToAbsolute (um.MappedUrl.Trim ());
253 Uri newUrl = new Uri ("http://host.com" + rawUrl);
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 ();
262 BuildUrlComponents (newUrl.AbsolutePath, newUrl.Query);
264 return url_components.Path;
267 string [] SplitHeader (int header_index)
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 ();
279 public string [] AcceptTypes {
281 if (worker_request == null)
284 if (accept_types == null)
285 accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
292 public WindowsIdentity LogonUserIdentity {
293 get { throw new NotImplementedException (); }
298 public string AnonymousID {
303 anonymous_id = value;
307 public string ApplicationPath {
309 if (worker_request == null)
311 return worker_request.GetAppPath ();
315 public HttpBrowserCapabilities Browser {
317 if (browser_capabilities == null)
319 browser_capabilities = HttpCapabilitiesBase.BrowserCapabilitiesProvider.GetBrowserCapabilities (this);
321 browser_capabilities = (HttpBrowserCapabilities)
322 HttpCapabilitiesBase.GetConfigCapabilities (null, this);
325 return browser_capabilities;
329 browser_capabilities = value;
333 internal bool BrowserMightHaveSpecialWriter {
335 return (browser_capabilities != null
336 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
340 internal bool BrowserMightHaveAdapters {
342 return (browser_capabilities != null
343 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
347 public HttpClientCertificate ClientCertificate {
349 if (client_cert == null)
350 client_cert = new HttpClientCertificate (worker_request);
355 static internal string GetParameter (string header, string attr)
357 int ap = header.IndexOf (attr);
362 if (ap >= header.Length)
365 char ending = header [ap];
369 int end = header.IndexOf (ending, ap+1);
371 return (ending == '"') ? null : header.Substring (ap);
373 return header.Substring (ap+1, end-ap-1);
376 public Encoding ContentEncoding {
378 if (encoding == null){
379 if (worker_request == null)
380 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
382 string content_type = ContentType;
383 string parameter = GetParameter (content_type, "; charset=");
384 if (parameter == null) {
385 encoding = WebEncoding.RequestEncoding;
388 // Do what the #1 web server does
389 encoding = Encoding.GetEncoding (parameter);
391 encoding = WebEncoding.RequestEncoding;
403 public int ContentLength {
405 if (content_length == -1){
406 if (worker_request == null)
409 string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
413 content_length = Int32.Parse (cl);
418 // content_length will still be < 0, but we know we gotta read from the client
419 if (content_length < 0)
422 return content_length;
426 public string ContentType {
428 if (content_type == null){
429 if (worker_request != null)
430 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
432 if (content_type == null)
433 content_type = String.Empty;
440 content_type = value;
444 public HttpCookieCollection Cookies {
446 if (cookies == null) {
447 if (worker_request == null) {
448 cookies = new HttpCookieCollection ();
450 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
451 cookies = new HttpCookieCollection (cookie_hv);
456 // For J2EE portal support we emulate cookies using the session.
457 GetSessionCookiesForPortal (cookies);
459 bool needValidation = validate_cookies;
461 needValidation |= validateRequestNewMode;
463 if (needValidation && !checked_cookies) {
464 // Setting this before calling the validator prevents
465 // possible endless recursion
466 checked_cookies = true;
467 ValidateCookieCollection (cookies);
475 public string CurrentExecutionFilePath {
477 if (current_exe_path != null)
478 return current_exe_path;
484 public string CurrentExecutionFilePathExtension {
485 get { return global::System.IO.Path.GetExtension (CurrentExecutionFilePath); }
488 public string AppRelativeCurrentExecutionFilePath {
490 return VirtualPathUtility.ToAppRelative (CurrentExecutionFilePath);
494 public string FilePath {
496 if (worker_request == null)
497 return "/"; // required for 2.0
499 if (file_path == null)
500 file_path = UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
506 internal string ClientFilePath {
508 if (client_file_path == null) {
509 if (worker_request == null)
512 return UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
515 return client_file_path;
519 if (value == null || value.Length == 0)
520 client_file_path = null;
522 client_file_path = value;
526 internal string BaseVirtualDir {
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);
533 int p = base_virtual_dir.LastIndexOf ('/');
537 base_virtual_dir = base_virtual_dir.Substring (0, p);
539 base_virtual_dir = "/";
541 return base_virtual_dir;
545 public HttpFileCollection Files {
548 files = new HttpFileCollection ();
549 if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
550 form = new WebROCollection ();
559 public Stream Filter {
564 if (input_filter == null)
565 input_filter = new InputFilterStream ();
571 // This checks that get_ was called before.
572 if (input_filter == null)
573 throw new HttpException ("Invalid filter");
579 // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
580 static Stream GetSubStream (Stream stream)
583 if (stream is IntPtrStream)
584 return new IntPtrStream (stream);
587 if (stream is MemoryStream) {
588 MemoryStream other = (MemoryStream) stream;
589 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
592 if (stream is TempFileStream) {
593 ((TempFileStream) stream).SavePosition ();
597 throw new NotSupportedException ("The stream is " + stream.GetType ());
600 static void EndSubStream (Stream stream)
602 if (stream is TempFileStream) {
603 ((TempFileStream) stream).RestorePosition ();
608 // Loads the data on the form for multipart/form-data
610 void LoadMultiPart ()
612 string boundary = GetParameter (ContentType, "; boundary=");
613 if (boundary == null)
616 Stream input = GetSubStream (InputStream);
617 HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
619 HttpMultipart.Element e;
620 while ((e = multi_part.ReadNextElement ()) != null) {
621 if (e.Filename == null){
622 byte [] copy = new byte [e.Length];
624 input.Position = e.Start;
625 input.Read (copy, 0, (int) e.Length);
627 form.Add (e.Name, ContentEncoding.GetString (copy));
630 // We use a substream, as in 2.x we will support large uploads streamed to disk,
632 HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
633 files.AddFile (e.Name, sub);
636 EndSubStream (input);
640 // Adds the key/value to the form, and sets the argumets to empty
642 void AddRawKeyValue (StringBuilder key, StringBuilder value)
644 string decodedKey = HttpUtility.UrlDecode (key.ToString (), ContentEncoding);
645 form.Add (decodedKey,
646 HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
653 // Loads the form data from on a application/x-www-form-urlencoded post
656 void RawLoadWwwForm ()
661 using (Stream input = GetSubStream (InputStream)) {
662 using (StreamReader s = new StreamReader (input, ContentEncoding)) {
663 StringBuilder key = new StringBuilder ();
664 StringBuilder value = new StringBuilder ();
667 while ((c = s.Read ()) != -1){
670 while ((c = s.Read ()) != -1){
672 AddRawKeyValue (key, value);
675 value.Append ((char) c);
678 AddRawKeyValue (key, value);
682 AddRawKeyValue (key, value);
684 key.Append ((char) c);
687 AddRawKeyValue (key, value);
689 EndSubStream (input);
694 bool IsContentType (string ct, bool starts_with)
697 return StrUtils.StartsWith (ContentType, ct, true);
699 return String.Compare (ContentType, ct, true, Helpers.InvariantCulture) == 0;
702 internal WebROCollection FormUnvalidated {
705 form = new WebROCollection ();
706 files = new HttpFileCollection ();
708 if (IsContentType ("multipart/form-data", true))
711 IsContentType ("application/x-www-form-urlencoded", true))
721 public NameValueCollection Form {
723 NameValueCollection form = FormUnvalidated;
725 if (validateRequestNewMode && !checked_form) {
726 if (!lazyFormValidation) {
727 // Setting this before calling the validator prevents
728 // possible endless recursion
730 ValidateNameValueCollection ("Form", form, RequestValidationSource.Form);
734 if (validate_form && !checked_form){
736 ValidateNameValueCollection ("Form", form);
743 public NameValueCollection Headers {
745 if (headers == null) {
746 headers = new HeadersCollection (this);
748 if (validateRequestNewMode) {
749 RequestValidator validator = RequestValidator.Current;
750 int validationFailureIndex;
752 foreach (string hkey in headers.AllKeys) {
753 string value = headers [hkey];
755 if (!validator.IsValidRequestString (HttpContext.Current, value, RequestValidationSource.Headers, hkey, out validationFailureIndex))
756 ThrowValidationException ("Headers", hkey, value);
766 public string HttpMethod {
768 if (http_method == null){
769 if (worker_request != null)
770 http_method = worker_request.GetHttpVerbName ();
778 void DoFilter (byte [] buffer)
780 if (input_filter == null || filter == null)
783 if (buffer.Length < 1024)
784 buffer = new byte [1024];
786 // Replace the input with the filtered input
787 input_filter.BaseStream = input_stream;
788 MemoryStream ms = new MemoryStream ();
790 int n = filter.Read (buffer, 0, buffer.Length);
793 ms.Write (buffer, 0, n);
795 // From now on input_stream has the filtered input
796 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
800 const int INPUT_BUFFER_SIZE = 32*1024;
802 TempFileStream GetTempStream ()
804 string tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
805 TempFileStream f = null;
807 Random rnd = new Random ();
812 path = System.IO.Path.Combine (tempdir, "tmp" + num.ToString("x") + ".req");
815 f = new TempFileStream (path);
816 } catch (SecurityException) {
817 // avoid an endless loop
825 void MakeInputStream ()
827 if (input_stream != null)
830 if (worker_request == null) {
831 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
832 DoFilter (new byte [1024]);
837 // Use an unmanaged memory block as this might be a large
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);
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);
854 input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
856 DoFilter (new byte [1024]);
861 total = buffer.Length;
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;
869 output.Write (buffer, 0, total);
871 if (total < content_length) {
872 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
875 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
876 n = worker_request.ReadEntityBody (buffer, min);
879 output.Write (buffer, 0, n);
881 } while (total < content_length);
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);
896 Marshal.Copy (buffer, 0, content, total);
898 if (total < content_length) {
899 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
902 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
903 n = worker_request.ReadEntityBody (buffer, min);
906 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
908 } while (total < content_length);
911 input_stream = new IntPtrStream (content, total);
913 // Buffers the request in a MemoryStream or writes to disk if threshold exceeded
914 MemoryStream ms = new MemoryStream ();
917 ms.Write (buffer, 0, total);
919 buffer = new byte [INPUT_BUFFER_SIZE];
920 long maxlength = config.MaxRequestLength * 1024L;
921 long disk_th = config.RequestLengthDiskThreshold * 1024L;
924 n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
928 if (total < 0 || total > maxlength)
929 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
931 if (ms != null && total > disk_th) {
932 // Swith to on-disk file.
933 request_file = GetTempStream ();
934 ms.WriteTo (request_file);
936 output = request_file;
938 output.Write (buffer, 0, n);
942 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
944 request_file.SetReadOnly ();
945 input_stream = request_file;
950 if (total < content_length)
951 throw HttpException.NewWithCode (411, "The request body is incomplete.", WebEventCodes.WebErrorOtherError);
955 internal void ReleaseResources ()
958 if (input_stream != null){
959 stream = input_stream;
966 if (request_file != null) {
967 stream = request_file;
975 public RequestContext RequestContext {
977 if (requestContext == null)
978 requestContext = new RequestContext (new HttpContextWrapper (this.context ?? HttpContext.Current), new RouteData ());
980 return requestContext;
983 internal set { requestContext = value; }
986 public ChannelBinding HttpChannelBinding {
988 throw new PlatformNotSupportedException ("This property is not supported.");
992 public Stream InputStream {
994 if (input_stream == null)
1001 public bool IsAuthenticated {
1003 if (context.User == null || context.User.Identity == null)
1005 return context.User.Identity.IsAuthenticated;
1009 public bool IsSecureConnection {
1011 if (worker_request == null)
1013 return worker_request.IsSecure ();
1017 public string this [string key] {
1018 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1020 // "The QueryString, Form, Cookies, or ServerVariables collection member
1021 // specified in the key parameter."
1022 string val = QueryString [key];
1026 HttpCookie cookie = Cookies [key];
1031 val = ServerVariables [key];
1037 public NameValueCollection Params {
1038 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1040 if (all_params == null)
1041 all_params = new HttpParamsCollection (QueryString, Form, ServerVariables, Cookies);
1047 internal string PathNoValidation {
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;
1055 original_path = ApplyUrlMapping (worker_request.GetUriPath ());
1058 return original_path;
1062 public string Path {
1064 if (unescaped_path == null) {
1065 unescaped_path = PathNoValidation;
1067 if (validateRequestNewMode) {
1068 RequestValidator validator = RequestValidator.Current;
1069 int validationFailureIndex;
1071 if (!validator.IsValidRequestString (HttpContext.Current, unescaped_path, RequestValidationSource.Path, null, out validationFailureIndex))
1072 ThrowValidationException ("Path", "Path", unescaped_path);
1077 return unescaped_path;
1081 public string PathInfo {
1083 if (path_info == null) {
1084 if (worker_request == null)
1085 return String.Empty;
1086 path_info = worker_request.GetPathInfo () ?? String.Empty;
1088 if (validateRequestNewMode) {
1089 RequestValidator validator = RequestValidator.Current;
1090 int validationFailureIndex;
1092 if (!validator.IsValidRequestString (HttpContext.Current, path_info, RequestValidationSource.PathInfo, null, out validationFailureIndex))
1093 ThrowValidationException ("PathInfo", "PathInfo", path_info);
1102 public string PhysicalApplicationPath {
1104 if (worker_request == null)
1105 throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
1107 string path = HttpRuntime.AppDomainAppPath;
1108 if (SecurityManager.SecurityEnabled) {
1109 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
1115 public string PhysicalPath {
1117 if (worker_request == null)
1118 return String.Empty; // don't check security with an empty string!
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);
1125 if (SecurityManager.SecurityEnabled) {
1126 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
1128 return physical_path;
1132 internal string RootVirtualDir {
1134 if (root_virtual_dir == null){
1135 string fp = FilePath;
1136 int p = fp.LastIndexOf ('/');
1139 root_virtual_dir = "/";
1141 root_virtual_dir = fp.Substring (0, p);
1143 return root_virtual_dir;
1147 internal WebROCollection QueryStringUnvalidated {
1149 if (query_string_nvc == null) {
1150 query_string_nvc = new WebROCollection ();
1151 string q = UrlComponents.Query;
1156 HttpUtility.ParseQueryString (q, ContentEncoding, query_string_nvc);
1159 query_string_nvc.Protect();
1162 return query_string_nvc;
1166 public NameValueCollection QueryString {
1168 NameValueCollection query_string_nvc = QueryStringUnvalidated;
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);
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);
1186 return query_string_nvc;
1190 public string RawUrl {
1192 if (raw_url == null) {
1193 if (worker_request != null)
1194 raw_url = worker_request.GetRawUrl ();
1196 raw_url = UrlComponents.Path + UrlComponents.Query;
1198 if (raw_url == null)
1199 raw_url = String.Empty;
1201 if (validateRequestNewMode) {
1202 RequestValidator validator = RequestValidator.Current;
1203 int validationFailureIndex;
1205 if (!validator.IsValidRequestString (HttpContext.Current, raw_url, RequestValidationSource.RawUrl, null, out validationFailureIndex))
1206 ThrowValidationException ("RawUrl", "RawUrl", raw_url);
1218 public string RequestType {
1220 if (request_type == null){
1221 if (worker_request != null) {
1222 request_type = worker_request.GetHttpVerbName ();
1223 http_method = request_type;
1225 request_type = "GET";
1228 return request_type;
1232 request_type = value;
1236 public NameValueCollection ServerVariables {
1237 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1239 if (server_variables == null)
1240 server_variables = new ServerVariablesCollection (this);
1242 return server_variables;
1246 public int TotalBytes {
1248 Stream ins = InputStream;
1249 return (int) ins.Length;
1255 if (cached_url == null) {
1256 if (orig_url == null)
1257 cached_url = UrlComponents.Uri;
1259 cached_url = new Uri (orig_url);
1266 public Uri UrlReferrer {
1268 if (worker_request == null)
1271 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
1278 } catch (UriFormatException) {}
1283 public string UserAgent {
1285 if (worker_request == null)
1288 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
1292 public string UserHostAddress {
1294 if (worker_request == null)
1297 return worker_request.GetRemoteAddress ();
1301 public string UserHostName {
1303 if (worker_request == null)
1306 return worker_request.GetRemoteName ();
1310 public string [] UserLanguages {
1312 if (worker_request == null)
1315 if (user_languages == null)
1316 user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
1318 return user_languages;
1322 public byte [] BinaryRead (int count)
1325 throw new ArgumentException ("count is < 0");
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));
1336 public int [] MapImageCoordinates (string imageFieldName)
1338 string method = HttpMethod;
1339 NameValueCollection coll = null;
1340 if (method == "HEAD" || method == "GET")
1342 else if (method == "POST")
1348 string x = coll [imageFieldName + ".x"];
1349 if (x == null || x == "")
1352 string y = coll [imageFieldName + ".y"];
1353 if (y == null || y == "")
1356 int [] result = new int [2];
1358 result [0] = Int32.Parse (x);
1359 result [1] = Int32.Parse (y);
1367 public string MapPath (string virtualPath)
1369 if (worker_request == null)
1372 return MapPath (virtualPath, BaseVirtualDir, true);
1375 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1377 if (worker_request == null)
1378 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
1380 if (virtualPath == null)
1383 virtualPath = virtualPath.Trim ();
1384 if (virtualPath.Length == 0)
1388 if (!VirtualPathUtility.IsValidVirtualPath (virtualPath))
1389 throw HttpException.NewWithCode (String.Format ("'{0}' is not a valid virtual path.", virtualPath), WebEventCodes.RuntimeErrorRequestAbort);
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);
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);
1410 if (!isAppVirtualPath && !virtualPath.StartsWith (appVirtualPath, RuntimeHelpers.StringComparison))
1411 throw new InvalidOperationException (String.Format ("Failed to map path '{0}'", virtualPath));
1413 return worker_request.MapPath (virtualPath);
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);
1422 public void SaveAs (string filename, bool includeHeaders)
1424 Stream output = new FileStream (filename, FileMode.Create);
1425 if (includeHeaders) {
1426 StringBuilder sb = new StringBuilder ();
1427 string version = String.Empty;
1429 if (worker_request != null) {
1430 version = worker_request.GetHttpVersion ();
1431 path = UrlComponents.Path;
1433 string qs = UrlComponents.Query;
1435 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1436 NameValueCollection coll = Headers;
1437 foreach (string k in coll.AllKeys) {
1440 sb.Append (coll [k]);
1445 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1446 output.Write (bytes, 0, bytes.Length);
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);
1453 long len = input.Length;
1454 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1455 byte [] data = new byte [buf_size];
1457 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1458 output.Write (data, 0, count);
1464 EndSubStream (input);
1468 public void ValidateInput ()
1470 validate_cookies = true;
1471 validate_query_string = true;
1472 validate_form = true;
1474 inputValidationEnabled = true;
1478 internal void Validate ()
1480 var cfg = HttpRuntime.Section;
1481 string query = UrlComponents.Query;
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.");
1486 string path = PathNoValidation;
1488 if (path.Length > cfg.MaxUrlLength)
1489 throw new HttpException (400, "The length of the URL for this request exceeds the configured maxUrlLength value.");
1491 char[] invalidChars = RequestPathInvalidCharacters;
1492 if (invalidChars != null) {
1493 int idx = path.IndexOfAny (invalidChars);
1495 throw HttpException.NewWithCode (
1496 String.Format ("A potentially dangerous Request.Path value was detected from the client ({0}).", path [idx]),
1497 WebEventCodes.RuntimeErrorValidationFailure
1502 if (validateRequestNewMode)
1506 #region internal routines
1507 internal string ClientTarget {
1509 return client_target;
1513 client_target = value;
1517 public bool IsLocal {
1519 string address = worker_request.GetRemoteAddress ();
1521 if (StrUtils.IsNullOrEmpty (address))
1524 if (address == "127.0.0.1")
1527 System.Net.IPAddress remoteAddr = System.Net.IPAddress.Parse (address);
1528 if (System.Net.IPAddress.IsLoopback (remoteAddr))
1531 for (int i = 0; i < host_addresses.Length; i++)
1532 if (remoteAddr.Equals (host_addresses [i]))
1539 internal void SetFilePath (string path)
1542 physical_path = null;
1543 original_path = null;
1546 internal void SetCurrentExePath (string path)
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;
1559 internal void SetPathInfo (string pi)
1563 original_path = null;
1565 string path = UrlComponents.Path;
1566 UrlComponents.Path = path + PathInfo;
1569 internal void SetFormCollection (WebROCollection coll, bool lazyValidation)
1574 lazyFormValidation = lazyValidation;
1577 internal void SetQueryStringCollection (WebROCollection coll, bool lazyValidation)
1581 query_string_nvc = coll;
1582 lazyQueryStringValidation = lazyValidation;
1585 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1586 internal void SetHeader (string name, string value)
1588 WebROCollection h = (WebROCollection) Headers;
1594 // Notice: there is nothing raw about this querystring.
1595 internal string QueryStringRaw {
1597 UriBuilder urlComponents = UrlComponents;
1599 if (urlComponents == null) {
1600 string ret = worker_request.GetQueryString ();
1602 if (ret == null || ret.Length == 0)
1603 return String.Empty;
1611 return UrlComponents.Query;
1615 UrlComponents.Query = value;
1617 query_string_nvc = null;
1621 // Internal, dont know what it does, so flagged as public so we can see it.
1622 internal void SetForm (WebROCollection coll)
1627 internal HttpWorkerRequest WorkerRequest {
1629 return worker_request;
1633 internal HttpContext Context {
1634 get { return context; }
1635 set { context = value; }
1638 static void ValidateNameValueCollection (string name, NameValueCollection coll)
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);
1650 static void ValidateNameValueCollection (string name, NameValueCollection coll, RequestValidationSource source)
1655 RequestValidator validator = RequestValidator.Current;
1656 int validationFailureIndex;
1657 HttpContext context = HttpContext.Current;
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);
1666 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1667 public void InsertEntityBody ()
1669 throw new PlatformNotSupportedException ("This method is not supported.");
1672 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1673 public void InsertEntityBody (byte[] buffer, int offset, int count)
1675 throw new PlatformNotSupportedException ("This method is not supported.");
1678 static void ValidateCookieCollection (HttpCookieCollection cookies)
1680 if (cookies == null)
1683 int size = cookies.Count;
1686 RequestValidator validator = RequestValidator.Current;
1687 int validationFailureIndex;
1688 HttpContext context = HttpContext.Current;
1692 for (int i = 0 ; i < size ; i++) {
1693 cookie = cookies[i];
1697 string value = cookie.Value;
1698 string name = cookie.Name;
1700 if (!String.IsNullOrEmpty (value)) {
1702 if (validateRequestNewMode)
1703 invalid = !validator.IsValidRequestString (context, value, RequestValidationSource.Cookies, name, out validationFailureIndex);
1706 invalid = IsInvalidString (value);
1709 ThrowValidationException ("Cookies", name, value);
1714 static void ThrowValidationException (string name, string key, string value)
1716 string v = "\"" + value + "\"";
1718 v = v.Substring (0, 16) + "...\"";
1720 string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1721 "detected from the client ({1}={2}).", name, key, v);
1723 throw new HttpRequestValidationException (msg);
1726 internal static void ValidateString (string key, string value, RequestValidationSource source)
1728 if (String.IsNullOrEmpty (value))
1730 #pragma warning disable 219
1732 #pragma warning restore 219
1733 if (IsInvalidString (value, out ignore))
1734 ThrowValidationException (source.ToString (), key, value);
1737 internal static bool IsInvalidString (string val)
1739 #pragma warning disable 219
1740 int validationFailureIndex;
1741 #pragma warning restore 219
1742 return IsInvalidString (val, out validationFailureIndex);
1745 internal static bool IsInvalidString (string val, out int validationFailureIndex)
1747 validationFailureIndex = 0;
1749 int len = val.Length;
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;
1764 } else if (current == '&' && next == '#') {
1765 validationFailureIndex = idx - 1;
1775 static System.Net.IPAddress [] GetLocalHostAddresses ()
1778 string hostName = System.Net.Dns.GetHostName ();
1779 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostAddresses (hostName);
1782 return new System.Net.IPAddress[0];
1788 #region Helper classes
1791 // Stream-based multipart handling.
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).
1799 class HttpMultipart {
1801 public class Element {
1802 public string ContentType;
1804 public string Filename;
1808 public override string ToString ()
1810 return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
1811 Start.ToString () + ", Length " + Length.ToString ();
1817 byte [] boundary_bytes;
1823 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
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.
1835 public HttpMultipart (Stream data, string b, Encoding encoding)
1839 boundary_bytes = encoding.GetBytes (b);
1840 buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1841 this.encoding = encoding;
1842 sb = new StringBuilder ();
1847 // CRLF or LF are ok as line endings.
1848 bool got_cr = false;
1852 b = data.ReadByte ();
1861 sb.Append ((char) b);
1867 return sb.ToString ();
1871 static string GetContentDispositionAttribute (string l, string name)
1873 int idx = l.IndexOf (name + "=\"");
1876 int begin = idx + name.Length + "=\"".Length;
1877 int end = l.IndexOf ('"', begin);
1882 return l.Substring (begin, end - begin);
1885 string GetContentDispositionAttributeWithEncoding (string l, string name)
1887 int idx = l.IndexOf (name + "=\"");
1890 int begin = idx + name.Length + "=\"".Length;
1891 int end = l.IndexOf ('"', begin);
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];
1902 return encoding.GetString (source);
1905 bool ReadBoundary ()
1908 string line = ReadLine ();
1911 if (line [0] != '-' || line [1] != '-')
1914 if (!StrUtils.EndsWith (line, boundary, false))
1922 string ReadHeaders ()
1924 string s = ReadLine ();
1931 bool CompareBytes (byte [] orig, byte [] other)
1933 for (int i = orig.Length - 1; i >= 0; i--)
1934 if (orig [i] != other [i])
1940 long MoveToNextBoundary ()
1943 bool got_cr = false;
1946 int c = data.ReadByte ();
1951 if (state == 0 && c == LF) {
1952 retval = data.Position - 1;
1956 c = data.ReadByte ();
1957 } else if (state == 0) {
1959 c = data.ReadByte ();
1960 } else if (state == 1 && c == '-') {
1961 c = data.ReadByte ();
1968 continue; // no ReadByte() here
1971 int nread = data.Read (buffer, 0, buffer.Length);
1972 int bl = buffer.Length;
1976 if (!CompareBytes (boundary_bytes, buffer)) {
1978 data.Position = retval + 2;
1983 c = data.ReadByte ();
1987 if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
1989 } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
1991 data.Position = retval + 2;
1996 c = data.ReadByte ();
1999 data.Position = retval + 2;
2005 state = 0; // no ReadByte() here
2012 public Element ReadNextElement ()
2014 if (at_eof || ReadBoundary ())
2017 Element elem = new Element ();
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 ();
2028 long start = data.Position;
2030 long pos = MoveToNextBoundary ();
2034 elem.Length = pos - start;
2038 static string StripPath (string path)
2040 if (path == null || path.Length == 0)
2043 if (path.IndexOf (":\\") != 1 && !path.StartsWith ("\\\\"))
2045 return path.Substring (path.LastIndexOf ('\\') + 1);