Repository: ktorio/ktor
Issue: CIO client gets into the failed loop

ktor-client-cio 1.2.4

I have setup when I need some ws connections to the same endpoint (and they are share the same client), but sometimes my connection loop stuck with following exception:
```java
java.nio.channels.UnresolvedAddressException
	at java.base/sun.nio.ch.Net.checkAddress(Net.java:130)
	at java.base/sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:675)
	at io.ktor.network.sockets.SocketImpl.connect$ktor_network(SocketImpl.kt:28)
	at io.ktor.network.sockets.TcpSocketBuilder.connect(Builders.kt:107)
	at io.ktor.network.sockets.TcpSocketBuilder.connect$default(Builders.kt:99)
	at io.ktor.client.engine.cio.ConnectionFactory.connect(ConnectionFactory.kt:23)
	at io.ktor.client.engine.cio.Endpoint$connect$$inlined$repeat$lambda$1.invokeSuspend(Endpoint.kt:145)
	at io.ktor.client.engine.cio.Endpoint$connect$$inlined$repeat$lambda$1.invoke(Endpoint.kt)
	at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturnIgnoreTimeout(Undispatched.kt:102)
	at kotlinx.coroutines.TimeoutKt.setupTimeout(Timeout.kt:78)
	at kotlinx.coroutines.TimeoutKt.access$setupTimeout(Timeout.kt:1)
	at kotlinx.coroutines.TimeoutKt.withTimeoutOrNull(Timeout.kt:57)
	at io.ktor.client.engine.cio.Endpoint.connect(Endpoint.kt:145)
	at io.ktor.client.engine.cio.Endpoint$makeDedicatedRequest$1.invokeSuspend(Endpoint.kt:91)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:835)
```

See that line:
https://github.com/ktorio/ktor/blob/86f7192d2dddd2605d3f7b83a0b9a7ed45b64e09/ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/Endpoint.kt#L30

It leads to early resolving of hostname when creating endpoint, but constructor of `InetSocketAddress` doesn't guarantee that hostname will be resolved (temporary dns server failure or network outage can cause it). It leads to two bugs:
1. [Retry attempts](https://github.com/ktorio/ktor/blob/86f7192d2dddd2605d3f7b83a0b9a7ed45b64e09/ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/Endpoint.kt#L144) is no sense here, as it will try to connect N times to unresolved address (and each time it fails)
2. When endpoint used by more than one connection (like my case with 2+ ws connections to the same endpoint) endpoint will be never recycled and stale into failed state forever (ws1 tries to connect and failed, but at the same time ws2 tries to connect too and recycling doesn't triggered, ws2 failed, but at the same time ws1 tries to connect...)

Changed files:
ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/Endpoint.kt

diff --git a/ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/Endpoint.kt b/ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/Endpoint.kt
index 655e3509b..328ea5efc 100644
--- a/ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/Endpoint.kt
+++ b/ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/Endpoint.kt
@@ -13,13 +13,15 @@ import io.ktor.util.date.*
 import kotlinx.atomicfu.*
 import kotlinx.coroutines.*
 import kotlinx.coroutines.channels.*
+import kotlinx.coroutines.channels.Channel
 import java.io.*
 import java.net.*
+import java.nio.channels.*
 import kotlin.coroutines.*
 
 internal class Endpoint(
-    host: String,
-    port: Int,
+    private val host: String,
+    private val port: Int,
     private val overProxy: Boolean,
     private val secure: Boolean,
     private val config: CIOEngineConfig,
@@ -27,8 +29,6 @@ internal class Endpoint(
     override val coroutineContext: CoroutineContext,
     private val onDone: () -> Unit
 ) : CoroutineScope, Closeable {
-    private val address = InetSocketAddress(host, port)
-
     private val connections: AtomicInt = atomic(0)
     private val tasks: Channel<RequestTask> = Channel(Channel.UNLIMITED)
     private val deliveryPoint: Channel<RequestTask> = Channel()
@@ -142,6 +142,10 @@ internal class Endpoint(
 
         try {
             repeat(retryAttempts) {
+                val address = InetSocketAddress(host, port)
+
+                if (address.isUnresolved) throw UnresolvedAddressException()
+
                 val connection = withTimeoutOrNull(connectTimeout) { connectionFactory.connect(address) }
                     ?: return@repeat