title: How to handle HTTP/2 GOAWAY with HttpClient?
body: <p>I am trying to continuously send GET and POST requests to a REST API every few minutes. The issue is that after exactly 1000 requests I receive a <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-6.8" rel="nofollow noreferrer"><code>GOAWAY</code></a> frame (and an <code>IOException</code>):</p>
  <blockquote>
  <p>The GOAWAY frame (type=0x7) is used to initiate shutdown of a connection or to signal serious error conditions.<br>
  <sub><a href="https://datatracker.ietf.org/doc/html/rfc7540#section-6.8" rel="nofollow noreferrer">§ 6.8, RFC 7540</a></sub></p>
  </blockquote>
  <hr />
  <p>I did a fair bit of research and found that not only is 1000 requests <a href="http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_requests" rel="nofollow noreferrer">nginx's default maximum</a>, Cloudfront (<a href="https://bugs.chromium.org/p/chromium/issues/detail?id=681477" rel="nofollow noreferrer">related Chromium issue</a>) and Discord also exhibit the same behavior.</p>
  <p>I tried to reproduce this problem with a local nginx server with the default HTTP/2 configuration:</p>
  <pre>
  server {
      listen 443 http2 ssl;
      http2_max_requests 1000;
      ...
  }
  </pre>
  <pre><code>var client = HttpClient.newBuilder()
          .version(HttpClient.Version.HTTP_2)
          .build();
  
  for (var i = 0; i &lt; 1100; i++) {
      var url = URI.create(String.format(&quot;https://localhost/images/test%d.jpg&quot;, i));
  
      var request = HttpRequest.newBuilder().uri(url).build();
  
      client.send(request, HttpResponse.BodyHandlers.discarding());
      System.out.printf(&quot;Image %d processed%n&quot;, i);
  }
  </code></pre>
  <p>And after approximately 1000 requests, I get a <code>GOAWAY</code> error as expected:</p>
  <pre>
  ...
  Image 998 processed
  <b>Exception in thread "main" java.io.IOException: /127.0.0.1:49259: GOAWAY received</b>
  </pre>
  <hr />
  <p>My first thought would be to check if the exception message contains the string <code>&quot;GOAWAY&quot;</code> and then retry the request accordingly:</p>
  <pre><code>try {
      client.send(request, HttpResponse.BodyHandlers.discarding());
  } catch (IOException e) {
      if (e.getMessage().contains(&quot;GOAWAY&quot;)) {
          client.send(request, HttpResponse.BodyHandlers.discarding());
      } else throw e;
  }
  </code></pre>
  <p>My issue with this approach is that the string comparison seems like it may be fragile. Additionally, since all I have is an IOException with a message, I can't differentiate between <code>GOAWAY</code> frames with a genuine error code (in which case I should probably stop sending requests) and those with <code>NO_ERROR</code> (in which case I could probably retry the request).</p>
  <p>How should I correctly deal with/handle <code>GOAWAY</code> errors (apart from using HTTP/1.1 instead)?</p>