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 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;
71 WebROCollection all_params;
72 WebROCollection headers;
74 InputFilterStream input_filter;
76 HttpCookieCollection cookies;
80 HttpFileCollection files;
82 ServerVariablesCollection server_variables;
83 HttpClientCertificate client_cert;
86 string [] accept_types;
87 string [] user_languages;
89 TempFileStream request_file;
92 bool validate_cookies, validate_query_string, validate_form;
93 bool checked_cookies, checked_query_string, checked_form;
95 public HttpRequest (string filename, string url, string queryString)
97 // warning 169: what are we supposed to do with filename?
99 this.filename = filename;
102 url_components = new UriBuilder (url);
103 url_components.Query = queryString;
105 query_string_nvc = new WebROCollection ();
106 HttpUtility.ParseQueryString (queryString, Encoding.Default, query_string_nvc);
107 query_string_nvc.Protect ();
110 UriBuilder UrlComponents {
112 if (url_components == null) {
113 url_components = new UriBuilder ();
114 url_components.Scheme = worker_request.GetProtocol ();
115 url_components.Host = worker_request.GetServerName ();
116 url_components.Port = worker_request.GetLocalPort ();
117 url_components.Path = worker_request.GetUriPath ();
119 byte[] queryStringRaw = worker_request.GetQueryStringRawBytes();
120 if(queryStringRaw != null)
121 url_components.Query = ContentEncoding.GetString(queryStringRaw);
123 url_components.Query = worker_request.GetQueryString();
125 return url_components;
129 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
131 this.worker_request = worker_request;
132 this.context = context;
135 string [] SplitHeader (int header_index)
137 string [] result = null;
138 string header = worker_request.GetKnownRequestHeader (header_index);
139 if (header != null && header != "" && header.Trim () != "") {
140 result = header.Split (',');
141 for (int i = result.Length - 1; i >= 0; i--)
142 result [i] = result [i].Trim ();
147 public string [] AcceptTypes {
149 if (worker_request == null)
152 if (accept_types == null)
153 accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
161 public string AnonymousID {
166 anonymous_id = value;
171 public string ApplicationPath {
173 if (worker_request == null)
175 return worker_request.GetAppPath ();
179 public HttpBrowserCapabilities Browser {
181 if (browser_capabilities == null)
182 browser_capabilities = (HttpBrowserCapabilities)
183 HttpCapabilitiesBase.GetConfigCapabilities (null, this);
185 return browser_capabilities;
189 browser_capabilities = value;
193 public HttpClientCertificate ClientCertificate {
195 if (client_cert == null)
196 client_cert = new HttpClientCertificate (worker_request);
201 static internal string GetParameter (string header, string attr)
203 int ap = header.IndexOf (attr);
208 if (ap >= header.Length)
211 char ending = header [ap];
215 int end = header.IndexOf (ending, ap+1);
217 return (ending == '"') ? null : header.Substring (ap);
219 return header.Substring (ap+1, end-ap-1);
222 public Encoding ContentEncoding {
224 if (encoding == null){
225 if (worker_request == null)
226 throw new HttpException ("No HttpWorkerRequest");
228 string content_type = ContentType;
229 string parameter = GetParameter (content_type, "; charset=");
230 if (parameter == null) {
231 encoding = WebEncoding.RequestEncoding;
234 // Do what the #1 web server does
235 encoding = Encoding.GetEncoding (parameter);
237 encoding = WebEncoding.RequestEncoding;
249 public int ContentLength {
251 if (content_length == -1){
252 if (worker_request == null)
255 string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
259 content_length = Int32.Parse (cl);
264 // content_length will still be < 0, but we know we gotta read from the client
265 if (content_length < 0)
268 return content_length;
272 public string ContentType {
274 if (content_type == null){
275 if (worker_request != null)
276 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
278 if (content_type == null)
279 content_type = String.Empty;
286 content_type = value;
290 public HttpCookieCollection Cookies {
292 if (cookies == null) {
293 if (worker_request == null) {
294 cookies = new HttpCookieCollection ();
296 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
297 cookies = new HttpCookieCollection (cookie_hv);
301 if (validate_cookies && !checked_cookies){
302 ValidateCookieCollection (cookies);
303 checked_cookies = true;
311 public string CurrentExecutionFilePath {
313 if (current_exe_path != null)
314 return current_exe_path;
320 public string FilePath {
322 if (worker_request == null)
323 return "/"; // required for 2.0
325 if (file_path == null)
326 file_path = UrlUtils.Canonic (worker_request.GetFilePath ());
332 internal string BaseVirtualDir {
334 if (base_virtual_dir == null){
335 base_virtual_dir = FilePath;
336 int p = base_virtual_dir.LastIndexOf ('/');
338 base_virtual_dir = base_virtual_dir.Substring (0, p);
340 return base_virtual_dir;
344 public HttpFileCollection Files {
347 files = new HttpFileCollection ();
348 if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
349 form = new WebROCollection ();
358 public Stream Filter {
363 if (input_filter == null)
364 input_filter = new InputFilterStream ();
370 // This checks that get_ was called before.
371 if (input_filter == null)
372 throw new HttpException ("Invalid filter");
378 // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
379 static Stream GetSubStream (Stream stream)
382 if (stream is IntPtrStream)
383 return new IntPtrStream (stream);
386 if (stream is MemoryStream) {
387 MemoryStream other = (MemoryStream) stream;
388 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
391 if (stream is TempFileStream) {
392 ((TempFileStream) stream).SavePosition ();
396 throw new NotSupportedException ("The stream is " + stream.GetType ());
399 static void EndSubStream (Stream stream)
401 if (stream is TempFileStream) {
402 ((TempFileStream) stream).RestorePosition ();
407 // Loads the data on the form for multipart/form-data
409 void LoadMultiPart ()
411 string boundary = GetParameter (ContentType, "; boundary=");
412 if (boundary == null)
415 Stream input = GetSubStream (InputStream);
416 HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
418 HttpMultipart.Element e;
419 while ((e = multi_part.ReadNextElement ()) != null) {
420 if (e.Filename == null){
421 byte [] copy = new byte [e.Length];
423 input.Position = e.Start;
424 input.Read (copy, 0, (int) e.Length);
426 form.Add (e.Name, ContentEncoding.GetString (copy));
429 // We use a substream, as in 2.x we will support large uploads streamed to disk,
431 HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
432 files.AddFile (e.Name, sub);
435 EndSubStream (input);
439 // Adds the key/value to the form, and sets the argumets to empty
441 void AddRawKeyValue (StringBuilder key, StringBuilder value)
443 form.Add (HttpUtility.UrlDecode (key.ToString (), ContentEncoding),
444 HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
451 // Loads the form data from on a application/x-www-form-urlencoded post
455 Stream input = GetSubStream (InputStream);
456 StreamReader s = new StreamReader (input, ContentEncoding);
458 StringBuilder key = new StringBuilder ();
459 StringBuilder value = new StringBuilder ();
462 while ((c = s.Read ()) != -1){
465 while ((c = s.Read ()) != -1){
467 AddRawKeyValue (key, value);
470 value.Append ((char) c);
473 AddRawKeyValue (key, value);
477 AddRawKeyValue (key, value);
479 key.Append ((char) c);
482 AddRawKeyValue (key, value);
484 EndSubStream (input);
487 bool IsContentType (string ct, bool starts_with)
490 return StrUtils.StartsWith (ContentType, ct, true);
492 return String.Compare (ContentType, ct, true, CultureInfo.InvariantCulture) == 0;
495 public NameValueCollection Form {
498 form = new WebROCollection ();
499 files = new HttpFileCollection ();
501 if (IsContentType ("application/x-www-form-urlencoded", true))
503 else if (IsContentType ("multipart/form-data", true))
509 if (validate_form && !checked_form){
510 ValidateNameValueCollection ("Form", form);
518 public NameValueCollection Headers {
520 if (headers == null){
521 headers = new WebROCollection ();
522 if (worker_request == null) {
527 for (int i = 0; i < HttpWorkerRequest.RequestHeaderMaximum; i++){
528 string hval = worker_request.GetKnownRequestHeader (i);
530 if (hval == null || hval == "")
533 headers.Add (HttpWorkerRequest.GetKnownRequestHeaderName (i), hval);
536 string [][] unknown = worker_request.GetUnknownRequestHeaders ();
537 if (unknown != null && unknown.GetUpperBound (0) != -1){
538 int top = unknown.GetUpperBound (0) + 1;
540 for (int i = 0; i < top; i++){
541 // should check if unknown [i] is not null, but MS does not.
543 headers.Add (unknown [i][0], unknown [i][1]);
552 public string HttpMethod {
554 if (http_method == null){
555 if (worker_request != null)
556 http_method = worker_request.GetHttpVerbName ();
566 const int INPUT_BUFFER_SIZE = 1024;
568 void MakeInputStream ()
570 if (worker_request == null)
571 throw new HttpException ("No HttpWorkerRequest");
573 // consider for perf:
574 // return ((ServletWorkerRequest)worker_request).InputStream();
577 // Use an unmanaged memory block as this might be a large
580 int content_length = ContentLength;
582 if (content_length == 0 && HttpMethod == "POST")
583 throw new HttpException (411, "Length expected");
586 HttpRuntimeSection config = (HttpRuntimeSection) WebConfigurationManager.GetSection ("system.web/httpRuntime");
588 HttpRuntimeConfig config = (HttpRuntimeConfig) HttpContext.GetAppConfig ("system.web/httpRuntime");
590 if (content_length > (config.MaxRequestLength * 1024))
591 throw new HttpException ("File exceeds httpRuntime limit");
593 byte[] content = new byte[content_length];
595 throw new HttpException (String.Format ("Not enough memory to allocate {0} bytes", content_length));
599 buffer = worker_request.GetPreloadedEntityBody ();
601 total = buffer.Length;
602 if (content_length > 0)
603 total = Math.Min (content_length, total);
604 Array.Copy (buffer, content, total);
609 buffer = new byte [INPUT_BUFFER_SIZE];
610 while (total < content_length){
612 n = worker_request.ReadEntityBody (buffer, Math.Min (content_length-total, INPUT_BUFFER_SIZE));
615 Array.Copy (buffer, 0, content, total, n);
618 if (total < content_length)
619 throw new HttpException (411, "The uploaded file is incomplete");
621 input_stream = new MemoryStream (content, 0, content.Length, false, true);
624 const int INPUT_BUFFER_SIZE = 32*1024;
626 TempFileStream GetTempStream ()
628 string tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
629 TempFileStream f = null;
631 Random rnd = new Random ();
636 path = System.IO.Path.Combine (tempdir, "tmp" + num.ToString("x") + ".req");
639 f = new TempFileStream (path);
640 } catch (SecurityException) {
641 // avoid an endless loop
649 void DoFilter (byte [] buffer)
651 if (input_filter == null || filter == null)
654 if (buffer.Length < 1024)
655 buffer = new byte [1024];
657 // Replace the input with the filtered input
658 input_filter.BaseStream = input_stream;
659 MemoryStream ms = new MemoryStream ();
661 int n = filter.Read (buffer, 0, buffer.Length);
664 ms.Write (buffer, 0, n);
666 // From now on input_stream has the filtered input
667 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
670 void MakeInputStream ()
672 if (input_stream != null)
675 if (worker_request == null) {
676 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
677 DoFilter (new byte [1024]);
682 // Use an unmanaged memory block as this might be a large
685 int content_length = ContentLength;
688 HttpRuntimeSection config = (HttpRuntimeSection) WebConfigurationManager.GetSection ("system.web/httpRuntime");
690 HttpRuntimeConfig config = (HttpRuntimeConfig) HttpContext.GetAppConfig ("system.web/httpRuntime");
692 if ((content_length / 1024) > config.MaxRequestLength)
693 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
697 buffer = worker_request.GetPreloadedEntityBody ();
698 // we check the instance field 'content_length' here, not the local var.
699 if (this.content_length <= 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
700 if (buffer == null || content_length == 0) {
701 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
703 input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
705 DoFilter (new byte [1024]);
710 total = buffer.Length;
712 if (content_length > 0 && (content_length / 1024) >= config.RequestLengthDiskThreshold) {
713 // Writes the request to disk
714 total = Math.Min (content_length, total);
715 request_file = GetTempStream ();
716 Stream output = request_file;
718 output.Write (buffer, 0, total);
720 if (total < content_length) {
721 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
724 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
725 n = worker_request.ReadEntityBody (buffer, min);
728 output.Write (buffer, 0, n);
730 } while (total < content_length);
733 request_file.SetReadOnly ();
734 input_stream = request_file;
735 } else if (content_length > 0) {
736 // Buffers the request in an IntPtrStream
737 total = Math.Min (content_length, total);
738 IntPtr content = Marshal.AllocHGlobal (content_length);
739 if (content == (IntPtr) 0)
740 throw new HttpException (String.Format ("Not enough memory to allocate {0} bytes.",
744 Marshal.Copy (buffer, 0, content, total);
746 if (total < content_length) {
747 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
750 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
751 n = worker_request.ReadEntityBody (buffer, min);
754 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
756 } while (total < content_length);
759 input_stream = new IntPtrStream (content, total);
761 // Buffers the request in a MemoryStream or writes to disk if threshold exceeded
762 MemoryStream ms = new MemoryStream ();
765 ms.Write (buffer, 0, total);
767 buffer = new byte [INPUT_BUFFER_SIZE];
768 long maxlength = config.MaxRequestLength * 1024;
769 long disk_th = config.RequestLengthDiskThreshold * 1024;
772 n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
776 if (total < 0 || total > maxlength)
777 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
779 if (ms != null && total > disk_th) {
780 // Swith to on-disk file.
781 request_file = GetTempStream ();
782 ms.WriteTo (request_file);
784 output = request_file;
786 output.Write (buffer, 0, n);
790 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
792 request_file.SetReadOnly ();
793 input_stream = request_file;
798 if (total < content_length)
799 throw new HttpException (411, "The request body is incomplete.");
802 internal void ReleaseResources ()
805 if (input_stream != null){
806 stream = input_stream;
813 if (request_file != null) {
814 stream = request_file;
822 public Stream InputStream {
824 if (input_stream == null)
831 public bool IsAuthenticated {
833 if (context.User == null || context.User.Identity == null)
835 return context.User.Identity.IsAuthenticated;
839 public bool IsSecureConnection {
841 if (worker_request == null)
843 return worker_request.IsSecure ();
847 public string this [string key] {
848 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
850 // "The QueryString, Form, Cookies, or ServerVariables collection member
851 // specified in the key parameter."
852 string val = QueryString [key];
856 HttpCookie cookie = Cookies [key];
861 val = ServerVariables [key];
867 public NameValueCollection Params {
868 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
870 if (all_params == null) {
871 all_params = new WebROCollection ();
873 all_params.Add (QueryString);
875 /* special handling for Cookies since
876 * it isn't a NameValueCollection. */
877 foreach (string key in Cookies.AllKeys) {
878 all_params.Add (key, Cookies[key].Value);
881 all_params.Add (Form);
882 all_params.Add (ServerVariables);
883 all_params.Protect ();
892 return UrlComponents.Path;
896 public string PathInfo {
898 if (path_info == null) {
899 if (worker_request == null)
901 path_info = worker_request.GetPathInfo ();
908 public string PhysicalApplicationPath {
910 if (worker_request == null)
911 throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
913 string path = HttpRuntime.AppDomainAppPath;
914 if (SecurityManager.SecurityEnabled) {
915 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
921 public string PhysicalPath {
923 if (worker_request == null)
924 return String.Empty; // don't check security with an empty string!
926 if (physical_path == null)
927 physical_path = MapPath (CurrentExecutionFilePath);
929 if (SecurityManager.SecurityEnabled) {
930 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
932 return physical_path;
936 internal string RootVirtualDir {
938 if (root_virtual_dir == null){
939 string fp = FilePath;
940 int p = fp.LastIndexOf ('/');
943 root_virtual_dir = "/";
945 root_virtual_dir = fp.Substring (0, p);
948 return root_virtual_dir;
952 public NameValueCollection QueryString {
954 if (query_string_nvc == null){
955 string q = UrlComponents.Query;
959 query_string_nvc = new WebROCollection ();
960 HttpUtility.ParseQueryString (q, ContentEncoding, query_string_nvc);
961 query_string_nvc.Protect();
964 if (validate_query_string && !checked_query_string) {
965 ValidateNameValueCollection ("QueryString", query_string_nvc);
966 checked_query_string = true;
969 return query_string_nvc;
973 public string RawUrl {
975 if (worker_request != null)
976 return worker_request.GetRawUrl ();
978 return UrlComponents.Path + UrlComponents.Query;
985 public string RequestType {
987 if (request_type == null){
988 if (worker_request != null) {
989 request_type = worker_request.GetHttpVerbName ();
990 http_method = request_type;
992 request_type = "GET";
999 request_type = value;
1003 public NameValueCollection ServerVariables {
1004 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1006 if (server_variables == null)
1007 server_variables = new ServerVariablesCollection (this);
1009 return server_variables;
1013 public int TotalBytes {
1015 Stream ins = InputStream;
1016 return (int) ins.Length;
1022 if (cached_url == null) {
1023 if (orig_url == null)
1024 cached_url = UrlComponents.Uri;
1026 cached_url = new Uri (orig_url);
1033 public Uri UrlReferrer {
1035 if (worker_request == null)
1038 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
1042 return new Uri (hr);
1046 public string UserAgent {
1048 if (worker_request == null)
1051 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
1055 public string UserHostAddress {
1057 if (worker_request == null)
1060 return worker_request.GetRemoteAddress ();
1064 public string UserHostName {
1066 if (worker_request == null)
1069 return worker_request.GetRemoteName ();
1073 public string [] UserLanguages {
1075 if (worker_request == null)
1078 if (user_languages == null)
1079 user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
1081 return user_languages;
1085 public byte [] BinaryRead (int count)
1088 throw new ArgumentException ("count is < 0");
1090 Stream s = InputStream;
1091 byte [] ret = new byte [count];
1092 if (s.Read (ret, 0, count) != count)
1093 throw new ArgumentException (
1094 String.Format ("count {0} exceeds length of available input {1}",
1095 count, s.Length - s.Position));
1099 public int [] MapImageCoordinates (string imageFieldName)
1101 string method = HttpMethod;
1102 NameValueCollection coll = null;
1103 if (method == "HEAD" || method == "GET")
1105 else if (method == "POST")
1111 string x = coll [imageFieldName + ".x"];
1112 if (x == null || x == "")
1115 string y = coll [imageFieldName + ".y"];
1116 if (y == null || y == "")
1119 int [] result = new int [2];
1121 result [0] = Int32.Parse (x);
1122 result [1] = Int32.Parse (y);
1130 public string MapPath (string virtualPath)
1132 if (worker_request == null)
1135 return MapPath (virtualPath, BaseVirtualDir, true);
1138 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1140 if (worker_request == null)
1141 throw new HttpException ("No HttpWorkerRequest");
1143 if (virtualPath == null || virtualPath == "")
1146 virtualPath = virtualPath.Trim ();
1148 if (virtualPath.IndexOf (':') != -1)
1149 throw new ArgumentNullException (
1150 String.Format ("MapPath: Invalid path '{0}', only virtual paths are accepted", virtualPath));
1153 if (virtualPath.StartsWith(vmw.common.IAppDomainConfig.WAR_ROOT_SYMBOL))
1156 if (baseVirtualDir == null)
1157 baseVirtualDir = RootVirtualDir;
1158 virtualPath = UrlUtils.Combine (baseVirtualDir, virtualPath);
1160 if (!allowCrossAppMapping){
1161 if (!StrUtils.StartsWith (virtualPath, RootVirtualDir, true))
1162 throw new HttpException ("MapPath: Mapping across applications not allowed");
1163 if (RootVirtualDir.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1164 throw new HttpException ("MapPath: Mapping across applications not allowed");
1166 return worker_request.MapPath (virtualPath);
1169 public void SaveAs (string filename, bool includeHeaders)
1171 Stream output = new FileStream (filename, FileMode.Create);
1172 if (includeHeaders) {
1173 StringBuilder sb = new StringBuilder ();
1174 string version = String.Empty;
1176 if (worker_request != null) {
1177 version = worker_request.GetHttpVersion ();
1178 path = UrlComponents.Path;
1180 string qs = UrlComponents.Query;
1182 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1183 NameValueCollection coll = Headers;
1184 foreach (string k in coll.AllKeys) {
1187 sb.Append (coll [k]);
1192 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1193 output.Write (bytes, 0, bytes.Length);
1196 // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1197 // to keep InputStream in its state.
1198 Stream input = GetSubStream (InputStream);
1200 long len = input.Length;
1201 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1202 byte [] data = new byte [buf_size];
1204 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1205 output.Write (data, 0, count);
1211 EndSubStream (input);
1215 public void ValidateInput ()
1217 validate_cookies = true;
1218 validate_query_string = true;
1219 validate_form = true;
1222 #region internal routines
1223 internal string ClientTarget {
1225 return client_target;
1229 client_target = value;
1240 string address = worker_request.GetRemoteAddress ();
1242 return (address == "127.0.0.1");
1246 internal void SetFilePath (string path)
1251 internal void SetCurrentExePath (string path)
1254 current_exe_path = path;
1256 UrlComponents.Path = path;
1257 // recreated on demand
1258 root_virtual_dir = null;
1259 base_virtual_dir = null;
1260 physical_path = null;
1263 internal void SetPathInfo (string pi)
1269 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1270 internal void SetHeader (string name, string value)
1272 WebROCollection h = (WebROCollection) Headers;
1278 // Notice: there is nothing raw about this querystring.
1279 internal string QueryStringRaw {
1281 return UrlComponents.Query;
1285 UrlComponents.Query = value;
1287 query_string_nvc = null;
1291 // Internal, dont know what it does, so flagged as public so we can see it.
1292 internal void SetForm (WebROCollection coll)
1297 internal HttpWorkerRequest WorkerRequest {
1299 return worker_request;
1303 internal HttpContext Context {
1309 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1314 foreach (string key in coll.Keys) {
1315 string val = coll [key];
1316 if (val != null && val != "" && CheckString (val))
1317 ThrowValidationException (name, key, val);
1321 static void ValidateCookieCollection (HttpCookieCollection cookies)
1323 if (cookies == null)
1326 int size = cookies.Count;
1328 for (int i = 0 ; i < size ; i++) {
1329 cookie = cookies[i];
1330 string value = cookie.Value;
1332 if (value != null && value != "" && CheckString (value))
1333 ThrowValidationException ("Cookies", cookie.Name, cookie.Value);
1337 static void ThrowValidationException (string name, string key, string value)
1339 string v = "\"" + value + "\"";
1341 v = v.Substring (0, 16) + "...\"";
1343 string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1344 "detected from the client ({1}={2}).", name, key, v);
1346 throw new HttpRequestValidationException (msg);
1349 static bool CheckString (string val)
1351 int len = val.Length;
1355 char current = val [0];
1356 for (int idx = 1; idx < len; idx++) {
1357 char next = val [idx];
1358 if (current == '<' || current == '\xff1c') {
1359 if (next == '!' || next < ' '
1360 || (next >= 'a' && next <= 'z')
1361 || (next >= 'A' && next <= 'Z'))
1363 } else if (current == '&' && next == '#') {
1376 #region Helper classes
1379 // Stream-based multipart handling.
1381 // In this incarnation deals with an HttpInputStream as we are now using
1382 // IntPtr-based streams instead of byte []. In the future, we will also
1383 // send uploads above a certain threshold into the disk (to implement
1384 // limit-less HttpInputFiles).
1387 class HttpMultipart {
1389 public class Element {
1390 public string ContentType;
1392 public string Filename;
1396 public override string ToString ()
1398 return string.Format ("ContentType {0}, Name {1}, Filename {2}, Start {3}, Length {4}",
1399 ContentType, Name, Filename, Start, Length);
1405 byte [] boundary_bytes;
1411 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
1414 // In the case of multipart entities, in which one or more different
1415 // sets of data are combined in a single body, a "multipart" media type
1416 // field must appear in the entity's header. The body must then contain
1417 // one or more body parts, each preceded by a boundary delimiter line,
1418 // and the last one followed by a closing boundary delimiter line.
1419 // After its boundary delimiter line, each body part then consists of a
1420 // header area, a blank line, and a body area. Thus a body part is
1421 // similar to an RFC 822 message in syntax, but different in meaning.
1423 public HttpMultipart (Stream data, string b, Encoding encoding)
1427 boundary_bytes = encoding.GetBytes (b);
1428 buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1429 this.encoding = encoding;
1430 sb = new StringBuilder ();
1435 // CRLF or LF are ok as line endings.
1436 bool got_cr = false;
1440 b = data.ReadByte ();
1449 sb.Append ((char) b);
1455 return sb.ToString ();
1459 static string GetContentDispositionAttribute (string l, string name)
1461 int idx = l.IndexOf (name + "=\"");
1464 int begin = idx + name.Length + "=\"".Length;
1465 int end = l.IndexOf ('"', begin);
1470 return l.Substring (begin, end - begin);
1473 string GetContentDispositionAttributeWithEncoding (string l, string name)
1475 int idx = l.IndexOf (name + "=\"");
1478 int begin = idx + name.Length + "=\"".Length;
1479 int end = l.IndexOf ('"', begin);
1485 string temp = l.Substring (begin, end - begin);
1486 byte [] source = new byte [temp.Length];
1487 for (int i = temp.Length - 1; i >= 0; i--)
1488 source [i] = (byte) temp [i];
1490 return encoding.GetString (source);
1493 bool ReadBoundary ()
1496 string line = ReadLine ();
1499 if (line [0] != '-' || line [1] != '-')
1502 if (!StrUtils.EndsWith (line, boundary, false))
1510 string ReadHeaders ()
1512 string s = ReadLine ();
1519 bool CompareBytes (byte [] orig, byte [] other)
1521 for (int i = orig.Length - 1; i >= 0; i--)
1522 if (orig [i] != other [i])
1528 long MoveToNextBoundary ()
1531 bool got_cr = false;
1534 int c = data.ReadByte ();
1539 if (state == 0 && c == LF) {
1540 retval = data.Position - 1;
1544 c = data.ReadByte ();
1545 } else if (state == 0) {
1547 c = data.ReadByte ();
1548 } else if (state == 1 && c == '-') {
1549 c = data.ReadByte ();
1556 continue; // no ReadByte() here
1559 int nread = data.Read (buffer, 0, buffer.Length);
1560 int bl = buffer.Length;
1564 if (!CompareBytes (boundary_bytes, buffer)) {
1566 data.Position = retval + 2;
1571 c = data.ReadByte ();
1575 if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
1577 } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
1579 data.Position = retval + 2;
1584 c = data.ReadByte ();
1587 data.Position = retval + 2;
1593 state = 0; // no ReadByte() here
1600 public Element ReadNextElement ()
1602 if (at_eof || ReadBoundary ())
1605 Element elem = new Element ();
1607 while ((header = ReadHeaders ()) != null) {
1608 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
1609 elem.Name = GetContentDispositionAttribute (header, "name");
1610 elem.Filename = GetContentDispositionAttributeWithEncoding (header, "filename");
1611 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
1612 elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
1616 long start = data.Position;
1618 long pos = MoveToNextBoundary ();
1622 elem.Length = pos - start;