@@ -371,13 +371,35 @@ func postResponseWithRetries(client *http.Client, proxyURL, backendID, requestID
371371 }
372372 continue
373373 }
374+ // Force the response body to be fully read by copying it to the discard target.
375+ //
376+ // The response is expected to be empty for this specific case, so reading the entire
377+ // response body should be safe and not cause any delays.
378+ //
379+ // The reason we do this is because under certain conditions (in particular, when
380+ // using HTTP/2) the server and client might both try to close the underlying stream
381+ // at the same time. When that happens, the client sends a PING message at the
382+ // same time it sends the `RST_STREAM` message.
383+ //
384+ // If this happens a lot, it can trigger protections that a lot of server deployments
385+ // have against PING flood attacks.
386+ //
387+ // Draining the response body forces the server-side `END_STREAM` message to
388+ // arrive before the client tries to send the `RST_STREAM` message. In that case,
389+ // the client will not send a PING and so will not trigger these PING flood protections.
390+ //
391+ // A future version of the Go standard library will likely change the client behavior
392+ // so that it is more conservative about sending these PING messages, and if
393+ // that happens then this line can be removed. The proposed change for that
394+ // is https://go-review.git.corp.google.com/c/net/+/720300
395+ io .Copy (io .Discard , proxyResp .Body )
374396 proxyResp .Body .Close ()
375397 if 500 <= proxyResp .StatusCode && proxyResp .StatusCode < 600 {
376398 if _ , seekErr := proxyReadSeeker .Seek (0 , io .SeekStart ); seekErr != nil {
377399 return err
378400 }
379401 continue
380- }
402+ }
381403 return nil
382404 }
383405 return err
0 commit comments