WingtipsAsyncClientHttpRequestInterceptor implements AsyncClientHttpRequestInterceptor { @Override @SuppressWarnings("deprecation") public ListenableFuture<ClientHttpResponse> intercept( HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution ) throws IOException { HttpRequestWrapperWithModifiableHeaders wrapperRequest = new HttpRequestWrapperWithModifiableHeaders(request); if (surroundCallsWithSubspan) { return createAsyncSubSpanAndExecute(wrapperRequest, body, execution); } return propagateTracingHeadersAndExecute(wrapperRequest, body, execution); } WingtipsAsyncClientHttpRequestInterceptor();  WingtipsAsyncClientHttpRequestInterceptor(boolean surroundCallsWithSubspan);  WingtipsAsyncClientHttpRequestInterceptor(
        boolean surroundCallsWithSubspan,
        HttpTagAndSpanNamingStrategy<HttpRequest, ClientHttpResponse> tagAndNamingStrategy,
        HttpTagAndSpanNamingAdapter<HttpRequest, ClientHttpResponse> tagAndNamingAdapter
    ); @Override @SuppressWarnings("deprecation") ListenableFuture<ClientHttpResponse> intercept(
        HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution
    ); static final WingtipsAsyncClientHttpRequestInterceptor DEFAULT_IMPL; }
@DataProvider(value = { "true | true", "true | false", "false | true", "false | false" }, splitBy = "\\|") @Test public void intercept_handles_span_closing_logic_even_if_execution_explodes( boolean currentSpanExists, boolean subspanOptionOn ) throws IOException { WingtipsAsyncClientHttpRequestInterceptor interceptor = new WingtipsAsyncClientHttpRequestInterceptor( subspanOptionOn, tagAndNamingStrategy, tagAndNamingAdapterMock ); Span rootSpan = (currentSpanExists) ? Tracer.getInstance().startRequestWithRootSpan("rootSpan") : null; TracingState tracingStateBeforeInterceptorCall = TracingState.getCurrentThreadTracingState(); RuntimeException executionExplosion = new RuntimeException("kaboom"); doAnswer(invocation -> { tracingStateAtTimeOfExecution = TracingState.getCurrentThreadTracingState(); throw executionExplosion; }).when(executionMock).executeAsync(any(HttpRequest.class), any(byte[].class)); Throwable ex = catchThrowable(() -> interceptor.intercept(requestMock, body, executionMock)); assertThat(ex).isSameAs(executionExplosion); if (subspanOptionOn) { assertThat(spanRecorder.completedSpans).hasSize(1); Span completedSpan = spanRecorder.completedSpans.get(0); if (currentSpanExists) { assertThat(completedSpan.getTraceId()).isEqualTo(rootSpan.getTraceId()); assertThat(completedSpan.getParentSpanId()).isEqualTo(rootSpan.getSpanId()); } else { assertThat(completedSpan.getParentSpanId()).isNull(); } assertThat(completedSpan.getSpanPurpose()).isEqualTo(SpanPurpose.CLIENT); } else { assertThat(spanRecorder.completedSpans).isEmpty(); assertThat(tracingStateAtTimeOfExecution).isEqualTo(tracingStateBeforeInterceptorCall); } assertThat(normalizeTracingState(TracingState.getCurrentThreadTracingState())) .isEqualTo(normalizeTracingState(tracingStateBeforeInterceptorCall)); }