package brave.httpclient; import brave.Span; import brave.Tracer; import brave.Tracer.SpanInScope; import brave.Tracing; import brave.http.HttpClientHandler; import brave.http.HttpTracing; import brave.propagation.TraceContext; import java.io.IOException; import org.apache.http.Header; import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpRequestWrapper; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.execchain.ClientExecChain; import zipkin.Endpoint; public final class TracingHttpClientBuilder extends HttpClientBuilder { public static HttpClientBuilder create(Tracing tracing) { return new TracingHttpClientBuilder(HttpTracing.create(tracing)); } public static HttpClientBuilder create(HttpTracing httpTracing) { return new TracingHttpClientBuilder(httpTracing); } final Tracer tracer; final TraceContext.Injector<HttpRequestWrapper> injector; final HttpClientHandler<HttpRequestWrapper, HttpResponse> handler; TracingHttpClientBuilder(HttpTracing httpTracing) { // intentionally hidden if (httpTracing == null) throw new NullPointerException("HttpTracing == null"); this.tracer = httpTracing.tracing().tracer(); this.handler = HttpClientHandler.create(httpTracing, new HttpAdapter()); this.injector = httpTracing.tracing().propagation().injector(HttpRequestWrapper::setHeader); } /** * main exec is the first in the execution chain, so last to execute. This creates a concrete http * request, so this is where the span is created. */ @Override protected ClientExecChain decorateMainExec(ClientExecChain exec) { return (route, request, context, execAware) -> { Span span = handler.handleSend(injector, request); CloseableHttpResponse response = null; Throwable error = null; try (SpanInScope ws = tracer.withSpanInScope(span)) { return response = exec.execute(route, request, context, execAware); } catch (IOException | HttpException | RuntimeException | Error e) { error = e; throw e; } finally { handler.handleReceive(response, error, span); } }; } static final class HttpAdapter extends brave.http.HttpClientAdapter<HttpRequestWrapper, HttpResponse> { @Override public boolean parseServerAddress(HttpRequestWrapper httpRequest, Endpoint.Builder builder) { HttpHost target = httpRequest.getTarget(); if (target == null) return false; if (builder.parseIp(target.getAddress()) || builder.parseIp(target.getHostName())) { builder.port(target.getPort()); return true; } return false; } @Override public String method(HttpRequestWrapper request) { return request.getRequestLine().getMethod(); } @Override public String path(HttpRequestWrapper request) { String result = request.getRequestLine().getUri(); int queryIndex = result.indexOf('?'); return queryIndex == -1 ? result : result.substring(0, queryIndex); } @Override public String url(HttpRequestWrapper request) { HttpHost target = request.getTarget(); if (target != null) return target.toURI() + request.getURI(); return request.getRequestLine().getUri(); } @Override public String requestHeader(HttpRequestWrapper request, String name) { Header result = request.getFirstHeader(name); return result != null ? result.getValue() : null; } @Override public Integer statusCode(HttpResponse response) { return response.getStatusLine().getStatusCode(); } } }