@@ -15,13 +15,15 @@ package wvlet.airframe.http
15
15
import java .io .{EOFException , IOException }
16
16
import java .lang .reflect .InvocationTargetException
17
17
import java .net ._
18
+ import java .nio .channels .ClosedChannelException
18
19
import java .util .concurrent .{ExecutionException , TimeoutException }
19
20
20
21
import javax .net .ssl .{SSLException , SSLHandshakeException , SSLKeyException , SSLPeerUnverifiedException }
21
22
import wvlet .airframe .control .ResultClass
22
23
import wvlet .airframe .control .ResultClass .{Failed , Succeeded , nonRetryableFailure , retryableFailure }
23
24
import wvlet .airframe .control .Retry .RetryContext
24
25
import wvlet .log .LogSupport
26
+
25
27
import scala .language .existentials
26
28
27
29
/**
@@ -120,7 +122,9 @@ object HttpClientException extends LogSupport {
120
122
" Idle connections will be closed" .r
121
123
)
122
124
123
- retriable400ErrorMessage.find { pattern => pattern.findFirstIn(m).isDefined }.isDefined
125
+ retriable400ErrorMessage.find { pattern =>
126
+ pattern.findFirstIn(m).isDefined
127
+ }.isDefined
124
128
}
125
129
126
130
/**
@@ -164,10 +168,11 @@ object HttpClientException extends LogSupport {
164
168
case e : java.lang.InterruptedException =>
165
169
// Retryable when the http client thread execution is interrupted.
166
170
retryableFailure(e)
167
- case e : ProtocolException => retryableFailure(e)
168
- case e : ConnectException => retryableFailure(e)
169
- case e : EOFException => retryableFailure(e)
170
- case e : TimeoutException => retryableFailure(e)
171
+ case e : ProtocolException => retryableFailure(e)
172
+ case e : ConnectException => retryableFailure(e)
173
+ case e : EOFException => retryableFailure(e)
174
+ case e : TimeoutException => retryableFailure(e)
175
+ case e : ClosedChannelException => retryableFailure(e)
171
176
case e : SocketException =>
172
177
e match {
173
178
case se : BindException => retryableFailure(e)
@@ -177,8 +182,17 @@ object HttpClientException extends LogSupport {
177
182
case other =>
178
183
nonRetryableFailure(e)
179
184
}
185
+ // Exceptions from Finagle. Using the string class names so as not to include Finagle dependencies.
186
+ case e : Throwable if finagleRetryableExceptionClasses.contains(e.getClass().getName) =>
187
+ retryableFailure(e)
180
188
}
181
189
190
+ private [http] val finagleRetryableExceptionClasses = Set (
191
+ " com.twitter.finagle.ChannelClosedException" ,
192
+ " com.twitter.finagle.ReadTimedOutException" ,
193
+ " com.twitter.finagle.WriteTimedOutException"
194
+ )
195
+
182
196
def sslExceptionClassifier : PartialFunction [Throwable , Failed ] = {
183
197
case e : SSLException =>
184
198
e match {
0 commit comments