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;
49 using System.Security.Authentication.ExtendedProtection;
50 using System.Web.Routing;
55 // CAS - no InheritanceDemand here as the class is sealed
56 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
57 public sealed partial class HttpRequest
59 HttpWorkerRequest worker_request;
61 WebROCollection query_string_nvc;
65 string orig_url = null;
66 UriBuilder url_components;
71 // On-demand computed values
73 HttpBrowserCapabilities browser_capabilities;
74 string file_path, base_virtual_dir, root_virtual_dir, client_file_path;
76 int content_length = -1;
78 string current_exe_path;
80 string unescaped_path;
83 string path_info_unvalidated;
85 string raw_url_unvalidated;
86 WebROCollection all_params;
87 NameValueCollection headers;
88 WebROCollection headers_unvalidated;
90 InputFilterStream input_filter;
92 HttpCookieCollection cookies;
93 HttpCookieCollection cookies_unvalidated;
97 HttpFileCollection files;
99 ServerVariablesCollection server_variables;
100 HttpClientCertificate client_cert;
103 string [] accept_types;
104 string [] user_languages;
106 TempFileStream request_file;
108 readonly static System.Net.IPAddress [] host_addresses;
111 bool validate_cookies, validate_query_string, validate_form;
112 bool checked_cookies, checked_query_string, checked_form;
113 static readonly UrlMappingCollection urlMappings;
114 readonly static char [] queryTrimChars = {'?'};
116 bool lazyFormValidation;
117 bool lazyQueryStringValidation;
118 bool inputValidationEnabled;
119 RequestContext requestContext;
120 BufferlessInputStream bufferlessInputStream;
122 static bool validateRequestNewMode;
123 internal static bool ValidateRequestNewMode {
124 get { return validateRequestNewMode; }
127 internal bool InputValidationEnabled {
128 get { return inputValidationEnabled; }
131 private static char[] RequestPathInvalidCharacters {
135 private static char[] CharsFromList (string list)
137 // List format is very strict and enforced by the Configuration
138 // there must be a single char separated by commas with no trailing comma
139 // whitespace is allowed though and should be trimmed.
141 string [] pieces = list.Split (',');
143 char [] chars = new char [pieces.Length];
144 for (int i = 0; i < chars.Length; i++) {
145 string trimmed = pieces [i].Trim ();
146 if (trimmed.Length != 1) {
147 // This should have been caught by System.Web.Configuration
148 // and throw a configuration error. This is just here for sanity
149 throw new System.Configuration.ConfigurationErrorsException ();
152 chars [i] = trimmed [0];
159 static HttpRequest ()
162 UrlMappingsSection ums = WebConfigurationManager.GetWebApplicationSection ("system.web/urlMappings") as UrlMappingsSection;
163 if (ums != null && ums.IsEnabled) {
164 urlMappings = ums.UrlMappings;
165 if (urlMappings.Count == 0)
170 Version validationMode = HttpRuntime.Section.RequestValidationMode;
172 if (validationMode >= new Version (4, 0)) {
173 validateRequestNewMode = true;
174 string invalidChars = HttpRuntime.Section.RequestPathInvalidCharacters;
175 if (!String.IsNullOrEmpty (invalidChars))
176 RequestPathInvalidCharacters = CharsFromList (invalidChars);
180 // unlikely to happen
183 host_addresses = GetLocalHostAddresses ();
186 public HttpRequest (string filename, string url, string queryString)
188 // warning 169: what are we supposed to do with filename?
190 //this.filename = filename;
193 url_components = new UriBuilder (url);
194 url_components.Query = queryString;
196 query_string_nvc = new WebROCollection ();
197 if (queryString != null)
198 HttpUtility.ParseQueryString (queryString, Encoding.Default, query_string_nvc);
199 query_string_nvc.Protect ();
202 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
204 this.worker_request = worker_request;
205 this.context = context;
208 internal UriBuilder UrlComponents {
210 if (url_components == null) {
212 byte[] queryStringRaw = worker_request.GetQueryStringRawBytes();
213 if(queryStringRaw != null)
214 query = ContentEncoding.GetString(queryStringRaw);
216 query = worker_request.GetQueryString();
218 BuildUrlComponents (ApplyUrlMapping (worker_request.GetUriPath ()), query);
220 return url_components;
224 void BuildUrlComponents (string path, string query)
226 if (url_components != null)
228 url_components = new UriBuilder ();
229 url_components.Scheme = worker_request.GetProtocol ();
230 url_components.Host = worker_request.GetServerName ();
231 url_components.Port = worker_request.GetLocalPort ();
232 url_components.Path = path;
233 if (query != null && query.Length > 0)
234 url_components.Query = query.TrimStart (queryTrimChars);
237 internal string ApplyUrlMapping (string url)
239 if (urlMappings == null)
242 string relUrl = VirtualPathUtility.ToAppRelative (url);
243 UrlMapping um = null;
245 foreach (UrlMapping u in urlMappings) {
248 if (String.Compare (relUrl, u.Url, StringComparison.Ordinal) == 0) {
257 string rawUrl = VirtualPathUtility.ToAbsolute (um.MappedUrl.Trim ());
258 Uri newUrl = new Uri ("http://host.com" + rawUrl);
260 if (url_components != null) {
261 url_components.Path = newUrl.AbsolutePath;
262 url_components.Query = newUrl.Query.TrimStart (queryTrimChars);
263 query_string_nvc = new WebROCollection ();
264 HttpUtility.ParseQueryString (newUrl.Query, Encoding.Default, query_string_nvc);
265 query_string_nvc.Protect ();
267 BuildUrlComponents (newUrl.AbsolutePath, newUrl.Query);
269 return url_components.Path;
272 string [] SplitHeader (int header_index)
274 string [] result = null;
275 string header = worker_request.GetKnownRequestHeader (header_index);
276 if (header != null && header != "" && header.Trim () != "") {
277 result = header.Split (',');
278 for (int i = result.Length - 1; i >= 0; i--)
279 result [i] = result [i].Trim ();
284 public string [] AcceptTypes {
286 if (worker_request == null)
289 if (accept_types == null)
290 accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
296 public WindowsIdentity LogonUserIdentity {
297 get { throw new NotImplementedException (); }
301 public string AnonymousID {
306 anonymous_id = value;
310 public string ApplicationPath {
312 if (worker_request == null)
314 return worker_request.GetAppPath ();
318 public HttpBrowserCapabilities Browser {
320 if (browser_capabilities == null)
322 browser_capabilities = HttpCapabilitiesBase.BrowserCapabilitiesProvider.GetBrowserCapabilities (this);
324 browser_capabilities = (HttpBrowserCapabilities)
325 HttpCapabilitiesBase.GetConfigCapabilities (null, this);
328 return browser_capabilities;
332 browser_capabilities = value;
336 internal bool BrowserMightHaveSpecialWriter {
338 return (browser_capabilities != null
339 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
343 internal bool BrowserMightHaveAdapters {
345 return (browser_capabilities != null
346 || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
350 public HttpClientCertificate ClientCertificate {
352 if (client_cert == null)
353 client_cert = new HttpClientCertificate (worker_request);
358 static internal string GetParameter (string header, string attr)
360 int ap = header.IndexOf (attr);
365 if (ap >= header.Length)
368 char ending = header [ap];
372 int end = header.IndexOf (ending, ap+1);
374 return (ending == '"') ? null : header.Substring (ap);
376 return header.Substring (ap+1, end-ap-1);
379 public Encoding ContentEncoding {
381 if (encoding == null){
382 if (worker_request == null)
383 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
385 string content_type = ContentType;
386 string parameter = GetParameter (content_type, "; charset=");
387 if (parameter == null) {
388 encoding = WebEncoding.RequestEncoding;
391 // Do what the #1 web server does
392 encoding = Encoding.GetEncoding (parameter);
394 encoding = WebEncoding.RequestEncoding;
406 public int ContentLength {
408 if (content_length == -1){
409 if (worker_request == null)
412 string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
416 content_length = Int32.Parse (cl);
421 // content_length will still be < 0, but we know we gotta read from the client
422 if (content_length < 0)
425 return content_length;
429 public string ContentType {
431 if (content_type == null){
432 if (worker_request != null)
433 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
435 if (content_type == null)
436 content_type = String.Empty;
443 content_type = value;
447 internal HttpCookieCollection CookiesNoValidation {
449 if (cookies_unvalidated == null) {
450 if (worker_request == null) {
451 cookies_unvalidated = new HttpCookieCollection ();
453 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
454 cookies_unvalidated = new HttpCookieCollection (cookie_hv);
458 return cookies_unvalidated;
462 public HttpCookieCollection Cookies {
464 if (cookies == null) {
465 cookies = CookiesNoValidation;
469 // For J2EE portal support we emulate cookies using the session.
470 GetSessionCookiesForPortal (cookies);
472 bool needValidation = validate_cookies;
474 needValidation |= validateRequestNewMode;
476 if (needValidation && !checked_cookies) {
477 // Setting this before calling the validator prevents
478 // possible endless recursion
479 checked_cookies = true;
480 ValidateCookieCollection (cookies);
488 public string CurrentExecutionFilePath {
490 if (current_exe_path != null)
491 return current_exe_path;
497 public string CurrentExecutionFilePathExtension {
498 get { return global::System.IO.Path.GetExtension (CurrentExecutionFilePath); }
501 public string AppRelativeCurrentExecutionFilePath {
503 return VirtualPathUtility.ToAppRelative (CurrentExecutionFilePath);
507 public string FilePath {
509 if (worker_request == null)
510 return "/"; // required for 2.0
512 if (file_path == null)
513 file_path = UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
519 internal string ClientFilePath {
521 if (client_file_path == null) {
522 if (worker_request == null)
525 return UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
528 return client_file_path;
532 if (value == null || value.Length == 0)
533 client_file_path = null;
535 client_file_path = value;
539 internal string BaseVirtualDir {
541 if (base_virtual_dir == null){
542 base_virtual_dir = FilePath;
543 if (UrlUtils.HasSessionId (base_virtual_dir))
544 base_virtual_dir = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (base_virtual_dir), base_virtual_dir);
546 int p = base_virtual_dir.LastIndexOf ('/');
550 base_virtual_dir = base_virtual_dir.Substring (0, p);
552 base_virtual_dir = "/";
554 return base_virtual_dir;
558 public HttpFileCollection Files {
561 files = new HttpFileCollection ();
562 if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
563 form = new WebROCollection ();
572 public Stream Filter {
577 if (input_filter == null)
578 input_filter = new InputFilterStream ();
584 // This checks that get_ was called before.
585 if (input_filter == null)
586 throw new HttpException ("Invalid filter");
592 // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
593 static Stream GetSubStream (Stream stream)
595 if (stream is IntPtrStream)
596 return new IntPtrStream (stream);
598 if (stream is MemoryStream) {
599 MemoryStream other = (MemoryStream) stream;
600 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
603 if (stream is TempFileStream) {
604 ((TempFileStream) stream).SavePosition ();
608 throw new NotSupportedException ("The stream is " + stream.GetType ());
611 static void EndSubStream (Stream stream)
613 if (stream is TempFileStream) {
614 ((TempFileStream) stream).RestorePosition ();
619 // Loads the data on the form for multipart/form-data
621 void LoadMultiPart ()
623 string boundary = GetParameter (ContentType, "; boundary=");
624 if (boundary == null)
627 Stream input = GetSubStream (InputStream);
628 HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
630 HttpMultipart.Element e;
631 while ((e = multi_part.ReadNextElement ()) != null) {
632 if (e.Filename == null){
633 byte [] copy = new byte [e.Length];
635 input.Position = e.Start;
636 input.Read (copy, 0, (int) e.Length);
638 form.Add (e.Name, ContentEncoding.GetString (copy));
641 // We use a substream, as in 2.x we will support large uploads streamed to disk,
643 HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
644 files.AddFile (e.Name, sub);
647 EndSubStream (input);
651 // Adds the key/value to the form, and sets the argumets to empty
653 void AddRawKeyValue (StringBuilder key, StringBuilder value)
655 string decodedKey = HttpUtility.UrlDecode (key.ToString (), ContentEncoding);
656 form.Add (decodedKey,
657 HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
664 // Loads the form data from on a application/x-www-form-urlencoded post
667 void RawLoadWwwForm ()
672 using (Stream input = GetSubStream (InputStream)) {
673 using (StreamReader s = new StreamReader (input, ContentEncoding)) {
674 StringBuilder key = new StringBuilder ();
675 StringBuilder value = new StringBuilder ();
678 while ((c = s.Read ()) != -1){
681 while ((c = s.Read ()) != -1){
683 AddRawKeyValue (key, value);
686 value.Append ((char) c);
689 AddRawKeyValue (key, value);
693 AddRawKeyValue (key, value);
695 key.Append ((char) c);
698 AddRawKeyValue (key, value);
700 EndSubStream (input);
705 bool IsContentType (string ct, bool starts_with)
708 return StrUtils.StartsWith (ContentType, ct, true);
710 return String.Compare (ContentType, ct, true, Helpers.InvariantCulture) == 0;
713 internal WebROCollection FormUnvalidated {
716 form = new WebROCollection ();
717 files = new HttpFileCollection ();
719 if (IsContentType ("multipart/form-data", true))
722 IsContentType ("application/x-www-form-urlencoded", true))
732 public NameValueCollection Form {
734 NameValueCollection form = FormUnvalidated;
736 if (validateRequestNewMode && !checked_form) {
737 if (!lazyFormValidation) {
738 // Setting this before calling the validator prevents
739 // possible endless recursion
741 ValidateNameValueCollection ("Form", form, RequestValidationSource.Form);
745 if (validate_form && !checked_form){
747 ValidateNameValueCollection ("Form", form);
754 internal NameValueCollection HeadersNoValidation {
756 if (headers_unvalidated == null) {
757 headers_unvalidated = new HeadersCollection (this);
760 return headers_unvalidated;
764 public NameValueCollection Headers {
766 if (headers == null) {
767 headers = HeadersNoValidation;
769 if (validateRequestNewMode) {
770 RequestValidator validator = RequestValidator.Current;
771 int validationFailureIndex;
773 foreach (string hkey in headers.AllKeys) {
774 string value = headers [hkey];
776 if (!validator.IsValidRequestString (HttpContext.Current, value, RequestValidationSource.Headers, hkey, out validationFailureIndex))
777 ThrowValidationException ("Headers", hkey, value);
787 public string HttpMethod {
789 if (http_method == null){
790 if (worker_request != null)
791 http_method = worker_request.GetHttpVerbName ();
799 void DoFilter (byte [] buffer)
801 if (input_filter == null || filter == null)
804 if (buffer.Length < 1024)
805 buffer = new byte [1024];
807 // Replace the input with the filtered input
808 input_filter.BaseStream = input_stream;
809 MemoryStream ms = new MemoryStream ();
811 int n = filter.Read (buffer, 0, buffer.Length);
814 ms.Write (buffer, 0, n);
816 // From now on input_stream has the filtered input
817 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
820 const int INPUT_BUFFER_SIZE = 32*1024;
822 TempFileStream GetTempStream ()
824 string tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
825 TempFileStream f = null;
827 Random rnd = new Random ();
832 path = System.IO.Path.Combine (tempdir, "tmp" + num.ToString("x") + ".req");
835 f = new TempFileStream (path);
836 } catch (SecurityException) {
837 // avoid an endless loop
845 void MakeInputStream ()
847 if (input_stream != null)
850 if (worker_request == null) {
851 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
852 DoFilter (new byte [1024]);
857 // Use an unmanaged memory block as this might be a large
860 int content_length = ContentLength;
861 int content_length_kb = content_length / 1024;
862 HttpRuntimeSection config = HttpRuntime.Section;
863 if (content_length_kb > config.MaxRequestLength)
864 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
868 buffer = worker_request.GetPreloadedEntityBody ();
869 // we check the instance field 'content_length' here, not the local var.
870 if (this.content_length <= 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
871 if (buffer == null || content_length == 0) {
872 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
874 input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
876 DoFilter (new byte [1024]);
881 total = buffer.Length;
883 if (content_length > 0 && content_length_kb >= config.RequestLengthDiskThreshold) {
884 // Writes the request to disk
885 total = Math.Min (content_length, total);
886 request_file = GetTempStream ();
887 Stream output = request_file;
889 output.Write (buffer, 0, 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 output.Write (buffer, 0, n);
901 } while (total < content_length);
904 request_file.SetReadOnly ();
905 input_stream = request_file;
906 } else if (content_length > 0) {
907 // Buffers the request in an IntPtrStream
908 total = Math.Min (content_length, total);
909 IntPtr content = Marshal.AllocHGlobal (content_length);
910 if (content == (IntPtr) 0)
911 throw HttpException.NewWithCode (
912 String.Format ("Not enough memory to allocate {0} bytes.", content_length),
913 WebEventCodes.WebErrorOtherError);
916 Marshal.Copy (buffer, 0, content, total);
918 if (total < content_length) {
919 buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
922 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
923 n = worker_request.ReadEntityBody (buffer, min);
926 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
928 } while (total < content_length);
931 input_stream = new IntPtrStream (content, total);
933 // Buffers the request in a MemoryStream or writes to disk if threshold exceeded
934 MemoryStream ms = new MemoryStream ();
937 ms.Write (buffer, 0, total);
939 buffer = new byte [INPUT_BUFFER_SIZE];
940 long maxlength = config.MaxRequestLength * 1024L;
941 long disk_th = config.RequestLengthDiskThreshold * 1024L;
944 n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
948 if (total < 0 || total > maxlength)
949 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
951 if (ms != null && total > disk_th) {
952 // Swith to on-disk file.
953 request_file = GetTempStream ();
954 ms.WriteTo (request_file);
956 output = request_file;
958 output.Write (buffer, 0, n);
962 input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
964 request_file.SetReadOnly ();
965 input_stream = request_file;
970 if (total < content_length)
971 throw HttpException.NewWithCode (411, "The request body is incomplete.", WebEventCodes.WebErrorOtherError);
974 internal void ReleaseResources ()
977 if (input_stream != null){
978 stream = input_stream;
985 if (request_file != null) {
986 stream = request_file;
994 public RequestContext RequestContext {
996 if (requestContext == null)
997 requestContext = new RequestContext (new HttpContextWrapper (this.context ?? HttpContext.Current), new RouteData ());
999 return requestContext;
1002 internal set { requestContext = value; }
1005 public ChannelBinding HttpChannelBinding {
1007 throw new PlatformNotSupportedException ("This property is not supported.");
1011 public Stream GetBufferlessInputStream ()
1013 if (bufferlessInputStream == null) {
1014 if (input_stream != null)
1015 throw new HttpException ("Input stream has already been created");
1017 // we don't need to hook up the filter here, because the raw stream should be returned
1018 bufferlessInputStream = new BufferlessInputStream (this);
1021 return bufferlessInputStream;
1025 // Stream that returns the data as it is read, without buffering
1027 class BufferlessInputStream : Stream {
1028 HttpRequest request;
1030 // cached, the request content-length
1033 // buffer that holds preloaded data
1034 byte [] preloadedBuffer;
1036 // indicates if we already served the whole preloaded buffer
1037 bool preloaded_served;
1039 // indicates if we already checked the request content-length against httpRuntime limit
1040 bool checked_maxRequestLength;
1042 // our stream position
1046 // @request: the containing request that created us, used to find out content length
1047 public BufferlessInputStream (HttpRequest request)
1049 this.request = request;
1050 content_length = request.ContentLength;
1053 public override bool CanRead {
1054 get { return true; }
1057 public override bool CanSeek {
1058 get { return false; }
1061 public override bool CanWrite {
1062 get { return false; }
1065 public override long Length {
1067 return content_length;
1071 public override long Position {
1076 throw new NotSupportedException ("This is a readonly stream");
1080 public override void Flush ()
1084 public override int Read (byte [] buffer, int offset, int count)
1087 throw new ArgumentNullException ("buffer");
1089 if (offset < 0 || count < 0)
1090 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
1092 if (buffer.Length - offset < count )
1093 throw new ArgumentException ("offset+count",
1094 "The size of the buffer is less than offset + count.");
1096 if (count == 0 || request.worker_request == null)
1099 if (!checked_maxRequestLength) {
1100 int content_length_kb = content_length / 1024;
1101 HttpRuntimeSection config = HttpRuntime.Section;
1102 if (content_length_kb > config.MaxRequestLength)
1103 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
1105 checked_maxRequestLength = true;
1108 // Serve the bytes we might have preloaded already.
1109 if (!preloaded_served) {
1110 if (preloadedBuffer == null)
1111 preloadedBuffer = request.worker_request.GetPreloadedEntityBody ();
1113 if (preloadedBuffer != null) {
1114 long bytes_left = preloadedBuffer.Length-position;
1115 int n = (int) Math.Min (count, bytes_left);
1116 Array.Copy (preloadedBuffer, position, buffer, offset, n);
1119 if (n == bytes_left)
1120 preloaded_served = true;
1125 preloaded_served = true;
1128 // serve bytes from worker request if available
1129 if (position < content_length) {
1130 long bytes_left = content_length-position;
1133 if (bytes_left < count)
1134 n = (int) bytes_left;
1136 int bytes_read = request.worker_request.ReadEntityBody (buffer, offset, n);
1137 position += bytes_read;
1144 public override long Seek (long offset, SeekOrigin origin)
1146 throw new NotSupportedException ("Can not seek on the HttpRequest.BufferlessInputStream");
1149 public override void SetLength (long value)
1151 throw new NotSupportedException ("Can not set length on the HttpRequest.BufferlessInputStream");
1154 public override void Write (byte [] buffer, int offset, int count)
1156 throw new NotSupportedException ("Can not write on the HttpRequest.BufferlessInputStream");
1160 // TODO: explicitly support the async methods if there is a convenient way of doing it
1164 public Stream InputStream {
1166 if (input_stream == null)
1169 return input_stream;
1173 public bool IsAuthenticated {
1175 if (context.User == null || context.User.Identity == null)
1177 return context.User.Identity.IsAuthenticated;
1181 public bool IsSecureConnection {
1183 if (worker_request == null)
1185 return worker_request.IsSecure ();
1189 public string this [string key] {
1190 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1192 // "The QueryString, Form, Cookies, or ServerVariables collection member
1193 // specified in the key parameter."
1194 string val = QueryString [key];
1198 HttpCookie cookie = Cookies [key];
1203 val = ServerVariables [key];
1209 public NameValueCollection Params {
1210 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1212 if (all_params == null)
1213 all_params = new HttpParamsCollection (QueryString, Form, ServerVariables, Cookies);
1219 internal string PathNoValidation {
1221 if (original_path == null) {
1222 if (url_components != null)
1223 // use only if it's already been instantiated, so that we can't go into endless
1224 // recursion in some scenarios
1225 original_path = UrlComponents.Path;
1227 original_path = ApplyUrlMapping (worker_request.GetUriPath ());
1230 return original_path;
1234 public string Path {
1236 if (unescaped_path == null) {
1237 unescaped_path = PathNoValidation;
1239 if (validateRequestNewMode) {
1240 RequestValidator validator = RequestValidator.Current;
1241 int validationFailureIndex;
1243 if (!validator.IsValidRequestString (HttpContext.Current, unescaped_path, RequestValidationSource.Path, null, out validationFailureIndex))
1244 ThrowValidationException ("Path", "Path", unescaped_path);
1249 return unescaped_path;
1253 internal string PathInfoNoValidation {
1255 if (path_info_unvalidated == null) {
1256 if (worker_request == null)
1257 return String.Empty;
1259 path_info_unvalidated = worker_request.GetPathInfo () ?? String.Empty;
1262 return path_info_unvalidated;
1266 public string PathInfo {
1268 if (path_info == null) {
1269 path_info = PathInfoNoValidation;
1271 if (validateRequestNewMode) {
1272 RequestValidator validator = RequestValidator.Current;
1273 int validationFailureIndex;
1275 if (!validator.IsValidRequestString (HttpContext.Current, path_info, RequestValidationSource.PathInfo, null, out validationFailureIndex))
1276 ThrowValidationException ("PathInfo", "PathInfo", path_info);
1285 public string PhysicalApplicationPath {
1287 if (worker_request == null)
1288 throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
1290 string path = HttpRuntime.AppDomainAppPath;
1291 if (SecurityManager.SecurityEnabled) {
1292 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
1298 public string PhysicalPath {
1300 if (worker_request == null)
1301 return String.Empty; // don't check security with an empty string!
1303 if (physical_path == null) {
1304 // Don't call HttpRequest.MapPath here, as that one *trims* the input
1305 physical_path = worker_request.MapPath (FilePath);
1308 if (SecurityManager.SecurityEnabled) {
1309 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
1311 return physical_path;
1315 internal string RootVirtualDir {
1317 if (root_virtual_dir == null){
1318 string fp = FilePath;
1319 int p = fp.LastIndexOf ('/');
1322 root_virtual_dir = "/";
1324 root_virtual_dir = fp.Substring (0, p);
1326 return root_virtual_dir;
1330 internal WebROCollection QueryStringUnvalidated {
1332 if (query_string_nvc == null) {
1333 query_string_nvc = new WebROCollection ();
1334 string q = UrlComponents.Query;
1339 HttpUtility.ParseQueryString (q, ContentEncoding, query_string_nvc);
1342 query_string_nvc.Protect();
1345 return query_string_nvc;
1349 public NameValueCollection QueryString {
1351 NameValueCollection query_string_nvc = QueryStringUnvalidated;
1353 if (validateRequestNewMode && !checked_query_string) {
1354 if (!lazyQueryStringValidation) {
1355 // Setting this before calling the validator prevents
1356 // possible endless recursion
1357 checked_query_string = true;
1358 ValidateNameValueCollection ("QueryString", query_string_nvc, RequestValidationSource.QueryString);
1362 if (validate_query_string && !checked_query_string) {
1363 // Setting this before calling the validator prevents
1364 // possible endless recursion
1365 checked_query_string = true;
1366 ValidateNameValueCollection ("QueryString", query_string_nvc);
1369 return query_string_nvc;
1373 internal string RawUrlUnvalidated {
1375 if (raw_url_unvalidated == null) {
1376 if (worker_request != null)
1377 raw_url_unvalidated = worker_request.GetRawUrl ();
1379 raw_url_unvalidated = UrlComponents.Path + UrlComponents.Query;
1381 if (raw_url_unvalidated == null)
1382 raw_url_unvalidated = String.Empty;
1385 return raw_url_unvalidated;
1389 public string RawUrl {
1391 if (raw_url == null) {
1392 raw_url = RawUrlUnvalidated;
1394 if (validateRequestNewMode) {
1395 RequestValidator validator = RequestValidator.Current;
1396 int validationFailureIndex;
1398 if (!validator.IsValidRequestString (HttpContext.Current, raw_url, RequestValidationSource.RawUrl, null, out validationFailureIndex))
1399 ThrowValidationException ("RawUrl", "RawUrl", raw_url);
1411 public string RequestType {
1413 if (request_type == null){
1414 if (worker_request != null) {
1415 request_type = worker_request.GetHttpVerbName ();
1416 http_method = request_type;
1418 request_type = "GET";
1421 return request_type;
1425 request_type = value;
1429 public NameValueCollection ServerVariables {
1430 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1432 if (server_variables == null)
1433 server_variables = new ServerVariablesCollection (this);
1435 return server_variables;
1439 public int TotalBytes {
1441 Stream ins = InputStream;
1442 return (int) ins.Length;
1447 public UnvalidatedRequestValues Unvalidated {
1449 var vals = new UnvalidatedRequestValues ();
1451 vals.Cookies = CookiesNoValidation;
1453 vals.Form = FormUnvalidated;
1454 vals.Headers = HeadersNoValidation;
1455 vals.Path = PathNoValidation;
1456 vals.PathInfo = PathInfoNoValidation;
1457 vals.QueryString = QueryStringUnvalidated;
1458 vals.RawUrl = RawUrlUnvalidated;
1468 if (cached_url == null) {
1469 if (orig_url == null)
1470 cached_url = UrlComponents.Uri;
1472 cached_url = new Uri (orig_url);
1479 public Uri UrlReferrer {
1481 if (worker_request == null)
1484 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
1491 } catch (UriFormatException) {}
1496 public string UserAgent {
1498 if (worker_request == null)
1501 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
1505 public string UserHostAddress {
1507 if (worker_request == null)
1510 return worker_request.GetRemoteAddress ();
1514 public string UserHostName {
1516 if (worker_request == null)
1519 return worker_request.GetRemoteName ();
1523 public string [] UserLanguages {
1525 if (worker_request == null)
1528 if (user_languages == null)
1529 user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
1531 return user_languages;
1535 public byte [] BinaryRead (int count)
1538 throw new ArgumentException ("count is < 0");
1540 Stream s = InputStream;
1541 byte [] ret = new byte [count];
1542 if (s.Read (ret, 0, count) != count)
1543 throw new ArgumentException (
1544 String.Format ("count {0} exceeds length of available input {1}",
1545 count, s.Length - s.Position));
1549 public int [] MapImageCoordinates (string imageFieldName)
1551 string method = HttpMethod;
1552 NameValueCollection coll = null;
1553 if (method == "HEAD" || method == "GET")
1555 else if (method == "POST")
1561 string x = coll [imageFieldName + ".x"];
1562 if (x == null || x == "")
1565 string y = coll [imageFieldName + ".y"];
1566 if (y == null || y == "")
1569 int [] result = new int [2];
1571 result [0] = Int32.Parse (x);
1572 result [1] = Int32.Parse (y);
1580 public string MapPath (string virtualPath)
1582 if (worker_request == null)
1585 return MapPath (virtualPath, BaseVirtualDir, true);
1588 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1590 if (worker_request == null)
1591 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
1593 if (virtualPath == null)
1596 virtualPath = virtualPath.Trim ();
1597 if (virtualPath.Length == 0)
1601 if (!VirtualPathUtility.IsValidVirtualPath (virtualPath))
1602 throw HttpException.NewWithCode (String.Format ("'{0}' is not a valid virtual path.", virtualPath), WebEventCodes.RuntimeErrorRequestAbort);
1604 string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
1605 if (!VirtualPathUtility.IsRooted (virtualPath)) {
1606 if (StrUtils.IsNullOrEmpty (baseVirtualDir))
1607 baseVirtualDir = appVirtualPath;
1608 virtualPath = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (baseVirtualDir), virtualPath);
1609 if (!VirtualPathUtility.IsAbsolute (virtualPath))
1610 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath, false);
1611 } else if (!VirtualPathUtility.IsAbsolute (virtualPath))
1612 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath, false);
1614 bool isAppVirtualPath = String.Compare (virtualPath, appVirtualPath, RuntimeHelpers.StringComparison) == 0;
1615 appVirtualPath = VirtualPathUtility.AppendTrailingSlash (appVirtualPath);
1616 if (!allowCrossAppMapping){
1617 if (!StrUtils.StartsWith (virtualPath, appVirtualPath, true))
1618 throw new ArgumentException ("MapPath: Mapping across applications not allowed");
1619 if (appVirtualPath.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1620 throw HttpException.NewWithCode ("MapPath: Mapping across applications not allowed", WebEventCodes.RuntimeErrorRequestAbort);
1623 if (!isAppVirtualPath && !virtualPath.StartsWith (appVirtualPath, RuntimeHelpers.StringComparison))
1624 throw new InvalidOperationException (String.Format ("Failed to map path '{0}'", virtualPath));
1625 string path = worker_request.MapPath (virtualPath);
1626 if (virtualPath [virtualPath.Length - 1] != '/' && path [path.Length - 1] == System.IO.Path.DirectorySeparatorChar)
1627 path = path.TrimEnd (System.IO.Path.DirectorySeparatorChar);
1631 public void SaveAs (string filename, bool includeHeaders)
1633 Stream output = new FileStream (filename, FileMode.Create);
1634 if (includeHeaders) {
1635 StringBuilder sb = new StringBuilder ();
1636 string version = String.Empty;
1638 if (worker_request != null) {
1639 version = worker_request.GetHttpVersion ();
1640 path = UrlComponents.Path;
1642 string qs = UrlComponents.Query;
1644 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1645 NameValueCollection coll = Headers;
1646 foreach (string k in coll.AllKeys) {
1649 sb.Append (coll [k]);
1654 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1655 output.Write (bytes, 0, bytes.Length);
1658 // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1659 // to keep InputStream in its state.
1660 Stream input = GetSubStream (InputStream);
1662 long len = input.Length;
1663 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1664 byte [] data = new byte [buf_size];
1666 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1667 output.Write (data, 0, count);
1673 EndSubStream (input);
1677 public void ValidateInput ()
1679 validate_cookies = true;
1680 validate_query_string = true;
1681 validate_form = true;
1683 inputValidationEnabled = true;
1687 internal void Validate ()
1689 var cfg = HttpRuntime.Section;
1690 string query = UrlComponents.Query;
1692 if (query != null && query.Length > cfg.MaxQueryStringLength)
1693 throw new HttpException (400, "The length of the query string for this request exceeds the configured maxQueryStringLength value.");
1695 string path = PathNoValidation;
1697 if (path.Length > cfg.MaxUrlLength)
1698 throw new HttpException (400, "The length of the URL for this request exceeds the configured maxUrlLength value.");
1700 char[] invalidChars = RequestPathInvalidCharacters;
1701 if (invalidChars != null) {
1702 int idx = path.IndexOfAny (invalidChars);
1704 throw HttpException.NewWithCode (
1705 String.Format ("A potentially dangerous Request.Path value was detected from the client ({0}).", path [idx]),
1706 WebEventCodes.RuntimeErrorValidationFailure
1711 if (validateRequestNewMode)
1715 #region internal routines
1716 internal string ClientTarget {
1718 return client_target;
1722 client_target = value;
1726 public bool IsLocal {
1728 string address = worker_request.GetRemoteAddress ();
1730 if (StrUtils.IsNullOrEmpty (address))
1733 if (address == "127.0.0.1")
1736 System.Net.IPAddress remoteAddr = System.Net.IPAddress.Parse (address);
1737 if (System.Net.IPAddress.IsLoopback (remoteAddr))
1740 for (int i = 0; i < host_addresses.Length; i++)
1741 if (remoteAddr.Equals (host_addresses [i]))
1748 internal void SetFilePath (string path)
1751 physical_path = null;
1752 original_path = null;
1755 internal void SetCurrentExePath (string path)
1758 current_exe_path = path;
1759 UrlComponents.Path = path + PathInfo;
1760 // recreated on demand
1761 root_virtual_dir = null;
1762 base_virtual_dir = null;
1763 physical_path = null;
1764 unescaped_path = null;
1765 original_path = null;
1768 internal void SetPathInfo (string pi)
1772 original_path = null;
1774 string path = UrlComponents.Path;
1775 UrlComponents.Path = path + PathInfo;
1778 internal void SetFormCollection (WebROCollection coll, bool lazyValidation)
1783 lazyFormValidation = lazyValidation;
1786 internal void SetQueryStringCollection (WebROCollection coll, bool lazyValidation)
1790 query_string_nvc = coll;
1791 lazyQueryStringValidation = lazyValidation;
1794 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1795 internal void SetHeader (string name, string value)
1797 WebROCollection h = (WebROCollection) Headers;
1803 // Notice: there is nothing raw about this querystring.
1804 internal string QueryStringRaw {
1806 UriBuilder urlComponents = UrlComponents;
1808 if (urlComponents == null) {
1809 string ret = worker_request.GetQueryString ();
1811 if (ret == null || ret.Length == 0)
1812 return String.Empty;
1820 return UrlComponents.Query;
1824 UrlComponents.Query = value;
1826 query_string_nvc = null;
1830 // Internal, dont know what it does, so flagged as public so we can see it.
1831 internal void SetForm (WebROCollection coll)
1836 internal HttpWorkerRequest WorkerRequest {
1838 return worker_request;
1842 internal HttpContext Context {
1843 get { return context; }
1844 set { context = value; }
1847 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1852 foreach (string key in coll.Keys) {
1853 string val = coll [key];
1854 if (val != null && val.Length > 0 && IsInvalidString (val))
1855 ThrowValidationException (name, key, val);
1859 static void ValidateNameValueCollection (string name, NameValueCollection coll, RequestValidationSource source)
1864 RequestValidator validator = RequestValidator.Current;
1865 int validationFailureIndex;
1866 HttpContext context = HttpContext.Current;
1868 foreach (string key in coll.Keys) {
1869 string val = coll [key];
1870 if (val != null && val.Length > 0 && !validator.IsValidRequestString (context, val, source, key, out validationFailureIndex))
1871 ThrowValidationException (name, key, val);
1875 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1876 public void InsertEntityBody ()
1878 throw new PlatformNotSupportedException ("This method is not supported.");
1881 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1882 public void InsertEntityBody (byte[] buffer, int offset, int count)
1884 throw new PlatformNotSupportedException ("This method is not supported.");
1887 static void ValidateCookieCollection (HttpCookieCollection cookies)
1889 if (cookies == null)
1892 int size = cookies.Count;
1895 RequestValidator validator = RequestValidator.Current;
1896 int validationFailureIndex;
1897 HttpContext context = HttpContext.Current;
1901 for (int i = 0 ; i < size ; i++) {
1902 cookie = cookies[i];
1906 string value = cookie.Value;
1907 string name = cookie.Name;
1909 if (!String.IsNullOrEmpty (value)) {
1911 if (validateRequestNewMode)
1912 invalid = !validator.IsValidRequestString (context, value, RequestValidationSource.Cookies, name, out validationFailureIndex);
1915 invalid = IsInvalidString (value);
1918 ThrowValidationException ("Cookies", name, value);
1923 static void ThrowValidationException (string name, string key, string value)
1925 string v = "\"" + value + "\"";
1927 v = v.Substring (0, 16) + "...\"";
1929 string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1930 "detected from the client ({1}={2}).", name, key, v);
1932 throw new HttpRequestValidationException (msg);
1935 internal static void ValidateString (string key, string value, RequestValidationSource source)
1937 if (String.IsNullOrEmpty (value))
1939 #pragma warning disable 219
1941 #pragma warning restore 219
1942 if (IsInvalidString (value, out ignore))
1943 ThrowValidationException (source.ToString (), key, value);
1946 internal static bool IsInvalidString (string val)
1948 #pragma warning disable 219
1949 int validationFailureIndex;
1950 #pragma warning restore 219
1951 return IsInvalidString (val, out validationFailureIndex);
1954 internal static bool IsInvalidString (string val, out int validationFailureIndex)
1956 validationFailureIndex = 0;
1958 int len = val.Length;
1962 char current = val [0];
1963 for (int idx = 1; idx < len; idx++) {
1964 char next = val [idx];
1965 // See http://secunia.com/advisories/14325
1966 if (current == '<' || current == '\xff1c') {
1967 if (next == '!' || next < ' '
1968 || (next >= 'a' && next <= 'z')
1969 || (next >= 'A' && next <= 'Z')) {
1970 validationFailureIndex = idx - 1;
1973 } else if (current == '&' && next == '#') {
1974 validationFailureIndex = idx - 1;
1984 static System.Net.IPAddress [] GetLocalHostAddresses ()
1987 string hostName = System.Net.Dns.GetHostName ();
1988 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostAddresses (hostName);
1991 return new System.Net.IPAddress[0];
1997 #region Helper classes
2000 // Stream-based multipart handling.
2002 // In this incarnation deals with an HttpInputStream as we are now using
2003 // IntPtr-based streams instead of byte []. In the future, we will also
2004 // send uploads above a certain threshold into the disk (to implement
2005 // limit-less HttpInputFiles).
2008 class HttpMultipart {
2010 public class Element {
2011 public string ContentType;
2013 public string Filename;
2017 public override string ToString ()
2019 return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
2020 Start.ToString () + ", Length " + Length.ToString ();
2026 byte [] boundary_bytes;
2032 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
2035 // In the case of multipart entities, in which one or more different
2036 // sets of data are combined in a single body, a "multipart" media type
2037 // field must appear in the entity's header. The body must then contain
2038 // one or more body parts, each preceded by a boundary delimiter line,
2039 // and the last one followed by a closing boundary delimiter line.
2040 // After its boundary delimiter line, each body part then consists of a
2041 // header area, a blank line, and a body area. Thus a body part is
2042 // similar to an RFC 822 message in syntax, but different in meaning.
2044 public HttpMultipart (Stream data, string b, Encoding encoding)
2048 boundary_bytes = encoding.GetBytes (b);
2049 buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
2050 this.encoding = encoding;
2051 sb = new StringBuilder ();
2056 // CRLF or LF are ok as line endings.
2057 bool got_cr = false;
2061 b = data.ReadByte ();
2070 sb.Append ((char) b);
2076 return sb.ToString ();
2080 static string GetContentDispositionAttribute (string l, string name)
2082 int idx = l.IndexOf (name + "=\"");
2085 int begin = idx + name.Length + "=\"".Length;
2086 int end = l.IndexOf ('"', begin);
2091 return l.Substring (begin, end - begin);
2094 string GetContentDispositionAttributeWithEncoding (string l, string name)
2096 int idx = l.IndexOf (name + "=\"");
2099 int begin = idx + name.Length + "=\"".Length;
2100 int end = l.IndexOf ('"', begin);
2106 string temp = l.Substring (begin, end - begin);
2107 byte [] source = new byte [temp.Length];
2108 for (int i = temp.Length - 1; i >= 0; i--)
2109 source [i] = (byte) temp [i];
2111 return encoding.GetString (source);
2114 bool ReadBoundary ()
2117 string line = ReadLine ();
2120 if (line [0] != '-' || line [1] != '-')
2123 if (!StrUtils.EndsWith (line, boundary, false))
2131 string ReadHeaders ()
2133 string s = ReadLine ();
2140 bool CompareBytes (byte [] orig, byte [] other)
2142 for (int i = orig.Length - 1; i >= 0; i--)
2143 if (orig [i] != other [i])
2149 long MoveToNextBoundary ()
2152 bool got_cr = false;
2155 int c = data.ReadByte ();
2160 if (state == 0 && c == LF) {
2161 retval = data.Position - 1;
2165 c = data.ReadByte ();
2166 } else if (state == 0) {
2168 c = data.ReadByte ();
2169 } else if (state == 1 && c == '-') {
2170 c = data.ReadByte ();
2177 continue; // no ReadByte() here
2180 int nread = data.Read (buffer, 0, buffer.Length);
2181 int bl = buffer.Length;
2185 if (!CompareBytes (boundary_bytes, buffer)) {
2187 data.Position = retval + 2;
2192 c = data.ReadByte ();
2196 if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
2198 } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
2200 data.Position = retval + 2;
2205 c = data.ReadByte ();
2208 data.Position = retval + 2;
2214 state = 0; // no ReadByte() here
2221 public Element ReadNextElement ()
2223 if (at_eof || ReadBoundary ())
2226 Element elem = new Element ();
2228 while ((header = ReadHeaders ()) != null) {
2229 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
2230 elem.Name = GetContentDispositionAttribute (header, "name");
2231 elem.Filename = StripPath (GetContentDispositionAttributeWithEncoding (header, "filename"));
2232 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
2233 elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
2237 long start = data.Position;
2239 long pos = MoveToNextBoundary ();
2243 elem.Length = pos - start;
2247 static string StripPath (string path)
2249 if (path == null || path.Length == 0)
2252 if (path.IndexOf (":\\") != 1 && !path.StartsWith ("\\\\"))
2254 return path.Substring (path.LastIndexOf ('\\') + 1);