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;
89 bool validate_cookies, validate_query_string, validate_form;
90 bool checked_cookies, checked_query_string, checked_form;
92 public HttpRequest (string filename, string url, string queryString)
94 // warning 169: what are we supposed to do with filename?
96 this.filename = filename;
98 uri_builder = new UriBuilder (url);
99 query_string = queryString;
102 void InitUriBuilder ()
104 uri_builder = new UriBuilder ();
105 uri_builder.Scheme = worker_request.GetProtocol ();
106 uri_builder.Host = worker_request.GetServerName ();
107 int port = worker_request.GetLocalPort ();
108 uri_builder.Port = port;
109 uri_builder.Path = worker_request.GetUriPath ();
110 if (query_string != null && query_string != "")
111 uri_builder.Query = query_string;
114 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
116 this.worker_request = worker_request;
117 this.context = context;
118 if (worker_request != null)
119 query_string = worker_request.GetQueryString ();
122 string [] SplitHeader (int header_index)
124 string [] result = null;
125 string header = worker_request.GetKnownRequestHeader (header_index);
126 if (header != null && header != "" && header.Trim () != "") {
127 result = header.Split (',');
128 for (int i = result.Length - 1; i >= 0; i--)
129 result [i] = result [i].Trim ();
134 public string [] AcceptTypes {
136 if (worker_request == null)
139 if (accept_types == null)
140 accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
146 public string ApplicationPath {
148 if (worker_request == null)
150 return worker_request.GetAppPath ();
154 public HttpBrowserCapabilities Browser {
156 if (browser_capabilities == null)
157 browser_capabilities = (HttpBrowserCapabilities)
158 HttpCapabilitiesBase.GetConfigCapabilities (null, this);
160 return browser_capabilities;
164 browser_capabilities = value;
168 public HttpClientCertificate ClientCertificate {
170 if (client_cert == null)
171 client_cert = new HttpClientCertificate (worker_request);
176 static internal string GetParameter (string header, string attr)
178 int ap = header.IndexOf (attr);
183 if (ap >= header.Length)
186 char ending = header [ap];
190 int end = header.IndexOf (ending, ap+1);
192 return (ending == '"') ? null : header.Substring (ap);
194 return header.Substring (ap+1, end-ap-1);
197 public Encoding ContentEncoding {
199 if (encoding == null){
200 if (worker_request == null)
201 throw new HttpException ("No HttpWorkerRequest");
203 string content_type = ContentType;
204 string parameter = GetParameter (content_type, "; charset=");
205 if (parameter == null) {
206 encoding = WebEncoding.RequestEncoding;
209 // Do what the #1 web server does
210 encoding = Encoding.GetEncoding (parameter);
212 encoding = WebEncoding.RequestEncoding;
224 public int ContentLength {
226 if (content_length == -1){
227 if (worker_request == null)
230 string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
234 content_length = Int32.Parse (cl);
239 // content_length will still be < 0, but we know we gotta read from the client
240 if (content_length < 0)
243 return content_length;
247 public string ContentType {
249 if (content_type == null){
250 if (worker_request != null)
251 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
253 if (content_type == null)
254 content_type = String.Empty;
261 content_type = value;
265 public HttpCookieCollection Cookies {
267 if (cookies == null) {
268 if (worker_request == null) {
269 cookies = new HttpCookieCollection ();
271 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
272 cookies = new HttpCookieCollection (cookie_hv);
276 if (validate_cookies && !checked_cookies){
277 ValidateCookieCollection (cookies);
278 checked_cookies = true;
286 public string CurrentExecutionFilePath {
288 if (current_exe_path != null)
289 return current_exe_path;
295 public string FilePath {
297 if (worker_request == null)
298 return "/"; // required for 2.0
300 if (file_path == null)
301 file_path = UrlUtils.Canonic (worker_request.GetFilePath ());
307 internal string BaseVirtualDir {
309 if (base_virtual_dir == null){
310 base_virtual_dir = FilePath;
311 int p = base_virtual_dir.LastIndexOf ('/');
313 base_virtual_dir = base_virtual_dir.Substring (0, p);
315 return base_virtual_dir;
319 public HttpFileCollection Files {
322 files = new HttpFileCollection ();
323 if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
324 form = new WebROCollection ();
333 public Stream Filter {
338 if (input_filter == null)
339 input_filter = new InputFilterStream ();
345 // This checks that get_ was called before.
346 if (input_filter == null)
347 throw new HttpException ("Invalid filter");
353 Stream StreamCopy (Stream stream)
356 if (stream is IntPtrStream)
357 return new IntPtrStream (stream);
360 if (stream is MemoryStream) {
361 MemoryStream other = (MemoryStream) stream;
362 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
365 throw new NotSupportedException ("The stream is " + stream.GetType ());
369 // Loads the data on the form for multipart/form-data
371 void LoadMultiPart ()
373 string boundary = GetParameter (ContentType, "; boundary=");
374 if (boundary == null)
377 Stream input = StreamCopy (InputStream);
378 HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
380 HttpMultipart.Element e;
381 while ((e = multi_part.ReadNextElement ()) != null) {
382 if (e.Filename == null){
383 byte [] copy = new byte [e.Length];
385 input.Position = e.Start;
386 input.Read (copy, 0, (int) e.Length);
388 form.Add (e.Name, ContentEncoding.GetString (copy));
391 // We use a substream, as in 2.x we will support large uploads streamed to disk,
393 HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
394 files.AddFile (e.Name, sub);
400 // Adds the key/value to the form, and sets the argumets to empty
402 void AddRawKeyValue (StringBuilder key, StringBuilder value)
404 form.Add (HttpUtility.UrlDecode (key.ToString (), ContentEncoding),
405 HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
412 // Loads the form data from on a application/x-www-form-urlencoded post
416 Stream input = StreamCopy (InputStream);
417 StreamReader s = new StreamReader (input, ContentEncoding);
419 StringBuilder key = new StringBuilder ();
420 StringBuilder value = new StringBuilder ();
423 while ((c = s.Read ()) != -1){
426 while ((c = s.Read ()) != -1){
428 AddRawKeyValue (key, value);
431 value.Append ((char) c);
434 AddRawKeyValue (key, value);
438 AddRawKeyValue (key, value);
440 key.Append ((char) c);
443 AddRawKeyValue (key, value);
446 bool IsContentType (string ct, bool starts_with)
449 return StrUtils.StartsWith (ContentType, ct, true);
451 return String.Compare (ContentType, ct, true, CultureInfo.InvariantCulture) == 0;
454 public NameValueCollection Form {
457 form = new WebROCollection ();
458 files = new HttpFileCollection ();
460 if (IsContentType ("application/x-www-form-urlencoded", false))
462 else if (IsContentType ("multipart/form-data", true))
468 if (validate_form && !checked_form){
469 ValidateNameValueCollection ("Form", form);
477 public NameValueCollection Headers {
479 if (headers == null){
480 headers = new WebROCollection ();
481 if (worker_request == null) {
486 for (int i = 0; i < HttpWorkerRequest.RequestHeaderMaximum; i++){
487 string hval = worker_request.GetKnownRequestHeader (i);
489 if (hval == null || hval == "")
492 headers.Add (HttpWorkerRequest.GetKnownRequestHeaderName (i), hval);
495 string [][] unknown = worker_request.GetUnknownRequestHeaders ();
496 if (unknown != null && unknown.GetUpperBound (0) != -1){
497 int top = unknown.GetUpperBound (0) + 1;
499 for (int i = 0; i < top; i++){
500 // should check if unknown [i] is not null, but MS does not.
502 headers.Add (unknown [i][0], unknown [i][1]);
511 public string HttpMethod {
513 if (http_method == null){
514 if (worker_request != null)
515 http_method = worker_request.GetHttpVerbName ();
525 const int INPUT_BUFFER_SIZE = 1024;
527 void MakeInputStream ()
529 if (worker_request == null)
530 throw new HttpException ("No HttpWorkerRequest");
532 // consider for perf:
533 // return ((ServletWorkerRequest)worker_request).InputStream();
536 // Use an unmanaged memory block as this might be a large
539 int content_length = ContentLength;
541 if (content_length == 0 && HttpMethod == "POST")
542 throw new HttpException (411, "Length expected");
544 HttpRuntimeConfig config = (HttpRuntimeConfig) HttpContext.GetAppConfig ("system.web/httpRuntime");
546 if (content_length > (config.MaxRequestLength * 1024))
547 throw new HttpException ("File exceeds httpRuntime limit");
549 byte[] content = new byte[content_length];
551 throw new HttpException (String.Format ("Not enough memory to allocate {0} bytes", content_length));
555 buffer = worker_request.GetPreloadedEntityBody ();
557 total = buffer.Length;
558 Array.Copy (buffer, content, total);
563 buffer = new byte [INPUT_BUFFER_SIZE];
564 while (total < content_length){
566 n = worker_request.ReadEntityBody (buffer, Math.Min (content_length-total, INPUT_BUFFER_SIZE));
569 Array.Copy (buffer, 0, content, total, n);
572 if (total < content_length)
573 throw new HttpException (411, "The uploaded file is incomplete");
575 input_stream = new MemoryStream (content, 0, content.Length, false, true);
578 const int INPUT_BUFFER_SIZE = 32*1024;
580 void DoFilter (byte [] buffer)
582 if (input_filter == null || filter == null)
585 // Replace the input with the filtered input
586 input_filter.BaseStream = input_stream;
587 MemoryStream ms = new MemoryStream ();
589 int n = filter.Read (buffer, 0, buffer.Length);
592 ms.Write (buffer, 0, n);
594 // From now on input_stream has the filtered input
595 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
598 void MakeInputStream ()
600 if (input_stream != null)
603 if (worker_request == null) {
604 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
605 DoFilter (new byte [1024]);
610 // Use an unmanaged memory block as this might be a large
613 int content_length = ContentLength;
615 HttpRuntimeConfig config = (HttpRuntimeConfig) HttpContext.GetAppConfig ("system.web/httpRuntime");
616 if ((content_length / 1024) > config.MaxRequestLength)
617 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
621 buffer = worker_request.GetPreloadedEntityBody ();
622 // we check the instance field 'content_length' here, not the local var.
623 if (this.content_length == 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
624 if (buffer == null || content_length == 0) {
625 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
627 input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
629 DoFilter (new byte [1024]);
634 total = buffer.Length;
636 if (content_length > 0) {
637 IntPtr content = Marshal.AllocHGlobal (content_length);
638 if (content == (IntPtr) 0)
639 throw new HttpException (String.Format ("Not enough memory to allocate {0} bytes.",
643 Marshal.Copy (buffer, 0, content, total);
645 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
646 while (total < content_length){
648 n = worker_request.ReadEntityBody (buffer, Math.Min (content_length-total, INPUT_BUFFER_SIZE));
651 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
654 input_stream = new IntPtrStream (content, total);
657 MemoryStream ms = new MemoryStream ();
659 ms.Write (buffer, 0, total);
660 buffer = new byte [INPUT_BUFFER_SIZE];
661 long maxlength = config.MaxRequestLength * 1024;
664 n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
668 if (total < 0 || total > maxlength)
669 throw new HttpException (400, "Upload size exceeds httpRuntime limit.");
670 ms.Write (buffer, 0, n);
672 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
676 if (total < content_length)
677 throw new HttpException (411, "The request body is incomplete.");
680 internal void ReleaseResources ()
682 if (input_stream != null){
683 input_stream.Close ();
688 public Stream InputStream {
690 if (input_stream == null)
697 public bool IsAuthenticated {
699 if (context.User == null || context.User.Identity == null)
701 return context.User.Identity.IsAuthenticated;
705 public bool IsSecureConnection {
707 if (worker_request == null)
709 return worker_request.IsSecure ();
713 public string this [string key] {
714 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
716 // "The QueryString, Form, Cookies, or ServerVariables collection member
717 // specified in the key parameter."
718 string val = QueryString [key];
722 HttpCookie cookie = Cookies [key];
727 val = ServerVariables [key];
733 public NameValueCollection Params {
734 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
736 if (all_params == null) {
737 all_params = new WebROCollection ();
739 all_params.Add (QueryString);
741 /* special handling for Cookies since
742 * it isn't a NameValueCollection. */
743 foreach (string key in Cookies.AllKeys) {
744 all_params.Add (key, Cookies[key].Value);
747 all_params.Add (Form);
748 all_params.Add (ServerVariables);
749 all_params.Protect ();
758 if (uri_builder == null)
761 return uri_builder.Path;
765 public string PathInfo {
767 if (path_info == null) {
768 if (worker_request == null)
770 path_info = worker_request.GetPathInfo ();
777 public string PhysicalApplicationPath {
779 if (worker_request == null)
780 throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
782 string path = HttpRuntime.AppDomainAppPath;
783 if (SecurityManager.SecurityEnabled) {
784 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
790 public string PhysicalPath {
792 if (worker_request == null)
793 return String.Empty; // don't check security with an empty string!
795 if (physical_path == null)
796 physical_path = MapPath (CurrentExecutionFilePath);
798 if (SecurityManager.SecurityEnabled) {
799 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
801 return physical_path;
805 internal string RootVirtualDir {
807 if (root_virtual_dir == null){
808 string fp = FilePath;
809 int p = fp.LastIndexOf ('/');
812 root_virtual_dir = "/";
814 root_virtual_dir = fp.Substring (0, p);
817 return root_virtual_dir;
821 public NameValueCollection QueryString {
823 if (query_string_nvc == null){
824 query_string_nvc = new WebROCollection ();
826 if (uri_builder == null)
829 string q = query_string;
830 if (q != null && q != ""){
831 string [] components = q.Split ('&');
832 foreach (string kv in components){
833 int pos = kv.IndexOf ('=');
835 query_string_nvc.Add (null, HttpUtility.UrlDecode (kv));
837 string key = HttpUtility.UrlDecode (kv.Substring (0, pos));
838 string val = HttpUtility.UrlDecode (kv.Substring (pos+1));
840 query_string_nvc.Add (key, val);
844 query_string_nvc.Protect ();
847 if (validate_query_string && !checked_query_string) {
848 ValidateNameValueCollection ("QueryString", query_string_nvc);
849 checked_query_string = true;
852 return query_string_nvc;
856 public string RawUrl {
858 if (worker_request != null)
859 return worker_request.GetRawUrl ();
861 if (query_string != null && query_string != "")
862 return uri_builder.Path + "?" + query_string;
864 return uri_builder.Path;
872 public string RequestType {
874 if (request_type == null){
875 if (worker_request != null) {
876 request_type = worker_request.GetHttpVerbName ();
877 http_method = request_type;
879 request_type = "GET";
886 request_type = value;
890 public NameValueCollection ServerVariables {
891 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
893 if (server_variables == null)
894 server_variables = new ServerVariablesCollection (this);
896 return server_variables;
900 public int TotalBytes {
902 Stream ins = InputStream;
903 return (int) ins.Length;
909 if (uri_builder == null)
912 return uri_builder.Uri;
916 public Uri UrlReferrer {
918 if (worker_request == null)
921 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
929 public string UserAgent {
931 if (worker_request == null)
934 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
938 public string UserHostAddress {
940 if (worker_request == null)
943 return worker_request.GetRemoteAddress ();
947 public string UserHostName {
949 if (worker_request == null)
952 return worker_request.GetRemoteName ();
956 public string [] UserLanguages {
958 if (worker_request == null)
961 if (user_languages == null)
962 user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
964 return user_languages;
968 public byte [] BinaryRead (int count)
971 throw new ArgumentException ("count is < 0");
973 Stream s = InputStream;
974 byte [] ret = new byte [count];
975 if (s.Read (ret, 0, count) != count)
976 throw new ArgumentException (
977 String.Format ("count {0} exceeds length of available input {1}",
978 count, s.Length - s.Position));
982 public int [] MapImageCoordinates (string imageFieldName)
984 string method = HttpMethod;
985 NameValueCollection coll = null;
986 if (method == "HEAD" || method == "GET")
988 else if (method == "POST")
994 string x = coll [imageFieldName + ".x"];
995 if (x == null || x == "")
998 string y = coll [imageFieldName + ".y"];
999 if (y == null || y == "")
1002 int [] result = new int [2];
1004 result [0] = Int32.Parse (x);
1005 result [1] = Int32.Parse (y);
1013 public string MapPath (string virtualPath)
1015 if (worker_request == null)
1018 return MapPath (virtualPath, BaseVirtualDir, true);
1021 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1023 if (worker_request == null)
1024 throw new HttpException ("No HttpWorkerRequest");
1026 if (virtualPath == null || virtualPath == "")
1029 virtualPath = virtualPath.Trim ();
1031 if (virtualPath.IndexOf (':') != -1)
1032 throw new ArgumentNullException (
1033 String.Format ("MapPath: Invalid path '{0}', only virtual paths are accepted", virtualPath));
1035 if (System.IO.Path.DirectorySeparatorChar != '/')
1036 virtualPath.Replace (System.IO.Path.DirectorySeparatorChar, '/');
1038 if (UrlUtils.IsRooted (virtualPath))
1039 virtualPath = UrlUtils.Canonic (virtualPath);
1041 if (baseVirtualDir == null)
1042 baseVirtualDir = RootVirtualDir;
1043 virtualPath = UrlUtils.Combine (baseVirtualDir, virtualPath);
1046 if (!allowCrossAppMapping){
1047 if (!StrUtils.StartsWith (virtualPath, RootVirtualDir, true))
1048 throw new HttpException ("MapPath: Mapping across applications not allowed");
1049 if (RootVirtualDir.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1050 throw new HttpException ("MapPath: Mapping across applications not allowed");
1052 return worker_request.MapPath (virtualPath);
1055 public void SaveAs (string filename, bool includeHeaders)
1057 Stream output = new FileStream (filename, FileMode.Create);
1058 if (includeHeaders) {
1059 StringBuilder sb = new StringBuilder ();
1060 string version = String.Empty;
1062 if (worker_request != null) {
1063 version = worker_request.GetHttpVersion ();
1065 path = uri_builder.Path;
1068 if (query_string != null && query_string != "")
1069 qs = "?" + query_string;
1071 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1072 NameValueCollection coll = Headers;
1073 foreach (string k in coll.AllKeys) {
1076 sb.Append (coll [k]);
1081 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1082 output.Write (bytes, 0, bytes.Length);
1085 // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1086 // to keep InputStream in its state.
1087 Stream input = StreamCopy (InputStream);
1089 long len = input.Length;
1090 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1091 byte [] data = new byte [buf_size];
1093 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1094 output.Write (data, 0, count);
1103 public void ValidateInput ()
1105 validate_cookies = true;
1106 validate_query_string = true;
1107 validate_form = true;
1110 #region internal routines
1111 internal string ClientTarget {
1113 return client_target;
1117 client_target = value;
1128 string address = worker_request.GetRemoteAddress ();
1130 return (address == "127.0.0.1");
1134 internal void SetFilePath (string path)
1139 internal void SetCurrentExePath (string path)
1141 current_exe_path = path;
1142 //if (uri_builder == null)
1143 // InitUriBuilder ();
1145 //uri_builder.Path = path;
1146 // recreated on demand
1147 root_virtual_dir = null;
1148 base_virtual_dir = null;
1149 physical_path = null;
1152 internal void SetPathInfo (string pi)
1157 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1158 internal void SetHeader (string name, string value)
1160 WebROCollection h = (WebROCollection) Headers;
1166 // Notice: there is nothing raw about this querystring.
1167 internal string QueryStringRaw {
1169 if (uri_builder == null)
1172 return query_string;
1176 if (uri_builder == null)
1179 query_string = value;
1180 query_string_nvc = null;
1181 if (uri_builder != null)
1182 uri_builder.Query = value;
1186 // Internal, dont know what it does, so flagged as public so we can see it.
1187 internal void SetForm (WebROCollection coll)
1192 internal HttpWorkerRequest WorkerRequest {
1194 return worker_request;
1198 internal HttpContext Context {
1204 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1209 foreach (string key in coll.Keys) {
1210 string val = coll [key];
1211 if (val != null && val != "" && CheckString (val))
1212 ThrowValidationException (name, key, val);
1216 static void ValidateCookieCollection (HttpCookieCollection cookies)
1218 if (cookies == null)
1221 int size = cookies.Count;
1223 for (int i = 0 ; i < size ; i++) {
1224 cookie = cookies[i];
1225 string value = cookie.Value;
1227 if (value != null && value != "" && CheckString (value))
1228 ThrowValidationException ("Cookies", cookie.Name, cookie.Value);
1232 static void ThrowValidationException (string name, string key, string value)
1234 string v = "\"" + value + "\"";
1236 v = v.Substring (0, 16) + "...\"";
1238 string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1239 "detected from the client ({1}={2}).", name, key, v);
1241 throw new HttpRequestValidationException (msg);
1244 static bool CheckString (string val)
1246 foreach (char c in val) {
1247 if (c == '<' || c == '>' || c == '\xff1c' || c == '\xff1e')
1257 #region Helper classes
1260 // Stream-based multipart handling.
1262 // In this incarnation deals with an HttpInputStream as we are now using
1263 // IntPtr-based streams instead of byte []. In the future, we will also
1264 // send uploads above a certain threshold into the disk (to implement
1265 // limit-less HttpInputFiles).
1268 class HttpMultipart {
1270 public class Element {
1271 public string ContentType;
1273 public string Filename;
1277 public override string ToString ()
1279 return string.Format ("ContentType {0}, Name {1}, Filename {2}, Start {3}, Length {4}",
1280 ContentType, Name, Filename, Start, Length);
1286 byte [] boundary_bytes;
1292 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
1295 // In the case of multipart entities, in which one or more different
1296 // sets of data are combined in a single body, a "multipart" media type
1297 // field must appear in the entity's header. The body must then contain
1298 // one or more body parts, each preceded by a boundary delimiter line,
1299 // and the last one followed by a closing boundary delimiter line.
1300 // After its boundary delimiter line, each body part then consists of a
1301 // header area, a blank line, and a body area. Thus a body part is
1302 // similar to an RFC 822 message in syntax, but different in meaning.
1304 public HttpMultipart (Stream data, string b, Encoding encoding)
1308 boundary_bytes = encoding.GetBytes (b);
1309 buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1310 this.encoding = encoding;
1311 sb = new StringBuilder ();
1316 // CRLF or LF are ok as line endings.
1317 bool got_cr = false;
1321 b = data.ReadByte ();
1330 sb.Append ((char) b);
1336 return sb.ToString ();
1340 static string GetContentDispositionAttribute (string l, string name)
1342 int idx = l.IndexOf (name + "=\"");
1345 int begin = idx + name.Length + "=\"".Length;
1346 int end = l.IndexOf ('"', begin);
1351 return l.Substring (begin, end - begin);
1354 bool ReadBoundary ()
1357 string line = ReadLine ();
1360 int ll = line.Length;
1361 int bl = boundary.Length;
1362 if (line [0] != '-' || line [1] != '-')
1365 if (!StrUtils.EndsWith (line, boundary, false))
1373 bool IsBoundary (string line)
1375 if (line.Length < 2)
1378 int ll = line.Length;
1379 int bl = boundary.Length;
1380 if (line [0] != '-' || line [1] != '-')
1383 if (line.IndexOf (boundary) != 2)
1386 if (ll == bl + 4 && line [ll -1] == '-' && line [ll - 2] == '-')
1389 return (at_eof || ll == bl + 2);
1392 string ReadHeaders ()
1394 string s = ReadLine ();
1401 bool CompareBytes (byte [] orig, byte [] other)
1403 for (int i = orig.Length - 1; i >= 0; i--)
1404 if (orig [i] != other [i])
1410 long MoveToNextBoundary ()
1413 bool got_cr = false;
1416 int c = data.ReadByte ();
1421 if (state == 0 && c == LF) {
1422 retval = data.Position - 1;
1426 c = data.ReadByte ();
1427 } else if (state == 0) {
1429 c = data.ReadByte ();
1430 } else if (state == 1 && c == '-') {
1431 c = data.ReadByte ();
1438 continue; // no ReadByte() here
1441 int nread = data.Read (buffer, 0, buffer.Length);
1442 int bl = buffer.Length;
1446 if (!CompareBytes (boundary_bytes, buffer)) {
1448 data.Position = retval + 2;
1453 c = data.ReadByte ();
1457 if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
1459 } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
1461 data.Position = retval + 2;
1466 c = data.ReadByte ();
1469 data.Position = retval + 2;
1475 state = 0; // no ReadByte() here
1482 public Element ReadNextElement ()
1484 if (at_eof || ReadBoundary ())
1487 Element elem = new Element ();
1489 while ((header = ReadHeaders ()) != null) {
1490 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
1491 elem.Name = GetContentDispositionAttribute (header, "name");
1492 elem.Filename = GetContentDispositionAttribute (header, "filename");
1493 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
1494 elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
1498 long start = data.Position;
1500 long pos = MoveToNextBoundary ();
1504 elem.Length = pos - start;