• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

box / box-java-sdk / #5975

19 Dec 2025 11:21AM UTC coverage: 35.648% (-0.001%) from 35.649%
#5975

push

github

web-flow
chore: release version 10.4.0 (#1659)

1 of 1 new or added line in 1 file covered. (100.0%)

8 existing lines in 4 files now uncovered.

18998 of 53294 relevant lines covered (35.65%)

0.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/com/box/sdkgen/networking/defaultnetworkclient/DefaultNetworkClient.java
1
package com.box.sdkgen.networking.defaultnetworkclient;
2

3
import static com.box.sdkgen.serialization.json.JsonManager.jsonToSerializedData;
4
import static com.box.sdkgen.serialization.json.JsonManager.sdToJson;
5
import static com.box.sdkgen.serialization.json.JsonManager.sdToUrlParams;
6
import static java.util.Collections.singletonList;
7
import static okhttp3.ConnectionSpec.MODERN_TLS;
8

9
import com.box.sdkgen.box.errors.BoxSDKError;
10
import com.box.sdkgen.networking.fetchoptions.FetchOptions;
11
import com.box.sdkgen.networking.fetchoptions.MultipartItem;
12
import com.box.sdkgen.networking.fetchoptions.ResponseFormat;
13
import com.box.sdkgen.networking.fetchresponse.FetchResponse;
14
import com.box.sdkgen.networking.network.NetworkSession;
15
import com.box.sdkgen.networking.networkclient.NetworkClient;
16
import java.io.IOException;
17
import java.util.Locale;
18
import java.util.Map;
19
import java.util.Objects;
20
import java.util.TreeMap;
21
import java.util.concurrent.TimeUnit;
22
import java.util.stream.Collectors;
23
import okhttp3.Call;
24
import okhttp3.Headers;
25
import okhttp3.HttpUrl;
26
import okhttp3.MediaType;
27
import okhttp3.MultipartBody;
28
import okhttp3.OkHttpClient;
29
import okhttp3.Request;
30
import okhttp3.RequestBody;
31
import okhttp3.Response;
32
import okio.BufferedSink;
33
import okio.Okio;
34
import okio.Source;
35

36
public class DefaultNetworkClient implements NetworkClient {
37

38
  protected OkHttpClient httpClient;
39

40
  public DefaultNetworkClient(OkHttpClient httpClient) {
×
41
    this.httpClient = httpClient;
×
42
  }
×
43

44
  public DefaultNetworkClient() {
×
45
    httpClient = getDefaultOkHttpClientBuilder().build();
×
46
  }
×
47

48
  public FetchResponse fetch(FetchOptions options) {
49
    NetworkSession networkSession =
50
        options.getNetworkSession() == null ? new NetworkSession() : options.getNetworkSession();
×
51

52
    FetchOptions fetchOptions =
×
53
        networkSession.getInterceptors().stream()
×
54
            .reduce(
×
55
                options,
56
                (modifiedOptions, interceptor) -> interceptor.beforeRequest(modifiedOptions),
×
57
                (o1, o2) -> o2);
×
58

59
    boolean authenticationNeeded = false;
×
60
    Request request;
61
    FetchResponse fetchResponse = null;
×
62
    Exception exceptionThrown = null;
×
63

64
    int attemptNumber = 1;
×
65
    int numberOfRetriesOnException = 0;
×
66
    int attemptForRetry = 0;
×
67
    boolean shouldRetry = false;
×
68

69
    while (true) {
70
      request = prepareRequest(fetchOptions, authenticationNeeded, networkSession);
×
71

72
      Response response = null;
×
73
      try {
74
        response = executeOnClient(request);
×
75

76
        Map<String, String> headersMap =
×
77
            response.headers().toMultimap().entrySet().stream()
×
78
                .collect(
×
79
                    Collectors.toMap(
×
80
                        Map.Entry::getKey,
81
                        e -> e.getValue().get(0),
×
82
                        (existing, replacement) -> existing,
×
83
                        () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER)));
×
84

85
        String responseUrl =
86
            response.networkResponse() != null
×
87
                ? response.networkResponse().request().url().toString()
×
88
                : response.request().url().toString();
×
89

90
        attemptForRetry = attemptNumber;
×
91
        fetchResponse =
92
            Objects.equals(fetchOptions.getResponseFormat().getEnumValue(), ResponseFormat.BINARY)
×
93
                ? new FetchResponse.Builder(response.code(), headersMap)
×
94
                    .content(response.body().byteStream())
×
95
                    .url(responseUrl)
×
96
                    .build()
×
97
                : new FetchResponse.Builder(response.code(), headersMap)
×
98
                    .data(
×
99
                        response.body() != null
×
100
                            ? jsonToSerializedData(response.body().string())
×
UNCOV
101
                            : null)
×
102
                    .url(responseUrl)
×
103
                    .build();
×
104

105
        fetchResponse =
×
106
            networkSession.getInterceptors().stream()
×
107
                .reduce(
×
108
                    fetchResponse,
109
                    (modifiedResponse, interceptor) -> interceptor.afterRequest(modifiedResponse),
×
110
                    (o1, o2) -> o2);
×
111

112
      } catch (Exception e) {
×
113
        exceptionThrown = e;
×
114
        numberOfRetriesOnException++;
×
115
        attemptForRetry = numberOfRetriesOnException;
×
116
        if (response != null) {
×
117
          response.close();
×
118
        }
119

120
        fetchResponse =
×
121
            new FetchResponse.Builder(0, new TreeMap<>(String.CASE_INSENSITIVE_ORDER)).build();
×
122
      }
×
123

124
      shouldRetry =
×
125
          networkSession
126
              .getRetryStrategy()
×
127
              .shouldRetry(fetchOptions, fetchResponse, attemptForRetry);
×
128

129
      if (shouldRetry) {
×
130
        double retryDelay =
×
131
            networkSession
132
                .getRetryStrategy()
×
133
                .retryAfter(fetchOptions, fetchResponse, attemptForRetry);
×
134
        if (retryDelay > 0) {
×
135
          try {
136
            TimeUnit.SECONDS.sleep((long) retryDelay);
×
137
          } catch (InterruptedException ie) {
×
138
            Thread.currentThread().interrupt();
×
139
            throw new BoxSDKError("Retry interrupted", ie);
×
140
          }
×
141
        }
142
        attemptNumber++;
×
143
        continue;
×
144
      }
145

146
      if (fetchResponse.getStatus() >= 300
×
147
          && fetchResponse.getStatus() < 400
×
148
          && fetchOptions.followRedirects) {
×
149
        if (!fetchResponse.getHeaders().containsKey("Location")) {
×
150
          throw new BoxSDKError(
×
151
              "Redirect response missing Location header for " + fetchOptions.getUrl());
×
152
        }
153
        return fetch(
×
154
            new FetchOptions.Builder(fetchResponse.getHeaders().get("Location"), "GET")
×
155
                .responseFormat(fetchOptions.getResponseFormat())
×
156
                .auth(fetchOptions.getAuth())
×
157
                .networkSession(networkSession)
×
158
                .build());
×
159
      }
160

161
      if (fetchResponse != null
×
162
          && fetchResponse.getStatus() >= 200
×
163
          && fetchResponse.getStatus() < 400) {
×
164
        return fetchResponse;
×
165
      }
166

167
      throwOnUnsuccessfulResponse(request, fetchResponse, exceptionThrown);
×
168
    }
×
169
  }
170

171
  private static Request prepareRequest(
172
      FetchOptions options, boolean reauthenticate, NetworkSession networkSession) {
173
    Request.Builder requestBuilder = new Request.Builder().url(options.getUrl());
×
174
    Headers headers = prepareHeaders(options, reauthenticate, networkSession);
×
175
    HttpUrl url = prepareUrl(options);
×
176
    RequestBody body = prepareRequestBody(options);
×
177

178
    requestBuilder.headers(headers);
×
179
    requestBuilder.url(url);
×
180
    requestBuilder.method(options.getMethod().toUpperCase(Locale.ROOT), body);
×
181
    return requestBuilder.build();
×
182
  }
183

184
  private static Headers prepareHeaders(
185
      FetchOptions options, boolean reauthenticate, NetworkSession networkSession) {
186
    Headers.Builder headersBuilder = new Headers.Builder();
×
187

188
    networkSession.getAdditionalHeaders().forEach(headersBuilder::add);
×
189

190
    if (options.getHeaders() != null) {
×
191
      options.getHeaders().forEach(headersBuilder::add);
×
192
    }
193
    if (options.getAuth() != null) {
×
194
      if (reauthenticate) {
×
195
        options.getAuth().refreshToken(networkSession);
×
196
      }
197
      headersBuilder.add(
×
198
          "Authorization", options.getAuth().retrieveAuthorizationHeader(networkSession));
×
199
    }
200
    return headersBuilder.build();
×
201
  }
202

203
  private static HttpUrl prepareUrl(FetchOptions options) {
204

205
    HttpUrl baseUrl = HttpUrl.parse(options.getUrl());
×
206
    if (baseUrl == null) {
×
207
      throw new IllegalArgumentException("Invalid URL " + options.getUrl());
×
208
    }
209
    HttpUrl.Builder urlBuilder = baseUrl.newBuilder();
×
210
    if (options.getParams() != null) {
×
211
      options.getParams().forEach(urlBuilder::addQueryParameter);
×
212
    }
213
    return urlBuilder.build();
×
214
  }
215

216
  private static RequestBody prepareRequestBody(FetchOptions options) {
217
    if (options.getMethod().equalsIgnoreCase("GET")) {
×
218
      return null;
×
219
    }
220
    String contentType = options.getContentType();
×
221
    MediaType mediaType = MediaType.parse(contentType);
×
222
    switch (contentType) {
×
223
      case "application/json":
224
      case "application/json-patch+json":
225
        return options.getData() != null
×
226
            ? RequestBody.create(sdToJson(options.getData()), mediaType)
×
227
            : RequestBody.create("", mediaType);
×
228
      case "application/x-www-form-urlencoded":
229
        return options.getData() != null
×
230
            ? RequestBody.create(sdToUrlParams(options.getData()), mediaType)
×
231
            : RequestBody.create("", mediaType);
×
232
      case "multipart/form-data":
233
        MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
×
234
        for (MultipartItem part : options.multipartData) {
×
235
          if (part.getData() != null) {
×
236
            bodyBuilder.addFormDataPart(part.getPartName(), sdToJson(part.getData()));
×
237
          } else {
238
            bodyBuilder.addFormDataPart(
×
239
                part.getPartName(),
×
240
                part.getFileName() != null ? part.getFileName() : "file",
×
241
                createMultipartRequestBody(part));
×
242
          }
243
        }
×
244
        return bodyBuilder.build();
×
245
      default:
246
        throw new IllegalArgumentException("Unsupported content type " + contentType);
×
247
    }
248
  }
249

250
  protected Call createNewCall(Request request) {
251
    return this.httpClient.newCall(request);
×
252
  }
253

254
  private Response executeOnClient(Request request) {
255
    try {
256
      return createNewCall(request).execute();
×
257
    } catch (IOException e) {
×
258
      throw new RuntimeException(e);
×
259
    }
260
  }
261

262
  public static RequestBody createMultipartRequestBody(MultipartItem part) {
263
    return new RequestBody() {
×
264
      @Override
265
      public MediaType contentType() {
266
        if (part.contentType != null) {
×
267
          return MediaType.parse(part.contentType);
×
268
        }
269
        return MediaType.parse("application/octet-stream");
×
270
      }
271

272
      @Override
273
      public void writeTo(BufferedSink sink) throws IOException {
274
        try (Source source = Okio.source(part.getFileStream())) {
×
275
          sink.writeAll(source);
×
276
        }
277
      }
×
278
    };
279
  }
280

281
  public static OkHttpClient.Builder getDefaultOkHttpClientBuilder() {
282
    return new OkHttpClient.Builder()
×
283
        .followSslRedirects(true)
×
284
        .followRedirects(false)
×
285
        .connectionSpecs(singletonList(MODERN_TLS))
×
286
        .retryOnConnectionFailure(false);
×
287
  }
288

289
  private static void throwOnUnsuccessfulResponse(
290
      Request request, FetchResponse fetchResponse, Exception exceptionThrown) {
291
    if (fetchResponse == null) {
×
292
      throw new RuntimeException(exceptionThrown.getMessage(), exceptionThrown);
×
293
    }
294
    throw new RuntimeException(fetchResponse.getData().toString());
×
295
  }
296
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc