|
21 | 21 | import com.google.api.gax.rpc.ResponseObserver;
|
22 | 22 | import com.google.api.gax.rpc.ServerStreamingCallable;
|
23 | 23 | import com.google.api.gax.rpc.StreamController;
|
| 24 | +import com.google.common.base.Throwables; |
24 | 25 |
|
25 | 26 | /**
|
26 | 27 | * This callable converts the "Received rst stream" exception into a retryable {@link ApiException}.
|
@@ -73,14 +74,29 @@ protected void onCompleteImpl() {
|
73 | 74 | }
|
74 | 75 |
|
75 | 76 | private Throwable convertException(Throwable t) {
|
76 |
| - // Long lived connections sometimes are disconnected via an RST frame. This error is |
77 |
| - // transient and should be retried. |
| 77 | + // Long lived connections sometimes are disconnected via an RST frame or a goaway. These errors |
| 78 | + // are transient and should be retried. |
| 79 | + if (isRstStreamError(t) || isGoAway(t)) { |
| 80 | + return new InternalException(t, ((InternalException) t).getStatusCode(), true); |
| 81 | + } |
| 82 | + return t; |
| 83 | + } |
| 84 | + |
| 85 | + private boolean isRstStreamError(Throwable t) { |
78 | 86 | if (t instanceof InternalException && t.getMessage() != null) {
|
79 | 87 | String error = t.getMessage().toLowerCase();
|
80 |
| - if (error.contains("rst_stream") || error.contains("rst stream")) { |
81 |
| - return new InternalException(t, ((InternalException) t).getStatusCode(), true); |
82 |
| - } |
| 88 | + return error.contains("rst_stream") || error.contains("rst stream"); |
83 | 89 | }
|
84 |
| - return t; |
| 90 | + return false; |
| 91 | + } |
| 92 | + |
| 93 | + private boolean isGoAway(Throwable t) { |
| 94 | + if (t instanceof InternalException) { |
| 95 | + Throwable rootCause = Throwables.getRootCause(t); |
| 96 | + String rootCauseMessage = rootCause.getMessage(); |
| 97 | + return rootCauseMessage != null |
| 98 | + && rootCauseMessage.contains("Stream closed before write could take place"); |
| 99 | + } |
| 100 | + return false; |
85 | 101 | }
|
86 | 102 | }
|
0 commit comments