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.Web.Configuration;
41 using System.Web.Util;
42 using System.Globalization;
44 namespace System.Web {
46 // CAS - no InheritanceDemand here as the class is sealed
47 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
48 public sealed partial class HttpRequest {
49 HttpWorkerRequest worker_request;
51 WebROCollection query_string_nvc;
55 string orig_url = null;
56 UriBuilder url_components;
61 // On-demand computed values
63 HttpBrowserCapabilities browser_capabilities;
64 string file_path, base_virtual_dir, root_virtual_dir;
66 int content_length = -1;
68 string current_exe_path;
70 string unescaped_path;
72 WebROCollection all_params;
73 WebROCollection headers;
75 InputFilterStream input_filter;
77 HttpCookieCollection cookies;
81 HttpFileCollection files;
83 ServerVariablesCollection server_variables;
84 HttpClientCertificate client_cert;
87 string [] accept_types;
88 string [] user_languages;
90 TempFileStream request_file;
92 readonly static System.Net.IPAddress [] host_addresses;
95 bool validate_cookies, validate_query_string, validate_form;
96 bool checked_cookies, checked_query_string, checked_form;
98 readonly static char [] queryTrimChars = {'?'};
100 static HttpRequest ()
102 host_addresses = GetLocalHostAddresses ();
105 public HttpRequest (string filename, string url, string queryString)
107 // warning 169: what are we supposed to do with filename?
109 //this.filename = filename;
112 url_components = new UriBuilder (url);
113 url_components.Query = queryString;
115 query_string_nvc = new WebROCollection ();
116 HttpUtility.ParseQueryString (queryString, Encoding.Default, query_string_nvc);
117 query_string_nvc.Protect ();
120 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
122 this.worker_request = worker_request;
123 this.context = context;
126 UriBuilder UrlComponents {
128 if (url_components == null) {
130 byte[] queryStringRaw = worker_request.GetQueryStringRawBytes();
131 if(queryStringRaw != null)
132 query = ContentEncoding.GetString(queryStringRaw);
134 query = worker_request.GetQueryString();
138 ApplyUrlMapping (worker_request.GetUriPath ()),
140 worker_request.GetUriPath (),
144 return url_components;
148 void BuildUrlComponents (string path, string query)
150 if (url_components != null)
152 url_components = new UriBuilder ();
153 url_components.Scheme = worker_request.GetProtocol ();
154 url_components.Host = worker_request.GetServerName ();
155 url_components.Port = worker_request.GetLocalPort ();
156 url_components.Path = path;
157 if (query != null && query.Length > 0)
158 url_components.Query = query.TrimStart (queryTrimChars);
162 internal string ApplyUrlMapping (string url)
164 UrlMappingsSection ums = WebConfigurationManager.GetSection ("system.web/urlMappings", ApplicationPath) as UrlMappingsSection;
165 UrlMappingCollection umc;
167 if (ums == null || !ums.IsEnabled || (umc = ums.UrlMappings).Count == 0)
170 string relUrl = VirtualPathUtility.ToAppRelative (url);
171 UrlMapping um = null;
173 foreach (UrlMapping u in umc) {
176 if (String.Compare (relUrl, u.Url, StringComparison.Ordinal) == 0) {
185 string rawUrl = VirtualPathUtility.ToAbsolute (um.MappedUrl.Trim ());
186 Uri newUrl = new Uri ("http://host.com" + rawUrl);
188 if (url_components != null) {
189 url_components.Path = newUrl.AbsolutePath;
190 url_components.Query = newUrl.Query.TrimStart (queryTrimChars);
191 query_string_nvc = new WebROCollection ();
192 HttpUtility.ParseQueryString (newUrl.Query, Encoding.Default, query_string_nvc);
193 query_string_nvc.Protect ();
195 BuildUrlComponents (newUrl.AbsolutePath, newUrl.Query);
197 return url_components.Path;
201 string [] SplitHeader (int header_index)
203 string [] result = null;
204 string header = worker_request.GetKnownRequestHeader (header_index);
205 if (header != null && header != "" && header.Trim () != "") {
206 result = header.Split (',');
207 for (int i = result.Length - 1; i >= 0; i--)
208 result [i] = result [i].Trim ();
213 public string [] AcceptTypes {
215 if (worker_request == null)
218 if (accept_types == null)
219 accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
227 public string AnonymousID {
232 anonymous_id = value;
237 public string ApplicationPath {
239 if (worker_request == null)
241 return worker_request.GetAppPath ();
245 public HttpBrowserCapabilities Browser {
247 if (browser_capabilities == null)
248 browser_capabilities = (HttpBrowserCapabilities)
249 HttpCapabilitiesBase.GetConfigCapabilities (null, this);
251 return browser_capabilities;
255 browser_capabilities = value;
259 public HttpClientCertificate ClientCertificate {
261 if (client_cert == null)
262 client_cert = new HttpClientCertificate (worker_request);
267 static internal string GetParameter (string header, string attr)
269 int ap = header.IndexOf (attr);
274 if (ap >= header.Length)
277 char ending = header [ap];
281 int end = header.IndexOf (ending, ap+1);
283 return (ending == '"') ? null : header.Substring (ap);
285 return header.Substring (ap+1, end-ap-1);
288 public Encoding ContentEncoding {
290 if (encoding == null){
291 if (worker_request == null)
292 throw new HttpException ("No HttpWorkerRequest");
294 string content_type = ContentType;
295 string parameter = GetParameter (content_type, "; charset=");
296 if (parameter == null) {
297 encoding = WebEncoding.RequestEncoding;
300 // Do what the #1 web server does
301 encoding = Encoding.GetEncoding (parameter);
303 encoding = WebEncoding.RequestEncoding;
315 public int ContentLength {
317 if (content_length == -1){
318 if (worker_request == null)
321 string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
325 content_length = Int32.Parse (cl);
330 // content_length will still be < 0, but we know we gotta read from the client
331 if (content_length < 0)
334 return content_length;
338 public string ContentType {
340 if (content_type == null){
341 if (worker_request != null)
342 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
344 if (content_type == null)
345 content_type = String.Empty;
352 content_type = value;
356 public HttpCookieCollection Cookies {
358 if (cookies == null) {
359 if (worker_request == null) {
360 cookies = new HttpCookieCollection ();
362 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
363 cookies = new HttpCookieCollection (cookie_hv);
368 // For J2EE portal support we emulate cookies using the session.
369 GetSessionCookiesForPortal (cookies);
371 if (validate_cookies && !checked_cookies){
372 ValidateCookieCollection (cookies);
373 checked_cookies = true;
381 public string CurrentExecutionFilePath {
383 if (current_exe_path != null)
384 return current_exe_path;
391 public string AppRelativeCurrentExecutionFilePath {
393 return VirtualPathUtility.ToAppRelative (CurrentExecutionFilePath);
398 public string FilePath {
400 if (worker_request == null)
401 return "/"; // required for 2.0
403 if (file_path == null)
404 file_path = UrlUtils.Canonic (
406 ApplyUrlMapping (worker_request.GetFilePath ())
408 worker_request.GetFilePath ()
416 internal string BaseVirtualDir {
418 if (base_virtual_dir == null){
419 base_virtual_dir = FilePath;
420 int p = base_virtual_dir.LastIndexOf ('/');
422 base_virtual_dir = base_virtual_dir.Substring (0, p);
424 return base_virtual_dir;
428 public HttpFileCollection Files {
431 files = new HttpFileCollection ();
432 if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
433 form = new WebROCollection ();
442 public Stream Filter {
447 if (input_filter == null)
448 input_filter = new InputFilterStream ();
454 // This checks that get_ was called before.
455 if (input_filter == null)
456 throw new HttpException ("Invalid filter");
462 // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
463 static Stream GetSubStream (Stream stream)
466 if (stream is IntPtrStream)
467 return new IntPtrStream (stream);
470 if (stream is MemoryStream) {
471 MemoryStream other = (MemoryStream) stream;
472 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
475 if (stream is TempFileStream) {
476 ((TempFileStream) stream).SavePosition ();
480 throw new NotSupportedException ("The stream is " + stream.GetType ());
483 static void EndSubStream (Stream stream)
485 if (stream is TempFileStream) {
486 ((TempFileStream) stream).RestorePosition ();
491 // Loads the data on the form for multipart/form-data
493 void LoadMultiPart ()
495 string boundary = GetParameter (ContentType, "; boundary=");
496 if (boundary == null)
499 Stream input = GetSubStream (InputStream);
500 HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
502 HttpMultipart.Element e;
503 while ((e = multi_part.ReadNextElement ()) != null) {
504 if (e.Filename == null){
505 byte [] copy = new byte [e.Length];
507 input.Position = e.Start;
508 input.Read (copy, 0, (int) e.Length);
510 form.Add (e.Name, ContentEncoding.GetString (copy));
513 // We use a substream, as in 2.x we will support large uploads streamed to disk,
515 HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
516 files.AddFile (e.Name, sub);
519 EndSubStream (input);
523 // Adds the key/value to the form, and sets the argumets to empty
525 void AddRawKeyValue (StringBuilder key, StringBuilder value)
527 string decodedKey = HttpUtility.UrlDecode (key.ToString (), ContentEncoding);
528 if (form.Get (decodedKey) != null)
529 // probably should warn about a duplicate here. Also, should we replace the old value instead?
532 form.Add (decodedKey,
533 HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
540 // Loads the form data from on a application/x-www-form-urlencoded post
543 void RawLoadWwwForm ()
548 Stream input = GetSubStream (InputStream);
549 StreamReader s = new StreamReader (input, ContentEncoding);
551 StringBuilder key = new StringBuilder ();
552 StringBuilder value = new StringBuilder ();
555 while ((c = s.Read ()) != -1){
558 while ((c = s.Read ()) != -1){
560 AddRawKeyValue (key, value);
563 value.Append ((char) c);
566 AddRawKeyValue (key, value);
570 AddRawKeyValue (key, value);
572 key.Append ((char) c);
575 AddRawKeyValue (key, value);
577 EndSubStream (input);
580 bool IsContentType (string ct, bool starts_with)
583 return StrUtils.StartsWith (ContentType, ct, true);
585 return String.Compare (ContentType, ct, true, CultureInfo.InvariantCulture) == 0;
588 public NameValueCollection Form {
591 form = new WebROCollection ();
592 files = new HttpFileCollection ();
594 if (IsContentType ("application/x-www-form-urlencoded", true))
596 else if (IsContentType ("multipart/form-data", true))
602 if (validate_form && !checked_form){
603 ValidateNameValueCollection ("Form", form);
611 public NameValueCollection Headers {
614 headers = new HeadersCollection (this);
620 public string HttpMethod {
622 if (http_method == null){
623 if (worker_request != null)
624 http_method = worker_request.GetHttpVerbName ();
632 void DoFilter (byte [] buffer)
634 if (input_filter == null || filter == null)
637 if (buffer.Length < 1024)
638 buffer = new byte [1024];
640 // Replace the input with the filtered input
641 input_filter.BaseStream = input_stream;
642 MemoryStream ms = new MemoryStream ();
644 int n = filter.Read (buffer, 0, buffer.Length);
647 ms.Write (buffer, 0, n);
649 // From now on input_stream has the filtered input
650 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
654 const int INPUT_BUFFER_SIZE = 32*1024;
656 TempFileStream GetTempStream ()
658 string tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
659 TempFileStream f = null;
661 Random rnd = new Random ();
666 path = System.IO.Path.Combine (tempdir, "tmp" + num.ToString("x") + ".req");
669 f = new TempFileStream (path);
670 } catch (SecurityException) {
671 // avoid an endless loop
679 void MakeInputStream ()
681 if (input_stream != null)
684 if (worker_request == null) {
685 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
686 DoFilter (new byte [1024]);
691 // Use an unmanaged memory block as this might be a large
694 int content_length = ContentLength;
697 HttpRuntimeSection config = (HttpRuntimeSection) WebConfigurationManager.GetSection ("system.web/httpRuntime");
699 HttpRuntimeConfig config = (HttpRuntimeConfig) HttpContext.GetAppConfig ("system.web/httpRuntime");
701 if ((content_length / 1024) > config.MaxRequestLength)
702 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
706 buffer = worker_request.GetPreloadedEntityBody ();
707 // we check the instance field 'content_length' here, not the local var.
708 if (this.content_length <= 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
709 if (buffer == null || content_length == 0) {
710 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
712 input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
714 DoFilter (new byte [1024]);
719 total = buffer.Length;
721 if (content_length > 0 && (content_length / 1024) >= config.RequestLengthDiskThreshold) {
722 // Writes the request to disk
723 total = Math.Min (content_length, total);
724 request_file = GetTempStream ();
725 Stream output = request_file;
727 output.Write (buffer, 0, total);
729 if (total < content_length) {
730 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
733 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
734 n = worker_request.ReadEntityBody (buffer, min);
737 output.Write (buffer, 0, n);
739 } while (total < content_length);
742 request_file.SetReadOnly ();
743 input_stream = request_file;
744 } else if (content_length > 0) {
745 // Buffers the request in an IntPtrStream
746 total = Math.Min (content_length, total);
747 IntPtr content = Marshal.AllocHGlobal (content_length);
748 if (content == (IntPtr) 0)
749 throw new HttpException (String.Format ("Not enough memory to allocate {0} bytes.",
753 Marshal.Copy (buffer, 0, content, total);
755 if (total < content_length) {
756 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
759 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
760 n = worker_request.ReadEntityBody (buffer, min);
763 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
765 } while (total < content_length);
768 input_stream = new IntPtrStream (content, total);
770 // Buffers the request in a MemoryStream or writes to disk if threshold exceeded
771 MemoryStream ms = new MemoryStream ();
774 ms.Write (buffer, 0, total);
776 buffer = new byte [INPUT_BUFFER_SIZE];
777 long maxlength = config.MaxRequestLength * 1024;
778 long disk_th = config.RequestLengthDiskThreshold * 1024;
781 n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
785 if (total < 0 || total > maxlength)
786 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
788 if (ms != null && total > disk_th) {
789 // Swith to on-disk file.
790 request_file = GetTempStream ();
791 ms.WriteTo (request_file);
793 output = request_file;
795 output.Write (buffer, 0, n);
799 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
801 request_file.SetReadOnly ();
802 input_stream = request_file;
807 if (total < content_length)
808 throw new HttpException (411, "The request body is incomplete.");
812 internal void ReleaseResources ()
815 if (input_stream != null){
816 stream = input_stream;
823 if (request_file != null) {
824 stream = request_file;
832 public Stream InputStream {
834 if (input_stream == null)
841 public bool IsAuthenticated {
843 if (context.User == null || context.User.Identity == null)
845 return context.User.Identity.IsAuthenticated;
849 public bool IsSecureConnection {
851 if (worker_request == null)
853 return worker_request.IsSecure ();
857 public string this [string key] {
858 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
860 // "The QueryString, Form, Cookies, or ServerVariables collection member
861 // specified in the key parameter."
862 string val = QueryString [key];
866 HttpCookie cookie = Cookies [key];
871 val = ServerVariables [key];
877 public NameValueCollection Params {
878 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
880 if (all_params == null)
881 all_params = new HttpParamsCollection (QueryString, Form, ServerVariables, Cookies);
889 if (unescaped_path == null)
891 unescaped_path = Uri.UnescapeDataString (UrlComponents.Path);
893 unescaped_path = HttpUtility.UrlDecode (UrlComponents.Path);
895 return unescaped_path;
899 public string PathInfo {
901 if (path_info == null) {
902 if (worker_request == null)
904 path_info = worker_request.GetPathInfo ();
911 public string PhysicalApplicationPath {
913 if (worker_request == null)
914 throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
916 string path = HttpRuntime.AppDomainAppPath;
917 if (SecurityManager.SecurityEnabled) {
918 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
924 public string PhysicalPath {
926 if (worker_request == null)
927 return String.Empty; // don't check security with an empty string!
929 if (physical_path == null) {
930 // Don't call HttpRequest.MapPath here, as that one *trims* the input
931 physical_path = worker_request.MapPath (FilePath);
934 if (SecurityManager.SecurityEnabled) {
935 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
937 return physical_path;
941 internal string RootVirtualDir {
943 if (root_virtual_dir == null){
944 string fp = FilePath;
945 int p = fp.LastIndexOf ('/');
948 root_virtual_dir = "/";
950 root_virtual_dir = fp.Substring (0, p);
952 return root_virtual_dir;
956 public NameValueCollection QueryString {
958 if (query_string_nvc == null){
959 string q = UrlComponents.Query;
963 query_string_nvc = new WebROCollection ();
964 HttpUtility.ParseQueryString (q, ContentEncoding, query_string_nvc);
965 query_string_nvc.Protect();
968 if (validate_query_string && !checked_query_string) {
969 ValidateNameValueCollection ("QueryString", query_string_nvc);
970 checked_query_string = true;
973 return query_string_nvc;
977 public string RawUrl {
979 if (worker_request != null)
980 return worker_request.GetRawUrl ();
982 return UrlComponents.Path + UrlComponents.Query;
989 public string RequestType {
991 if (request_type == null){
992 if (worker_request != null) {
993 request_type = worker_request.GetHttpVerbName ();
994 http_method = request_type;
996 request_type = "GET";
1003 request_type = value;
1007 public NameValueCollection ServerVariables {
1008 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1010 if (server_variables == null)
1011 server_variables = new ServerVariablesCollection (this);
1013 return server_variables;
1017 public int TotalBytes {
1019 Stream ins = InputStream;
1020 return (int) ins.Length;
1026 if (cached_url == null) {
1027 if (orig_url == null)
1028 cached_url = UrlComponents.Uri;
1030 cached_url = new Uri (orig_url);
1037 public Uri UrlReferrer {
1039 if (worker_request == null)
1042 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
1046 return new Uri (hr);
1050 public string UserAgent {
1052 if (worker_request == null)
1055 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
1059 public string UserHostAddress {
1061 if (worker_request == null)
1064 return worker_request.GetRemoteAddress ();
1068 public string UserHostName {
1070 if (worker_request == null)
1073 return worker_request.GetRemoteName ();
1077 public string [] UserLanguages {
1079 if (worker_request == null)
1082 if (user_languages == null)
1083 user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
1085 return user_languages;
1089 public byte [] BinaryRead (int count)
1092 throw new ArgumentException ("count is < 0");
1094 Stream s = InputStream;
1095 byte [] ret = new byte [count];
1096 if (s.Read (ret, 0, count) != count)
1097 throw new ArgumentException (
1098 String.Format ("count {0} exceeds length of available input {1}",
1099 count, s.Length - s.Position));
1103 public int [] MapImageCoordinates (string imageFieldName)
1105 string method = HttpMethod;
1106 NameValueCollection coll = null;
1107 if (method == "HEAD" || method == "GET")
1109 else if (method == "POST")
1115 string x = coll [imageFieldName + ".x"];
1116 if (x == null || x == "")
1119 string y = coll [imageFieldName + ".y"];
1120 if (y == null || y == "")
1123 int [] result = new int [2];
1125 result [0] = Int32.Parse (x);
1126 result [1] = Int32.Parse (y);
1134 public string MapPath (string virtualPath)
1136 if (worker_request == null)
1139 return MapPath (virtualPath, BaseVirtualDir, true);
1142 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1144 if (worker_request == null)
1145 throw new HttpException ("No HttpWorkerRequest");
1147 if (virtualPath == null)
1150 virtualPath = virtualPath.Trim ();
1151 if (virtualPath.Length == 0)
1155 if (virtualPath.IndexOf (':') != -1)
1156 throw new HttpException (String.Format ("'{0}' is not a valid virtual path.", virtualPath));
1158 string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
1160 if (!VirtualPathUtility.IsRooted (virtualPath)) {
1161 if (StrUtils.IsNullOrEmpty (baseVirtualDir))
1162 baseVirtualDir = appVirtualPath;
1163 virtualPath = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (baseVirtualDir), virtualPath);
1165 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath);
1167 if (!allowCrossAppMapping){
1168 if (!StrUtils.StartsWith (virtualPath, appVirtualPath, true))
1169 throw new HttpException ("MapPath: Mapping across applications not allowed");
1170 if (appVirtualPath.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1171 throw new HttpException ("MapPath: Mapping across applications not allowed");
1174 return worker_request.MapPath (virtualPath);
1176 string path = worker_request.MapPath (virtualPath);
1177 if (virtualPath [virtualPath.Length - 1] != '/' && path [path.Length - 1] == System.IO.Path.DirectorySeparatorChar)
1178 path = path.TrimEnd (System.IO.Path.DirectorySeparatorChar);
1183 public void SaveAs (string filename, bool includeHeaders)
1185 Stream output = new FileStream (filename, FileMode.Create);
1186 if (includeHeaders) {
1187 StringBuilder sb = new StringBuilder ();
1188 string version = String.Empty;
1190 if (worker_request != null) {
1191 version = worker_request.GetHttpVersion ();
1192 path = UrlComponents.Path;
1194 string qs = UrlComponents.Query;
1196 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1197 NameValueCollection coll = Headers;
1198 foreach (string k in coll.AllKeys) {
1201 sb.Append (coll [k]);
1206 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1207 output.Write (bytes, 0, bytes.Length);
1210 // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1211 // to keep InputStream in its state.
1212 Stream input = GetSubStream (InputStream);
1214 long len = input.Length;
1215 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1216 byte [] data = new byte [buf_size];
1218 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1219 output.Write (data, 0, count);
1225 EndSubStream (input);
1229 public void ValidateInput ()
1231 validate_cookies = true;
1232 validate_query_string = true;
1233 validate_form = true;
1236 #region internal routines
1237 internal string ClientTarget {
1239 return client_target;
1243 client_target = value;
1254 string address = worker_request.GetRemoteAddress ();
1256 if (StrUtils.IsNullOrEmpty (address))
1259 if (address == "127.0.0.1")
1262 System.Net.IPAddress remoteAddr = System.Net.IPAddress.Parse (address);
1263 if (System.Net.IPAddress.IsLoopback (remoteAddr))
1266 for (int i = 0; i < host_addresses.Length; i++)
1267 if (remoteAddr.Equals (host_addresses [i]))
1274 internal void SetFilePath (string path)
1277 physical_path = null;
1280 internal void SetCurrentExePath (string path)
1283 current_exe_path = path;
1285 UrlComponents.Path = path;
1286 // recreated on demand
1287 root_virtual_dir = null;
1288 base_virtual_dir = null;
1289 physical_path = null;
1290 unescaped_path = null;
1293 internal void SetPathInfo (string pi)
1299 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1300 internal void SetHeader (string name, string value)
1302 WebROCollection h = (WebROCollection) Headers;
1308 // Notice: there is nothing raw about this querystring.
1309 internal string QueryStringRaw {
1311 return UrlComponents.Query;
1315 UrlComponents.Query = value;
1317 query_string_nvc = null;
1321 // Internal, dont know what it does, so flagged as public so we can see it.
1322 internal void SetForm (WebROCollection coll)
1327 internal HttpWorkerRequest WorkerRequest {
1329 return worker_request;
1333 internal HttpContext Context {
1339 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1344 foreach (string key in coll.Keys) {
1345 string val = coll [key];
1346 if (val != null && val != "" && CheckString (val))
1347 ThrowValidationException (name, key, val);
1351 static void ValidateCookieCollection (HttpCookieCollection cookies)
1353 if (cookies == null)
1356 int size = cookies.Count;
1358 for (int i = 0 ; i < size ; i++) {
1359 cookie = cookies[i];
1360 string value = cookie.Value;
1362 if (value != null && value != "" && CheckString (value))
1363 ThrowValidationException ("Cookies", cookie.Name, cookie.Value);
1367 static void ThrowValidationException (string name, string key, string value)
1369 string v = "\"" + value + "\"";
1371 v = v.Substring (0, 16) + "...\"";
1373 string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1374 "detected from the client ({1}={2}).", name, key, v);
1376 throw new HttpRequestValidationException (msg);
1379 static bool CheckString (string val)
1381 int len = val.Length;
1385 char current = val [0];
1386 for (int idx = 1; idx < len; idx++) {
1387 char next = val [idx];
1388 if (current == '<' || current == '\xff1c') {
1389 if (next == '!' || next < ' '
1390 || (next >= 'a' && next <= 'z')
1391 || (next >= 'A' && next <= 'Z'))
1393 } else if (current == '&' && next == '#') {
1403 static System.Net.IPAddress [] GetLocalHostAddresses ()
1406 string hostName = System.Net.Dns.GetHostName ();
1408 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostAddresses (hostName);
1410 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostByName (hostName).AddressList;
1416 return new System.Net.IPAddress[0];
1422 #region Helper classes
1425 // Stream-based multipart handling.
1427 // In this incarnation deals with an HttpInputStream as we are now using
1428 // IntPtr-based streams instead of byte []. In the future, we will also
1429 // send uploads above a certain threshold into the disk (to implement
1430 // limit-less HttpInputFiles).
1433 class HttpMultipart {
1435 public class Element {
1436 public string ContentType;
1438 public string Filename;
1442 public override string ToString ()
1444 return string.Format ("ContentType {0}, Name {1}, Filename {2}, Start {3}, Length {4}",
1445 ContentType, Name, Filename, Start, Length);
1451 byte [] boundary_bytes;
1457 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
1460 // In the case of multipart entities, in which one or more different
1461 // sets of data are combined in a single body, a "multipart" media type
1462 // field must appear in the entity's header. The body must then contain
1463 // one or more body parts, each preceded by a boundary delimiter line,
1464 // and the last one followed by a closing boundary delimiter line.
1465 // After its boundary delimiter line, each body part then consists of a
1466 // header area, a blank line, and a body area. Thus a body part is
1467 // similar to an RFC 822 message in syntax, but different in meaning.
1469 public HttpMultipart (Stream data, string b, Encoding encoding)
1473 boundary_bytes = encoding.GetBytes (b);
1474 buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1475 this.encoding = encoding;
1476 sb = new StringBuilder ();
1481 // CRLF or LF are ok as line endings.
1482 bool got_cr = false;
1486 b = data.ReadByte ();
1495 sb.Append ((char) b);
1501 return sb.ToString ();
1505 static string GetContentDispositionAttribute (string l, string name)
1507 int idx = l.IndexOf (name + "=\"");
1510 int begin = idx + name.Length + "=\"".Length;
1511 int end = l.IndexOf ('"', begin);
1516 return l.Substring (begin, end - begin);
1519 string GetContentDispositionAttributeWithEncoding (string l, string name)
1521 int idx = l.IndexOf (name + "=\"");
1524 int begin = idx + name.Length + "=\"".Length;
1525 int end = l.IndexOf ('"', begin);
1531 string temp = l.Substring (begin, end - begin);
1532 byte [] source = new byte [temp.Length];
1533 for (int i = temp.Length - 1; i >= 0; i--)
1534 source [i] = (byte) temp [i];
1536 return encoding.GetString (source);
1539 bool ReadBoundary ()
1542 string line = ReadLine ();
1545 if (line [0] != '-' || line [1] != '-')
1548 if (!StrUtils.EndsWith (line, boundary, false))
1556 string ReadHeaders ()
1558 string s = ReadLine ();
1565 bool CompareBytes (byte [] orig, byte [] other)
1567 for (int i = orig.Length - 1; i >= 0; i--)
1568 if (orig [i] != other [i])
1574 long MoveToNextBoundary ()
1577 bool got_cr = false;
1580 int c = data.ReadByte ();
1585 if (state == 0 && c == LF) {
1586 retval = data.Position - 1;
1590 c = data.ReadByte ();
1591 } else if (state == 0) {
1593 c = data.ReadByte ();
1594 } else if (state == 1 && c == '-') {
1595 c = data.ReadByte ();
1602 continue; // no ReadByte() here
1605 int nread = data.Read (buffer, 0, buffer.Length);
1606 int bl = buffer.Length;
1610 if (!CompareBytes (boundary_bytes, buffer)) {
1612 data.Position = retval + 2;
1617 c = data.ReadByte ();
1621 if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
1623 } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
1625 data.Position = retval + 2;
1630 c = data.ReadByte ();
1633 data.Position = retval + 2;
1639 state = 0; // no ReadByte() here
1646 public Element ReadNextElement ()
1648 if (at_eof || ReadBoundary ())
1651 Element elem = new Element ();
1653 while ((header = ReadHeaders ()) != null) {
1654 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
1655 elem.Name = GetContentDispositionAttribute (header, "name");
1656 elem.Filename = GetContentDispositionAttributeWithEncoding (header, "filename");
1657 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
1658 elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
1662 long start = data.Position;
1664 long pos = MoveToNextBoundary ();
1668 elem.Length = pos - start;