<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body>
    <p>Looking at the application code, the relevant parts appear to be
      this:<br>
      <br>
      ServerConnectionConfig.builder()<br>
              .maxIdleTimeoutInSeconds(50)<br>
      ...<br>
      new Http3ApplicationProtocolFactory(<br>
          (request, response) -> {<br>
            try {<br>
              Thread.sleep(Duration.ofSeconds(40));<br>
            } catch (InterruptedException e) {<br>
              Thread.interrupted();<br>
            }<br>
            response.setStatus(200);<br>
      ...<br>
      HttpClient.newBuilder()<br>
        .version(Version.valueOf("HTTP_3"))<br>
        .sslContext(sslContext)<br>
        .build()<br>
        .send(<br>
            HttpRequest.newBuilder()<br>
                .timeout(Duration.ofSeconds(45))<br>
                .uri(URI.create(<a class="moz-txt-link-rfc2396E" href="https://localhost:8080">"https://localhost:8080"</a>))<br>
                .GET()<br>
      <br>
      The server is configured to have a QUIC idle timeout of 50
      seconds. On the client side, the JDK's HttpClient for HTTP/3 by
      default uses a idle timeout of 30 seconds. These timeouts
      translate to the QUIC connection level idle timeouts, which decide
      when to terminate the connection if there's no UDP traffic on that
      connection. The QUIC RFC specifies that each of the endpoints (the
      server and the client) can choose to advertise a idle timeout of
      their own, but the negotiated idle timeout will always be the
      lower of those two and that negotiated timeout must be honoured by
      both sides of the connection. So in this case, the server
      advertises 50 seconds and the client advertises 30 seconds, so the
      30 second timeout is the negotiated one for this connection and
      thus if the connection doesn't generate any traffic for that
      duration (like in this case), it gets idle terminated.<br>
      <br>
      The default idle timeout for JDK's HttpClient can be configured
      using the "jdk.httpclient.keepalive.timeout.h3" system property
<a class="moz-txt-link-freetext" href="https://download.java.net/java/early_access/jdk26/docs/api/java.net.http/module-summary.html">https://download.java.net/java/early_access/jdk26/docs/api/java.net.http/module-summary.html</a>.
      So setting it to -Djdk.httpclient.keepalive.timeout.h3=50 (for
      example), should then allow your application to stay idle without
      generating any UDP traffic for the QUIC connection for that long.<br>
      <br>
      -Jaikiran<br>
    </p>
    <div class="moz-cite-prefix">On 10/11/25 7:08 pm, Josiah Noel wrote:<br>
    </div>
    <blockquote type="cite" cite="mid:CAJ_t5UZmcA8+e5AGbMx9ctBt=GfZS4ea-1qzF=tAmLSRGLTH7w@mail.gmail.com">
      
      <div dir="ltr">
        <div>To emulate a few of my worst case latency scenarios
          that I have seen in production, I have added a sleep with a
          thirty second timer before I send a 200 status.</div>
        <div><br>
        </div>
        <div>Once more I have attached my jbang script as well as the
          extended logs when I ran with
-Djdk.httpclient.HttpClient.log=requests,headers,errors,http3,quic:control:retransmit.</div>
        <span class="gmail_signature_prefix">-- </span><br>
        <div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature">
          <div dir="ltr">Cheers, Josiah.</div>
        </div>
        <input name="virtru-metadata" type="hidden" value="{"email-policy":{"state":"closed","expirationUnit":"days","disableCopyPaste":false,"disablePrint":false,"disableForwarding":false,"enableNoauth":false,"persistentProtection":false,"expandedWatermarking":false,"expires":false,"isManaged":false,"sms":false},"attachments":{},"compose-id":"1","compose-window":{"secure":false}}"></div>
    </blockquote>
  </body>
</html>