2 // System.Web.HttpRequest.cs
6 // Miguel de Icaza (miguel@novell.com)
7 // Gonzalo Paniagua Javier (gonzalo@novell.com)
8 // Marek Habersack <mhabersack@novell.com>
12 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
13 // Copyright (C) 2011-2012 Xamarin, Inc (http://xamarin.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Collections.Specialized;
38 using System.Runtime.InteropServices;
39 using System.Security;
40 using System.Security.Permissions;
41 using System.Security.Principal;
42 using System.Web.Configuration;
43 using System.Web.Management;
45 using System.Web.Util;
46 using System.Globalization;
48 using System.Security.Authentication.ExtendedProtection;
49 using System.Web.Routing;
53 // CAS - no InheritanceDemand here as the class is sealed
54 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
55 public sealed partial class HttpRequest
57 HttpWorkerRequest worker_request;
59 WebROCollection query_string_nvc;
63 string orig_url = null;
64 UriBuilder url_components;
69 // On-demand computed values
71 HttpBrowserCapabilities browser_capabilities;
72 string file_path, base_virtual_dir, root_virtual_dir, client_file_path;
74 int content_length = -1;
76 string current_exe_path;
78 string unescaped_path;
81 string path_info_unvalidated;
83 string raw_url_unvalidated;
84 WebROCollection all_params;
85 NameValueCollection headers;
86 WebROCollection headers_unvalidated;
88 InputFilterStream input_filter;
90 HttpCookieCollection cookies;
91 HttpCookieCollection cookies_unvalidated;
95 HttpFileCollection files;
97 ServerVariablesCollection server_variables;
98 HttpClientCertificate client_cert;
101 string [] accept_types;
102 string [] user_languages;
104 TempFileStream request_file;
106 readonly static System.Net.IPAddress [] host_addresses;
109 bool validate_cookies, validate_query_string, validate_form;
110 bool checked_cookies, checked_query_string, checked_form;
111 static readonly UrlMappingCollection urlMappings;
112 readonly static char [] queryTrimChars = {'?'};
113 bool lazyFormValidation;
114 bool lazyQueryStringValidation;
115 bool inputValidationEnabled;
116 RequestContext requestContext;
117 BufferlessInputStream bufferlessInputStream;
119 static bool validateRequestNewMode;
120 internal static bool ValidateRequestNewMode {
121 get { return validateRequestNewMode; }
124 internal bool InputValidationEnabled {
125 get { return inputValidationEnabled; }
128 private static char[] RequestPathInvalidCharacters {
132 private static char[] CharsFromList (string list)
134 // List format is very strict and enforced by the Configuration
135 // there must be a single char separated by commas with no trailing comma
136 // whitespace is allowed though and should be trimmed.
138 string [] pieces = list.Split (',');
140 char [] chars = new char [pieces.Length];
141 for (int i = 0; i < chars.Length; i++) {
142 string trimmed = pieces [i].Trim ();
143 if (trimmed.Length != 1) {
144 // This should have been caught by System.Web.Configuration
145 // and throw a configuration error. This is just here for sanity
146 throw new System.Configuration.ConfigurationErrorsException ();
149 chars [i] = trimmed [0];
155 static HttpRequest ()
158 UrlMappingsSection ums = WebConfigurationManager.GetWebApplicationSection ("system.web/urlMappings") as UrlMappingsSection;
159 if (ums != null && ums.IsEnabled) {
160 urlMappings = ums.UrlMappings;
161 if (urlMappings.Count == 0)
165 Version validationMode = HttpRuntime.Section.RequestValidationMode;
167 if (validationMode >= new Version (4, 0)) {
168 validateRequestNewMode = true;
169 string invalidChars = HttpRuntime.Section.RequestPathInvalidCharacters;
170 if (!String.IsNullOrEmpty (invalidChars))
171 RequestPathInvalidCharacters = CharsFromList (invalidChars);
174 // unlikely to happen
177 host_addresses = GetLocalHostAddresses ();
180 public HttpRequest (string filename, string url, string queryString)
182 // warning 169: what are we supposed to do with filename?
184 //this.filename = filename;
187 url_components = new UriBuilder (url);
188 url_components.Query = queryString;
190 query_string_nvc = new WebROCollection ();
191 if (queryString != null)
192 HttpUtility.ParseQueryString (queryString, Encoding.Default, query_string_nvc);
193 query_string_nvc.Protect ();
196 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
198 this.worker_request = worker_request;
199 this.context = context;
202 internal UriBuilder UrlComponents {
204 if (url_components == null) {
206 byte[] queryStringRaw = worker_request.GetQueryStringRawBytes();
207 if(queryStringRaw != null)
208 query = ContentEncoding.GetString(queryStringRaw);
210 query = worker_request.GetQueryString();
212 BuildUrlComponents (ApplyUrlMapping (worker_request.GetUriPath ()), query);
214 return url_components;
218 void BuildUrlComponents (string path, string query)
220 if (url_components != null)
222 url_components = new UriBuilder ();
223 url_components.Scheme = worker_request.GetProtocol ();
224 url_components.Host = worker_request.GetServerName ();
225 url_components.Port = worker_request.GetLocalPort ();
226 url_components.Path = path;
227 if (query != null && query.Length > 0)
228 url_components.Query = query.TrimStart (queryTrimChars);
231 internal string ApplyUrlMapping (string url)
233 if (urlMappings == null)
236 string relUrl = VirtualPathUtility.ToAppRelative (url);
237 UrlMapping um = null;
239 foreach (UrlMapping u in urlMappings) {
242 if (String.Compare (relUrl, u.Url, StringComparison.Ordinal) == 0) {
251 string rawUrl = VirtualPathUtility.ToAbsolute (um.MappedUrl.Trim ());
252 Uri newUrl = new Uri ("http://host.com" + rawUrl);
254 if (url_components != null) {
255 url_components.Path = newUrl.AbsolutePath;
256 url_components.Query = newUrl.Query.TrimStart (queryTrimChars);
257 query_string_nvc = new WebROCollection ();
258 HttpUtility.ParseQueryString (newUrl.Query, Encoding.Default, query_string_nvc);
259 query_string_nvc.Protect ();
261 BuildUrlComponents (newUrl.AbsolutePath, newUrl.Query);
263 return url_components.Path;
266 string [] SplitHeader (int header_index)
268 string [] result = null;
269 string header = worker_request.GetKnownRequestHeader (header_index);
270 if (header != null && header != "" && header.Trim () != "") {
271 result = header.Split (',');
272 for (int i = result.Length - 1; i >= 0; i--)
273 result [i] = result [i].Trim ();
278 public string [] AcceptTypes {
280 if (worker_request == null)
283 if (accept_types == null)
284 accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
290 public WindowsIdentity LogonUserIdentity {
291 get { throw new NotImplementedException (); }
295 public string AnonymousID {
300 anonymous_id = value;
304 public string ApplicationPath {
306 if (worker_request == null)
308 return worker_request.GetAppPath ();
312 public HttpBrowserCapabilities Browser {
314 if (browser_capabilities == null)
315 browser_capabilities = HttpCapabilitiesBase.BrowserCapabilitiesProvider.GetBrowserCapabilities (this);
317 return browser_capabilities;
321 browser_capabilities = value;
325 internal bool BrowserMightHaveSpecialWriter {
327 return (browser_capabilities != null
328 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
332 internal bool BrowserMightHaveAdapters {
334 return (browser_capabilities != null
335 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
339 public HttpClientCertificate ClientCertificate {
341 if (client_cert == null)
342 client_cert = new HttpClientCertificate (worker_request);
347 static internal string GetParameter (string header, string attr)
349 int ap = header.IndexOf (attr);
354 if (ap >= header.Length)
357 char ending = header [ap];
361 int end = header.IndexOf (ending, ap+1);
363 return (ending == '"') ? null : header.Substring (ap);
365 return header.Substring (ap+1, end-ap-1);
368 public Encoding ContentEncoding {
370 if (encoding == null){
371 if (worker_request == null)
372 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
374 string content_type = ContentType;
375 string parameter = GetParameter (content_type, "; charset=");
376 if (parameter == null) {
377 encoding = WebEncoding.RequestEncoding;
380 // Do what the #1 web server does
381 encoding = Encoding.GetEncoding (parameter);
383 encoding = WebEncoding.RequestEncoding;
395 public int ContentLength {
397 if (content_length == -1){
398 if (worker_request == null)
401 string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
405 content_length = Int32.Parse (cl);
410 // content_length will still be < 0, but we know we gotta read from the client
411 if (content_length < 0)
414 return content_length;
418 public string ContentType {
420 if (content_type == null){
421 if (worker_request != null)
422 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
424 if (content_type == null)
425 content_type = String.Empty;
432 content_type = value;
436 internal HttpCookieCollection CookiesNoValidation {
438 if (cookies_unvalidated == null) {
439 if (worker_request == null) {
440 cookies_unvalidated = new HttpCookieCollection ();
442 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
443 cookies_unvalidated = new HttpCookieCollection (cookie_hv);
447 return cookies_unvalidated;
451 public HttpCookieCollection Cookies {
453 if (cookies == null) {
454 cookies = CookiesNoValidation;
457 bool needValidation = validate_cookies;
458 needValidation |= validateRequestNewMode;
459 if (needValidation && !checked_cookies) {
460 // Setting this before calling the validator prevents
461 // possible endless recursion
462 checked_cookies = true;
463 ValidateCookieCollection (cookies);
471 public string CurrentExecutionFilePath {
473 if (current_exe_path != null)
474 return current_exe_path;
479 public string CurrentExecutionFilePathExtension {
480 get { return global::System.IO.Path.GetExtension (CurrentExecutionFilePath); }
482 public string AppRelativeCurrentExecutionFilePath {
484 return VirtualPathUtility.ToAppRelative (CurrentExecutionFilePath);
488 public string FilePath {
490 if (worker_request == null)
491 return "/"; // required for 2.0
493 if (file_path == null)
494 file_path = UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
500 internal string ClientFilePath {
502 if (client_file_path == null) {
503 if (worker_request == null)
506 return UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
509 return client_file_path;
513 if (value == null || value.Length == 0)
514 client_file_path = null;
516 client_file_path = value;
520 internal string BaseVirtualDir {
522 if (base_virtual_dir == null){
523 base_virtual_dir = FilePath;
524 if (UrlUtils.HasSessionId (base_virtual_dir))
525 base_virtual_dir = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (base_virtual_dir), base_virtual_dir);
527 int p = base_virtual_dir.LastIndexOf ('/');
531 base_virtual_dir = base_virtual_dir.Substring (0, p);
533 base_virtual_dir = "/";
535 return base_virtual_dir;
539 public HttpFileCollection Files {
542 files = new HttpFileCollection ();
543 if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
544 form = new WebROCollection ();
553 public Stream Filter {
558 if (input_filter == null)
559 input_filter = new InputFilterStream ();
565 // This checks that get_ was called before.
566 if (input_filter == null)
567 throw new HttpException ("Invalid filter");
573 // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
574 static Stream GetSubStream (Stream stream)
576 if (stream is IntPtrStream)
577 return new IntPtrStream (stream);
579 if (stream is MemoryStream) {
580 MemoryStream other = (MemoryStream) stream;
581 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
584 if (stream is TempFileStream) {
585 ((TempFileStream) stream).SavePosition ();
589 throw new NotSupportedException ("The stream is " + stream.GetType ());
592 static void EndSubStream (Stream stream)
594 if (stream is TempFileStream) {
595 ((TempFileStream) stream).RestorePosition ();
600 // Loads the data on the form for multipart/form-data
602 void LoadMultiPart ()
604 string boundary = GetParameter (ContentType, "; boundary=");
605 if (boundary == null)
608 Stream input = GetSubStream (InputStream);
609 HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
611 HttpMultipart.Element e;
612 while ((e = multi_part.ReadNextElement ()) != null) {
613 if (e.Filename == null){
614 byte [] copy = new byte [e.Length];
616 input.Position = e.Start;
617 input.Read (copy, 0, (int) e.Length);
619 form.Add (e.Name, ContentEncoding.GetString (copy));
622 // We use a substream, as in 2.x we will support large uploads streamed to disk,
624 HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
625 files.AddFile (e.Name, sub);
628 EndSubStream (input);
632 // Adds the key/value to the form, and sets the argumets to empty
634 void AddRawKeyValue (StringBuilder key, StringBuilder value)
636 string decodedKey = HttpUtility.UrlDecode (key.ToString (), ContentEncoding);
637 form.Add (decodedKey,
638 HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
645 // Loads the form data from on a application/x-www-form-urlencoded post
649 using (Stream input = GetSubStream (InputStream)) {
650 using (StreamReader s = new StreamReader (input, ContentEncoding)) {
651 StringBuilder key = new StringBuilder ();
652 StringBuilder value = new StringBuilder ();
655 while ((c = s.Read ()) != -1){
658 while ((c = s.Read ()) != -1){
660 AddRawKeyValue (key, value);
663 value.Append ((char) c);
666 AddRawKeyValue (key, value);
670 AddRawKeyValue (key, value);
672 key.Append ((char) c);
675 AddRawKeyValue (key, value);
677 EndSubStream (input);
682 bool IsContentType (string ct, bool starts_with)
685 return StrUtils.StartsWith (ContentType, ct, true);
687 return String.Compare (ContentType, ct, true, Helpers.InvariantCulture) == 0;
690 internal WebROCollection FormUnvalidated {
693 form = new WebROCollection ();
694 files = new HttpFileCollection ();
696 if (IsContentType ("multipart/form-data", true))
699 IsContentType ("application/x-www-form-urlencoded", true))
709 public NameValueCollection Form {
711 NameValueCollection form = FormUnvalidated;
712 if (validateRequestNewMode && !checked_form) {
713 if (!lazyFormValidation) {
714 // Setting this before calling the validator prevents
715 // possible endless recursion
717 ValidateNameValueCollection ("Form", form, RequestValidationSource.Form);
720 if (validate_form && !checked_form){
722 ValidateNameValueCollection ("Form", form);
729 internal NameValueCollection HeadersNoValidation {
731 if (headers_unvalidated == null) {
732 headers_unvalidated = new HeadersCollection (this);
735 return headers_unvalidated;
739 public NameValueCollection Headers {
741 if (headers == null) {
742 headers = HeadersNoValidation;
743 if (validateRequestNewMode) {
744 RequestValidator validator = RequestValidator.Current;
745 int validationFailureIndex;
747 foreach (string hkey in headers.AllKeys) {
748 string value = headers [hkey];
750 if (!validator.IsValidRequestString (HttpContext.Current, value, RequestValidationSource.Headers, hkey, out validationFailureIndex))
751 ThrowValidationException ("Headers", hkey, value);
760 public string HttpMethod {
762 if (http_method == null){
763 if (worker_request != null)
764 http_method = worker_request.GetHttpVerbName ();
772 void DoFilter (byte [] buffer)
774 if (input_filter == null || filter == null)
777 if (buffer.Length < 1024)
778 buffer = new byte [1024];
780 // Replace the input with the filtered input
781 input_filter.BaseStream = input_stream;
782 MemoryStream ms = new MemoryStream ();
784 int n = filter.Read (buffer, 0, buffer.Length);
787 ms.Write (buffer, 0, n);
789 // From now on input_stream has the filtered input
790 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
793 const int INPUT_BUFFER_SIZE = 32*1024;
795 TempFileStream GetTempStream ()
797 string tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
798 TempFileStream f = null;
800 Random rnd = new Random ();
805 path = System.IO.Path.Combine (tempdir, "tmp" + num.ToString("x") + ".req");
808 f = new TempFileStream (path);
809 } catch (SecurityException) {
810 // avoid an endless loop
818 void MakeInputStream ()
820 if (input_stream != null)
823 if (worker_request == null) {
824 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
825 DoFilter (new byte [1024]);
830 // Use an unmanaged memory block as this might be a large
833 int content_length = ContentLength;
834 int content_length_kb = content_length / 1024;
835 HttpRuntimeSection config = HttpRuntime.Section;
836 if (content_length_kb > config.MaxRequestLength)
837 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
841 buffer = worker_request.GetPreloadedEntityBody ();
842 // we check the instance field 'content_length' here, not the local var.
843 if (this.content_length <= 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
844 if (buffer == null || content_length == 0) {
845 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
847 input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
849 DoFilter (new byte [1024]);
854 total = buffer.Length;
856 if (content_length > 0 && content_length_kb >= config.RequestLengthDiskThreshold) {
857 // Writes the request to disk
858 total = Math.Min (content_length, total);
859 request_file = GetTempStream ();
860 Stream output = request_file;
862 output.Write (buffer, 0, total);
864 if (total < content_length) {
865 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
868 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
869 n = worker_request.ReadEntityBody (buffer, min);
872 output.Write (buffer, 0, n);
874 } while (total < content_length);
877 request_file.SetReadOnly ();
878 input_stream = request_file;
879 } else if (content_length > 0) {
880 // Buffers the request in an IntPtrStream
881 total = Math.Min (content_length, total);
882 IntPtr content = Marshal.AllocHGlobal (content_length);
883 if (content == (IntPtr) 0)
884 throw HttpException.NewWithCode (
885 String.Format ("Not enough memory to allocate {0} bytes.", content_length),
886 WebEventCodes.WebErrorOtherError);
889 Marshal.Copy (buffer, 0, content, total);
891 if (total < content_length) {
892 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
895 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
896 n = worker_request.ReadEntityBody (buffer, min);
899 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
901 } while (total < content_length);
904 input_stream = new IntPtrStream (content, total);
906 // Buffers the request in a MemoryStream or writes to disk if threshold exceeded
907 MemoryStream ms = new MemoryStream ();
910 ms.Write (buffer, 0, total);
912 buffer = new byte [INPUT_BUFFER_SIZE];
913 long maxlength = config.MaxRequestLength * 1024L;
914 long disk_th = config.RequestLengthDiskThreshold * 1024L;
917 n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
921 if (total < 0 || total > maxlength)
922 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
924 if (ms != null && total > disk_th) {
925 // Swith to on-disk file.
926 request_file = GetTempStream ();
927 ms.WriteTo (request_file);
929 output = request_file;
931 output.Write (buffer, 0, n);
935 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
937 request_file.SetReadOnly ();
938 input_stream = request_file;
943 if (total < content_length)
944 throw HttpException.NewWithCode (411, "The request body is incomplete.", WebEventCodes.WebErrorOtherError);
947 internal void ReleaseResources ()
950 if (input_stream != null){
951 stream = input_stream;
958 if (request_file != null) {
959 stream = request_file;
966 public RequestContext RequestContext {
968 if (requestContext == null)
969 requestContext = new RequestContext (new HttpContextWrapper (this.context ?? HttpContext.Current), new RouteData ());
971 return requestContext;
974 internal set { requestContext = value; }
977 public ChannelBinding HttpChannelBinding {
979 throw new PlatformNotSupportedException ("This property is not supported.");
983 public Stream GetBufferlessInputStream ()
985 if (bufferlessInputStream == null) {
986 if (input_stream != null)
987 throw new HttpException ("Input stream has already been created");
989 // we don't need to hook up the filter here, because the raw stream should be returned
990 bufferlessInputStream = new BufferlessInputStream (this);
993 return bufferlessInputStream;
997 // Stream that returns the data as it is read, without buffering
999 class BufferlessInputStream : Stream {
1000 HttpRequest request;
1002 // cached, the request content-length
1005 // buffer that holds preloaded data
1006 byte [] preloadedBuffer;
1008 // indicates if we already served the whole preloaded buffer
1009 bool preloaded_served;
1011 // indicates if we already checked the request content-length against httpRuntime limit
1012 bool checked_maxRequestLength;
1014 // our stream position
1018 // @request: the containing request that created us, used to find out content length
1019 public BufferlessInputStream (HttpRequest request)
1021 this.request = request;
1022 content_length = request.ContentLength;
1025 public override bool CanRead {
1026 get { return true; }
1029 public override bool CanSeek {
1030 get { return false; }
1033 public override bool CanWrite {
1034 get { return false; }
1037 public override long Length {
1039 return content_length;
1043 public override long Position {
1048 throw new NotSupportedException ("This is a readonly stream");
1052 public override void Flush ()
1056 public override int Read (byte [] buffer, int offset, int count)
1059 throw new ArgumentNullException ("buffer");
1061 if (offset < 0 || count < 0)
1062 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
1064 if (buffer.Length - offset < count )
1065 throw new ArgumentException ("offset+count",
1066 "The size of the buffer is less than offset + count.");
1068 if (count == 0 || request.worker_request == null)
1071 if (!checked_maxRequestLength) {
1072 int content_length_kb = content_length / 1024;
1073 HttpRuntimeSection config = HttpRuntime.Section;
1074 if (content_length_kb > config.MaxRequestLength)
1075 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
1077 checked_maxRequestLength = true;
1080 // Serve the bytes we might have preloaded already.
1081 if (!preloaded_served) {
1082 if (preloadedBuffer == null)
1083 preloadedBuffer = request.worker_request.GetPreloadedEntityBody ();
1085 if (preloadedBuffer != null) {
1086 long bytes_left = preloadedBuffer.Length-position;
1087 int n = (int) Math.Min (count, bytes_left);
1088 Array.Copy (preloadedBuffer, position, buffer, offset, n);
1091 if (n == bytes_left)
1092 preloaded_served = true;
1097 preloaded_served = true;
1100 // serve bytes from worker request if available
1101 if (position < content_length) {
1102 long bytes_left = content_length-position;
1105 if (bytes_left < count)
1106 n = (int) bytes_left;
1108 int bytes_read = request.worker_request.ReadEntityBody (buffer, offset, n);
1109 position += bytes_read;
1116 public override long Seek (long offset, SeekOrigin origin)
1118 throw new NotSupportedException ("Can not seek on the HttpRequest.BufferlessInputStream");
1121 public override void SetLength (long value)
1123 throw new NotSupportedException ("Can not set length on the HttpRequest.BufferlessInputStream");
1126 public override void Write (byte [] buffer, int offset, int count)
1128 throw new NotSupportedException ("Can not write on the HttpRequest.BufferlessInputStream");
1132 // TODO: explicitly support the async methods if there is a convenient way of doing it
1135 public Stream InputStream {
1137 if (input_stream == null)
1140 return input_stream;
1144 public bool IsAuthenticated {
1146 if (context.User == null || context.User.Identity == null)
1148 return context.User.Identity.IsAuthenticated;
1152 public bool IsSecureConnection {
1154 if (worker_request == null)
1156 return worker_request.IsSecure ();
1160 public string this [string key] {
1161 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1163 // "The QueryString, Form, Cookies, or ServerVariables collection member
1164 // specified in the key parameter."
1165 string val = QueryString [key];
1169 HttpCookie cookie = Cookies [key];
1174 val = ServerVariables [key];
1180 public NameValueCollection Params {
1181 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1183 if (all_params == null)
1184 all_params = new HttpParamsCollection (QueryString, Form, ServerVariables, Cookies);
1190 internal string PathNoValidation {
1192 if (original_path == null) {
1193 if (url_components != null)
1194 // use only if it's already been instantiated, so that we can't go into endless
1195 // recursion in some scenarios
1196 original_path = UrlComponents.Path;
1198 original_path = ApplyUrlMapping (worker_request.GetUriPath ());
1201 return original_path;
1205 public string Path {
1207 if (unescaped_path == null) {
1208 unescaped_path = PathNoValidation;
1209 if (validateRequestNewMode) {
1210 RequestValidator validator = RequestValidator.Current;
1211 int validationFailureIndex;
1213 if (!validator.IsValidRequestString (HttpContext.Current, unescaped_path, RequestValidationSource.Path, null, out validationFailureIndex))
1214 ThrowValidationException ("Path", "Path", unescaped_path);
1218 return unescaped_path;
1222 internal string PathInfoNoValidation {
1224 if (path_info_unvalidated == null) {
1225 if (worker_request == null)
1226 return String.Empty;
1228 path_info_unvalidated = worker_request.GetPathInfo () ?? String.Empty;
1231 return path_info_unvalidated;
1235 public string PathInfo {
1237 if (path_info == null) {
1238 path_info = PathInfoNoValidation;
1239 if (validateRequestNewMode) {
1240 RequestValidator validator = RequestValidator.Current;
1241 int validationFailureIndex;
1243 if (!validator.IsValidRequestString (HttpContext.Current, path_info, RequestValidationSource.PathInfo, null, out validationFailureIndex))
1244 ThrowValidationException ("PathInfo", "PathInfo", path_info);
1252 public string PhysicalApplicationPath {
1254 if (worker_request == null)
1255 throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
1257 string path = HttpRuntime.AppDomainAppPath;
1258 if (SecurityManager.SecurityEnabled) {
1259 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
1265 public string PhysicalPath {
1267 if (worker_request == null)
1268 return String.Empty; // don't check security with an empty string!
1270 if (physical_path == null) {
1271 // Don't call HttpRequest.MapPath here, as that one *trims* the input
1272 physical_path = worker_request.MapPath (FilePath);
1275 if (SecurityManager.SecurityEnabled) {
1276 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
1278 return physical_path;
1282 internal string RootVirtualDir {
1284 if (root_virtual_dir == null){
1285 string fp = FilePath;
1286 int p = fp.LastIndexOf ('/');
1289 root_virtual_dir = "/";
1291 root_virtual_dir = fp.Substring (0, p);
1293 return root_virtual_dir;
1297 internal WebROCollection QueryStringUnvalidated {
1299 if (query_string_nvc == null) {
1300 query_string_nvc = new WebROCollection ();
1301 string q = UrlComponents.Query;
1306 HttpUtility.ParseQueryString (q, ContentEncoding, query_string_nvc);
1309 query_string_nvc.Protect();
1312 return query_string_nvc;
1316 public NameValueCollection QueryString {
1318 NameValueCollection query_string_nvc = QueryStringUnvalidated;
1319 if (validateRequestNewMode && !checked_query_string) {
1320 if (!lazyQueryStringValidation) {
1321 // Setting this before calling the validator prevents
1322 // possible endless recursion
1323 checked_query_string = true;
1324 ValidateNameValueCollection ("QueryString", query_string_nvc, RequestValidationSource.QueryString);
1327 if (validate_query_string && !checked_query_string) {
1328 // Setting this before calling the validator prevents
1329 // possible endless recursion
1330 checked_query_string = true;
1331 ValidateNameValueCollection ("QueryString", query_string_nvc);
1334 return query_string_nvc;
1338 internal string RawUrlUnvalidated {
1340 if (raw_url_unvalidated == null) {
1341 if (worker_request != null)
1342 raw_url_unvalidated = worker_request.GetRawUrl ();
1344 raw_url_unvalidated = UrlComponents.Path + UrlComponents.Query;
1346 if (raw_url_unvalidated == null)
1347 raw_url_unvalidated = String.Empty;
1350 return raw_url_unvalidated;
1354 public string RawUrl {
1356 if (raw_url == null) {
1357 raw_url = RawUrlUnvalidated;
1358 if (validateRequestNewMode) {
1359 RequestValidator validator = RequestValidator.Current;
1360 int validationFailureIndex;
1362 if (!validator.IsValidRequestString (HttpContext.Current, raw_url, RequestValidationSource.RawUrl, null, out validationFailureIndex))
1363 ThrowValidationException ("RawUrl", "RawUrl", raw_url);
1374 public string RequestType {
1376 if (request_type == null){
1377 if (worker_request != null) {
1378 request_type = worker_request.GetHttpVerbName ();
1379 http_method = request_type;
1381 request_type = "GET";
1384 return request_type;
1388 request_type = value;
1392 public NameValueCollection ServerVariables {
1393 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1395 if (server_variables == null)
1396 server_variables = new ServerVariablesCollection (this);
1398 return server_variables;
1402 public int TotalBytes {
1404 Stream ins = InputStream;
1405 return (int) ins.Length;
1409 public UnvalidatedRequestValues Unvalidated {
1411 var vals = new UnvalidatedRequestValues ();
1413 vals.Cookies = CookiesNoValidation;
1415 vals.Form = FormUnvalidated;
1416 vals.Headers = HeadersNoValidation;
1417 vals.Path = PathNoValidation;
1418 vals.PathInfo = PathInfoNoValidation;
1419 vals.QueryString = QueryStringUnvalidated;
1420 vals.RawUrl = RawUrlUnvalidated;
1429 if (cached_url == null) {
1430 if (orig_url == null)
1431 cached_url = UrlComponents.Uri;
1433 cached_url = new Uri (orig_url);
1440 public Uri UrlReferrer {
1442 if (worker_request == null)
1445 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
1452 } catch (UriFormatException) {}
1457 public string UserAgent {
1459 if (worker_request == null)
1462 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
1466 public string UserHostAddress {
1468 if (worker_request == null)
1471 return worker_request.GetRemoteAddress ();
1475 public string UserHostName {
1477 if (worker_request == null)
1480 return worker_request.GetRemoteName ();
1484 public string [] UserLanguages {
1486 if (worker_request == null)
1489 if (user_languages == null)
1490 user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
1492 return user_languages;
1496 public byte [] BinaryRead (int count)
1499 throw new ArgumentException ("count is < 0");
1501 Stream s = InputStream;
1502 byte [] ret = new byte [count];
1503 if (s.Read (ret, 0, count) != count)
1504 throw new ArgumentException (
1505 String.Format ("count {0} exceeds length of available input {1}",
1506 count, s.Length - s.Position));
1510 public int [] MapImageCoordinates (string imageFieldName)
1512 string method = HttpMethod;
1513 NameValueCollection coll = null;
1514 if (method == "HEAD" || method == "GET")
1516 else if (method == "POST")
1522 string x = coll [imageFieldName + ".x"];
1523 if (x == null || x == "")
1526 string y = coll [imageFieldName + ".y"];
1527 if (y == null || y == "")
1530 int [] result = new int [2];
1532 result [0] = Int32.Parse (x);
1533 result [1] = Int32.Parse (y);
1541 public string MapPath (string virtualPath)
1543 if (worker_request == null)
1546 return MapPath (virtualPath, BaseVirtualDir, true);
1549 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1551 if (worker_request == null)
1552 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
1554 if (virtualPath == null)
1557 virtualPath = virtualPath.Trim ();
1558 if (virtualPath.Length == 0)
1562 if (!VirtualPathUtility.IsValidVirtualPath (virtualPath))
1563 throw HttpException.NewWithCode (String.Format ("'{0}' is not a valid virtual path.", virtualPath), WebEventCodes.RuntimeErrorRequestAbort);
1565 string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
1566 if (!VirtualPathUtility.IsRooted (virtualPath)) {
1567 if (StrUtils.IsNullOrEmpty (baseVirtualDir))
1568 baseVirtualDir = appVirtualPath;
1569 virtualPath = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (baseVirtualDir), virtualPath);
1570 if (!VirtualPathUtility.IsAbsolute (virtualPath))
1571 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath, false);
1572 } else if (!VirtualPathUtility.IsAbsolute (virtualPath))
1573 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath, false);
1575 bool isAppVirtualPath = String.Compare (virtualPath, appVirtualPath, RuntimeHelpers.StringComparison) == 0;
1576 appVirtualPath = VirtualPathUtility.AppendTrailingSlash (appVirtualPath);
1577 if (!allowCrossAppMapping){
1578 if (!StrUtils.StartsWith (virtualPath, appVirtualPath, true))
1579 throw new ArgumentException ("MapPath: Mapping across applications not allowed");
1580 if (appVirtualPath.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1581 throw HttpException.NewWithCode ("MapPath: Mapping across applications not allowed", WebEventCodes.RuntimeErrorRequestAbort);
1584 if (!isAppVirtualPath && !virtualPath.StartsWith (appVirtualPath, RuntimeHelpers.StringComparison))
1585 throw new InvalidOperationException (String.Format ("Failed to map path '{0}'", virtualPath));
1586 string path = worker_request.MapPath (virtualPath);
1587 if (virtualPath [virtualPath.Length - 1] != '/' && path [path.Length - 1] == System.IO.Path.DirectorySeparatorChar)
1588 path = path.TrimEnd (System.IO.Path.DirectorySeparatorChar);
1592 public void SaveAs (string filename, bool includeHeaders)
1594 Stream output = new FileStream (filename, FileMode.Create);
1595 if (includeHeaders) {
1596 StringBuilder sb = new StringBuilder ();
1597 string version = String.Empty;
1599 if (worker_request != null) {
1600 version = worker_request.GetHttpVersion ();
1601 path = UrlComponents.Path;
1603 string qs = UrlComponents.Query;
1605 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1606 NameValueCollection coll = Headers;
1607 foreach (string k in coll.AllKeys) {
1610 sb.Append (coll [k]);
1615 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1616 output.Write (bytes, 0, bytes.Length);
1619 // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1620 // to keep InputStream in its state.
1621 Stream input = GetSubStream (InputStream);
1623 long len = input.Length;
1624 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1625 byte [] data = new byte [buf_size];
1627 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1628 output.Write (data, 0, count);
1634 EndSubStream (input);
1638 public void ValidateInput ()
1640 validate_cookies = true;
1641 validate_query_string = true;
1642 validate_form = true;
1643 inputValidationEnabled = true;
1645 internal void Validate ()
1647 var cfg = HttpRuntime.Section;
1648 string query = UrlComponents.Query;
1650 if (query != null && query.Length > cfg.MaxQueryStringLength)
1651 throw new HttpException (400, "The length of the query string for this request exceeds the configured maxQueryStringLength value.");
1653 string path = PathNoValidation;
1655 if (path.Length > cfg.MaxUrlLength)
1656 throw new HttpException (400, "The length of the URL for this request exceeds the configured maxUrlLength value.");
1658 char[] invalidChars = RequestPathInvalidCharacters;
1659 if (invalidChars != null) {
1660 int idx = path.IndexOfAny (invalidChars);
1662 throw HttpException.NewWithCode (
1663 String.Format ("A potentially dangerous Request.Path value was detected from the client ({0}).", path [idx]),
1664 WebEventCodes.RuntimeErrorValidationFailure
1669 if (validateRequestNewMode)
1672 #region internal routines
1673 internal string ClientTarget {
1675 return client_target;
1679 client_target = value;
1683 public bool IsLocal {
1685 string address = worker_request.GetRemoteAddress ();
1687 if (StrUtils.IsNullOrEmpty (address))
1690 if (address == "127.0.0.1")
1693 System.Net.IPAddress remoteAddr = System.Net.IPAddress.Parse (address);
1694 if (System.Net.IPAddress.IsLoopback (remoteAddr))
1697 for (int i = 0; i < host_addresses.Length; i++)
1698 if (remoteAddr.Equals (host_addresses [i]))
1705 internal void SetFilePath (string path)
1708 physical_path = null;
1709 original_path = null;
1712 internal void SetCurrentExePath (string path)
1715 current_exe_path = path;
1716 UrlComponents.Path = path + PathInfo;
1717 // recreated on demand
1718 root_virtual_dir = null;
1719 base_virtual_dir = null;
1720 physical_path = null;
1721 unescaped_path = null;
1722 original_path = null;
1725 internal void SetPathInfo (string pi)
1729 original_path = null;
1731 string path = UrlComponents.Path;
1732 UrlComponents.Path = path + PathInfo;
1734 internal void SetFormCollection (WebROCollection coll, bool lazyValidation)
1739 lazyFormValidation = lazyValidation;
1742 internal void SetQueryStringCollection (WebROCollection coll, bool lazyValidation)
1746 query_string_nvc = coll;
1747 lazyQueryStringValidation = lazyValidation;
1749 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1750 internal void SetHeader (string name, string value)
1752 WebROCollection h = (WebROCollection) Headers;
1758 // Notice: there is nothing raw about this querystring.
1759 internal string QueryStringRaw {
1761 UriBuilder urlComponents = UrlComponents;
1763 if (urlComponents == null) {
1764 string ret = worker_request.GetQueryString ();
1766 if (ret == null || ret.Length == 0)
1767 return String.Empty;
1775 return UrlComponents.Query;
1779 UrlComponents.Query = value;
1781 query_string_nvc = null;
1785 // Internal, dont know what it does, so flagged as public so we can see it.
1786 internal void SetForm (WebROCollection coll)
1791 internal HttpWorkerRequest WorkerRequest {
1793 return worker_request;
1797 internal HttpContext Context {
1798 get { return context; }
1799 set { context = value; }
1802 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1807 foreach (string key in coll.Keys) {
1808 string val = coll [key];
1809 if (val != null && val.Length > 0 && IsInvalidString (val))
1810 ThrowValidationException (name, key, val);
1813 static void ValidateNameValueCollection (string name, NameValueCollection coll, RequestValidationSource source)
1818 RequestValidator validator = RequestValidator.Current;
1819 int validationFailureIndex;
1820 HttpContext context = HttpContext.Current;
1822 foreach (string key in coll.Keys) {
1823 string val = coll [key];
1824 if (val != null && val.Length > 0 && !validator.IsValidRequestString (context, val, source, key, out validationFailureIndex))
1825 ThrowValidationException (name, key, val);
1829 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1830 public void InsertEntityBody ()
1832 throw new PlatformNotSupportedException ("This method is not supported.");
1835 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1836 public void InsertEntityBody (byte[] buffer, int offset, int count)
1838 throw new PlatformNotSupportedException ("This method is not supported.");
1840 static void ValidateCookieCollection (HttpCookieCollection cookies)
1842 if (cookies == null)
1845 int size = cookies.Count;
1847 RequestValidator validator = RequestValidator.Current;
1848 int validationFailureIndex;
1849 HttpContext context = HttpContext.Current;
1852 for (int i = 0 ; i < size ; i++) {
1853 cookie = cookies[i];
1857 string value = cookie.Value;
1858 string name = cookie.Name;
1860 if (!String.IsNullOrEmpty (value)) {
1861 if (validateRequestNewMode)
1862 invalid = !validator.IsValidRequestString (context, value, RequestValidationSource.Cookies, name, out validationFailureIndex);
1864 invalid = IsInvalidString (value);
1867 ThrowValidationException ("Cookies", name, value);
1872 static void ThrowValidationException (string name, string key, string value)
1874 string v = "\"" + value + "\"";
1876 v = v.Substring (0, 16) + "...\"";
1878 string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1879 "detected from the client ({1}={2}).", name, key, v);
1881 throw new HttpRequestValidationException (msg);
1883 internal static void ValidateString (string key, string value, RequestValidationSource source)
1885 if (String.IsNullOrEmpty (value))
1887 #pragma warning disable 219
1889 #pragma warning restore 219
1890 if (IsInvalidString (value, out ignore))
1891 ThrowValidationException (source.ToString (), key, value);
1893 internal static bool IsInvalidString (string val)
1895 #pragma warning disable 219
1896 int validationFailureIndex;
1897 #pragma warning restore 219
1898 return IsInvalidString (val, out validationFailureIndex);
1901 internal static bool IsInvalidString (string val, out int validationFailureIndex)
1903 validationFailureIndex = 0;
1905 int len = val.Length;
1909 char current = val [0];
1910 for (int idx = 1; idx < len; idx++) {
1911 char next = val [idx];
1912 // See http://secunia.com/advisories/14325
1913 if (current == '<' || current == '\xff1c') {
1914 if (next == '!' || next < ' '
1915 || (next >= 'a' && next <= 'z')
1916 || (next >= 'A' && next <= 'Z')) {
1917 validationFailureIndex = idx - 1;
1920 } else if (current == '&' && next == '#') {
1921 validationFailureIndex = idx - 1;
1931 static System.Net.IPAddress [] GetLocalHostAddresses ()
1934 string hostName = System.Net.Dns.GetHostName ();
1935 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostAddresses (hostName);
1938 return new System.Net.IPAddress[0];
1944 #region Helper classes
1947 // Stream-based multipart handling.
1949 // In this incarnation deals with an HttpInputStream as we are now using
1950 // IntPtr-based streams instead of byte []. In the future, we will also
1951 // send uploads above a certain threshold into the disk (to implement
1952 // limit-less HttpInputFiles).
1955 class HttpMultipart {
1957 public class Element {
1958 public string ContentType;
1960 public string Filename;
1964 public override string ToString ()
1966 return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
1967 Start.ToString () + ", Length " + Length.ToString ();
1973 byte [] boundary_bytes;
1979 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
1982 // In the case of multipart entities, in which one or more different
1983 // sets of data are combined in a single body, a "multipart" media type
1984 // field must appear in the entity's header. The body must then contain
1985 // one or more body parts, each preceded by a boundary delimiter line,
1986 // and the last one followed by a closing boundary delimiter line.
1987 // After its boundary delimiter line, each body part then consists of a
1988 // header area, a blank line, and a body area. Thus a body part is
1989 // similar to an RFC 822 message in syntax, but different in meaning.
1991 public HttpMultipart (Stream data, string b, Encoding encoding)
1995 boundary_bytes = encoding.GetBytes (b);
1996 buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1997 this.encoding = encoding;
1998 sb = new StringBuilder ();
2003 // CRLF or LF are ok as line endings.
2004 bool got_cr = false;
2008 b = data.ReadByte ();
2017 sb.Append ((char) b);
2023 return sb.ToString ();
2027 static string GetContentDispositionAttribute (string l, string name)
2029 int idx = l.IndexOf (name + "=\"");
2032 int begin = idx + name.Length + "=\"".Length;
2033 int end = l.IndexOf ('"', begin);
2038 return l.Substring (begin, end - begin);
2041 string GetContentDispositionAttributeWithEncoding (string l, string name)
2043 int idx = l.IndexOf (name + "=\"");
2046 int begin = idx + name.Length + "=\"".Length;
2047 int end = l.IndexOf ('"', begin);
2053 string temp = l.Substring (begin, end - begin);
2054 byte [] source = new byte [temp.Length];
2055 for (int i = temp.Length - 1; i >= 0; i--)
2056 source [i] = (byte) temp [i];
2058 return encoding.GetString (source);
2061 bool ReadBoundary ()
2064 string line = ReadLine ();
2067 if (line [0] != '-' || line [1] != '-')
2070 if (!StrUtils.EndsWith (line, boundary, false))
2078 string ReadHeaders ()
2080 string s = ReadLine ();
2087 bool CompareBytes (byte [] orig, byte [] other)
2089 for (int i = orig.Length - 1; i >= 0; i--)
2090 if (orig [i] != other [i])
2096 long MoveToNextBoundary ()
2099 bool got_cr = false;
2102 int c = data.ReadByte ();
2107 if (state == 0 && c == LF) {
2108 retval = data.Position - 1;
2112 c = data.ReadByte ();
2113 } else if (state == 0) {
2115 c = data.ReadByte ();
2116 } else if (state == 1 && c == '-') {
2117 c = data.ReadByte ();
2124 continue; // no ReadByte() here
2127 int nread = data.Read (buffer, 0, buffer.Length);
2128 int bl = buffer.Length;
2132 if (!CompareBytes (boundary_bytes, buffer)) {
2134 data.Position = retval + 2;
2139 c = data.ReadByte ();
2143 if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
2145 } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
2147 data.Position = retval + 2;
2152 c = data.ReadByte ();
2155 data.Position = retval + 2;
2161 state = 0; // no ReadByte() here
2168 public Element ReadNextElement ()
2170 if (at_eof || ReadBoundary ())
2173 Element elem = new Element ();
2175 while ((header = ReadHeaders ()) != null) {
2176 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
2177 elem.Name = GetContentDispositionAttribute (header, "name");
2178 elem.Filename = StripPath (GetContentDispositionAttributeWithEncoding (header, "filename"));
2179 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
2180 elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
2184 long start = data.Position;
2186 long pos = MoveToNextBoundary ();
2190 elem.Length = pos - start;
2194 static string StripPath (string path)
2196 if (path == null || path.Length == 0)
2199 if (path.IndexOf (":\\") != 1 && !path.StartsWith ("\\\\"))
2201 return path.Substring (path.LastIndexOf ('\\') + 1);