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

smartsheet / smartsheet-java-sdk / #64

27 May 2026 12:40PM UTC coverage: 59.947% (-0.1%) from 60.09%
#64

push

github

web-flow
Add claude skills, docs and improved reports tests. (#172)

* Add claude skills, docs and improved reports tests.

* Fix tests and lint issues.

4544 of 7580 relevant lines covered (59.95%)

0.6 hits per line

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

73.5
/src/main/java/com/smartsheet/api/internal/http/DefaultHttpClient.java
1
/*
2
 * Copyright (C) 2025 Smartsheet
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.smartsheet.api.internal.http;
18

19
import com.smartsheet.api.Trace;
20
import com.smartsheet.api.internal.json.JacksonJsonSerializer;
21
import com.smartsheet.api.internal.json.JsonSerializer;
22
import com.smartsheet.api.internal.util.StreamUtil;
23
import com.smartsheet.api.internal.util.Util;
24
import com.smartsheet.api.models.Error;
25
import org.apache.http.Header;
26
import org.apache.http.NoHttpResponseException;
27
import org.apache.http.client.ClientProtocolException;
28
import org.apache.http.client.NonRepeatableRequestException;
29
import org.apache.http.client.config.RequestConfig;
30
import org.apache.http.client.methods.CloseableHttpResponse;
31
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
32
import org.apache.http.client.methods.HttpGet;
33
import org.apache.http.client.methods.HttpPost;
34
import org.apache.http.client.methods.HttpPut;
35
import org.apache.http.client.methods.HttpRequestBase;
36
import org.apache.http.client.methods.HttpRequestWrapper;
37
import org.apache.http.entity.ContentType;
38
import org.apache.http.entity.InputStreamEntity;
39
import org.apache.http.impl.client.CloseableHttpClient;
40
import org.apache.http.impl.client.HttpClients;
41
import org.apache.http.protocol.BasicHttpContext;
42
import org.apache.http.protocol.HttpContext;
43
import org.jetbrains.annotations.Nullable;
44
import org.slf4j.Logger;
45
import org.slf4j.LoggerFactory;
46

47
import java.io.ByteArrayInputStream;
48
import java.io.IOException;
49
import java.io.InputStream;
50
import java.io.OutputStream;
51
import java.io.PrintWriter;
52
import java.util.HashMap;
53
import java.util.HashSet;
54
import java.util.Map;
55
import java.util.Random;
56
import java.util.Set;
57

58
/**
59
 * This is the Apache HttpClient based HttpClient implementation.
60
 * <p>
61
 * Thread Safety: This class is thread safe because it is immutable and the underlying Apache CloseableHttpClient is
62
 * thread safe.
63
 *
64
 * @see <a href="http://hc.apache.org/httpcomponents-client-ga/index.html">Apache HttpClient</a>
65
 */
66
public class DefaultHttpClient implements HttpClient {
67

68
    /**
69
     * logger for general errors, warnings, etc
70
     */
71
    protected static final Logger logger = LoggerFactory.getLogger(DefaultHttpClient.class);
1✔
72

73
    /**
74
     * used by default retry/timeout logic and available for overriders
75
     */
76
    protected static final String JSON_MIME_TYPE = ContentType.APPLICATION_JSON.getMimeType();
1✔
77

78
    protected JsonSerializer jsonSerializer;
79

80
    protected long maxRetryTimeMillis = 15000;
1✔
81

82
    /**
83
     * Represents the underlying Apache CloseableHttpClient.
84
     * <p>
85
     * It will be initialized in constructor and will not change afterwards.
86
     */
87
    private final CloseableHttpClient httpClient;
88

89
    /**
90
     * The apache http response.
91
     */
92
    private CloseableHttpResponse apacheHttpResponse;
93

94
    /**
95
     * to avoid creating new sets for each call (we use Sets for practical and perf reasons)
96
     */
97
    private static final Set<Trace> REQUEST_RESPONSE_SUMMARY = Set.of(
1✔
98
            Trace.RequestHeaders,
99
            Trace.RequestBodySummary,
100
            Trace.ResponseHeaders,
101
            Trace.ResponseBodySummary
102
    );
103

104
    /**
105
     * default values for trace-logging extracted from system-properties (can still be overwritten at the instance level)
106
     */
107
    private static final boolean TRACE_PRETTY_PRINT_DEFAULT = Boolean.parseBoolean(System.getProperty("Smartsheet.trace.pretty", "true"));
1✔
108

109
    // empty by default
110
    private static final Set<Trace> TRACE_DEFAULT_TRACE_SET = Trace.parse(System.getProperty("Smartsheet.trace.parts"));
1✔
111

112
    /**
113
     * where to send trace logs
114
     */
115
    private static PrintWriter traceWriter;
116

117
    private final Random random = new Random();
1✔
118

119
    static {
120
        // default trace stream
121
        setTraceStream(System.out);
1✔
122
        if (!TRACE_DEFAULT_TRACE_SET.isEmpty()) {
1✔
123
            traceWriter.println("default trace logging - pretty:" + TRACE_PRETTY_PRINT_DEFAULT + " parts:" + TRACE_DEFAULT_TRACE_SET);
×
124
        }
125
    }
1✔
126

127
    /**
128
     * the set of Trace levels to use in trace-logging
129
     */
130
    private final Set<Trace> traces = new HashSet<>(TRACE_DEFAULT_TRACE_SET);
1✔
131

132
    /**
133
     * whether to log pretty or compact
134
     */
135
    private boolean tracePrettyPrint = TRACE_PRETTY_PRINT_DEFAULT;
1✔
136

137
    private static final String LOG_ARG = "{}";
138
    private static final String ERROR_OCCURRED = "Error occurred.";
139

140
    /**
141
     * Constructor.
142
     */
143
    public DefaultHttpClient() {
144
        this(HttpClients.createDefault(), new JacksonJsonSerializer());
1✔
145
    }
1✔
146

147
    /**
148
     * Constructor.
149
     * <p>
150
     * Parameters: - httpClient : the Apache CloseableHttpClient to use
151
     * <p>
152
     * Exceptions: - IllegalArgumentException : if any argument is null
153
     *
154
     * @param httpClient the http client
155
     */
156
    public DefaultHttpClient(CloseableHttpClient httpClient, JsonSerializer jsonSerializer) {
1✔
157
        this.httpClient = Util.throwIfNull(httpClient);
1✔
158
        this.jsonSerializer = jsonSerializer;
1✔
159
    }
1✔
160

161
    /**
162
     * Log to the SLF4J logger (level based upon response status code). Override this function to add logging
163
     * or capture performance metrics.
164
     *
165
     * @param request        request
166
     * @param requestEntity  request body
167
     * @param response       response
168
     * @param responseEntity response body
169
     * @param durationMillis response time in ms
170
     */
171
    public void logRequest(HttpRequestBase request, HttpEntitySnapshot requestEntity,
172
                           HttpResponse response, HttpEntitySnapshot responseEntity, long durationMillis) throws IOException {
173

174
        logger.info("{} {}, Response Code:{}, Request completed in {} ms", request.getMethod(), request.getURI(),
1✔
175
                response.getStatusCode(), durationMillis);
1✔
176
        logger.debug(LOG_ARG, RequestAndResponseData.of(request, requestEntity, response, responseEntity, REQUEST_RESPONSE_SUMMARY));
1✔
177
    }
1✔
178

179
    /**
180
     * Make an HTTP request and return the response.
181
     *
182
     * @param smartsheetRequest the smartsheet request
183
     * @return the HTTP response
184
     * @throws HttpClientException the HTTP client exception
185
     */
186
    public HttpResponse request(HttpRequest smartsheetRequest) throws HttpClientException {
187
        Util.throwIfNull(smartsheetRequest);
1✔
188
        if (smartsheetRequest.getUri() == null) {
1✔
189
            throw new IllegalArgumentException("A Request URI is required.");
1✔
190
        }
191

192
        long start = System.currentTimeMillis();
1✔
193

194
        HttpRequestBase apacheHttpRequest;
195
        HttpResponse smartsheetResponse;
196

197
        InputStream bodyStream = null;
1✔
198
        if (smartsheetRequest.getEntity() != null && smartsheetRequest.getEntity().getContent() != null) {
1✔
199
            bodyStream = smartsheetRequest.getEntity().getContent();
1✔
200
        }
201
        // the retry logic will consume the body stream so we make sure it supports mark/reset and mark it
202
        boolean canRetryRequest = bodyStream == null || bodyStream.markSupported();
1✔
203
        if (!canRetryRequest) {
1✔
204
            try {
205
                // attempt to wrap the body stream in a input-stream that does support mark/reset
206
                bodyStream = new ByteArrayInputStream(StreamUtil.readBytesFromStream(bodyStream));
1✔
207
                // close the old stream (just to be tidy) and then replace it with a reset-able stream
208
                smartsheetRequest.getEntity().getContent().close();
1✔
209
                smartsheetRequest.getEntity().setContent(bodyStream);
1✔
210
                canRetryRequest = true;
1✔
211
            } catch (IOException ignore) {
1✔
212
            }
1✔
213
        }
214

215
        // the retry loop
216
        while (true) {
217
            int attempt = 0;
1✔
218
            apacheHttpRequest = createApacheRequest(smartsheetRequest);
1✔
219

220
            // Set HTTP headers
221
            if (smartsheetRequest.getHeaders() != null) {
1✔
222
                for (Map.Entry<String, String> header : smartsheetRequest.getHeaders().entrySet()) {
1✔
223
                    apacheHttpRequest.addHeader(header.getKey(), header.getValue());
1✔
224
                }
1✔
225
            }
226

227
            HttpEntitySnapshot responseEntityCopy = null;
1✔
228
            // Set HTTP entity
229
            HttpEntitySnapshot requestEntityCopy = copyRequestEntity(smartsheetRequest, apacheHttpRequest);
1✔
230

231
            // mark the body so we can reset on retry
232
            if (canRetryRequest && bodyStream != null) {
1✔
233
                bodyStream.mark((int) smartsheetRequest.getEntity().getContentLength());
1✔
234
            }
235

236
            // Make the HTTP request
237
            smartsheetResponse = new HttpResponse();
1✔
238
            HttpContext context = new BasicHttpContext();
1✔
239
            try {
240
                long startTime = System.currentTimeMillis();
1✔
241
                apacheHttpResponse = this.httpClient.execute(apacheHttpRequest, context);
1✔
242
                long endTime = System.currentTimeMillis();
1✔
243

244
                HttpEntitySnapshot newResponseCopy = updateWithResponse(apacheHttpRequest, context, smartsheetResponse);
1✔
245
                if (newResponseCopy != null) {
1✔
246
                    responseEntityCopy = newResponseCopy;
1✔
247
                }
248

249
                long responseTime = endTime - startTime;
1✔
250
                logRequest(apacheHttpRequest, requestEntityCopy, smartsheetResponse, responseEntityCopy, responseTime);
1✔
251

252
                // trace-logging of request and response (if so configured)
253
                if (traces.size() > 0) {
1✔
254
                    RequestAndResponseData requestAndResponseData = RequestAndResponseData.of(apacheHttpRequest,
1✔
255
                            requestEntityCopy, smartsheetResponse, responseEntityCopy, traces);
256
                    traceWriter.println(requestAndResponseData.toString(tracePrettyPrint));
1✔
257
                }
258

259
                if (smartsheetResponse.getStatusCode() == 200) {
1✔
260
                    // call successful, exit the retry loop
261
                    break;
1✔
262
                }
263

264
                // the retry logic might consume the content stream so we make sure it supports mark/reset and mark it
265
                InputStream contentStream = smartsheetResponse.getEntity().getContent();
1✔
266
                if (!contentStream.markSupported()) {
1✔
267
                    // wrap the response stream in a input-stream that does support mark/reset
268
                    contentStream = new ByteArrayInputStream(StreamUtil.readBytesFromStream(contentStream));
1✔
269
                    // close the old stream (just to be tidy) and then replace it with a reset-able stream
270
                    smartsheetResponse.getEntity().getContent().close();
1✔
271
                    smartsheetResponse.getEntity().setContent(contentStream);
1✔
272
                }
273
                try {
274
                    contentStream.mark((int) smartsheetResponse.getEntity().getContentLength());
1✔
275
                    long timeSpent = System.currentTimeMillis() - start;
1✔
276
                    if (!shouldRetry(++attempt, timeSpent, smartsheetResponse)) {
1✔
277
                        // should not retry, or retry time exceeded, exit the retry loop
278
                        break;
279
                    }
280
                } finally {
281
                    if (bodyStream != null) {
1✔
282
                        bodyStream.reset();
1✔
283
                    }
284
                    contentStream.reset();
1✔
285
                }
286
                // moving this to finally causes issues because socket is closed (which means response stream is closed)
287
                this.releaseConnection();
×
288

289
            } catch (ClientProtocolException e) {
1✔
290
                logger.warn("ClientProtocolException " + e.getMessage());
1✔
291
                logger.warn(LOG_ARG, RequestAndResponseData.of(apacheHttpRequest, requestEntityCopy, smartsheetResponse,
1✔
292
                        responseEntityCopy, REQUEST_RESPONSE_SUMMARY));
293
                try {
294
                    // if this is a PUT and was retried by the http client, the body content stream is at the
295
                    // end and is a NonRepeatableRequest. If we marked the body content stream prior to execute,
296
                    // reset and retry
297
                    if (canRetryRequest && e.getCause() instanceof NonRepeatableRequestException) {
1✔
298
                        if (smartsheetRequest.getEntity() != null) {
×
299
                            smartsheetRequest.getEntity().getContent().reset();
×
300
                        }
301
                        continue;
×
302
                    }
303
                } catch (IOException ignore) {
×
304
                }
1✔
305
                throw new HttpClientException(ERROR_OCCURRED, e);
1✔
306
            } catch (NoHttpResponseException e) {
×
307
                logger.warn("NoHttpResponseException {}", e.getMessage());
×
308
                logger.warn(LOG_ARG, RequestAndResponseData.of(apacheHttpRequest, requestEntityCopy, smartsheetResponse,
×
309
                        responseEntityCopy, REQUEST_RESPONSE_SUMMARY));
310
                try {
311
                    // check to see if the response was empty and this was a POST or PATCH. All other HTTP methods
312
                    // will be automatically retried by the http client.
313
                    // (POST and PATCH are non-idempotent and are not retried automatically, but are safe for us to retry)
314
                    if (canRetryRequest &&
×
315
                            (smartsheetRequest.getMethod() == HttpMethod.POST ||
×
316
                             smartsheetRequest.getMethod() == HttpMethod.PATCH)) {
×
317
                        if (smartsheetRequest.getEntity() != null) {
×
318
                            smartsheetRequest.getEntity().getContent().reset();
×
319
                        }
320
                        continue;
×
321
                    }
322
                } catch (IOException ignore) {
×
323
                }
×
324
                throw new HttpClientException(ERROR_OCCURRED, e);
×
325
            } catch (IOException e) {
1✔
326
                logger.warn(LOG_ARG, RequestAndResponseData.of(apacheHttpRequest, requestEntityCopy, smartsheetResponse,
1✔
327
                        responseEntityCopy, REQUEST_RESPONSE_SUMMARY));
328
                throw new HttpClientException(ERROR_OCCURRED, e);
1✔
329
            }
×
330
        }
×
331
        return smartsheetResponse;
1✔
332
    }
333

334
    private HttpEntitySnapshot copyRequestEntity(HttpRequest smartsheetRequest, HttpRequestBase apacheHttpRequest) {
335
        final HttpEntity entity = smartsheetRequest.getEntity();
1✔
336
        HttpEntitySnapshot requestEntityCopy = null;
1✔
337
        if (apacheHttpRequest instanceof HttpEntityEnclosingRequestBase && entity != null && entity.getContent() != null) {
1✔
338
            try {
339
                // we need access to the original request stream so we can log it (in the event of errors and/or tracing)
340
                requestEntityCopy = new HttpEntitySnapshot(entity);
1✔
341
            } catch (IOException iox) {
×
342
                logger.error("failed to make copy of original request entity", iox);
×
343
            }
1✔
344

345
            InputStreamEntity streamEntity = new InputStreamEntity(entity.getContent(), entity.getContentLength());
1✔
346
            // why?  not supported by library?
347
            streamEntity.setChunked(false);
1✔
348
            ((HttpEntityEnclosingRequestBase) apacheHttpRequest).setEntity(streamEntity);
1✔
349
        }
350
        return requestEntityCopy;
1✔
351
    }
352

353
    @Nullable
354
    private HttpEntitySnapshot updateWithResponse(HttpRequestBase apacheHttpRequest, HttpContext context,
355
                                                  HttpResponse smartsheetResponse) throws IOException {
356
        // Set request headers to values ACTUALLY SENT (not just created by us), this would include:
357
        // 'Connection', 'Accept-Encoding', etc. However, if a proxy is used, this may be the proxy's CONNECT
358
        // request, hence the test for HTTP method first
359
        Object httpRequest = context.getAttribute("http.request");
1✔
360
        if (httpRequest != null && HttpRequestWrapper.class.isAssignableFrom(httpRequest.getClass())) {
1✔
361
            HttpRequestWrapper actualRequest = (HttpRequestWrapper) httpRequest;
1✔
362
            switch (HttpMethod.valueOf(actualRequest.getMethod())) {
1✔
363
                case GET:
364
                case POST:
365
                case PUT:
366
                case DELETE:
367
                case PATCH:
368
                    apacheHttpRequest.setHeaders(actualRequest.getAllHeaders());
1✔
369
                    break;
1✔
370
                default:
371
                    break;
372
            }
373
        }
374

375
        // Set returned headers
376
        smartsheetResponse.setHeaders(new HashMap<>());
1✔
377
        for (Header header : apacheHttpResponse.getAllHeaders()) {
1✔
378
            smartsheetResponse.getHeaders().put(header.getName(), header.getValue());
1✔
379
        }
380
        smartsheetResponse.setStatus(apacheHttpResponse.getStatusLine().getStatusCode(),
1✔
381
                apacheHttpResponse.getStatusLine().toString());
1✔
382

383
        // Set returned entities
384
        if (apacheHttpResponse.getEntity() != null) {
1✔
385
            HttpEntity httpEntity = new HttpEntity();
1✔
386
            httpEntity.setContentType(apacheHttpResponse.getEntity().getContentType().getValue());
1✔
387
            httpEntity.setContentLength(apacheHttpResponse.getEntity().getContentLength());
1✔
388
            httpEntity.setContent(apacheHttpResponse.getEntity().getContent());
1✔
389
            smartsheetResponse.setEntity(httpEntity);
1✔
390
            return new HttpEntitySnapshot(httpEntity);
1✔
391
        }
392

393
        return null;
×
394
    }
395

396
    /**
397
     * Create the Apache HTTP request. Override this function to inject additional
398
     * haaders in the request or use a proxy.
399
     *
400
     * @param smartsheetRequest (request method and base URI come from here)
401
     * @return the Apache HTTP request
402
     */
403
    public HttpRequestBase createApacheRequest(HttpRequest smartsheetRequest) {
404
        HttpRequestBase apacheHttpRequest;
405

406
        // Create Apache HTTP request based on the smartsheetRequest request type
407
        switch (smartsheetRequest.getMethod()) {
1✔
408
            case GET:
409
                apacheHttpRequest = new HttpGet(smartsheetRequest.getUri());
1✔
410
                break;
1✔
411
            case POST:
412
                apacheHttpRequest = new HttpPost(smartsheetRequest.getUri());
1✔
413
                break;
1✔
414
            case PUT:
415
                apacheHttpRequest = new HttpPut(smartsheetRequest.getUri());
1✔
416
                break;
1✔
417
            case DELETE:
418
                apacheHttpRequest = new HttpDeleteWithBody(smartsheetRequest.getUri());
1✔
419
                break;
1✔
420
            case PATCH:
421
                apacheHttpRequest = new HttpPatch(smartsheetRequest.getUri());
1✔
422
                break;
1✔
423
            default:
424
                throw new UnsupportedOperationException("Request method " + smartsheetRequest.getMethod() + " is not supported!");
×
425
        }
426

427
        RequestConfig.Builder builder = RequestConfig.custom();
1✔
428
        if (apacheHttpRequest.getConfig() != null) {
1✔
429
            builder = RequestConfig.copy(apacheHttpRequest.getConfig());
×
430
        }
431
        builder.setRedirectsEnabled(true);
1✔
432
        RequestConfig config = builder.build();
1✔
433
        apacheHttpRequest.setConfig(config);
1✔
434
        return apacheHttpRequest;
1✔
435
    }
436

437
    /**
438
     * Set the max retry time for API calls which fail and are retry-able.
439
     */
440
    public void setMaxRetryTimeMillis(long maxRetryTimeMillis) {
441
        this.maxRetryTimeMillis = maxRetryTimeMillis;
×
442
    }
×
443

444
    /**
445
     * The backoff calculation routine. Uses exponential backoff. If the maximum elapsed time
446
     * has expired, this calculation returns -1 causing the caller to fall out of the retry loop.
447
     *
448
     * @return -1 to fall out of retry loop, positive number indicates backoff time
449
     */
450
    public long calcBackoff(int previousAttempts, long totalElapsedTimeMillis, Error error) {
451

452
        long backoffMillis = (long) (Math.pow(2, previousAttempts) * 1000) + random.nextInt(1000);
×
453

454
        if (totalElapsedTimeMillis + backoffMillis > maxRetryTimeMillis) {
×
455
            logger.info(
×
456
                    "Elapsed time {} + backoff time {} exceeds max retry time {}, exiting retry loop",
457
                    totalElapsedTimeMillis,
×
458
                    backoffMillis,
×
459
                    maxRetryTimeMillis
×
460
            );
461
            return -1;
×
462
        }
463
        return backoffMillis;
×
464
    }
465

466
    /**
467
     * Called when an API request fails to determine if it can retry the request.
468
     * Calls calcBackoff to determine the time to wait in between retries.
469
     *
470
     * @param previousAttempts       number of attempts (including this one) to execute request
471
     * @param totalElapsedTimeMillis total time spent in millis for all previous (and this) attempt
472
     * @param response               the failed HttpResponse
473
     * @return true if this request can be retried
474
     */
475
    public boolean shouldRetry(int previousAttempts, long totalElapsedTimeMillis, HttpResponse response) {
476
        String contentType = response.getEntity().getContentType();
1✔
477
        if (contentType != null && !contentType.startsWith(JSON_MIME_TYPE)) {
1✔
478
            // it's not JSON; don't even try to parse it
479
            return false;
1✔
480
        }
481
        Error error;
482
        try {
483
            error = jsonSerializer.deserialize(Error.class, response.getEntity().getContent());
1✔
484
        } catch (IOException e) {
×
485
            return false;
×
486
        }
1✔
487
        switch (error.getErrorCode()) {
1✔
488
            case 4001:
489
                // Smartsheet.com is currently offline for system maintenance. Please check back again shortly.
490
            case 4002:
491
                // Server timeout exceeded. Request has failed
492
            case 4003:
493
                // Rate limit exceeded.
494
            case 4004:
495
                // An unexpected error has occurred. Please retry your request
496
                // If you encounter this error repeatedly, please contact api@smartsheet.com for assistance
497
                break;
×
498
            default:
499
                return false;
1✔
500
        }
501

502
        long backoffMillis = calcBackoff(previousAttempts, totalElapsedTimeMillis, error);
×
503
        if (backoffMillis < 0) {
×
504
            return false;
×
505
        }
506

507
        logger.info("HttpError StatusCode={}: Retrying in {} milliseconds", response.getStatusCode(), backoffMillis);
×
508
        try {
509
            Thread.sleep(backoffMillis);
×
510
        } catch (InterruptedException e) {
×
511
            logger.warn("sleep interrupted", e);
×
512
            return false;
×
513
        }
×
514
        return true;
×
515
    }
516

517
    /**
518
     * Close the HttpClient.
519
     *
520
     * @throws IOException Signals that an I/O exception has occurred.
521
     */
522
    @Override
523
    public void close() throws IOException {
524
        this.httpClient.close();
1✔
525
    }
1✔
526

527
    /* (non-Javadoc)
528
     * @see com.smartsheet.api.internal.http.HttpClient#releaseConnection()
529
     */
530
    @Override
531
    public void releaseConnection() {
532
        if (apacheHttpResponse != null) {
1✔
533
            try {
534
                apacheHttpResponse.close();
1✔
535
                apacheHttpResponse = null;
1✔
536
            } catch (IOException e) {
×
537
                logger.error("error closing Apache HttpResponse", e);
×
538
            }
1✔
539
        }
540
    }
1✔
541

542
    /**
543
     * set the traces for this client
544
     *
545
     * @param traces the fields to include in trace-logging
546
     */
547
    public void setTraces(Trace... traces) {
548
        this.traces.clear();
1✔
549
        for (Trace trace : traces) {
1✔
550
            if (!trace.addReplacements(this.traces)) {
1✔
551
                this.traces.add(trace);
×
552
            }
553
        }
554
    }
1✔
555

556
    /**
557
     * set whether to use nicely-formatted JSON or more compact format JSON in trace logging
558
     *
559
     * @param pretty whether to print JSON in a "pretty" format or compact
560
     */
561
    public void setTracePrettyPrint(boolean pretty) {
562
        tracePrettyPrint = pretty;
×
563
    }
×
564

565
    /**
566
     * only included for testing purposes
567
     */
568
    public static void setTraceStream(OutputStream traceStream) {
569
        traceWriter = new PrintWriter(traceStream, true);
1✔
570
    }
1✔
571
}
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