4646import java .util .ArrayList ;
4747import java .util .HashMap ;
4848import java .util .List ;
49- import java .util .Locale ;
5049import java .util .Map ;
50+ import java .util .Set ;
5151import java .util .concurrent .TimeUnit ;
5252import java .util .regex .Pattern ;
53+ import java .util .stream .IntStream ;
5354import javax .annotation .Nonnull ;
5455import javax .annotation .Nullable ;
5556import okhttp3 .HttpUrl ;
57+ import okhttp3 .Interceptor ;
5658import okhttp3 .MediaType ;
5759import okhttp3 .OkHttpClient ;
5860import okhttp3 .Request ;
5961import okhttp3 .RequestBody ;
6062import okhttp3 .Response ;
61- import okhttp3 .ResponseBody ;
6263
6364/** Client to perform MinIO administration operations. */
6465public class MinioAdminClient {
@@ -111,12 +112,12 @@ public String toString() {
111112 }
112113
113114 private String userAgent = Utils .getDefaultUserAgent ();
114- private PrintWriter traceStream ;
115+ private volatile PrintWriter traceStream ;
115116
116117 private HttpUrl baseUrl ;
117118 private String region ;
118119 private Provider provider ;
119- private OkHttpClient httpClient ;
120+ private volatile OkHttpClient httpClient ;
120121
121122 private MinioAdminClient (
122123 HttpUrl baseUrl , String region , Provider provider , OkHttpClient httpClient ) {
@@ -132,6 +133,39 @@ private Credentials getCredentials() throws MinioException {
132133 return creds ;
133134 }
134135
136+ private static int getStatusRetryInterceptorIndex (List <Interceptor > interceptors ) {
137+ return IntStream .range (0 , interceptors .size ())
138+ .filter (i -> interceptors .get (i ) instanceof Http .StatusRetryInterceptor )
139+ .findFirst ()
140+ .orElse (-1 );
141+ }
142+
143+ private OkHttpClient getHttpClient (PrintWriter traceStream ) {
144+ if (traceStream == null ) return this .httpClient ;
145+
146+ OkHttpClient httpClient = this .httpClient ;
147+ List <Interceptor > interceptors = httpClient .interceptors ();
148+ int i = getStatusRetryInterceptorIndex (interceptors );
149+ Http .StatusRetryInterceptor interceptor =
150+ i < 0 ? null : (Http .StatusRetryInterceptor ) interceptors .get (i );
151+
152+ OkHttpClient .Builder builder = httpClient .newBuilder ();
153+ if (interceptor == null ) {
154+ builder .addInterceptor (new Http .StatusRetryInterceptor (interceptor , traceStream , false ));
155+ } else {
156+ builder .interceptors ().clear ();
157+ for (int j = 0 ; j < interceptors .size (); j ++) {
158+ if (i == j ) {
159+ builder .addInterceptor (new Http .StatusRetryInterceptor (interceptor , traceStream , false ));
160+ } else {
161+ builder .addInterceptor (interceptors .get (j ));
162+ }
163+ }
164+ }
165+
166+ return builder .build ();
167+ }
168+
135169 private Response httpExecute (
136170 Http .Method method , Command command , Multimap <String , String > queryParamMap , byte [] body )
137171 throws IOException , MinioException {
@@ -179,40 +213,8 @@ private Response httpExecute(
179213 creds .secretKey (),
180214 request .header ("x-amz-content-sha256" ));
181215
182- PrintWriter traceStream = this .traceStream ;
183- if (traceStream != null ) {
184- StringBuilder traceBuilder = new StringBuilder ();
185- traceBuilder .append ("---------START-HTTP---------\n " );
186- String encodedPath = request .url ().encodedPath ();
187- String encodedQuery = request .url ().encodedQuery ();
188- if (encodedQuery != null ) encodedPath += "?" + encodedQuery ;
189- traceBuilder .append (request .method ()).append (" " ).append (encodedPath ).append (" HTTP/1.1\n " );
190- traceBuilder .append (
191- request
192- .headers ()
193- .toString ()
194- .replaceAll ("Signature=([0-9a-f]+)" , "Signature=*REDACTED*" )
195- .replaceAll ("Credential=([^/]+)" , "Credential=*REDACTED*" ));
196- if (body != null ) traceBuilder .append ("\n " ).append (new String (body , StandardCharsets .UTF_8 ));
197- traceStream .println (traceBuilder .toString ());
198- }
199-
200- OkHttpClient httpClient = this .httpClient ;
216+ OkHttpClient httpClient = getHttpClient (this .traceStream );
201217 Response response = httpClient .newCall (request ).execute ();
202-
203- if (traceStream != null ) {
204- String trace =
205- response .protocol ().toString ().toUpperCase (Locale .US )
206- + " "
207- + response .code ()
208- + "\n "
209- + response .headers ();
210- traceStream .println (trace );
211- ResponseBody responseBody = response .peekBody (1024 * 1024 );
212- traceStream .println (responseBody .string ());
213- traceStream .println ("----------END-HTTP----------" );
214- }
215-
216218 if (response .isSuccessful ()) return response ;
217219
218220 throw new MinioException ("Request failed with response: " + response .body ().string ());
@@ -926,6 +928,38 @@ public void traceOff() {
926928 this .traceStream = null ;
927929 }
928930
931+ /**
932+ * Sets request retry parameters. Any null/invalid values disable retry.
933+ *
934+ * <pre>Example:{@code
935+ * minioClient.setRetry(ImmutableSet.of(408, 504), 250, 3);
936+ * }</pre>
937+ *
938+ * @param retryStatusCodes HTTP status codes to be retried.
939+ * @param delayMs Delay between retries.
940+ * @param maxRetries Maximum number of retry attempts.
941+ */
942+ public synchronized void setRetry (
943+ Set <Integer > retryStatusCodes , Long delayMs , Integer maxRetries ) {
944+ Interceptor interceptor =
945+ new Http .StatusRetryInterceptor (
946+ retryStatusCodes , delayMs == null ? 0 : delayMs , maxRetries == null ? 0 : maxRetries );
947+
948+ List <Interceptor > interceptors = this .httpClient .interceptors ();
949+ int i = getStatusRetryInterceptorIndex (interceptors );
950+ OkHttpClient .Builder builder = this .httpClient .newBuilder ();
951+ if (i >= 0 ) {
952+ builder .interceptors ().clear ();
953+ for (int j = 0 ; j < interceptors .size (); j ++) {
954+ builder .addInterceptor (i == j ? interceptor : interceptors .get (j ));
955+ }
956+ } else {
957+ builder .addInterceptor (interceptor );
958+ }
959+
960+ this .httpClient = builder .build ();
961+ }
962+
929963 public static Builder builder () {
930964 return new Builder ();
931965 }
0 commit comments