mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-08 23:54:39 +00:00
fix(quartz/desktop): close socket on connect failure; NODELAY for desktop pre-init client
TcpNoDelaySocketFactory's connecting overloads used
`socket().apply { connect(...) }`, which leaks the file descriptor if
bind/connect throws (the JDK's connecting Socket constructors close on
failure; ours didn't). Wrapped in a helper that closes on throw. OkHttp
only calls the no-arg overload, so this guards any other direct caller.
DesktopHttpClient's pre-init `simpleClient` (direct relay sockets opened
before setInstance) now gets the same TcpNoDelaySocketFactory as
directClient. failClosedClient is left as-is: it's a SOCKS client and
OkHttp bypasses the socket factory for SOCKS proxies.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TtDNpayEYvJH7QuPswND3A
This commit is contained in:
+6
@@ -154,6 +154,12 @@ class DesktopHttpClient(
|
||||
private val simpleClient: OkHttpClient by lazy {
|
||||
OkHttpClient
|
||||
.Builder()
|
||||
// Direct relay sockets opened before setInstance() should
|
||||
// get the same TCP_NODELAY as directClient — see
|
||||
// TcpNoDelaySocketFactory. (failClosedClient is SOCKS, and
|
||||
// OkHttp bypasses the socket factory for SOCKS proxies, so
|
||||
// it doesn't need this.)
|
||||
.socketFactory(TcpNoDelaySocketFactory)
|
||||
.connectTimeout(BASE_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.readTimeout(BASE_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.writeTimeout(BASE_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
|
||||
+27
-8
@@ -47,12 +47,31 @@ import javax.net.SocketFactory
|
||||
object TcpNoDelaySocketFactory : SocketFactory() {
|
||||
private fun socket() = Socket().apply { tcpNoDelay = true }
|
||||
|
||||
/**
|
||||
* Runs [block] on a fresh NODELAY socket, closing it if the
|
||||
* bind/connect throws — the JDK's connecting `Socket(...)`
|
||||
* constructors do this, but our `Socket().apply { connect(...) }`
|
||||
* form would otherwise leak the descriptor on a failed connect.
|
||||
* (OkHttp only calls the no-arg overload, so this guards the other
|
||||
* overloads for any direct caller.)
|
||||
*/
|
||||
private inline fun connecting(block: (Socket) -> Unit): Socket {
|
||||
val s = socket()
|
||||
try {
|
||||
block(s)
|
||||
} catch (t: Throwable) {
|
||||
runCatching { s.close() }
|
||||
throw t
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
override fun createSocket(): Socket = socket()
|
||||
|
||||
override fun createSocket(
|
||||
host: String?,
|
||||
port: Int,
|
||||
): Socket = socket().apply { connect(InetSocketAddress(host, port)) }
|
||||
): Socket = connecting { it.connect(InetSocketAddress(host, port)) }
|
||||
|
||||
override fun createSocket(
|
||||
host: String?,
|
||||
@@ -60,15 +79,15 @@ object TcpNoDelaySocketFactory : SocketFactory() {
|
||||
localHost: InetAddress?,
|
||||
localPort: Int,
|
||||
): Socket =
|
||||
socket().apply {
|
||||
bind(InetSocketAddress(localHost, localPort))
|
||||
connect(InetSocketAddress(host, port))
|
||||
connecting {
|
||||
it.bind(InetSocketAddress(localHost, localPort))
|
||||
it.connect(InetSocketAddress(host, port))
|
||||
}
|
||||
|
||||
override fun createSocket(
|
||||
host: InetAddress?,
|
||||
port: Int,
|
||||
): Socket = socket().apply { connect(InetSocketAddress(host, port)) }
|
||||
): Socket = connecting { it.connect(InetSocketAddress(host, port)) }
|
||||
|
||||
override fun createSocket(
|
||||
address: InetAddress?,
|
||||
@@ -76,8 +95,8 @@ object TcpNoDelaySocketFactory : SocketFactory() {
|
||||
localAddress: InetAddress?,
|
||||
localPort: Int,
|
||||
): Socket =
|
||||
socket().apply {
|
||||
bind(InetSocketAddress(localAddress, localPort))
|
||||
connect(InetSocketAddress(address, port))
|
||||
connecting {
|
||||
it.bind(InetSocketAddress(localAddress, localPort))
|
||||
it.connect(InetSocketAddress(address, port))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user