/// <summary>
        /// Returns network stream connected to server. It could be a proxy or a
        /// real server Uri.
        /// </summary>
        /// <param name="proxyServer">Uri that describes the proxy server.</param>
        /// <param name="targetServer">Uri that describes the target (real) server.</param>
        /// <returns>Nerwork stream connected to server.</returns>
        private InputNetworkStreamWrapper EstablishConnection(Uri proxyServer, Uri targetServer)
        {
            InputNetworkStreamWrapper retStream = null;

            // Create a socket and set reuse true.
            // But before creating new socket we look in the list of existing sockets. If socket for this host already
            // exist - use it. No need to create new socket.
            string remoteServer = targetServer.Host + ":" + targetServer.Port;
            lock (m_ConnectedStreams)
            {
                ArrayList removeStreamList = new ArrayList();

                for (int i = 0; i < m_ConnectedStreams.Count; i++)
                {
                    InputNetworkStreamWrapper inputStream = (InputNetworkStreamWrapper)m_ConnectedStreams[i];

                    if (inputStream.m_rmAddrAndPort == remoteServer && !inputStream.m_InUse)
                    {
                        // Re-use the connected socket.
                        // But first we need to know that socket is not closed.
                        try
                        {
                            // If socket is closed ( from this or other side ) the call throws exception.
                            if (inputStream.m_Socket.Poll(1, SelectMode.SelectWrite))
                            {
                                // No exception, good we can condtinue and re-use connected stream.

                                // Control flow returning here means persistent connection actually works. 
                                inputStream.m_InUse = true;
                                inputStream.m_lastUsed = DateTime.Now;

                                retStream = inputStream;
                                break;
                            }
                            else
                            {
                                removeStreamList.Add(inputStream);
                            }

                        }
                        catch (Exception)
                        {
                            removeStreamList.Add(inputStream);
                        }

                    }
                }

                for (int i = 0; i < removeStreamList.Count; i++)
                {
                    InputNetworkStreamWrapper removeStream = (InputNetworkStreamWrapper)removeStreamList[i];

                    // Means socket was closed. Remove it from the list.
                    m_ConnectedStreams.Remove(removeStream);

                    removeStream.Dispose();
                }
            }

            if (retStream == null)
            {
                // Existing connection did not worked. Need to establish new one.
                IPAddress address = null;
                UriHostNameType hostNameType = proxyServer.HostNameType;
                if (hostNameType == UriHostNameType.IPv4)
                {
                    address = IPAddress.Parse(proxyServer.Host);
                }
                else if (hostNameType == UriHostNameType.Dns)
                {
                    IPHostEntry hostEntry = null;

                    try
                    {
                        hostEntry = Dns.GetHostEntry(proxyServer.Host);
                    }
                    catch(SocketException se)
                    {
                        throw new WebException("host not available", se, WebExceptionStatus.ConnectFailure, null);
                    }

                    int addressListSize = hostEntry.AddressList.Length;
                    for (int i = 0; i < addressListSize; i++)
                    {
                        if ((address = hostEntry.AddressList[i]) != null)
                        {
                            break;
                        }
                    }

                    if (address == null)
                    {
                        throw new WebException("Unable to resolve Dns entry to valid IPv4 Address", WebExceptionStatus.NameResolutionFailure);
                    }
                }
                else
                {
                    throw new WebException("Only IPv4 or Dns host names allowed.");
                }

                // If socket was not found in waiting connections, then we create new one.
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);

                // Connect to remote endpoint
                try
                {
                    IPEndPoint remoteEP = new IPEndPoint(address, proxyServer.Port);
                    socket.Connect((EndPoint)remoteEP);
                }
                catch (SocketException e)
                {
                    throw new WebException("connection failed", e, WebExceptionStatus.ConnectFailure, null);
                }

                bool isHttps = m_originalUrl.Scheme == "https";

                // We have connected socket. Create request stream
                retStream = new InputNetworkStreamWrapper(new NetworkStream(socket), socket, !isHttps, proxyServer.Host + ":" + proxyServer.Port);

                // For https proxy works differenly from http.
                if (isHttps)
                {
                    // If proxy is set, then for https we need to send "CONNECT" command to proxy.
                    // Once this command is send, the socket from proxy works as if it is the socket to the destination server.
                    if (proxyServer != targetServer)
                    {
                        String request = "CONNECT " + remoteServer + " HTTP/" + ProtocolVersion + "\r\n\r\n";
                        Byte[] bytesToSend = Encoding.UTF8.GetBytes(request);
                        retStream.Write(bytesToSend, 0, bytesToSend.Length);

                        // Now proxy should respond with the connected status. If it is successul, then we are good to go.
                        CoreResponseData respData = ParseHTTPResponse(retStream, m_keepAlive);
                        if (respData.m_statusCode != (int)HttpStatusCode.OK)
                        {
                            throw new WebException("Proxy returned " + respData.m_statusCode, WebExceptionStatus.ConnectFailure);
                        }
                    }

                    // Once connection estiblished need to create secure stream and authenticate server.
                    SslStream sslStream = new SslStream(retStream.m_Socket);

                    // Throws exception is fails.
                    sslStream.AuthenticateAsClient(m_originalUrl.Host, null, m_caCerts, m_caCerts != null ? SslVerification.CertificateRequired : SslVerification.NoVerification, SslProtocols.Default);

                    // Changes the stream to SSL stream.
                    retStream.m_Stream = sslStream;

                    // Changes the address. Originally socket was connected to proxy, now as if it connected to m_originalUrl.Host on m_originalUrl.Port
                    retStream.m_rmAddrAndPort = m_originalUrl.Host + ":" + m_originalUrl.Port;
                }

                lock (m_ConnectedStreams)
                {
                    m_ConnectedStreams.Add(retStream);

                    // if the current stream list is empty then start the timer that drops unused connections.
                    if (m_ConnectedStreams.Count == 1)
                    {
                        m_DropOldConnectionsTimer.Change(HttpListener.DefaultKeepAliveMilliseconds, System.Threading.Timeout.Infinite);
                    }
                }
            }

            return retStream;
        }