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

box / box-java-sdk / #6648

10 Apr 2026 11:15AM UTC coverage: 12.674% (-0.008%) from 12.682%
#6648

Pull #1779

github

web-flow
Merge eeb116ec9 into 2c17bb242
Pull Request #1779: fix(boxsdkgen): Fix error propagation in Java (box/box-codegen#936)

0 of 3 new or added lines in 2 files covered. (0.0%)

17 existing lines in 9 files now uncovered.

8368 of 66023 relevant lines covered (12.67%)

0.13 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) {
UNCOV
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

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

90
        attemptForRetry = attemptNumber;
×
UNCOV
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())
×
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;
×
NEW
116
        fetchResponse = new FetchResponse.Builder(0, new TreeMap<>()).build();
×
117
        if (response != null) {
×
118
          response.close();
×
119
        }
UNCOV
120
      }
×
121

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

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

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

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

165
      throwOnUnsuccessfulResponse(request, fetchResponse, exceptionThrown);
×
166
    }
×
167
  }
168

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

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

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

186
    networkSession.getAdditionalHeaders().forEach(headersBuilder::add);
×
187

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

201
  private static HttpUrl prepareUrl(FetchOptions options) {
202

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

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

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

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

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

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

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

287
  private static void throwOnUnsuccessfulResponse(
288
      Request request, FetchResponse fetchResponse, Exception exceptionThrown) {
289
    if (fetchResponse == null) {
×
290
      throw new RuntimeException(exceptionThrown.getMessage(), exceptionThrown);
×
291
    }
292
    throw new RuntimeException(fetchResponse.getData().toString());
×
293
  }
294
}
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

© 2026 Coveralls, Inc