Close owned WebSocket clients after reconnects
This commit is contained in:
parent
19b349143d
commit
271cdd18e3
@ -53,16 +53,18 @@ fun <S> websocketClient(
|
|||||||
/**
|
/**
|
||||||
* Create kilopaarsec transport over websocket (ws or wss).
|
* Create kilopaarsec transport over websocket (ws or wss).
|
||||||
* @param path websocket path (must start with ws:// or wss:// and contain a path part)
|
* @param path websocket path (must start with ws:// or wss:// and contain a path part)
|
||||||
* @client use default [HttpClient], it installs [WebSockets] plugin
|
* @param client optional caller-owned client. When omitted, a client with the
|
||||||
|
* [WebSockets] plugin is created for this device and closed with it.
|
||||||
*/
|
*/
|
||||||
fun websocketTransportDevice(
|
fun websocketTransportDevice(
|
||||||
path: String,
|
path: String,
|
||||||
useTextFrames: Boolean = false,
|
useTextFrames: Boolean = false,
|
||||||
client: HttpClient = HttpClient {
|
client: HttpClient? = null,
|
||||||
install(WebSockets)
|
|
||||||
},
|
|
||||||
): Transport.Device {
|
): Transport.Device {
|
||||||
|
|
||||||
|
val ownsClient = client == null
|
||||||
|
val actualClient = client ?: HttpClient { install(WebSockets) }
|
||||||
|
|
||||||
val log = LogTag("WSTD")
|
val log = LogTag("WSTD")
|
||||||
var u = Url(path)
|
var u = Url(path)
|
||||||
log.debug { "Creating websocket transport device at $u" }
|
log.debug { "Creating websocket transport device at $u" }
|
||||||
@ -82,7 +84,7 @@ fun websocketTransportDevice(
|
|||||||
globalLaunch {
|
globalLaunch {
|
||||||
val log = LogTag("KC:${counter.incrementAndGet()}")
|
val log = LogTag("KC:${counter.incrementAndGet()}")
|
||||||
try {
|
try {
|
||||||
client.webSocket({
|
actualClient.webSocket({
|
||||||
url.protocol = u.protocol
|
url.protocol = u.protocol
|
||||||
url.host = u.host
|
url.host = u.host
|
||||||
url.port = u.port
|
url.port = u.port
|
||||||
@ -148,6 +150,8 @@ fun websocketTransportDevice(
|
|||||||
else log.warning { "unexpected IO error $x" }
|
else log.warning { "unexpected IO error $x" }
|
||||||
runCatching { output.close() }
|
runCatching { output.close() }
|
||||||
runCatching { input.close() }
|
runCatching { input.close() }
|
||||||
|
} finally {
|
||||||
|
if (ownsClient) actualClient.close()
|
||||||
}
|
}
|
||||||
log.info { "closing connection" }
|
log.info { "closing connection" }
|
||||||
}
|
}
|
||||||
@ -162,4 +166,3 @@ fun websocketTransportDevice(
|
|||||||
})
|
})
|
||||||
return device
|
return device
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapter
|
||||||
|
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class WebsocketClientResourceTest {
|
||||||
|
@Test
|
||||||
|
fun failedReconnectsDoNotLeakFileDescriptors() = runBlocking {
|
||||||
|
val descriptors = Path.of("/proc/self/fd")
|
||||||
|
if (!Files.isDirectory(descriptors)) return@runBlocking
|
||||||
|
|
||||||
|
val client = websocketClient<Unit>("ws://127.0.0.1:1/kp")
|
||||||
|
try {
|
||||||
|
delay(2_500)
|
||||||
|
val before = descriptorCount(descriptors)
|
||||||
|
delay(7_000)
|
||||||
|
val after = descriptorCount(descriptors)
|
||||||
|
assertTrue(after <= before + 3, "file descriptors grew from $before to $after")
|
||||||
|
} finally {
|
||||||
|
client.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun descriptorCount(path: Path): Long = Files.list(path).use { it.count() }
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user