title: Multiple requests (with some cancelled) from Chrome to Apache throws net::ERR_HTTP2_PROTOCOL_ERROR
body: <p>I have a search component which works like this:</p>
  
  <ol>
  <li>An user performs a search by selecting multiples searches (from 1 to around 25).</li>
  <li>When the user submit the search form, the front send one HTTP request to the API  per selected search. The frontend is an Angular 8 app and the API is a Java API running in Tomcat 9 behind Apache 2 (with HTTP2 support) acting as a reverse proxy.</li>
  <li>If the user submit the search form a second time, each unfinished request is cancel and we go back to the second step.</li>
  </ol>
  
  <p>When the second request is send, an error <code>net::ERR_HTTP2_PROTOCOL_ERROR</code> is sometimes throw, it happens <strong>only</strong> if at least one HTTP request has been canceled.</p>
  
  <p>I tried to isolate the problem as much as I could. The following program (hosted on a Gitlab repository) reproduce the issue with minimal code: <a href="https://gitlab.com/dorianm/apache2-http2-chrome-error" rel="nofollow noreferrer">dorianm/apache2-http2-chrome-error</a></p>
  
  <p>It contains a minimal <a href="https://gitlab.com/dorianm/apache2-http2-chrome-error/blob/master/conf/httpd.conf" rel="nofollow noreferrer">Apache 2 configuration</a> (only 43 rows):</p>
  
  <pre><code># [...]
  
  DocumentRoot /usr/local/apache2/htdocs/
  &lt;Directory "/usr/local/apache2/htdocs/"&gt;
      Options ExecCGI
      AddHandler cgi-script .pl
      Require all granted
  &lt;/Directory&gt;
  
  Protocols h2
  
  SSLEngine on
  SSLProtocol all -SSLv2 -SSLv3
  SSLCipherSuite !EDH:!AECDH:!ADH:!DSS:!RC4:ECDSA:HIGH:+3DES
  SSLHonorCipherOrder on
  SSLCertificateFile /usr/local/apache2/conf/server.crt
  SSLCertificateKeyFile /usr/local/apache2/conf/server.key
  
  # [...]
  </code></pre>
  
  <p>A simple basic <a href="https://gitlab.com/dorianm/apache2-http2-chrome-error/blob/master/src/cgi.pl" rel="nofollow noreferrer">CGI Perl script</a>:</p>
  
  <pre class="lang-perl prettyprint-override"><code>#!/usr/bin/env perl
  
  print "Content-Type: application/json\n\n";
  print '[{ … }]'; # ~100kb of random JSON
  </code></pre>
  
  <p>And a <a href="https://gitlab.com/dorianm/apache2-http2-chrome-error/blob/master/src/index.html" rel="nofollow noreferrer">Javascript script</a> which call the CGI script from the frontend/cancel requests/…:</p>
  
  <pre class="lang-js prettyprint-override"><code>let runningQueries = [];
  let intervalId;
  const getIntervalTime = () =&gt; document.getElementById("intervalTime").value; // 50ms by default
  
  setInterval(() =&gt; {
      fetch("https://localhost/cgi.pl?constant");
  }, 200);
  
  const searches = [];
  for (let i = 1; i &lt;= 10; i++) {
      searches.push(`https://localhost/cgi.pl?${i}`);
  }
  
  function start() {
      intervalId = setInterval(() =&gt; {
          runningQueries.forEach(controller =&gt; {
              controller.abort();
          });
  
          runningQueries = [];
  
          searches.forEach(url =&gt; {
              const controller = new AbortController();
              runningQueries.push(controller);
              fetch(url, {signal: controller.signal}).catch(err =&gt; {
                  console.log(err);
              });
          });
      }, getIntervalTime());
  }
  
  function stop() {
      clearInterval(intervalId);
  }
  </code></pre>
  
  <p>Two buttons are available in front to execute the <code>start</code> and the <code>stop</code> functions. Thanks to that code, I can reproduce my issue:</p>
  
  <p><a href="https://i.stack.imgur.com/EiBpH.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/EiBpH.png" alt="Chrome issue"></a></p>
  
  <p>I export Chrome logs (from chrome://net-export/ tools):</p>
  
  <pre><code>t=5644 [st=194]     +HTTP_TRANSACTION_SEND_REQUEST  [dt=9]
  t=5644 [st=194]        HTTP_TRANSACTION_HTTP2_SEND_REQUEST_HEADERS
                         --&gt; :method: GET
                             :authority: localhost
                             :scheme: https
                             :path: /cgi.pl?constant
                             authorization: [30 bytes were stripped]
                             sec-fetch-mode: cors
                             user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36
                             accept: */*
                             sec-fetch-site: same-origin
                             referer: https://localhost/index.html
                             accept-encoding: gzip, deflate, br
                             accept-language: fr,en;q=0.9,de;q=0.8,it;q=0.7
                             cookie: [1585 bytes were stripped]
  t=5653 [st=203]     -HTTP_TRANSACTION_SEND_REQUEST
  t=5653 [st=203]     +HTTP_TRANSACTION_READ_HEADERS  [dt=180]
  t=5833 [st=383]        HTTP2_STREAM_ERROR
                         --&gt; description = "Server reset stream."
                         --&gt; net_error = "ERR_HTTP2_PROTOCOL_ERROR"
                         --&gt; stream_id = 2945
  t=5833 [st=383]     -HTTP_TRANSACTION_READ_HEADERS
                       --&gt; net_error = -337 (ERR_HTTP2_PROTOCOL_ERROR)
  t=5833 [st=383]   -URL_REQUEST_START_JOB
                     --&gt; net_error = -337 (ERR_HTTP2_PROTOCOL_ERROR)
  t=5833 [st=383]    URL_REQUEST_DELEGATE_RESPONSE_STARTED  [dt=0]
  t=5833 [st=383] -REQUEST_ALIVE
                   --&gt; net_error = -337 (ERR_HTTP2_PROTOCOL_ERROR)
  
  </code></pre>
  
  <p>And I change Apache logs to the trace level. The only interesting part is the following:</p>
  
  <pre><code>[Fri Oct 18 09:59:34.120151 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1839]], frames=1895/2902 (r/s)
  [Fri Oct 18 09:59:34.120168 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1841]], frames=1895/2903 (r/s)
  [Fri Oct 18 09:59:34.120186 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1843]], frames=1895/2904 (r/s)
  [Fri Oct 18 09:59:34.120194 2019] [authz_core:debug] [pid 10:tid 139650130171648] mod_authz_core.c(820): [client 192.168.192.1:36934] AH01626: authorization result of Require all granted: granted, referer: https://localhost/index.html
  [Fri Oct 18 09:59:34.120202 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1845]], frames=1895/2905 (r/s)
  [Fri Oct 18 09:59:34.120212 2019] [authz_core:debug] [pid 10:tid 139650130171648] mod_authz_core.c(820): [client 192.168.192.1:36934] AH01626: authorization result of &lt;RequireAny&gt;: granted, referer: https://localhost/index.html
  [Fri Oct 18 09:59:34.120220 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1847]], frames=1895/2906 (r/s)
  [Fri Oct 18 09:59:34.120235 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1849]], frames=1895/2907 (r/s)
  [Fri Oct 18 09:59:34.120248 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1851]], frames=1895/2908 (r/s)
  [Fri Oct 18 09:59:34.120286 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1853]], frames=1895/2909 (r/s)
  [Fri Oct 18 09:59:34.120303 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1855]], frames=1895/2910 (r/s)
  [Fri Oct 18 09:59:34.120311 2019] [ssl:debug] [pid 10:tid 139650146973440] ssl_engine_kernel.c(383): [client 192.168.192.1:36934] AH02034: Subsequent (No.2) HTTPS request received for child 554050783019 (server localhost:443), referer: https://localhost/index.html
  [Fri Oct 18 09:59:34.120320 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1857]], frames=1895/2911 (r/s)
  [Fri Oct 18 09:59:34.120337 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1859]], frames=1895/2912 (r/s)
  [Fri Oct 18 09:59:34.120354 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1861]], frames=1895/2913 (r/s)
  [Fri Oct 18 09:59:34.120363 2019] [authz_core:debug] [pid 10:tid 139650146973440] mod_authz_core.c(820): [client 192.168.192.1:36934] AH01626: authorization result of Require all granted: granted, referer: https://localhost/index.html
  [Fri Oct 18 09:59:34.120378 2019] [authz_core:debug] [pid 10:tid 139650146973440] mod_authz_core.c(820): [client 192.168.192.1:36934] AH01626: authorization result of &lt;RequireAny&gt;: granted, referer: https://localhost/index.html
  [Fri Oct 18 09:59:34.120378 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1863]], frames=1895/2914 (r/s)
  [Fri Oct 18 09:59:34.122672 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1865]], frames=1895/2915 (r/s)
  [Fri Oct 18 09:59:34.122922 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1867]], frames=1895/2916 (r/s)
  [Fri Oct 18 09:59:34.122960 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1869]], frames=1895/2917 (r/s)
  [Fri Oct 18 09:59:34.122979 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1871]], frames=1895/2918 (r/s)
  [Fri Oct 18 09:59:34.123018 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1873]], frames=1895/2919 (r/s)
  [Fri Oct 18 09:59:34.123030 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1875]], frames=1895/2920 (r/s)
  [Fri Oct 18 09:59:34.123047 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1877]], frames=1895/2921 (r/s)
  [Fri Oct 18 09:59:34.123064 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1879]], frames=1895/2922 (r/s)
  [Fri Oct 18 09:59:34.123081 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1881]], frames=1895/2923 (r/s)
  [Fri Oct 18 09:59:34.123097 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1883]], frames=1895/2924 (r/s)
  [Fri Oct 18 09:59:34.123114 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1885]], frames=1895/2925 (r/s)
  [Fri Oct 18 09:59:34.123195 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1887]], frames=1895/2926 (r/s)
  [Fri Oct 18 09:59:34.123216 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1889]], frames=1895/2927 (r/s)
  [Fri Oct 18 09:59:34.123233 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(593): [client 192.168.192.1:36934] AH03068: h2_session(128,BUSY,11): sent FRAME[RST_STREAM[length=4, flags=0, stream=1809]], frames=1895/2928 (r/s)
  [Fri Oct 18 09:59:34.123276 2019] [http2:debug] [pid 10:tid 139649980819200] h2_session.c(267): [client 192.168.192.1:36934] AH03065: h2_stream(128-1809,CLOSED): closing with err=1 protocol error
  </code></pre>
  
  <p>The last line reports <code>h2_stream(128-1809,CLOSED): closing with err=1 protocol</code>.</p>
  
  <p>I test this with the following environment:</p>
  
  <ul>
  <li>Chrome Version 77.0.3865.120 (Official Build) (64-bit) on macOS Catalina 10.15</li>
  <li>Apache 2.4.41 in a Docker container (image <a href="https://hub.docker.com/_/httpd" rel="nofollow noreferrer">httpd:latest</a>)</li>
  </ul>
  
  <p>What I am missing ? Maybe it's an Apache bug ? A Chrome bug ? A bad Apache configuration ?</p>
  
  <p>Thanks you in advance.</p>