diff --git a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/HqInteropGetClient.kt b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/HqInteropGetClient.kt index a1745efb06..10c249c9f8 100644 --- a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/HqInteropGetClient.kt +++ b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/HqInteropGetClient.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.quic.interop.runner import com.vitorpamplona.quic.connection.QuicConnection +import com.vitorpamplona.quic.connection.QuicConnectionDriver import kotlinx.coroutines.flow.toList /** @@ -38,6 +39,7 @@ import kotlinx.coroutines.flow.toList */ class HqInteropGetClient( private val conn: QuicConnection, + private val driver: QuicConnectionDriver, ) : GetClient { override suspend fun get( @Suppress("UNUSED_PARAMETER") authority: String, @@ -47,6 +49,8 @@ class HqInteropGetClient( val request = "GET $path\r\n".encodeToByteArray() stream.send.enqueue(request) stream.send.finish() + // Nudge the send loop — see Http3GetClient.get for rationale. + driver.wakeup() val chunks = stream.incoming.toList() val total = chunks.sumOf { it.size } diff --git a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/Http3GetClient.kt b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/Http3GetClient.kt index 734be314a0..ca2571d840 100644 --- a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/Http3GetClient.kt +++ b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/Http3GetClient.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.quic.interop.runner import com.vitorpamplona.quic.QuicWriter import com.vitorpamplona.quic.connection.QuicConnection +import com.vitorpamplona.quic.connection.QuicConnectionDriver import com.vitorpamplona.quic.connection.drainPeerInitiatedUniStreamsIntoBlackHole import com.vitorpamplona.quic.http3.Http3Frame import com.vitorpamplona.quic.http3.Http3FrameReader @@ -60,6 +61,7 @@ data class GetResponse( */ class Http3GetClient( private val conn: QuicConnection, + private val driver: QuicConnectionDriver, ) : GetClient { suspend fun init(scope: CoroutineScope) { // Control stream: type-0x00 prefix followed by a SETTINGS frame @@ -111,6 +113,11 @@ class Http3GetClient( val stream = conn.openBidiStream() stream.send.enqueue(encodeRequest(authority, path)) stream.send.finish() + // Nudge the send loop. Without this it suspends until PTO (~1s) + // or until an inbound packet arrives. For the multiplexing path + // this was the dominant throughput bottleneck — chunks of 64 + // requests sat idle for ~1s each waiting to be drained. + driver.wakeup() val reader = Http3FrameReader() var status = 0 diff --git a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt index 5532186659..40bab3887a 100644 --- a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt +++ b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt @@ -291,16 +291,16 @@ private fun runTransferTest( val client: GetClient = when (negotiated) { "h3" -> { - Http3GetClient(conn).also { it.init(scope) } + Http3GetClient(conn, driver).also { it.init(scope) } } "hq-interop" -> { - HqInteropGetClient(conn) + HqInteropGetClient(conn, driver) } else -> { System.err.println("unrecognized negotiated ALPN '$negotiated'; defaulting to hq-interop") - HqInteropGetClient(conn) + HqInteropGetClient(conn, driver) } }