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;
54 string filename, query_string;
55 UriBuilder uri_builder;
60 // On-demand computed values
62 HttpBrowserCapabilities browser_capabilities;
63 string file_path, base_virtual_dir, root_virtual_dir;
65 int content_length = -1;
67 string current_exe_path;
70 WebROCollection all_params;
71 WebROCollection headers;
73 InputFilterStream input_filter;
75 HttpCookieCollection cookies;
79 HttpFileCollection files;
81 ServerVariablesCollection server_variables;
82 HttpClientCertificate client_cert;
85 string [] accept_types;
86 string [] user_languages;
90 bool validate_cookies, validate_query_string, validate_form;
91 bool checked_cookies, checked_query_string, checked_form;
93 public HttpRequest (string filename, string url, string queryString)
95 // warning 169: what are we supposed to do with filename?
97 this.filename = filename;
99 uri_builder = new UriBuilder (url);
100 query_string = queryString;
103 void InitUriBuilder ()
105 uri_builder = new UriBuilder ();
106 uri_builder.Scheme = worker_request.GetProtocol ();
107 uri_builder.Host = worker_request.GetServerName ();
108 int port = worker_request.GetLocalPort ();
109 uri_builder.Port = port;
110 uri_builder.Path = worker_request.GetUriPath ();
111 if (query_string != null && query_string != "")
112 uri_builder.Query = query_string;
115 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
117 this.worker_request = worker_request;
118 this.context = context;
119 if (worker_request != null)
120 query_string = worker_request.GetQueryString ();
123 string [] SplitHeader (int header_index)
125 string [] result = null;
126 string header = worker_request.GetKnownRequestHeader (header_index);
127 if (header != null && header != "" && header.Trim () != "") {
128 result = header.Split (',');
129 for (int i = result.Length - 1; i >= 0; i--)
130 result [i] = result [i].Trim ();
135 public string [] AcceptTypes {
137 if (worker_request == null)
140 if (accept_types == null)
141 accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
149 public string AnonymousID {
154 anonymous_id = value;
159 public string ApplicationPath {
161 if (worker_request == null)
163 return worker_request.GetAppPath ();
167 public HttpBrowserCapabilities Browser {
169 if (browser_capabilities == null)
170 browser_capabilities = (HttpBrowserCapabilities)
171 HttpCapabilitiesBase.GetConfigCapabilities (null, this);
173 return browser_capabilities;
177 browser_capabilities = value;
181 public HttpClientCertificate ClientCertificate {
183 if (client_cert == null)
184 client_cert = new HttpClientCertificate (worker_request);
189 static internal string GetParameter (string header, string attr)
191 int ap = header.IndexOf (attr);
196 if (ap >= header.Length)
199 char ending = header [ap];
203 int end = header.IndexOf (ending, ap+1);
205 return (ending == '"') ? null : header.Substring (ap);
207 return header.Substring (ap+1, end-ap-1);
210 public Encoding ContentEncoding {
212 if (encoding == null){
213 if (worker_request == null)
214 throw new HttpException ("No HttpWorkerRequest");
216 string content_type = ContentType;
217 string parameter = GetParameter (content_type, "; charset=");
218 if (parameter == null) {
219 encoding = WebEncoding.RequestEncoding;
222 // Do what the #1 web server does
223 encoding = Encoding.GetEncoding (parameter);
225 encoding = WebEncoding.RequestEncoding;
237 public int ContentLength {
239 if (content_length == -1){
240 if (worker_request == null)
243 string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
247 content_length = Int32.Parse (cl);
252 // content_length will still be < 0, but we know we gotta read from the client
253 if (content_length < 0)
256 return content_length;
260 public string ContentType {
262 if (content_type == null){
263 if (worker_request != null)
264 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
266 if (content_type == null)
267 content_type = String.Empty;
274 content_type = value;
278 public HttpCookieCollection Cookies {
280 if (cookies == null) {
281 if (worker_request == null) {
282 cookies = new HttpCookieCollection ();
284 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
285 cookies = new HttpCookieCollection (cookie_hv);
289 if (validate_cookies && !checked_cookies){
290 ValidateCookieCollection (cookies);
291 checked_cookies = true;
299 public string CurrentExecutionFilePath {
301 if (current_exe_path != null)
302 return current_exe_path;
308 public string FilePath {
310 if (worker_request == null)
311 return "/"; // required for 2.0
313 if (file_path == null)
314 file_path = UrlUtils.Canonic (worker_request.GetFilePath ());
320 internal string BaseVirtualDir {
322 if (base_virtual_dir == null){
323 base_virtual_dir = FilePath;
324 int p = base_virtual_dir.LastIndexOf ('/');
326 base_virtual_dir = base_virtual_dir.Substring (0, p);
328 return base_virtual_dir;
332 public HttpFileCollection Files {
335 files = new HttpFileCollection ();
336 if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
337 form = new WebROCollection ();
346 public Stream Filter {
351 if (input_filter == null)
352 input_filter = new InputFilterStream ();
358 // This checks that get_ was called before.
359 if (input_filter == null)
360 throw new HttpException ("Invalid filter");
366 Stream StreamCopy (Stream stream)
369 if (stream is IntPtrStream)
370 return new IntPtrStream (stream);
373 if (stream is MemoryStream) {
374 MemoryStream other = (MemoryStream) stream;
375 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
378 throw new NotSupportedException ("The stream is " + stream.GetType ());
382 // Loads the data on the form for multipart/form-data
384 void LoadMultiPart ()
386 string boundary = GetParameter (ContentType, "; boundary=");
387 if (boundary == null)
390 Stream input = StreamCopy (InputStream);
391 HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
393 HttpMultipart.Element e;
394 while ((e = multi_part.ReadNextElement ()) != null) {
395 if (e.Filename == null){
396 byte [] copy = new byte [e.Length];
398 input.Position = e.Start;
399 input.Read (copy, 0, (int) e.Length);
401 form.Add (e.Name, ContentEncoding.GetString (copy));
404 // We use a substream, as in 2.x we will support large uploads streamed to disk,
406 HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
407 files.AddFile (e.Name, sub);
413 // Adds the key/value to the form, and sets the argumets to empty
415 void AddRawKeyValue (StringBuilder key, StringBuilder value)
417 form.Add (HttpUtility.UrlDecode (key.ToString (), ContentEncoding),
418 HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
425 // Loads the form data from on a application/x-www-form-urlencoded post
429 Stream input = StreamCopy (InputStream);
430 StreamReader s = new StreamReader (input, ContentEncoding);
432 StringBuilder key = new StringBuilder ();
433 StringBuilder value = new StringBuilder ();
436 while ((c = s.Read ()) != -1){
439 while ((c = s.Read ()) != -1){
441 AddRawKeyValue (key, value);
444 value.Append ((char) c);
447 AddRawKeyValue (key, value);
451 AddRawKeyValue (key, value);
453 key.Append ((char) c);
456 AddRawKeyValue (key, value);
459 bool IsContentType (string ct, bool starts_with)
462 return StrUtils.StartsWith (ContentType, ct, true);
464 return String.Compare (ContentType, ct, true, CultureInfo.InvariantCulture) == 0;
467 public NameValueCollection Form {
470 form = new WebROCollection ();
471 files = new HttpFileCollection ();
473 if (IsContentType ("application/x-www-form-urlencoded", true))
475 else if (IsContentType ("multipart/form-data", true))
481 if (validate_form && !checked_form){
482 ValidateNameValueCollection ("Form", form);
490 public NameValueCollection Headers {
492 if (headers == null){
493 headers = new WebROCollection ();
494 if (worker_request == null) {
499 for (int i = 0; i < HttpWorkerRequest.RequestHeaderMaximum; i++){
500 string hval = worker_request.GetKnownRequestHeader (i);
502 if (hval == null || hval == "")
505 headers.Add (HttpWorkerRequest.GetKnownRequestHeaderName (i), hval);
508 string [][] unknown = worker_request.GetUnknownRequestHeaders ();
509 if (unknown != null && unknown.GetUpperBound (0) != -1){
510 int top = unknown.GetUpperBound (0) + 1;
512 for (int i = 0; i < top; i++){
513 // should check if unknown [i] is not null, but MS does not.
515 headers.Add (unknown [i][0], unknown [i][1]);
524 public string HttpMethod {
526 if (http_method == null){
527 if (worker_request != null)
528 http_method = worker_request.GetHttpVerbName ();
538 const int INPUT_BUFFER_SIZE = 1024;
540 void MakeInputStream ()
542 if (worker_request == null)
543 throw new HttpException ("No HttpWorkerRequest");
545 // consider for perf:
546 // return ((ServletWorkerRequest)worker_request).InputStream();
549 // Use an unmanaged memory block as this might be a large
552 int content_length = ContentLength;
554 if (content_length == 0 && HttpMethod == "POST")
555 throw new HttpException (411, "Length expected");
558 HttpRuntimeSection config = (HttpRuntimeSection) WebConfigurationManager.GetSection ("system.web/httpRuntime");
560 HttpRuntimeConfig config = (HttpRuntimeConfig) HttpContext.GetAppConfig ("system.web/httpRuntime");
563 if (content_length > (config.MaxRequestLength * 1024))
564 throw new HttpException ("File exceeds httpRuntime limit");
566 byte[] content = new byte[content_length];
568 throw new HttpException (String.Format ("Not enough memory to allocate {0} bytes", content_length));
572 buffer = worker_request.GetPreloadedEntityBody ();
574 total = buffer.Length;
575 Array.Copy (buffer, content, total);
580 buffer = new byte [INPUT_BUFFER_SIZE];
581 while (total < content_length){
583 n = worker_request.ReadEntityBody (buffer, Math.Min (content_length-total, INPUT_BUFFER_SIZE));
586 Array.Copy (buffer, 0, content, total, n);
589 if (total < content_length)
590 throw new HttpException (411, "The uploaded file is incomplete");
592 input_stream = new MemoryStream (content, 0, content.Length, false, true);
595 const int INPUT_BUFFER_SIZE = 32*1024;
597 void DoFilter (byte [] buffer)
599 if (input_filter == null || filter == null)
602 if (buffer.Length < 1024)
603 buffer = new byte [1024];
605 // Replace the input with the filtered input
606 input_filter.BaseStream = input_stream;
607 MemoryStream ms = new MemoryStream ();
609 int n = filter.Read (buffer, 0, buffer.Length);
612 ms.Write (buffer, 0, n);
614 // From now on input_stream has the filtered input
615 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
618 void MakeInputStream ()
620 if (input_stream != null)
623 if (worker_request == null) {
624 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
625 DoFilter (new byte [1024]);
630 // Use an unmanaged memory block as this might be a large
633 int content_length = ContentLength;
636 HttpRuntimeSection config = (HttpRuntimeSection) WebConfigurationManager.GetSection ("system.web/httpRuntime");
638 HttpRuntimeConfig config = (HttpRuntimeConfig) HttpContext.GetAppConfig ("system.web/httpRuntime");
640 if ((content_length / 1024) > config.MaxRequestLength)
641 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
645 buffer = worker_request.GetPreloadedEntityBody ();
646 // we check the instance field 'content_length' here, not the local var.
647 if (this.content_length == 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
648 if (buffer == null || content_length == 0) {
649 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
651 input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
653 DoFilter (new byte [1024]);
658 total = buffer.Length;
660 if (content_length > 0) {
661 total = Math.Min (content_length, total);
662 IntPtr content = Marshal.AllocHGlobal (content_length);
663 if (content == (IntPtr) 0)
664 throw new HttpException (String.Format ("Not enough memory to allocate {0} bytes.",
668 Marshal.Copy (buffer, 0, content, total);
670 if (total < content_length) {
671 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
674 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
675 n = worker_request.ReadEntityBody (buffer, min);
678 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
680 } while (total < content_length);
683 input_stream = new IntPtrStream (content, total);
686 MemoryStream ms = new MemoryStream ();
688 ms.Write (buffer, 0, total);
689 buffer = new byte [INPUT_BUFFER_SIZE];
690 long maxlength = config.MaxRequestLength * 1024;
693 n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
697 if (total < 0 || total > maxlength)
698 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
699 ms.Write (buffer, 0, n);
701 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
705 if (total < content_length)
706 throw new HttpException (411, "The request body is incomplete.");
709 internal void ReleaseResources ()
711 if (input_stream != null){
712 input_stream.Close ();
717 public Stream InputStream {
719 if (input_stream == null)
726 public bool IsAuthenticated {
728 if (context.User == null || context.User.Identity == null)
730 return context.User.Identity.IsAuthenticated;
734 public bool IsSecureConnection {
736 if (worker_request == null)
738 return worker_request.IsSecure ();
742 public string this [string key] {
743 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
745 // "The QueryString, Form, Cookies, or ServerVariables collection member
746 // specified in the key parameter."
747 string val = QueryString [key];
751 HttpCookie cookie = Cookies [key];
756 val = ServerVariables [key];
762 public NameValueCollection Params {
763 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
765 if (all_params == null) {
766 all_params = new WebROCollection ();
768 all_params.Add (QueryString);
770 /* special handling for Cookies since
771 * it isn't a NameValueCollection. */
772 foreach (string key in Cookies.AllKeys) {
773 all_params.Add (key, Cookies[key].Value);
776 all_params.Add (Form);
777 all_params.Add (ServerVariables);
778 all_params.Protect ();
787 if (uri_builder == null)
790 return uri_builder.Path;
794 public string PathInfo {
796 if (path_info == null) {
797 if (worker_request == null)
799 path_info = worker_request.GetPathInfo ();
806 public string PhysicalApplicationPath {
808 if (worker_request == null)
809 throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
811 string path = HttpRuntime.AppDomainAppPath;
812 if (SecurityManager.SecurityEnabled) {
813 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
819 public string PhysicalPath {
821 if (worker_request == null)
822 return String.Empty; // don't check security with an empty string!
824 if (physical_path == null)
825 physical_path = MapPath (CurrentExecutionFilePath);
827 if (SecurityManager.SecurityEnabled) {
828 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
830 return physical_path;
834 internal string RootVirtualDir {
836 if (root_virtual_dir == null){
837 string fp = FilePath;
838 int p = fp.LastIndexOf ('/');
841 root_virtual_dir = "/";
843 root_virtual_dir = fp.Substring (0, p);
846 return root_virtual_dir;
850 public NameValueCollection QueryString {
852 if (query_string_nvc == null){
853 query_string_nvc = new WebROCollection ();
855 if (uri_builder == null)
858 string q = query_string;
859 if (q != null && q != ""){
860 string [] components = q.Split ('&');
861 foreach (string kv in components){
862 int pos = kv.IndexOf ('=');
864 query_string_nvc.Add (null, HttpUtility.UrlDecode (kv));
866 string key = HttpUtility.UrlDecode (kv.Substring (0, pos));
867 string val = HttpUtility.UrlDecode (kv.Substring (pos+1));
869 query_string_nvc.Add (key, val);
873 query_string_nvc.Protect ();
876 if (validate_query_string && !checked_query_string) {
877 ValidateNameValueCollection ("QueryString", query_string_nvc);
878 checked_query_string = true;
881 return query_string_nvc;
885 public string RawUrl {
887 if (worker_request != null)
888 return worker_request.GetRawUrl ();
890 if (query_string != null && query_string != "")
891 return uri_builder.Path + "?" + query_string;
893 return uri_builder.Path;
901 public string RequestType {
903 if (request_type == null){
904 if (worker_request != null) {
905 request_type = worker_request.GetHttpVerbName ();
906 http_method = request_type;
908 request_type = "GET";
915 request_type = value;
919 public NameValueCollection ServerVariables {
920 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
922 if (server_variables == null)
923 server_variables = new ServerVariablesCollection (this);
925 return server_variables;
929 public int TotalBytes {
931 Stream ins = InputStream;
932 return (int) ins.Length;
938 if (uri_builder == null)
941 if (cached_url == null) {
942 UriBuilder builder = new UriBuilder (uri_builder.Uri);
943 builder.Path += path_info;
944 cached_url = builder.Uri;
950 public Uri UrlReferrer {
952 if (worker_request == null)
955 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
963 public string UserAgent {
965 if (worker_request == null)
968 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
972 public string UserHostAddress {
974 if (worker_request == null)
977 return worker_request.GetRemoteAddress ();
981 public string UserHostName {
983 if (worker_request == null)
986 return worker_request.GetRemoteName ();
990 public string [] UserLanguages {
992 if (worker_request == null)
995 if (user_languages == null)
996 user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
998 return user_languages;
1002 public byte [] BinaryRead (int count)
1005 throw new ArgumentException ("count is < 0");
1007 Stream s = InputStream;
1008 byte [] ret = new byte [count];
1009 if (s.Read (ret, 0, count) != count)
1010 throw new ArgumentException (
1011 String.Format ("count {0} exceeds length of available input {1}",
1012 count, s.Length - s.Position));
1016 public int [] MapImageCoordinates (string imageFieldName)
1018 string method = HttpMethod;
1019 NameValueCollection coll = null;
1020 if (method == "HEAD" || method == "GET")
1022 else if (method == "POST")
1028 string x = coll [imageFieldName + ".x"];
1029 if (x == null || x == "")
1032 string y = coll [imageFieldName + ".y"];
1033 if (y == null || y == "")
1036 int [] result = new int [2];
1038 result [0] = Int32.Parse (x);
1039 result [1] = Int32.Parse (y);
1047 public string MapPath (string virtualPath)
1049 if (worker_request == null)
1052 return MapPath (virtualPath, BaseVirtualDir, true);
1055 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1057 if (worker_request == null)
1058 throw new HttpException ("No HttpWorkerRequest");
1060 if (virtualPath == null || virtualPath == "")
1063 virtualPath = virtualPath.Trim ();
1065 if (virtualPath.IndexOf (':') != -1)
1066 throw new ArgumentNullException (
1067 String.Format ("MapPath: Invalid path '{0}', only virtual paths are accepted", virtualPath));
1070 if (virtualPath.StartsWith(vmw.common.IAppDomainConfig.WAR_ROOT_SYMBOL))
1073 if (System.IO.Path.DirectorySeparatorChar != '/')
1074 virtualPath = virtualPath.Replace (System.IO.Path.DirectorySeparatorChar, '/');
1076 if (UrlUtils.IsRooted (virtualPath))
1077 virtualPath = UrlUtils.Canonic (virtualPath);
1079 if (baseVirtualDir == null)
1080 baseVirtualDir = RootVirtualDir;
1081 virtualPath = UrlUtils.Combine (baseVirtualDir, virtualPath);
1084 if (!allowCrossAppMapping){
1085 if (!StrUtils.StartsWith (virtualPath, RootVirtualDir, true))
1086 throw new HttpException ("MapPath: Mapping across applications not allowed");
1087 if (RootVirtualDir.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1088 throw new HttpException ("MapPath: Mapping across applications not allowed");
1090 return worker_request.MapPath (virtualPath);
1093 public void SaveAs (string filename, bool includeHeaders)
1095 Stream output = new FileStream (filename, FileMode.Create);
1096 if (includeHeaders) {
1097 StringBuilder sb = new StringBuilder ();
1098 string version = String.Empty;
1100 if (worker_request != null) {
1101 version = worker_request.GetHttpVersion ();
1103 path = uri_builder.Path;
1106 if (query_string != null && query_string != "")
1107 qs = "?" + query_string;
1109 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1110 NameValueCollection coll = Headers;
1111 foreach (string k in coll.AllKeys) {
1114 sb.Append (coll [k]);
1119 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1120 output.Write (bytes, 0, bytes.Length);
1123 // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1124 // to keep InputStream in its state.
1125 Stream input = StreamCopy (InputStream);
1127 long len = input.Length;
1128 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1129 byte [] data = new byte [buf_size];
1131 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1132 output.Write (data, 0, count);
1141 public void ValidateInput ()
1143 validate_cookies = true;
1144 validate_query_string = true;
1145 validate_form = true;
1148 #region internal routines
1149 internal string ClientTarget {
1151 return client_target;
1155 client_target = value;
1166 string address = worker_request.GetRemoteAddress ();
1168 return (address == "127.0.0.1");
1172 internal void SetFilePath (string path)
1177 internal void SetCurrentExePath (string path)
1180 current_exe_path = path;
1182 if (uri_builder == null)
1184 uri_builder.Path = path;
1185 // recreated on demand
1186 root_virtual_dir = null;
1187 base_virtual_dir = null;
1188 physical_path = null;
1191 internal void SetPathInfo (string pi)
1197 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1198 internal void SetHeader (string name, string value)
1200 WebROCollection h = (WebROCollection) Headers;
1206 // Notice: there is nothing raw about this querystring.
1207 internal string QueryStringRaw {
1209 if (uri_builder == null)
1212 return query_string;
1216 if (uri_builder == null)
1219 query_string = value;
1220 query_string_nvc = null;
1221 if (uri_builder != null)
1222 uri_builder.Query = value;
1226 // Internal, dont know what it does, so flagged as public so we can see it.
1227 internal void SetForm (WebROCollection coll)
1232 internal HttpWorkerRequest WorkerRequest {
1234 return worker_request;
1238 internal HttpContext Context {
1244 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1249 foreach (string key in coll.Keys) {
1250 string val = coll [key];
1251 if (val != null && val != "" && CheckString (val))
1252 ThrowValidationException (name, key, val);
1256 static void ValidateCookieCollection (HttpCookieCollection cookies)
1258 if (cookies == null)
1261 int size = cookies.Count;
1263 for (int i = 0 ; i < size ; i++) {
1264 cookie = cookies[i];
1265 string value = cookie.Value;
1267 if (value != null && value != "" && CheckString (value))
1268 ThrowValidationException ("Cookies", cookie.Name, cookie.Value);
1272 static void ThrowValidationException (string name, string key, string value)
1274 string v = "\"" + value + "\"";
1276 v = v.Substring (0, 16) + "...\"";
1278 string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1279 "detected from the client ({1}={2}).", name, key, v);
1281 throw new HttpRequestValidationException (msg);
1284 static bool CheckString (string val)
1286 int len = val.Length;
1288 for (int idx = 0; idx < len - 1; idx ++) {
1290 char c2 = val[idx+1];
1291 if (c1 == '<' || c1 == '\xff1c') {
1293 || (c2 >= 'a' && c2 <= 'z')
1294 || (c2 >= 'A' && c2 <= 'Z'))
1297 else if (c1 == '&') {
1309 #region Helper classes
1312 // Stream-based multipart handling.
1314 // In this incarnation deals with an HttpInputStream as we are now using
1315 // IntPtr-based streams instead of byte []. In the future, we will also
1316 // send uploads above a certain threshold into the disk (to implement
1317 // limit-less HttpInputFiles).
1320 class HttpMultipart {
1322 public class Element {
1323 public string ContentType;
1325 public string Filename;
1329 public override string ToString ()
1331 return string.Format ("ContentType {0}, Name {1}, Filename {2}, Start {3}, Length {4}",
1332 ContentType, Name, Filename, Start, Length);
1338 byte [] boundary_bytes;
1344 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
1347 // In the case of multipart entities, in which one or more different
1348 // sets of data are combined in a single body, a "multipart" media type
1349 // field must appear in the entity's header. The body must then contain
1350 // one or more body parts, each preceded by a boundary delimiter line,
1351 // and the last one followed by a closing boundary delimiter line.
1352 // After its boundary delimiter line, each body part then consists of a
1353 // header area, a blank line, and a body area. Thus a body part is
1354 // similar to an RFC 822 message in syntax, but different in meaning.
1356 public HttpMultipart (Stream data, string b, Encoding encoding)
1360 boundary_bytes = encoding.GetBytes (b);
1361 buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1362 this.encoding = encoding;
1363 sb = new StringBuilder ();
1368 // CRLF or LF are ok as line endings.
1369 bool got_cr = false;
1373 b = data.ReadByte ();
1382 sb.Append ((char) b);
1388 return sb.ToString ();
1392 static string GetContentDispositionAttribute (string l, string name)
1394 int idx = l.IndexOf (name + "=\"");
1397 int begin = idx + name.Length + "=\"".Length;
1398 int end = l.IndexOf ('"', begin);
1403 return l.Substring (begin, end - begin);
1406 bool ReadBoundary ()
1409 string line = ReadLine ();
1412 if (line [0] != '-' || line [1] != '-')
1415 if (!StrUtils.EndsWith (line, boundary, false))
1423 string ReadHeaders ()
1425 string s = ReadLine ();
1432 bool CompareBytes (byte [] orig, byte [] other)
1434 for (int i = orig.Length - 1; i >= 0; i--)
1435 if (orig [i] != other [i])
1441 long MoveToNextBoundary ()
1444 bool got_cr = false;
1447 int c = data.ReadByte ();
1452 if (state == 0 && c == LF) {
1453 retval = data.Position - 1;
1457 c = data.ReadByte ();
1458 } else if (state == 0) {
1460 c = data.ReadByte ();
1461 } else if (state == 1 && c == '-') {
1462 c = data.ReadByte ();
1469 continue; // no ReadByte() here
1472 int nread = data.Read (buffer, 0, buffer.Length);
1473 int bl = buffer.Length;
1477 if (!CompareBytes (boundary_bytes, buffer)) {
1479 data.Position = retval + 2;
1484 c = data.ReadByte ();
1488 if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
1490 } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
1492 data.Position = retval + 2;
1497 c = data.ReadByte ();
1500 data.Position = retval + 2;
1506 state = 0; // no ReadByte() here
1513 public Element ReadNextElement ()
1515 if (at_eof || ReadBoundary ())
1518 Element elem = new Element ();
1520 while ((header = ReadHeaders ()) != null) {
1521 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
1522 elem.Name = GetContentDispositionAttribute (header, "name");
1523 elem.Filename = GetContentDispositionAttribute (header, "filename");
1524 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
1525 elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
1529 long start = data.Position;
1531 long pos = MoveToNextBoundary ();
1535 elem.Length = pos - start;