Remove duplicated files from system.dll (saves about 100kb)
[mono.git] / mcs / class / System / System.Net / HttpConnection.cs
index c4921284bba37860f673e9154b4775d19b1fe592..a34f19024c80761d1d16660ed488d4b62bd8ee33 100644 (file)
@@ -2,9 +2,10 @@
 // System.Net.HttpConnection
 //
 // Author:
-//     Gonzalo Paniagua Javier (gonzalo@novell.com)
+//     Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
 //
-// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2012 Xamarin, Inc. (http://xamarin.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -28,6 +29,8 @@
 
 #if SECURITY_DEP
 
+extern alias MonoSecurity;
+
 using System.IO;
 using System.Net.Sockets;
 using System.Reflection;
@@ -35,11 +38,12 @@ using System.Text;
 using System.Threading;
 using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
-using Mono.Security.Protocol.Tls;
+using MonoSecurity::Mono.Security.Protocol.Tls;
 
 namespace System.Net {
        sealed class HttpConnection
        {
+               static AsyncCallback onread_cb = new AsyncCallback (OnRead);
                const int BufferSize = 8192;
                Socket sock;
                Stream stream;
@@ -58,6 +62,10 @@ namespace System.Net {
                AsymmetricAlgorithm key;
                int s_timeout = 90000; // 90k ms for first request, 15k ms from then on
                Timer timer;
+               IPEndPoint local_ep;
+               HttpListener last_listener;
+               int [] client_cert_errors;
+               X509Certificate2 client_cert;
 
                public HttpConnection (Socket sock, EndPointListener epl, bool secure, X509Certificate2 cert, AsymmetricAlgorithm key)
                {
@@ -68,14 +76,35 @@ namespace System.Net {
                        if (secure == false) {
                                stream = new NetworkStream (sock, false);
                        } else {
-                               SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, false);
+                               SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, true, false);
                                ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
+                               ssl_stream.ClientCertValidationDelegate += OnClientCertificateValidation;
                                stream = ssl_stream;
                        }
                        timer = new Timer (OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
                        Init ();
                }
 
+               internal int [] ClientCertificateErrors {
+                       get { return client_cert_errors; }
+               }
+
+               internal X509Certificate2 ClientCertificate {
+                       get { return client_cert; }
+               }
+
+               bool OnClientCertificateValidation (X509Certificate certificate, int[] errors)
+               {
+                       if (certificate == null)
+                               return true;
+                       X509Certificate2 cert = certificate as X509Certificate2;
+                       if (cert == null)
+                               cert = new X509Certificate2 (certificate.GetRawCertData ());
+                       client_cert = cert;
+                       client_cert_errors = errors;
+                       return true;
+               }
+
                AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost)
                {
                        return key;
@@ -95,12 +124,22 @@ namespace System.Net {
                        context = new HttpListenerContext (this);
                }
 
+               public bool IsClosed {
+                       get { return (sock == null); }
+               }
+
                public int Reuses {
                        get { return reuses; }
                }
 
                public IPEndPoint LocalEndPoint {
-                       get { return (IPEndPoint) sock.LocalEndPoint; }
+                       get {
+                               if (local_ep != null)
+                                       return local_ep;
+
+                               local_ep = (IPEndPoint) sock.LocalEndPoint;
+                               return local_ep;
+                       }
                }
 
                public IPEndPoint RemoteEndPoint {
@@ -118,11 +157,8 @@ namespace System.Net {
 
                void OnTimeout (object unused)
                {
+                       CloseSocket ();
                        Unbind ();
-                       try {
-                               sock.Close (); // stream disposed
-                       } catch {
-                       }
                }
 
                public void BeginReadRequest ()
@@ -133,9 +169,11 @@ namespace System.Net {
                                if (reuses == 1)
                                        s_timeout = 15000;
                                timer.Change (s_timeout, Timeout.Infinite);
-                               stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
+                               stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
                        } catch {
+                               timer.Change (Timeout.Infinite, Timeout.Infinite);
                                CloseSocket ();
+                               Unbind ();
                        }
                }
 
@@ -167,10 +205,15 @@ namespace System.Net {
                        return o_stream;
                }
 
-               void OnRead (IAsyncResult ares)
+               static void OnRead (IAsyncResult ares)
                {
-                       timer.Change (Timeout.Infinite, Timeout.Infinite);
                        HttpConnection cnc = (HttpConnection) ares.AsyncState;
+                       cnc.OnReadInternal (ares);
+               }
+
+               void OnReadInternal (IAsyncResult ares)
+               {
+                       timer.Change (Timeout.Infinite, Timeout.Infinite);
                        int nread = -1;
                        try {
                                nread = stream.EndRead (ares);
@@ -183,8 +226,10 @@ namespace System.Net {
                        } catch {
                                if (ms != null && ms.Length > 0)
                                        SendError ();
-                               if (sock != null)
+                               if (sock != null) {
                                        CloseSocket ();
+                                       Unbind ();
+                               }
                                return;
                        }
 
@@ -192,6 +237,7 @@ namespace System.Net {
                                //if (ms.Length > 0)
                                //      SendError (); // Why bother?
                                CloseSocket ();
+                               Unbind ();
                                return;
                        }
 
@@ -208,11 +254,28 @@ namespace System.Net {
                                if (!epl.BindContext (context)) {
                                        SendError ("Invalid host", 400);
                                        Close (true);
+                                       return;
+                               }
+                               HttpListener listener = context.Listener;
+                               if (last_listener != listener) {
+                                       RemoveConnection ();
+                                       listener.AddConnection (this);
+                                       last_listener = listener;
                                }
+
                                context_bound = true;
+                               listener.RegisterContext (context);
                                return;
                        }
-                       stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
+                       stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
+               }
+
+               void RemoveConnection ()
+               {
+                       if (last_listener == null)
+                               epl.RemoveConnection (this);
+                       else
+                               last_listener.RemoveConnection (this);
                }
 
                enum InputState {
@@ -297,7 +360,7 @@ namespace System.Net {
                string ReadLine (byte [] buffer, int offset, int len, ref int used)
                {
                        if (current_line == null)
-                               current_line = new StringBuilder ();
+                               current_line = new StringBuilder (128);
                        int last = offset + len;
                        used = 0;
                        for (int i = offset; i < last && line_state != LineState.LF; i++) {
@@ -371,13 +434,16 @@ namespace System.Net {
                        } finally {
                                sock = null;
                        }
+                       RemoveConnection ();
                }
 
                internal void Close (bool force_close)
                {
                        if (sock != null) {
                                Stream st = GetResponseStream ();
-                               st.Close ();
+                               if (st != null)
+                                       st.Close ();
+
                                o_stream = null;
                        }
 
@@ -423,6 +489,7 @@ namespace System.Net {
                                                s.Close ();
                                }
                                Unbind ();
+                               RemoveConnection ();
                                return;
                        }
                }