[System] HttpListenerRequest: ignore bad cookies and keep request alive (#5657)
[mono.git] / mcs / class / System / System.Net / HttpListenerRequest.cs
index e1c032c6da0c44650c08dbf9b93849a61a3b3a70..e351807aa3befe092968fe95930f0c1820320e00 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-#if SECURITY_DEP
-
-#if MONOTOUCH || MONODROID
-using Mono.Security.Protocol.Tls;
-#else
-extern alias MonoSecurity;
-using MonoSecurity::Mono.Security.Protocol.Tls;
-#endif
-
 using System.Collections;
 using System.Collections.Specialized;
 using System.Globalization;
@@ -45,6 +36,7 @@ using System.Security.Cryptography.X509Certificates;
 using System.Text;
 using System.Security.Authentication.ExtendedProtection;
 using System.Threading.Tasks;
+using System.Net;
 
 namespace System.Net {
        public sealed class HttpListenerRequest
@@ -142,16 +134,64 @@ namespace System.Net {
                        foreach (string kv in components) {
                                int pos = kv.IndexOf ('=');
                                if (pos == -1) {
-                                       query_string.Add (null, HttpUtility.UrlDecode (kv));
+                                       query_string.Add (null, WebUtility.UrlDecode (kv));
                                } else {
-                                       string key = HttpUtility.UrlDecode (kv.Substring (0, pos));
-                                       string val = HttpUtility.UrlDecode (kv.Substring (pos + 1));
+                                       string key = WebUtility.UrlDecode (kv.Substring (0, pos));
+                                       string val = WebUtility.UrlDecode (kv.Substring (pos + 1));
                                        
                                        query_string.Add (key, val);
                                }
                        }
                }
 
+               static bool MaybeUri (string s)
+               {
+                       int p = s.IndexOf (':');
+                       if (p == -1)
+                               return false;
+
+                       if (p >= 10)
+                               return false;
+
+                       return IsPredefinedScheme (s.Substring (0, p));
+               }
+
+               //
+               // Using a simple block of if's is twice as slow as the compiler generated
+               // switch statement.   But using this tuned code is faster than the
+               // compiler generated code, with a million loops on x86-64:
+               //
+               // With "http": .10 vs .51 (first check)
+               // with "https": .16 vs .51 (second check)
+               // with "foo": .22 vs .31 (never found)
+               // with "mailto": .12 vs .51  (last check)
+               //
+               //
+               static bool IsPredefinedScheme (string scheme)
+               {
+                       if (scheme == null || scheme.Length < 3)
+                               return false;
+                       
+                       char c = scheme [0];
+                       if (c == 'h')
+                               return (scheme == "http" || scheme == "https");
+                       if (c == 'f')
+                               return (scheme == "file" || scheme == "ftp");
+                               
+                       if (c == 'n'){
+                               c = scheme [1];
+                               if (c == 'e')
+                                       return (scheme == "news" || scheme == "net.pipe" || scheme == "net.tcp");
+                               if (scheme == "nntp")
+                                       return true;
+                               return false;
+                       }
+                       if ((c == 'g' && scheme == "gopher") || (c == 'm' && scheme == "mailto"))
+                               return true;
+
+                       return false;
+               }
+
                internal void FinishInitialization ()
                {
                        string host = UserHostName;
@@ -162,7 +202,7 @@ namespace System.Net {
 
                        string path;
                        Uri raw_uri = null;
-                       if (Uri.MaybeUri (raw_url) && Uri.TryCreate (raw_url, UriKind.Absolute, out raw_uri))
+                       if (MaybeUri (raw_url.ToLowerInvariant ()) && Uri.TryCreate (raw_url, UriKind.Absolute, out raw_uri))
                                path = raw_uri.PathAndQuery;
                        else
                                path = raw_url;
@@ -182,12 +222,17 @@ namespace System.Net {
                                                                host, LocalEndPoint.Port);
 
                        if (!Uri.TryCreate (base_uri + path, UriKind.Absolute, out url)){
-                               context.ErrorMessage = "Invalid url: " + base_uri + path;
+                               context.ErrorMessage = WebUtility.HtmlEncode ("Invalid url: " + base_uri + path);
                                return;
                        }
 
                        CreateQueryString (url.Query);
 
+                       // Use reference source HttpListenerRequestUriBuilder to process url.
+                       // Fixes #29927
+                       url = HttpListenerRequestUriBuilder.GetRequestUri (raw_url, url.Scheme,
+                                                               url.Authority, url.LocalPath, url.Query);
+
                        if (version >= HttpVersion.Version11) {
                                string t_encoding = Headers ["Transfer-Encoding"];
                                is_chunked = (t_encoding != null && String.Compare (t_encoding, "chunked", StringComparison.OrdinalIgnoreCase) == 0);
@@ -285,16 +330,20 @@ namespace System.Net {
                                                        if (current != null) {
                                                                cookies.Add (current);
                                                        }
-                                                       current = new Cookie ();
-                                                       int idx = str.IndexOf ('=');
-                                                       if (idx > 0) {
-                                                               current.Name = str.Substring (0, idx).Trim ();
-                                                               current.Value =  str.Substring (idx + 1).Trim ();
-                                                       } else {
-                                                               current.Name = str.Trim ();
-                                                               current.Value = String.Empty;
+                                                       try {
+                                                               current = new Cookie ();
+                                                               int idx = str.IndexOf ('=');
+                                                               if (idx > 0) {
+                                                                       current.Name = str.Substring (0, idx).Trim ();
+                                                                       current.Value =  str.Substring (idx + 1).Trim ();
+                                                               } else {
+                                                                       current.Name = str.Trim ();
+                                                                       current.Value = String.Empty;
+                                                               }
+                                                               current.Version = version;
+                                                       } catch (CookieException) {
+                                                               current = null;
                                                        }
-                                                       current.Version = version;
                                                }
                                        }
                                        if (current != null) {
@@ -323,7 +372,7 @@ namespace System.Net {
                                                return false;
                                        if (InputStream.EndRead (ares) <= 0)
                                                return true;
-                               } catch (ObjectDisposedException e) {
+                               } catch (ObjectDisposedException) {
                                        input_stream = null;
                                        return true;
                                } catch {
@@ -357,7 +406,7 @@ namespace System.Net {
                }
 
                public long ContentLength64 {
-                       get { return content_length; }
+                       get { return is_chunked ? -1 : content_length; }
                }
 
                public string ContentType {
@@ -404,7 +453,7 @@ namespace System.Net {
                }
 
                public bool IsLocal {
-                       get { return IPAddress.IsLoopback (RemoteEndPoint.Address); }
+                       get { return LocalEndPoint.Address.Equals (RemoteEndPoint.Address); }
                }
 
                public bool IsSecureConnection {
@@ -532,5 +581,4 @@ namespace System.Net {
                }
        }
 }
-#endif