Connects to the server.

        .. note:: This method is automatically invoked by
            :meth:`SMTP.__aenter__`. The code is mostly borrowed from the
            :func:`asyncio.streams.open_connection` source code.

        Raises:
            ConnectionError subclass: If the connection between client and
                SMTP server can not be established.

        Returns:
            (int, str): A (code, message) 2-tuple containing the server
                response.

async def connect(self):
        """
        Connects to the server.

        .. note:: This method is automatically invoked by
            :meth:`SMTP.__aenter__`. The code is mostly borrowed from the
            :func:`asyncio.streams.open_connection` source code.

        Raises:
            ConnectionError subclass: If the connection between client and
                SMTP server can not be established.

        Returns:
            (int, str): A (code, message) 2-tuple containing the server
                response.
        """
        # First build the reader:
        self.reader = SMTPStreamReader(loop=self.loop)

        # Then build the protocol:
        protocol = asyncio.StreamReaderProtocol(self.reader, loop=self.loop)

        # With the just-built reader and protocol, create the connection and
        # get the transport stream:
        conn = {
            "protocol_factory": lambda: protocol,
            "host": self.hostname,
            "port": self.port,
        }

        if self.use_aioopenssl:
            conn.update(
                {
                    "use_starttls": not self.ssl_context,
                    "ssl_context_factory": lambda transport: self.ssl_context,
                    "server_hostname": self.hostname,  # For SSL
                }
            )

            import aioopenssl

            # This may raise a ConnectionError exception, which we let bubble up.
            self.transport, _ = await aioopenssl.create_starttls_connection(
                self.loop, **conn
            )
            # HACK: aioopenssl transports don't implement is_closing, and thus drain() fails...
            self.transport.is_closing = lambda: False
        else:
            conn["ssl"] = self.ssl_context
            # This may raise a ConnectionError exception, which we let bubble up.
            self.transport, _ = await self.loop.create_connection(**conn)

        # If the connection has been established, build the writer:
        self.writer = SMTPStreamWriter(self.transport, protocol, self.reader, self.loop)

        code, message = await self.reader.read_reply()

        if code != 220:
            raise ConnectionRefusedError(code, message)

        return code, message