[System] HttpListenerRequest: ignore bad cookies and keep request alive (#5657)
[mono.git] / mcs / class / System / System.Net / HttpListenerRequest.cs
index 37be80f1b94cc425d25146785eb129a14f144912..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;
 using System.IO;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
-#if NET_4_0
 using System.Security.Authentication.ExtendedProtection;
-#endif
-#if NET_4_5
 using System.Threading.Tasks;
-#endif
+using System.Net;
 
 namespace System.Net {
        public sealed class HttpListenerRequest
        {
-#if NET_4_0
                class Context : TransportContext
                {
                        public override ChannelBinding GetChannelBinding (ChannelBindingKind kind)
@@ -61,7 +48,6 @@ namespace System.Net {
                                throw new NotImplementedException ();
                        }
                }
-#endif
 
                string [] accept_types;
                Encoding content_encoding;
@@ -148,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;
@@ -168,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;
@@ -188,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);
@@ -291,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) {
@@ -329,6 +372,9 @@ namespace System.Net {
                                                return false;
                                        if (InputStream.EndRead (ares) <= 0)
                                                return true;
+                               } catch (ObjectDisposedException) {
+                                       input_stream = null;
+                                       return true;
                                } catch {
                                        return false;
                                }
@@ -360,7 +406,7 @@ namespace System.Net {
                }
 
                public long ContentLength64 {
-                       get { return content_length; }
+                       get { return is_chunked ? -1 : content_length; }
                }
 
                public string ContentType {
@@ -407,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 {
@@ -509,7 +555,6 @@ namespace System.Net {
                        return context.Connection.ClientCertificate;
                }
 
-#if NET_4_0
                [MonoTODO]
                public string ServiceName {
                        get {
@@ -522,20 +567,11 @@ namespace System.Net {
                                return new Context ();
                        }
                }
-#endif
                
-#if NET_4_5
+               [MonoTODO]
                public bool IsWebSocketRequest {
                        get {
-                               string connection = headers.Get ("Connection");
-                               if (connection == null || ! connection.Equals ("upgrade", StringComparison.OrdinalIgnoreCase)) {
-                                       return false;
-                               }
-                               string upgrade = headers.Get ("Upgrade");
-                               if (upgrade == null || ! upgrade.Equals ("websocket", StringComparison.OrdinalIgnoreCase)) {
-                                       return false;
-                               }
-                               return true;
+                               return false;
                        }
                }
 
@@ -543,8 +579,6 @@ namespace System.Net {
                {
                        return Task<X509Certificate2>.Factory.FromAsync (BeginGetClientCertificate, EndGetClientCertificate, null);
                }
-#endif
        }
 }
-#endif