2 // System.Web.HttpRequest.cs
6 // Miguel de Icaza (miguel@novell.com)
7 // Gonzalo Paniagua Javier (gonzalo@novell.com)
11 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
34 using System.Collections.Specialized;
36 using System.Runtime.InteropServices;
37 using System.Security;
38 using System.Security.Permissions;
39 using System.Security.Principal;
40 using System.Web.Configuration;
42 using System.Web.Util;
43 using System.Globalization;
45 namespace System.Web {
47 // CAS - no InheritanceDemand here as the class is sealed
48 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
49 public sealed partial class HttpRequest {
50 HttpWorkerRequest worker_request;
52 WebROCollection query_string_nvc;
56 string orig_url = null;
57 UriBuilder url_components;
62 // On-demand computed values
64 HttpBrowserCapabilities browser_capabilities;
65 string file_path, base_virtual_dir, root_virtual_dir;
67 int content_length = -1;
69 string current_exe_path;
71 string unescaped_path;
73 WebROCollection all_params;
74 WebROCollection headers;
76 InputFilterStream input_filter;
78 HttpCookieCollection cookies;
82 HttpFileCollection files;
84 ServerVariablesCollection server_variables;
85 HttpClientCertificate client_cert;
88 string [] accept_types;
89 string [] user_languages;
91 TempFileStream request_file;
93 readonly static System.Net.IPAddress [] host_addresses;
96 bool validate_cookies, validate_query_string, validate_form;
97 bool checked_cookies, checked_query_string, checked_form;
99 readonly static char [] queryTrimChars = {'?'};
101 static HttpRequest ()
103 host_addresses = GetLocalHostAddresses ();
106 public HttpRequest (string filename, string url, string queryString)
108 // warning 169: what are we supposed to do with filename?
110 //this.filename = filename;
113 url_components = new UriBuilder (url);
114 url_components.Query = queryString;
116 query_string_nvc = new WebROCollection ();
117 if (queryString != null)
118 HttpUtility.ParseQueryString (queryString, Encoding.Default, query_string_nvc);
119 query_string_nvc.Protect ();
122 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
124 this.worker_request = worker_request;
125 this.context = context;
128 internal UriBuilder UrlComponents {
130 if (url_components == null) {
132 byte[] queryStringRaw = worker_request.GetQueryStringRawBytes();
133 if(queryStringRaw != null)
134 query = ContentEncoding.GetString(queryStringRaw);
136 query = worker_request.GetQueryString();
140 ApplyUrlMapping (worker_request.GetUriPath ()),
142 worker_request.GetUriPath (),
146 return url_components;
150 void BuildUrlComponents (string path, string query)
152 if (url_components != null)
154 url_components = new UriBuilder ();
155 url_components.Scheme = worker_request.GetProtocol ();
156 url_components.Host = worker_request.GetServerName ();
157 url_components.Port = worker_request.GetLocalPort ();
158 url_components.Path = path;
159 if (query != null && query.Length > 0)
160 url_components.Query = query.TrimStart (queryTrimChars);
164 internal string ApplyUrlMapping (string url)
166 if (WebConfigurationManager.HasConfigErrors)
169 UrlMappingsSection ums = WebConfigurationManager.GetSection ("system.web/urlMappings", ApplicationPath) as UrlMappingsSection;
170 UrlMappingCollection umc;
172 if (ums == null || !ums.IsEnabled || (umc = ums.UrlMappings).Count == 0)
175 string relUrl = VirtualPathUtility.ToAppRelative (url);
176 UrlMapping um = null;
178 foreach (UrlMapping u in umc) {
181 if (String.Compare (relUrl, u.Url, StringComparison.Ordinal) == 0) {
190 string rawUrl = VirtualPathUtility.ToAbsolute (um.MappedUrl.Trim ());
191 Uri newUrl = new Uri ("http://host.com" + rawUrl);
193 if (url_components != null) {
194 url_components.Path = newUrl.AbsolutePath;
195 url_components.Query = newUrl.Query.TrimStart (queryTrimChars);
196 query_string_nvc = new WebROCollection ();
197 HttpUtility.ParseQueryString (newUrl.Query, Encoding.Default, query_string_nvc);
198 query_string_nvc.Protect ();
200 BuildUrlComponents (newUrl.AbsolutePath, newUrl.Query);
202 return url_components.Path;
206 string [] SplitHeader (int header_index)
208 string [] result = null;
209 string header = worker_request.GetKnownRequestHeader (header_index);
210 if (header != null && header != "" && header.Trim () != "") {
211 result = header.Split (',');
212 for (int i = result.Length - 1; i >= 0; i--)
213 result [i] = result [i].Trim ();
218 public string [] AcceptTypes {
220 if (worker_request == null)
223 if (accept_types == null)
224 accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
232 public WindowsIdentity LogonUserIdentity {
233 get { throw new NotImplementedException (); }
238 public string AnonymousID {
243 anonymous_id = value;
248 public string ApplicationPath {
250 if (worker_request == null)
252 return worker_request.GetAppPath ();
256 public HttpBrowserCapabilities Browser {
258 if (browser_capabilities == null)
259 browser_capabilities = (HttpBrowserCapabilities)
260 HttpCapabilitiesBase.GetConfigCapabilities (null, this);
262 return browser_capabilities;
266 browser_capabilities = value;
271 internal bool BrowserMightHaveSpecialWriter {
273 return (browser_capabilities != null
274 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
278 internal bool BrowserMightHaveAdapters {
280 return (browser_capabilities != null
281 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
286 public HttpClientCertificate ClientCertificate {
288 if (client_cert == null)
289 client_cert = new HttpClientCertificate (worker_request);
294 static internal string GetParameter (string header, string attr)
296 int ap = header.IndexOf (attr);
301 if (ap >= header.Length)
304 char ending = header [ap];
308 int end = header.IndexOf (ending, ap+1);
310 return (ending == '"') ? null : header.Substring (ap);
312 return header.Substring (ap+1, end-ap-1);
315 public Encoding ContentEncoding {
317 if (encoding == null){
318 if (worker_request == null)
319 throw new HttpException ("No HttpWorkerRequest");
321 string content_type = ContentType;
322 string parameter = GetParameter (content_type, "; charset=");
323 if (parameter == null) {
324 encoding = WebEncoding.RequestEncoding;
327 // Do what the #1 web server does
328 encoding = Encoding.GetEncoding (parameter);
330 encoding = WebEncoding.RequestEncoding;
342 public int ContentLength {
344 if (content_length == -1){
345 if (worker_request == null)
348 string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
352 content_length = Int32.Parse (cl);
357 // content_length will still be < 0, but we know we gotta read from the client
358 if (content_length < 0)
361 return content_length;
365 public string ContentType {
367 if (content_type == null){
368 if (worker_request != null)
369 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
371 if (content_type == null)
372 content_type = String.Empty;
379 content_type = value;
383 public HttpCookieCollection Cookies {
385 if (cookies == null) {
386 if (worker_request == null) {
387 cookies = new HttpCookieCollection ();
389 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
390 cookies = new HttpCookieCollection (cookie_hv);
395 // For J2EE portal support we emulate cookies using the session.
396 GetSessionCookiesForPortal (cookies);
398 if (validate_cookies && !checked_cookies){
399 ValidateCookieCollection (cookies);
400 checked_cookies = true;
408 public string CurrentExecutionFilePath {
410 if (current_exe_path != null)
411 return current_exe_path;
418 public string AppRelativeCurrentExecutionFilePath {
420 return VirtualPathUtility.ToAppRelative (CurrentExecutionFilePath);
425 public string FilePath {
427 if (worker_request == null)
428 return "/"; // required for 2.0
430 if (file_path == null)
431 file_path = UrlUtils.Canonic (
433 ApplyUrlMapping (worker_request.GetFilePath ())
435 worker_request.GetFilePath ()
443 internal string BaseVirtualDir {
445 if (base_virtual_dir == null){
446 base_virtual_dir = FilePath;
447 if (UrlUtils.HasSessionId (base_virtual_dir))
448 base_virtual_dir = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (base_virtual_dir), base_virtual_dir);
450 int p = base_virtual_dir.LastIndexOf ('/');
454 base_virtual_dir = base_virtual_dir.Substring (0, p);
456 base_virtual_dir = "/";
458 return base_virtual_dir;
462 public HttpFileCollection Files {
465 files = new HttpFileCollection ();
466 if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
467 form = new WebROCollection ();
476 public Stream Filter {
481 if (input_filter == null)
482 input_filter = new InputFilterStream ();
488 // This checks that get_ was called before.
489 if (input_filter == null)
490 throw new HttpException ("Invalid filter");
496 // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
497 static Stream GetSubStream (Stream stream)
500 if (stream is IntPtrStream)
501 return new IntPtrStream (stream);
504 if (stream is MemoryStream) {
505 MemoryStream other = (MemoryStream) stream;
506 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
509 if (stream is TempFileStream) {
510 ((TempFileStream) stream).SavePosition ();
514 throw new NotSupportedException ("The stream is " + stream.GetType ());
517 static void EndSubStream (Stream stream)
519 if (stream is TempFileStream) {
520 ((TempFileStream) stream).RestorePosition ();
525 // Loads the data on the form for multipart/form-data
527 void LoadMultiPart ()
529 string boundary = GetParameter (ContentType, "; boundary=");
530 if (boundary == null)
533 Stream input = GetSubStream (InputStream);
534 HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
536 HttpMultipart.Element e;
537 while ((e = multi_part.ReadNextElement ()) != null) {
538 if (e.Filename == null){
539 byte [] copy = new byte [e.Length];
541 input.Position = e.Start;
542 input.Read (copy, 0, (int) e.Length);
544 form.Add (e.Name, ContentEncoding.GetString (copy));
547 // We use a substream, as in 2.x we will support large uploads streamed to disk,
549 HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
550 files.AddFile (e.Name, sub);
553 EndSubStream (input);
557 // Adds the key/value to the form, and sets the argumets to empty
559 void AddRawKeyValue (StringBuilder key, StringBuilder value)
561 string decodedKey = HttpUtility.UrlDecode (key.ToString (), ContentEncoding);
562 form.Add (decodedKey,
563 HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
570 // Loads the form data from on a application/x-www-form-urlencoded post
573 void RawLoadWwwForm ()
578 using (Stream input = GetSubStream (InputStream)) {
579 using (StreamReader s = new StreamReader (input, ContentEncoding)) {
580 StringBuilder key = new StringBuilder ();
581 StringBuilder value = new StringBuilder ();
584 while ((c = s.Read ()) != -1){
587 while ((c = s.Read ()) != -1){
589 AddRawKeyValue (key, value);
592 value.Append ((char) c);
595 AddRawKeyValue (key, value);
599 AddRawKeyValue (key, value);
601 key.Append ((char) c);
604 AddRawKeyValue (key, value);
606 EndSubStream (input);
611 bool IsContentType (string ct, bool starts_with)
614 return StrUtils.StartsWith (ContentType, ct, true);
616 return String.Compare (ContentType, ct, true, CultureInfo.InvariantCulture) == 0;
619 public NameValueCollection Form {
622 form = new WebROCollection ();
623 files = new HttpFileCollection ();
625 if (IsContentType ("multipart/form-data", true))
628 IsContentType ("application/x-www-form-urlencoded", true))
634 if (validate_form && !checked_form){
636 ValidateNameValueCollection ("Form", form);
643 public NameValueCollection Headers {
646 headers = new HeadersCollection (this);
652 public string HttpMethod {
654 if (http_method == null){
655 if (worker_request != null)
656 http_method = worker_request.GetHttpVerbName ();
664 void DoFilter (byte [] buffer)
666 if (input_filter == null || filter == null)
669 if (buffer.Length < 1024)
670 buffer = new byte [1024];
672 // Replace the input with the filtered input
673 input_filter.BaseStream = input_stream;
674 MemoryStream ms = new MemoryStream ();
676 int n = filter.Read (buffer, 0, buffer.Length);
679 ms.Write (buffer, 0, n);
681 // From now on input_stream has the filtered input
682 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
686 const int INPUT_BUFFER_SIZE = 32*1024;
688 TempFileStream GetTempStream ()
690 string tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
691 TempFileStream f = null;
693 Random rnd = new Random ();
698 path = System.IO.Path.Combine (tempdir, "tmp" + num.ToString("x") + ".req");
701 f = new TempFileStream (path);
702 } catch (SecurityException) {
703 // avoid an endless loop
711 void MakeInputStream ()
713 if (input_stream != null)
716 if (worker_request == null) {
717 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
718 DoFilter (new byte [1024]);
723 // Use an unmanaged memory block as this might be a large
726 int content_length = ContentLength;
729 HttpRuntimeSection config = (HttpRuntimeSection) WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime");
731 HttpRuntimeConfig config = (HttpRuntimeConfig) HttpContext.GetAppConfig ("system.web/httpRuntime");
733 if ((content_length / 1024) > config.MaxRequestLength)
734 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
738 buffer = worker_request.GetPreloadedEntityBody ();
739 // we check the instance field 'content_length' here, not the local var.
740 if (this.content_length <= 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
741 if (buffer == null || content_length == 0) {
742 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
744 input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
746 DoFilter (new byte [1024]);
751 total = buffer.Length;
753 if (content_length > 0 && (content_length / 1024) >= config.RequestLengthDiskThreshold) {
754 // Writes the request to disk
755 total = Math.Min (content_length, total);
756 request_file = GetTempStream ();
757 Stream output = request_file;
759 output.Write (buffer, 0, total);
761 if (total < content_length) {
762 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
765 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
766 n = worker_request.ReadEntityBody (buffer, min);
769 output.Write (buffer, 0, n);
771 } while (total < content_length);
774 request_file.SetReadOnly ();
775 input_stream = request_file;
776 } else if (content_length > 0) {
777 // Buffers the request in an IntPtrStream
778 total = Math.Min (content_length, total);
779 IntPtr content = Marshal.AllocHGlobal (content_length);
780 if (content == (IntPtr) 0)
781 throw new HttpException (String.Format ("Not enough memory to allocate {0} bytes.",
785 Marshal.Copy (buffer, 0, content, total);
787 if (total < content_length) {
788 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
791 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
792 n = worker_request.ReadEntityBody (buffer, min);
795 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
797 } while (total < content_length);
800 input_stream = new IntPtrStream (content, total);
802 // Buffers the request in a MemoryStream or writes to disk if threshold exceeded
803 MemoryStream ms = new MemoryStream ();
806 ms.Write (buffer, 0, total);
808 buffer = new byte [INPUT_BUFFER_SIZE];
809 long maxlength = config.MaxRequestLength * 1024L;
810 long disk_th = config.RequestLengthDiskThreshold * 1024L;
813 n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
817 if (total < 0 || total > maxlength)
818 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
820 if (ms != null && total > disk_th) {
821 // Swith to on-disk file.
822 request_file = GetTempStream ();
823 ms.WriteTo (request_file);
825 output = request_file;
827 output.Write (buffer, 0, n);
831 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
833 request_file.SetReadOnly ();
834 input_stream = request_file;
839 if (total < content_length)
840 throw new HttpException (411, "The request body is incomplete.");
844 internal void ReleaseResources ()
847 if (input_stream != null){
848 stream = input_stream;
855 if (request_file != null) {
856 stream = request_file;
864 public Stream InputStream {
866 if (input_stream == null)
873 public bool IsAuthenticated {
875 if (context.User == null || context.User.Identity == null)
877 return context.User.Identity.IsAuthenticated;
881 public bool IsSecureConnection {
883 if (worker_request == null)
885 return worker_request.IsSecure ();
889 public string this [string key] {
890 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
892 // "The QueryString, Form, Cookies, or ServerVariables collection member
893 // specified in the key parameter."
894 string val = QueryString [key];
898 HttpCookie cookie = Cookies [key];
903 val = ServerVariables [key];
909 public NameValueCollection Params {
910 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
912 if (all_params == null)
913 all_params = new HttpParamsCollection (QueryString, Form, ServerVariables, Cookies);
921 if (unescaped_path == null) {
923 if (url_components != null) {
924 // use only if it's already been instantiated, so that we can't go into endless
925 // recursion in some scenarios
926 path = UrlComponents.Path;
929 path = ApplyUrlMapping (worker_request.GetUriPath ());
931 path = worker_request.GetUriPath ();
936 unescaped_path = Uri.UnescapeDataString (path);
938 unescaped_path = HttpUtility.UrlDecode (path);
942 return unescaped_path;
946 public string PathInfo {
948 if (path_info == null) {
949 if (worker_request == null)
951 path_info = worker_request.GetPathInfo ();
958 public string PhysicalApplicationPath {
960 if (worker_request == null)
961 throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
963 string path = HttpRuntime.AppDomainAppPath;
964 if (SecurityManager.SecurityEnabled) {
965 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
971 public string PhysicalPath {
973 if (worker_request == null)
974 return String.Empty; // don't check security with an empty string!
976 if (physical_path == null) {
977 // Don't call HttpRequest.MapPath here, as that one *trims* the input
978 physical_path = worker_request.MapPath (FilePath);
981 if (SecurityManager.SecurityEnabled) {
982 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
984 return physical_path;
988 internal string RootVirtualDir {
990 if (root_virtual_dir == null){
991 string fp = FilePath;
992 int p = fp.LastIndexOf ('/');
995 root_virtual_dir = "/";
997 root_virtual_dir = fp.Substring (0, p);
999 return root_virtual_dir;
1003 public NameValueCollection QueryString {
1005 if (query_string_nvc == null) {
1006 query_string_nvc = new WebROCollection ();
1007 string q = UrlComponents.Query;
1012 HttpUtility.ParseQueryString (q, ContentEncoding, query_string_nvc);
1015 query_string_nvc.Protect();
1018 if (validate_query_string && !checked_query_string) {
1019 ValidateNameValueCollection ("QueryString", query_string_nvc);
1020 checked_query_string = true;
1023 return query_string_nvc;
1027 public string RawUrl {
1029 if (worker_request != null)
1030 return worker_request.GetRawUrl ();
1032 return UrlComponents.Path + UrlComponents.Query;
1039 public string RequestType {
1041 if (request_type == null){
1042 if (worker_request != null) {
1043 request_type = worker_request.GetHttpVerbName ();
1044 http_method = request_type;
1046 request_type = "GET";
1049 return request_type;
1053 request_type = value;
1057 public NameValueCollection ServerVariables {
1058 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1060 if (server_variables == null)
1061 server_variables = new ServerVariablesCollection (this);
1063 return server_variables;
1067 public int TotalBytes {
1069 Stream ins = InputStream;
1070 return (int) ins.Length;
1076 if (cached_url == null) {
1077 if (orig_url == null)
1078 cached_url = UrlComponents.Uri;
1080 cached_url = new Uri (orig_url);
1087 public Uri UrlReferrer {
1089 if (worker_request == null)
1092 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
1099 } catch (UriFormatException) {}
1104 public string UserAgent {
1106 if (worker_request == null)
1109 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
1113 public string UserHostAddress {
1115 if (worker_request == null)
1118 return worker_request.GetRemoteAddress ();
1122 public string UserHostName {
1124 if (worker_request == null)
1127 return worker_request.GetRemoteName ();
1131 public string [] UserLanguages {
1133 if (worker_request == null)
1136 if (user_languages == null)
1137 user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
1139 return user_languages;
1143 public byte [] BinaryRead (int count)
1146 throw new ArgumentException ("count is < 0");
1148 Stream s = InputStream;
1149 byte [] ret = new byte [count];
1150 if (s.Read (ret, 0, count) != count)
1151 throw new ArgumentException (
1152 String.Format ("count {0} exceeds length of available input {1}",
1153 count, s.Length - s.Position));
1157 public int [] MapImageCoordinates (string imageFieldName)
1159 string method = HttpMethod;
1160 NameValueCollection coll = null;
1161 if (method == "HEAD" || method == "GET")
1163 else if (method == "POST")
1169 string x = coll [imageFieldName + ".x"];
1170 if (x == null || x == "")
1173 string y = coll [imageFieldName + ".y"];
1174 if (y == null || y == "")
1177 int [] result = new int [2];
1179 result [0] = Int32.Parse (x);
1180 result [1] = Int32.Parse (y);
1188 public string MapPath (string virtualPath)
1190 if (worker_request == null)
1193 return MapPath (virtualPath, BaseVirtualDir, true);
1196 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1198 if (worker_request == null)
1199 throw new HttpException ("No HttpWorkerRequest");
1201 if (virtualPath == null)
1204 virtualPath = virtualPath.Trim ();
1205 if (virtualPath.Length == 0)
1209 if (!VirtualPathUtility.IsValidVirtualPath (virtualPath))
1210 throw new HttpException (String.Format ("'{0}' is not a valid virtual path.", virtualPath));
1212 string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
1214 if (!VirtualPathUtility.IsRooted (virtualPath)) {
1215 if (StrUtils.IsNullOrEmpty (baseVirtualDir))
1216 baseVirtualDir = appVirtualPath;
1217 virtualPath = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (baseVirtualDir), virtualPath);
1219 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath);
1221 if (!allowCrossAppMapping){
1222 if (!StrUtils.StartsWith (virtualPath, appVirtualPath, true))
1223 throw new HttpException ("MapPath: Mapping across applications not allowed");
1224 if (appVirtualPath.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1225 throw new HttpException ("MapPath: Mapping across applications not allowed");
1228 return worker_request.MapPath (virtualPath);
1230 string path = worker_request.MapPath (virtualPath);
1231 if (virtualPath [virtualPath.Length - 1] != '/' && path [path.Length - 1] == System.IO.Path.DirectorySeparatorChar)
1232 path = path.TrimEnd (System.IO.Path.DirectorySeparatorChar);
1237 public void SaveAs (string filename, bool includeHeaders)
1239 Stream output = new FileStream (filename, FileMode.Create);
1240 if (includeHeaders) {
1241 StringBuilder sb = new StringBuilder ();
1242 string version = String.Empty;
1244 if (worker_request != null) {
1245 version = worker_request.GetHttpVersion ();
1246 path = UrlComponents.Path;
1248 string qs = UrlComponents.Query;
1250 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1251 NameValueCollection coll = Headers;
1252 foreach (string k in coll.AllKeys) {
1255 sb.Append (coll [k]);
1260 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1261 output.Write (bytes, 0, bytes.Length);
1264 // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1265 // to keep InputStream in its state.
1266 Stream input = GetSubStream (InputStream);
1268 long len = input.Length;
1269 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1270 byte [] data = new byte [buf_size];
1272 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1273 output.Write (data, 0, count);
1279 EndSubStream (input);
1283 public void ValidateInput ()
1285 validate_cookies = true;
1286 validate_query_string = true;
1287 validate_form = true;
1290 #region internal routines
1291 internal string ClientTarget {
1293 return client_target;
1297 client_target = value;
1308 string address = worker_request.GetRemoteAddress ();
1310 if (StrUtils.IsNullOrEmpty (address))
1313 if (address == "127.0.0.1")
1316 System.Net.IPAddress remoteAddr = System.Net.IPAddress.Parse (address);
1317 if (System.Net.IPAddress.IsLoopback (remoteAddr))
1320 for (int i = 0; i < host_addresses.Length; i++)
1321 if (remoteAddr.Equals (host_addresses [i]))
1328 internal void SetFilePath (string path)
1331 physical_path = null;
1334 internal void SetCurrentExePath (string path)
1337 current_exe_path = path;
1338 UrlComponents.Path = path;
1339 // recreated on demand
1340 root_virtual_dir = null;
1341 base_virtual_dir = null;
1342 physical_path = null;
1343 unescaped_path = null;
1346 internal void SetPathInfo (string pi)
1352 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1353 internal void SetHeader (string name, string value)
1355 WebROCollection h = (WebROCollection) Headers;
1361 // Notice: there is nothing raw about this querystring.
1362 internal string QueryStringRaw {
1364 UriBuilder urlComponents = UrlComponents;
1366 if (urlComponents == null) {
1367 string ret = worker_request.GetQueryString ();
1369 if (ret == null || ret.Length == 0)
1370 return String.Empty;
1378 return UrlComponents.Query;
1382 UrlComponents.Query = value;
1384 query_string_nvc = null;
1388 // Internal, dont know what it does, so flagged as public so we can see it.
1389 internal void SetForm (WebROCollection coll)
1394 internal HttpWorkerRequest WorkerRequest {
1396 return worker_request;
1400 internal HttpContext Context {
1406 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1411 foreach (string key in coll.Keys) {
1412 string val = coll [key];
1413 if (val != null && val.Length > 0 && CheckString (val))
1414 ThrowValidationException (name, key, val);
1418 static void ValidateCookieCollection (HttpCookieCollection cookies)
1420 if (cookies == null)
1423 int size = cookies.Count;
1425 for (int i = 0 ; i < size ; i++) {
1426 cookie = cookies[i];
1427 string value = cookie.Value;
1429 if (value != null && value != "" && CheckString (value))
1430 ThrowValidationException ("Cookies", cookie.Name, cookie.Value);
1434 static void ThrowValidationException (string name, string key, string value)
1436 string v = "\"" + value + "\"";
1438 v = v.Substring (0, 16) + "...\"";
1440 string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1441 "detected from the client ({1}={2}).", name, key, v);
1443 throw new HttpRequestValidationException (msg);
1446 static bool CheckString (string val)
1448 int len = val.Length;
1452 char current = val [0];
1453 for (int idx = 1; idx < len; idx++) {
1454 char next = val [idx];
1455 // See http://secunia.com/advisories/14325
1456 if (current == '<' || current == '\xff1c') {
1457 if (next == '!' || next < ' '
1458 || (next >= 'a' && next <= 'z')
1459 || (next >= 'A' && next <= 'Z'))
1461 } else if (current == '&' && next == '#') {
1471 static System.Net.IPAddress [] GetLocalHostAddresses ()
1474 string hostName = System.Net.Dns.GetHostName ();
1476 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostAddresses (hostName);
1478 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostByName (hostName).AddressList;
1484 return new System.Net.IPAddress[0];
1490 #region Helper classes
1493 // Stream-based multipart handling.
1495 // In this incarnation deals with an HttpInputStream as we are now using
1496 // IntPtr-based streams instead of byte []. In the future, we will also
1497 // send uploads above a certain threshold into the disk (to implement
1498 // limit-less HttpInputFiles).
1501 class HttpMultipart {
1503 public class Element {
1504 public string ContentType;
1506 public string Filename;
1510 public override string ToString ()
1512 return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
1513 Start.ToString () + ", Length " + Length.ToString ();
1519 byte [] boundary_bytes;
1525 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
1528 // In the case of multipart entities, in which one or more different
1529 // sets of data are combined in a single body, a "multipart" media type
1530 // field must appear in the entity's header. The body must then contain
1531 // one or more body parts, each preceded by a boundary delimiter line,
1532 // and the last one followed by a closing boundary delimiter line.
1533 // After its boundary delimiter line, each body part then consists of a
1534 // header area, a blank line, and a body area. Thus a body part is
1535 // similar to an RFC 822 message in syntax, but different in meaning.
1537 public HttpMultipart (Stream data, string b, Encoding encoding)
1541 boundary_bytes = encoding.GetBytes (b);
1542 buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1543 this.encoding = encoding;
1544 sb = new StringBuilder ();
1549 // CRLF or LF are ok as line endings.
1550 bool got_cr = false;
1554 b = data.ReadByte ();
1563 sb.Append ((char) b);
1569 return sb.ToString ();
1573 static string GetContentDispositionAttribute (string l, string name)
1575 int idx = l.IndexOf (name + "=\"");
1578 int begin = idx + name.Length + "=\"".Length;
1579 int end = l.IndexOf ('"', begin);
1584 return l.Substring (begin, end - begin);
1587 string GetContentDispositionAttributeWithEncoding (string l, string name)
1589 int idx = l.IndexOf (name + "=\"");
1592 int begin = idx + name.Length + "=\"".Length;
1593 int end = l.IndexOf ('"', begin);
1599 string temp = l.Substring (begin, end - begin);
1600 byte [] source = new byte [temp.Length];
1601 for (int i = temp.Length - 1; i >= 0; i--)
1602 source [i] = (byte) temp [i];
1604 return encoding.GetString (source);
1607 bool ReadBoundary ()
1610 string line = ReadLine ();
1613 if (line [0] != '-' || line [1] != '-')
1616 if (!StrUtils.EndsWith (line, boundary, false))
1624 string ReadHeaders ()
1626 string s = ReadLine ();
1633 bool CompareBytes (byte [] orig, byte [] other)
1635 for (int i = orig.Length - 1; i >= 0; i--)
1636 if (orig [i] != other [i])
1642 long MoveToNextBoundary ()
1645 bool got_cr = false;
1648 int c = data.ReadByte ();
1653 if (state == 0 && c == LF) {
1654 retval = data.Position - 1;
1658 c = data.ReadByte ();
1659 } else if (state == 0) {
1661 c = data.ReadByte ();
1662 } else if (state == 1 && c == '-') {
1663 c = data.ReadByte ();
1670 continue; // no ReadByte() here
1673 int nread = data.Read (buffer, 0, buffer.Length);
1674 int bl = buffer.Length;
1678 if (!CompareBytes (boundary_bytes, buffer)) {
1680 data.Position = retval + 2;
1685 c = data.ReadByte ();
1689 if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
1691 } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
1693 data.Position = retval + 2;
1698 c = data.ReadByte ();
1701 data.Position = retval + 2;
1707 state = 0; // no ReadByte() here
1714 public Element ReadNextElement ()
1716 if (at_eof || ReadBoundary ())
1719 Element elem = new Element ();
1721 while ((header = ReadHeaders ()) != null) {
1722 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
1723 elem.Name = GetContentDispositionAttribute (header, "name");
1724 elem.Filename = StripPath (GetContentDispositionAttributeWithEncoding (header, "filename"));
1725 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
1726 elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
1730 long start = data.Position;
1732 long pos = MoveToNextBoundary ();
1736 elem.Length = pos - start;
1740 static string StripPath (string path)
1742 if (path == null || path.Length == 0)
1745 if (path.IndexOf (":\\") != 1 && !path.StartsWith ("\\\\"))
1747 return path.Substring (path.LastIndexOf ('\\') + 1);