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)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
35 using System.Collections.Specialized;
37 using System.Runtime.InteropServices;
38 using System.Security;
39 using System.Security.Permissions;
40 using System.Security.Principal;
41 using System.Web.Configuration;
42 using System.Web.Management;
44 using System.Web.Util;
45 using System.Globalization;
48 using System.Security.Authentication.ExtendedProtection;
49 using System.Web.Routing;
54 // CAS - no InheritanceDemand here as the class is sealed
55 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
56 public sealed partial class HttpRequest
58 HttpWorkerRequest worker_request;
60 WebROCollection query_string_nvc;
64 string orig_url = null;
65 UriBuilder url_components;
70 // On-demand computed values
72 HttpBrowserCapabilities browser_capabilities;
73 string file_path, base_virtual_dir, root_virtual_dir, client_file_path;
75 int content_length = -1;
77 string current_exe_path;
79 string unescaped_path;
83 WebROCollection all_params;
84 WebROCollection headers;
86 InputFilterStream input_filter;
88 HttpCookieCollection cookies;
92 HttpFileCollection files;
94 ServerVariablesCollection server_variables;
95 HttpClientCertificate client_cert;
98 string [] accept_types;
99 string [] user_languages;
101 TempFileStream request_file;
103 readonly static System.Net.IPAddress [] host_addresses;
106 bool validate_cookies, validate_query_string, validate_form;
107 bool checked_cookies, checked_query_string, checked_form;
108 static readonly UrlMappingCollection urlMappings;
109 readonly static char [] queryTrimChars = {'?'};
111 RequestContext requestContext;
113 static bool validateRequestNewMode;
114 internal static bool ValidateRequestNewMode {
115 get { return validateRequestNewMode; }
118 internal static char[] RequestPathInvalidCharacters {
123 static HttpRequest ()
126 UrlMappingsSection ums = WebConfigurationManager.GetWebApplicationSection ("system.web/urlMappings") as UrlMappingsSection;
127 if (ums != null && ums.IsEnabled) {
128 urlMappings = ums.UrlMappings;
129 if (urlMappings.Count == 0)
134 HttpRuntimeSection runtimeConfig = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
135 Version validationMode = runtimeConfig.RequestValidationMode;
137 if (validationMode >= new Version (4, 0)) {
138 validateRequestNewMode = true;
139 string invalidChars = runtimeConfig.RequestPathInvalidCharacters;
140 if (!String.IsNullOrEmpty (invalidChars))
141 RequestPathInvalidCharacters = invalidChars.ToCharArray ();
145 // unlikely to happen
148 host_addresses = GetLocalHostAddresses ();
151 public HttpRequest (string filename, string url, string queryString)
153 // warning 169: what are we supposed to do with filename?
155 //this.filename = filename;
158 url_components = new UriBuilder (url);
159 url_components.Query = queryString;
161 query_string_nvc = new WebROCollection ();
162 if (queryString != null)
163 HttpUtility.ParseQueryString (queryString, Encoding.Default, query_string_nvc);
164 query_string_nvc.Protect ();
167 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
169 this.worker_request = worker_request;
170 this.context = context;
173 internal UriBuilder UrlComponents {
175 if (url_components == null) {
177 byte[] queryStringRaw = worker_request.GetQueryStringRawBytes();
178 if(queryStringRaw != null)
179 query = ContentEncoding.GetString(queryStringRaw);
181 query = worker_request.GetQueryString();
183 BuildUrlComponents (ApplyUrlMapping (worker_request.GetUriPath ()), query);
185 return url_components;
189 void BuildUrlComponents (string path, string query)
191 if (url_components != null)
193 url_components = new UriBuilder ();
194 url_components.Scheme = worker_request.GetProtocol ();
195 url_components.Host = worker_request.GetServerName ();
196 url_components.Port = worker_request.GetLocalPort ();
197 url_components.Path = path;
198 if (query != null && query.Length > 0)
199 url_components.Query = query.TrimStart (queryTrimChars);
202 internal string ApplyUrlMapping (string url)
204 if (urlMappings == null)
207 string relUrl = VirtualPathUtility.ToAppRelative (url);
208 UrlMapping um = null;
210 foreach (UrlMapping u in urlMappings) {
213 if (String.Compare (relUrl, u.Url, StringComparison.Ordinal) == 0) {
222 string rawUrl = VirtualPathUtility.ToAbsolute (um.MappedUrl.Trim ());
223 Uri newUrl = new Uri ("http://host.com" + rawUrl);
225 if (url_components != null) {
226 url_components.Path = newUrl.AbsolutePath;
227 url_components.Query = newUrl.Query.TrimStart (queryTrimChars);
228 query_string_nvc = new WebROCollection ();
229 HttpUtility.ParseQueryString (newUrl.Query, Encoding.Default, query_string_nvc);
230 query_string_nvc.Protect ();
232 BuildUrlComponents (newUrl.AbsolutePath, newUrl.Query);
234 return url_components.Path;
237 string [] SplitHeader (int header_index)
239 string [] result = null;
240 string header = worker_request.GetKnownRequestHeader (header_index);
241 if (header != null && header != "" && header.Trim () != "") {
242 result = header.Split (',');
243 for (int i = result.Length - 1; i >= 0; i--)
244 result [i] = result [i].Trim ();
249 public string [] AcceptTypes {
251 if (worker_request == null)
254 if (accept_types == null)
255 accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
262 public WindowsIdentity LogonUserIdentity {
263 get { throw new NotImplementedException (); }
268 public string AnonymousID {
273 anonymous_id = value;
277 public string ApplicationPath {
279 if (worker_request == null)
281 return worker_request.GetAppPath ();
285 public HttpBrowserCapabilities Browser {
287 if (browser_capabilities == null)
288 browser_capabilities = (HttpBrowserCapabilities)
289 HttpCapabilitiesBase.GetConfigCapabilities (null, this);
291 return browser_capabilities;
295 browser_capabilities = value;
299 internal bool BrowserMightHaveSpecialWriter {
301 return (browser_capabilities != null
302 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
306 internal bool BrowserMightHaveAdapters {
308 return (browser_capabilities != null
309 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
313 public HttpClientCertificate ClientCertificate {
315 if (client_cert == null)
316 client_cert = new HttpClientCertificate (worker_request);
321 static internal string GetParameter (string header, string attr)
323 int ap = header.IndexOf (attr);
328 if (ap >= header.Length)
331 char ending = header [ap];
335 int end = header.IndexOf (ending, ap+1);
337 return (ending == '"') ? null : header.Substring (ap);
339 return header.Substring (ap+1, end-ap-1);
342 public Encoding ContentEncoding {
344 if (encoding == null){
345 if (worker_request == null)
346 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
348 string content_type = ContentType;
349 string parameter = GetParameter (content_type, "; charset=");
350 if (parameter == null) {
351 encoding = WebEncoding.RequestEncoding;
354 // Do what the #1 web server does
355 encoding = Encoding.GetEncoding (parameter);
357 encoding = WebEncoding.RequestEncoding;
369 public int ContentLength {
371 if (content_length == -1){
372 if (worker_request == null)
375 string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
379 content_length = Int32.Parse (cl);
384 // content_length will still be < 0, but we know we gotta read from the client
385 if (content_length < 0)
388 return content_length;
392 public string ContentType {
394 if (content_type == null){
395 if (worker_request != null)
396 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
398 if (content_type == null)
399 content_type = String.Empty;
406 content_type = value;
410 public HttpCookieCollection Cookies {
412 if (cookies == null) {
413 if (worker_request == null) {
414 cookies = new HttpCookieCollection ();
416 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
417 cookies = new HttpCookieCollection (cookie_hv);
422 // For J2EE portal support we emulate cookies using the session.
423 GetSessionCookiesForPortal (cookies);
425 bool needValidation = validate_cookies;
427 needValidation |= validateRequestNewMode;
429 if (needValidation && !checked_cookies) {
430 // Setting this before calling the validator prevents
431 // possible endless recursion
432 checked_cookies = true;
433 ValidateCookieCollection (cookies);
441 public string CurrentExecutionFilePath {
443 if (current_exe_path != null)
444 return current_exe_path;
450 public string CurrentExecutionFilePathExtension {
451 get { return global::System.IO.Path.GetExtension (CurrentExecutionFilePath); }
454 public string AppRelativeCurrentExecutionFilePath {
456 return VirtualPathUtility.ToAppRelative (CurrentExecutionFilePath);
460 public string FilePath {
462 if (worker_request == null)
463 return "/"; // required for 2.0
465 if (file_path == null)
466 file_path = UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
472 internal string ClientFilePath {
474 if (client_file_path == null) {
475 if (worker_request == null)
478 return UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
481 return client_file_path;
485 if (value == null || value.Length == 0)
486 client_file_path = null;
488 client_file_path = value;
492 internal string BaseVirtualDir {
494 if (base_virtual_dir == null){
495 base_virtual_dir = FilePath;
496 if (UrlUtils.HasSessionId (base_virtual_dir))
497 base_virtual_dir = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (base_virtual_dir), base_virtual_dir);
499 int p = base_virtual_dir.LastIndexOf ('/');
503 base_virtual_dir = base_virtual_dir.Substring (0, p);
505 base_virtual_dir = "/";
507 return base_virtual_dir;
511 public HttpFileCollection Files {
514 files = new HttpFileCollection ();
515 if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
516 form = new WebROCollection ();
525 public Stream Filter {
530 if (input_filter == null)
531 input_filter = new InputFilterStream ();
537 // This checks that get_ was called before.
538 if (input_filter == null)
539 throw new HttpException ("Invalid filter");
545 // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
546 static Stream GetSubStream (Stream stream)
549 if (stream is IntPtrStream)
550 return new IntPtrStream (stream);
553 if (stream is MemoryStream) {
554 MemoryStream other = (MemoryStream) stream;
555 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
558 if (stream is TempFileStream) {
559 ((TempFileStream) stream).SavePosition ();
563 throw new NotSupportedException ("The stream is " + stream.GetType ());
566 static void EndSubStream (Stream stream)
568 if (stream is TempFileStream) {
569 ((TempFileStream) stream).RestorePosition ();
574 // Loads the data on the form for multipart/form-data
576 void LoadMultiPart ()
578 string boundary = GetParameter (ContentType, "; boundary=");
579 if (boundary == null)
582 Stream input = GetSubStream (InputStream);
583 HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
585 HttpMultipart.Element e;
586 while ((e = multi_part.ReadNextElement ()) != null) {
587 if (e.Filename == null){
588 byte [] copy = new byte [e.Length];
590 input.Position = e.Start;
591 input.Read (copy, 0, (int) e.Length);
593 form.Add (e.Name, ContentEncoding.GetString (copy));
596 // We use a substream, as in 2.x we will support large uploads streamed to disk,
598 HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
599 files.AddFile (e.Name, sub);
602 EndSubStream (input);
606 // Adds the key/value to the form, and sets the argumets to empty
608 void AddRawKeyValue (StringBuilder key, StringBuilder value)
610 string decodedKey = HttpUtility.UrlDecode (key.ToString (), ContentEncoding);
611 form.Add (decodedKey,
612 HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
619 // Loads the form data from on a application/x-www-form-urlencoded post
622 void RawLoadWwwForm ()
627 using (Stream input = GetSubStream (InputStream)) {
628 using (StreamReader s = new StreamReader (input, ContentEncoding)) {
629 StringBuilder key = new StringBuilder ();
630 StringBuilder value = new StringBuilder ();
633 while ((c = s.Read ()) != -1){
636 while ((c = s.Read ()) != -1){
638 AddRawKeyValue (key, value);
641 value.Append ((char) c);
644 AddRawKeyValue (key, value);
648 AddRawKeyValue (key, value);
650 key.Append ((char) c);
653 AddRawKeyValue (key, value);
655 EndSubStream (input);
660 bool IsContentType (string ct, bool starts_with)
663 return StrUtils.StartsWith (ContentType, ct, true);
665 return String.Compare (ContentType, ct, true, Helpers.InvariantCulture) == 0;
668 public NameValueCollection Form {
671 form = new WebROCollection ();
672 files = new HttpFileCollection ();
674 if (IsContentType ("multipart/form-data", true))
677 IsContentType ("application/x-www-form-urlencoded", true))
684 if (validateRequestNewMode && !checked_form) {
685 // Setting this before calling the validator prevents
686 // possible endless recursion
688 ValidateNameValueCollection ("Form", query_string_nvc, RequestValidationSource.Form);
691 if (validate_form && !checked_form){
693 ValidateNameValueCollection ("Form", form);
700 public NameValueCollection Headers {
702 if (headers == null) {
703 headers = new HeadersCollection (this);
705 if (validateRequestNewMode) {
706 RequestValidator validator = RequestValidator.Current;
707 int validationFailureIndex;
709 foreach (string hkey in headers.AllKeys) {
710 string value = headers [hkey];
712 if (!validator.IsValidRequestString (HttpContext.Current, value, RequestValidationSource.Headers, hkey, out validationFailureIndex))
713 ThrowValidationException ("Headers", hkey, value);
723 public string HttpMethod {
725 if (http_method == null){
726 if (worker_request != null)
727 http_method = worker_request.GetHttpVerbName ();
735 void DoFilter (byte [] buffer)
737 if (input_filter == null || filter == null)
740 if (buffer.Length < 1024)
741 buffer = new byte [1024];
743 // Replace the input with the filtered input
744 input_filter.BaseStream = input_stream;
745 MemoryStream ms = new MemoryStream ();
747 int n = filter.Read (buffer, 0, buffer.Length);
750 ms.Write (buffer, 0, n);
752 // From now on input_stream has the filtered input
753 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
757 const int INPUT_BUFFER_SIZE = 32*1024;
759 TempFileStream GetTempStream ()
761 string tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
762 TempFileStream f = null;
764 Random rnd = new Random ();
769 path = System.IO.Path.Combine (tempdir, "tmp" + num.ToString("x") + ".req");
772 f = new TempFileStream (path);
773 } catch (SecurityException) {
774 // avoid an endless loop
782 void MakeInputStream ()
784 if (input_stream != null)
787 if (worker_request == null) {
788 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
789 DoFilter (new byte [1024]);
794 // Use an unmanaged memory block as this might be a large
797 int content_length = ContentLength;
798 int content_length_kb = content_length / 1024;
799 HttpRuntimeSection config = (HttpRuntimeSection) WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime");
800 if (content_length_kb > config.MaxRequestLength)
801 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
805 buffer = worker_request.GetPreloadedEntityBody ();
806 // we check the instance field 'content_length' here, not the local var.
807 if (this.content_length <= 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
808 if (buffer == null || content_length == 0) {
809 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
811 input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
813 DoFilter (new byte [1024]);
818 total = buffer.Length;
820 if (content_length > 0 && content_length_kb >= config.RequestLengthDiskThreshold) {
821 // Writes the request to disk
822 total = Math.Min (content_length, total);
823 request_file = GetTempStream ();
824 Stream output = request_file;
826 output.Write (buffer, 0, total);
828 if (total < content_length) {
829 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
832 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
833 n = worker_request.ReadEntityBody (buffer, min);
836 output.Write (buffer, 0, n);
838 } while (total < content_length);
841 request_file.SetReadOnly ();
842 input_stream = request_file;
843 } else if (content_length > 0) {
844 // Buffers the request in an IntPtrStream
845 total = Math.Min (content_length, total);
846 IntPtr content = Marshal.AllocHGlobal (content_length);
847 if (content == (IntPtr) 0)
848 throw HttpException.NewWithCode (
849 String.Format ("Not enough memory to allocate {0} bytes.", content_length),
850 WebEventCodes.WebErrorOtherError);
853 Marshal.Copy (buffer, 0, content, total);
855 if (total < content_length) {
856 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
859 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
860 n = worker_request.ReadEntityBody (buffer, min);
863 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
865 } while (total < content_length);
868 input_stream = new IntPtrStream (content, total);
870 // Buffers the request in a MemoryStream or writes to disk if threshold exceeded
871 MemoryStream ms = new MemoryStream ();
874 ms.Write (buffer, 0, total);
876 buffer = new byte [INPUT_BUFFER_SIZE];
877 long maxlength = config.MaxRequestLength * 1024L;
878 long disk_th = config.RequestLengthDiskThreshold * 1024L;
881 n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
885 if (total < 0 || total > maxlength)
886 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
888 if (ms != null && total > disk_th) {
889 // Swith to on-disk file.
890 request_file = GetTempStream ();
891 ms.WriteTo (request_file);
893 output = request_file;
895 output.Write (buffer, 0, n);
899 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
901 request_file.SetReadOnly ();
902 input_stream = request_file;
907 if (total < content_length)
908 throw HttpException.NewWithCode (411, "The request body is incomplete.", WebEventCodes.WebErrorOtherError);
912 internal void ReleaseResources ()
915 if (input_stream != null){
916 stream = input_stream;
923 if (request_file != null) {
924 stream = request_file;
932 public RequestContext RequestContext {
934 if (requestContext == null)
935 requestContext = new RequestContext (new HttpContextWrapper (this.context ?? HttpContext.Current), new RouteData ());
937 return requestContext;
940 internal set { requestContext = value; }
943 public ChannelBinding HttpChannelBinding {
945 throw new PlatformNotSupportedException ("This property is not supported.");
949 public Stream InputStream {
951 if (input_stream == null)
958 public bool IsAuthenticated {
960 if (context.User == null || context.User.Identity == null)
962 return context.User.Identity.IsAuthenticated;
966 public bool IsSecureConnection {
968 if (worker_request == null)
970 return worker_request.IsSecure ();
974 public string this [string key] {
975 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
977 // "The QueryString, Form, Cookies, or ServerVariables collection member
978 // specified in the key parameter."
979 string val = QueryString [key];
983 HttpCookie cookie = Cookies [key];
988 val = ServerVariables [key];
994 public NameValueCollection Params {
995 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
997 if (all_params == null)
998 all_params = new HttpParamsCollection (QueryString, Form, ServerVariables, Cookies);
1004 internal string PathNoValidation {
1006 if (original_path == null) {
1007 if (url_components != null)
1008 // use only if it's already been instantiated, so that we can't go into endless
1009 // recursion in some scenarios
1010 original_path = UrlComponents.Path;
1012 original_path = ApplyUrlMapping (worker_request.GetUriPath ());
1015 return original_path;
1019 public string Path {
1021 if (unescaped_path == null) {
1022 unescaped_path = Uri.UnescapeDataString (PathNoValidation);
1024 if (validateRequestNewMode) {
1025 RequestValidator validator = RequestValidator.Current;
1026 int validationFailureIndex;
1028 if (!validator.IsValidRequestString (HttpContext.Current, unescaped_path, RequestValidationSource.Path, null, out validationFailureIndex))
1029 ThrowValidationException ("Path", "Path", unescaped_path);
1034 return unescaped_path;
1038 public string PathInfo {
1040 if (path_info == null) {
1041 if (worker_request == null)
1042 return String.Empty;
1043 path_info = worker_request.GetPathInfo () ?? String.Empty;
1045 if (validateRequestNewMode) {
1046 RequestValidator validator = RequestValidator.Current;
1047 int validationFailureIndex;
1049 if (!validator.IsValidRequestString (HttpContext.Current, path_info, RequestValidationSource.PathInfo, null, out validationFailureIndex))
1050 ThrowValidationException ("PathInfo", "PathInfo", path_info);
1059 public string PhysicalApplicationPath {
1061 if (worker_request == null)
1062 throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
1064 string path = HttpRuntime.AppDomainAppPath;
1065 if (SecurityManager.SecurityEnabled) {
1066 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
1072 public string PhysicalPath {
1074 if (worker_request == null)
1075 return String.Empty; // don't check security with an empty string!
1077 if (physical_path == null) {
1078 // Don't call HttpRequest.MapPath here, as that one *trims* the input
1079 physical_path = worker_request.MapPath (FilePath);
1082 if (SecurityManager.SecurityEnabled) {
1083 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
1085 return physical_path;
1089 internal string RootVirtualDir {
1091 if (root_virtual_dir == null){
1092 string fp = FilePath;
1093 int p = fp.LastIndexOf ('/');
1096 root_virtual_dir = "/";
1098 root_virtual_dir = fp.Substring (0, p);
1100 return root_virtual_dir;
1104 public NameValueCollection QueryString {
1106 if (query_string_nvc == null) {
1107 query_string_nvc = new WebROCollection ();
1108 string q = UrlComponents.Query;
1113 HttpUtility.ParseQueryString (q, ContentEncoding, query_string_nvc);
1116 query_string_nvc.Protect();
1119 if (validateRequestNewMode && !checked_query_string) {
1120 // Setting this before calling the validator prevents
1121 // possible endless recursion
1122 checked_query_string = true;
1123 ValidateNameValueCollection ("QueryString", query_string_nvc, RequestValidationSource.QueryString);
1126 if (validate_query_string && !checked_query_string) {
1127 ValidateNameValueCollection ("QueryString", query_string_nvc);
1128 checked_query_string = true;
1131 return query_string_nvc;
1135 public string RawUrl {
1137 if (raw_url == null) {
1138 if (worker_request != null)
1139 raw_url = worker_request.GetRawUrl ();
1141 raw_url = UrlComponents.Path + UrlComponents.Query;
1143 if (raw_url == null)
1144 raw_url = String.Empty;
1146 if (validateRequestNewMode) {
1147 RequestValidator validator = RequestValidator.Current;
1148 int validationFailureIndex;
1150 if (!validator.IsValidRequestString (HttpContext.Current, raw_url, RequestValidationSource.RawUrl, null, out validationFailureIndex))
1151 ThrowValidationException ("RawUrl", "RawUrl", raw_url);
1163 public string RequestType {
1165 if (request_type == null){
1166 if (worker_request != null) {
1167 request_type = worker_request.GetHttpVerbName ();
1168 http_method = request_type;
1170 request_type = "GET";
1173 return request_type;
1177 request_type = value;
1181 public NameValueCollection ServerVariables {
1182 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1184 if (server_variables == null)
1185 server_variables = new ServerVariablesCollection (this);
1187 return server_variables;
1191 public int TotalBytes {
1193 Stream ins = InputStream;
1194 return (int) ins.Length;
1200 if (cached_url == null) {
1201 if (orig_url == null)
1202 cached_url = UrlComponents.Uri;
1204 cached_url = new Uri (orig_url);
1211 public Uri UrlReferrer {
1213 if (worker_request == null)
1216 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
1223 } catch (UriFormatException) {}
1228 public string UserAgent {
1230 if (worker_request == null)
1233 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
1237 public string UserHostAddress {
1239 if (worker_request == null)
1242 return worker_request.GetRemoteAddress ();
1246 public string UserHostName {
1248 if (worker_request == null)
1251 return worker_request.GetRemoteName ();
1255 public string [] UserLanguages {
1257 if (worker_request == null)
1260 if (user_languages == null)
1261 user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
1263 return user_languages;
1267 public byte [] BinaryRead (int count)
1270 throw new ArgumentException ("count is < 0");
1272 Stream s = InputStream;
1273 byte [] ret = new byte [count];
1274 if (s.Read (ret, 0, count) != count)
1275 throw new ArgumentException (
1276 String.Format ("count {0} exceeds length of available input {1}",
1277 count, s.Length - s.Position));
1281 public int [] MapImageCoordinates (string imageFieldName)
1283 string method = HttpMethod;
1284 NameValueCollection coll = null;
1285 if (method == "HEAD" || method == "GET")
1287 else if (method == "POST")
1293 string x = coll [imageFieldName + ".x"];
1294 if (x == null || x == "")
1297 string y = coll [imageFieldName + ".y"];
1298 if (y == null || y == "")
1301 int [] result = new int [2];
1303 result [0] = Int32.Parse (x);
1304 result [1] = Int32.Parse (y);
1312 public string MapPath (string virtualPath)
1314 if (worker_request == null)
1317 return MapPath (virtualPath, BaseVirtualDir, true);
1320 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1322 if (worker_request == null)
1323 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
1325 if (virtualPath == null)
1328 virtualPath = virtualPath.Trim ();
1329 if (virtualPath.Length == 0)
1333 if (!VirtualPathUtility.IsValidVirtualPath (virtualPath))
1334 throw HttpException.NewWithCode (String.Format ("'{0}' is not a valid virtual path.", virtualPath), WebEventCodes.RuntimeErrorRequestAbort);
1336 string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
1337 if (!VirtualPathUtility.IsRooted (virtualPath)) {
1338 if (StrUtils.IsNullOrEmpty (baseVirtualDir))
1339 baseVirtualDir = appVirtualPath;
1340 virtualPath = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (baseVirtualDir), virtualPath);
1341 if (!VirtualPathUtility.IsAbsolute (virtualPath))
1342 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath);
1343 } else if (!VirtualPathUtility.IsAbsolute (virtualPath))
1344 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath);
1346 if (!allowCrossAppMapping){
1347 if (!StrUtils.StartsWith (virtualPath, appVirtualPath, true))
1348 throw HttpException.NewWithCode ("MapPath: Mapping across applications not allowed", WebEventCodes.RuntimeErrorRequestAbort);
1349 if (appVirtualPath.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1350 throw HttpException.NewWithCode ("MapPath: Mapping across applications not allowed", WebEventCodes.RuntimeErrorRequestAbort);
1353 return worker_request.MapPath (virtualPath);
1355 string path = worker_request.MapPath (virtualPath);
1356 if (virtualPath [virtualPath.Length - 1] != '/' && path [path.Length - 1] == System.IO.Path.DirectorySeparatorChar)
1357 path = path.TrimEnd (System.IO.Path.DirectorySeparatorChar);
1362 public void SaveAs (string filename, bool includeHeaders)
1364 Stream output = new FileStream (filename, FileMode.Create);
1365 if (includeHeaders) {
1366 StringBuilder sb = new StringBuilder ();
1367 string version = String.Empty;
1369 if (worker_request != null) {
1370 version = worker_request.GetHttpVersion ();
1371 path = UrlComponents.Path;
1373 string qs = UrlComponents.Query;
1375 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1376 NameValueCollection coll = Headers;
1377 foreach (string k in coll.AllKeys) {
1380 sb.Append (coll [k]);
1385 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1386 output.Write (bytes, 0, bytes.Length);
1389 // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1390 // to keep InputStream in its state.
1391 Stream input = GetSubStream (InputStream);
1393 long len = input.Length;
1394 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1395 byte [] data = new byte [buf_size];
1397 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1398 output.Write (data, 0, count);
1404 EndSubStream (input);
1408 public void ValidateInput ()
1410 validate_cookies = true;
1411 validate_query_string = true;
1412 validate_form = true;
1415 internal void Validate ()
1417 var cfg = WebConfigurationManager.GetSection ("system.web/httpRuntime") as HttpRuntimeSection;
1418 string query = UrlComponents.Query;
1420 if (query != null && query.Length > cfg.MaxQueryStringLength)
1421 throw new HttpException (400, "The length of the query string for this request exceeds the configured maxQueryStringLength value.");
1423 string path = PathNoValidation;
1425 if (path.Length > cfg.MaxUrlLength)
1426 throw new HttpException (400, "The length of the URL for this request exceeds the configured maxUrlLength value.");
1428 char[] invalidChars = RequestPathInvalidCharacters;
1429 if (invalidChars != null) {
1430 int idx = path.IndexOfAny (invalidChars);
1432 throw HttpException.NewWithCode (
1433 String.Format ("A potentially dangerous Request.Path value was detected from the client ({0}).", path [idx]),
1434 WebEventCodes.RuntimeErrorValidationFailure
1440 #region internal routines
1441 internal string ClientTarget {
1443 return client_target;
1447 client_target = value;
1451 public bool IsLocal {
1453 string address = worker_request.GetRemoteAddress ();
1455 if (StrUtils.IsNullOrEmpty (address))
1458 if (address == "127.0.0.1")
1461 System.Net.IPAddress remoteAddr = System.Net.IPAddress.Parse (address);
1462 if (System.Net.IPAddress.IsLoopback (remoteAddr))
1465 for (int i = 0; i < host_addresses.Length; i++)
1466 if (remoteAddr.Equals (host_addresses [i]))
1473 internal void SetFilePath (string path)
1476 physical_path = null;
1477 original_path = null;
1480 internal void SetCurrentExePath (string path)
1483 current_exe_path = path;
1484 UrlComponents.Path = path + PathInfo;
1485 // recreated on demand
1486 root_virtual_dir = null;
1487 base_virtual_dir = null;
1488 physical_path = null;
1489 unescaped_path = null;
1490 original_path = null;
1493 internal void SetPathInfo (string pi)
1497 original_path = null;
1499 string path = UrlComponents.Path;
1500 UrlComponents.Path = path + PathInfo;
1503 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1504 internal void SetHeader (string name, string value)
1506 WebROCollection h = (WebROCollection) Headers;
1512 // Notice: there is nothing raw about this querystring.
1513 internal string QueryStringRaw {
1515 UriBuilder urlComponents = UrlComponents;
1517 if (urlComponents == null) {
1518 string ret = worker_request.GetQueryString ();
1520 if (ret == null || ret.Length == 0)
1521 return String.Empty;
1529 return UrlComponents.Query;
1533 UrlComponents.Query = value;
1535 query_string_nvc = null;
1539 // Internal, dont know what it does, so flagged as public so we can see it.
1540 internal void SetForm (WebROCollection coll)
1545 internal HttpWorkerRequest WorkerRequest {
1547 return worker_request;
1551 internal HttpContext Context {
1552 get { return context; }
1553 set { context = value; }
1556 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1561 foreach (string key in coll.Keys) {
1562 string val = coll [key];
1563 if (val != null && val.Length > 0 && IsInvalidString (val))
1564 ThrowValidationException (name, key, val);
1568 static void ValidateNameValueCollection (string name, NameValueCollection coll, RequestValidationSource source)
1573 RequestValidator validator = RequestValidator.Current;
1574 int validationFailureIndex;
1575 HttpContext context = HttpContext.Current;
1577 foreach (string key in coll.Keys) {
1578 string val = coll [key];
1579 if (val != null && val.Length > 0 && !validator.IsValidRequestString (context, val, source, key, out validationFailureIndex))
1580 ThrowValidationException (name, key, val);
1584 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1585 public void InsertEntityBody ()
1587 throw new PlatformNotSupportedException ("This method is not supported.");
1590 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1591 public void InsertEntityBody (byte[] buffer, int offset, int count)
1593 throw new PlatformNotSupportedException ("This method is not supported.");
1596 static void ValidateCookieCollection (HttpCookieCollection cookies)
1598 if (cookies == null)
1601 int size = cookies.Count;
1604 RequestValidator validator = RequestValidator.Current;
1605 int validationFailureIndex;
1606 HttpContext context = HttpContext.Current;
1610 for (int i = 0 ; i < size ; i++) {
1611 cookie = cookies[i];
1615 string value = cookie.Value;
1616 string name = cookie.Name;
1618 if (!String.IsNullOrEmpty (value)) {
1620 if (validateRequestNewMode)
1621 invalid = !validator.IsValidRequestString (context, value, RequestValidationSource.Cookies, name, out validationFailureIndex);
1624 invalid = IsInvalidString (value);
1627 ThrowValidationException ("Cookies", name, value);
1632 static void ThrowValidationException (string name, string key, string value)
1634 string v = "\"" + value + "\"";
1636 v = v.Substring (0, 16) + "...\"";
1638 string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1639 "detected from the client ({1}={2}).", name, key, v);
1641 throw new HttpRequestValidationException (msg);
1645 internal static bool IsInvalidString (string val)
1647 int validationFailureIndex;
1649 return IsInvalidString (val, out validationFailureIndex);
1652 internal static bool IsInvalidString (string val, out int validationFailureIndex)
1654 validationFailureIndex = 0;
1656 int len = val.Length;
1660 char current = val [0];
1661 for (int idx = 1; idx < len; idx++) {
1662 char next = val [idx];
1663 // See http://secunia.com/advisories/14325
1664 if (current == '<' || current == '\xff1c') {
1665 if (next == '!' || next < ' '
1666 || (next >= 'a' && next <= 'z')
1667 || (next >= 'A' && next <= 'Z')) {
1668 validationFailureIndex = idx - 1;
1671 } else if (current == '&' && next == '#') {
1672 validationFailureIndex = idx - 1;
1682 static System.Net.IPAddress [] GetLocalHostAddresses ()
1685 string hostName = System.Net.Dns.GetHostName ();
1686 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostAddresses (hostName);
1689 return new System.Net.IPAddress[0];
1695 #region Helper classes
1698 // Stream-based multipart handling.
1700 // In this incarnation deals with an HttpInputStream as we are now using
1701 // IntPtr-based streams instead of byte []. In the future, we will also
1702 // send uploads above a certain threshold into the disk (to implement
1703 // limit-less HttpInputFiles).
1706 class HttpMultipart {
1708 public class Element {
1709 public string ContentType;
1711 public string Filename;
1715 public override string ToString ()
1717 return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
1718 Start.ToString () + ", Length " + Length.ToString ();
1724 byte [] boundary_bytes;
1730 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
1733 // In the case of multipart entities, in which one or more different
1734 // sets of data are combined in a single body, a "multipart" media type
1735 // field must appear in the entity's header. The body must then contain
1736 // one or more body parts, each preceded by a boundary delimiter line,
1737 // and the last one followed by a closing boundary delimiter line.
1738 // After its boundary delimiter line, each body part then consists of a
1739 // header area, a blank line, and a body area. Thus a body part is
1740 // similar to an RFC 822 message in syntax, but different in meaning.
1742 public HttpMultipart (Stream data, string b, Encoding encoding)
1746 boundary_bytes = encoding.GetBytes (b);
1747 buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1748 this.encoding = encoding;
1749 sb = new StringBuilder ();
1754 // CRLF or LF are ok as line endings.
1755 bool got_cr = false;
1759 b = data.ReadByte ();
1768 sb.Append ((char) b);
1774 return sb.ToString ();
1778 static string GetContentDispositionAttribute (string l, string name)
1780 int idx = l.IndexOf (name + "=\"");
1783 int begin = idx + name.Length + "=\"".Length;
1784 int end = l.IndexOf ('"', begin);
1789 return l.Substring (begin, end - begin);
1792 string GetContentDispositionAttributeWithEncoding (string l, string name)
1794 int idx = l.IndexOf (name + "=\"");
1797 int begin = idx + name.Length + "=\"".Length;
1798 int end = l.IndexOf ('"', begin);
1804 string temp = l.Substring (begin, end - begin);
1805 byte [] source = new byte [temp.Length];
1806 for (int i = temp.Length - 1; i >= 0; i--)
1807 source [i] = (byte) temp [i];
1809 return encoding.GetString (source);
1812 bool ReadBoundary ()
1815 string line = ReadLine ();
1818 if (line [0] != '-' || line [1] != '-')
1821 if (!StrUtils.EndsWith (line, boundary, false))
1829 string ReadHeaders ()
1831 string s = ReadLine ();
1838 bool CompareBytes (byte [] orig, byte [] other)
1840 for (int i = orig.Length - 1; i >= 0; i--)
1841 if (orig [i] != other [i])
1847 long MoveToNextBoundary ()
1850 bool got_cr = false;
1853 int c = data.ReadByte ();
1858 if (state == 0 && c == LF) {
1859 retval = data.Position - 1;
1863 c = data.ReadByte ();
1864 } else if (state == 0) {
1866 c = data.ReadByte ();
1867 } else if (state == 1 && c == '-') {
1868 c = data.ReadByte ();
1875 continue; // no ReadByte() here
1878 int nread = data.Read (buffer, 0, buffer.Length);
1879 int bl = buffer.Length;
1883 if (!CompareBytes (boundary_bytes, buffer)) {
1885 data.Position = retval + 2;
1890 c = data.ReadByte ();
1894 if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
1896 } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
1898 data.Position = retval + 2;
1903 c = data.ReadByte ();
1906 data.Position = retval + 2;
1912 state = 0; // no ReadByte() here
1919 public Element ReadNextElement ()
1921 if (at_eof || ReadBoundary ())
1924 Element elem = new Element ();
1926 while ((header = ReadHeaders ()) != null) {
1927 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
1928 elem.Name = GetContentDispositionAttribute (header, "name");
1929 elem.Filename = StripPath (GetContentDispositionAttributeWithEncoding (header, "filename"));
1930 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
1931 elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
1935 long start = data.Position;
1937 long pos = MoveToNextBoundary ();
1941 elem.Length = pos - start;
1945 static string StripPath (string path)
1947 if (path == null || path.Length == 0)
1950 if (path.IndexOf (":\\") != 1 && !path.StartsWith ("\\\\"))
1952 return path.Substring (path.LastIndexOf ('\\') + 1);