Integrate trace api with AdvancedHttpClient

This commit is contained in:
Sebastian Sdorra
2020-10-26 16:54:05 +01:00
parent 09d85f6dbb
commit eb7a7837d7
4 changed files with 126 additions and 49 deletions

View File

@@ -21,30 +21,28 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.net.ahc;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import org.apache.shiro.codec.Base64;
import sonia.scm.util.HttpUtil;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.nio.charset.StandardCharsets;
//~--- JDK imports ------------------------------------------------------------
/**
* Base class for http requests.
*
* @author Sebastian Sdorra
* @param <T> request implementation
*
*
* @since 1.46
*/
public abstract class BaseHttpRequest<T extends BaseHttpRequest>
@@ -75,7 +73,7 @@ public abstract class BaseHttpRequest<T extends BaseHttpRequest>
*
* @throws IOException
*/
public AdvancedHttpResponse request() throws IOException
public AdvancedHttpResponse request() throws IOException
{
return client.request(this);
}
@@ -102,7 +100,7 @@ public abstract class BaseHttpRequest<T extends BaseHttpRequest>
String auth = Strings.nullToEmpty(username).concat(":").concat(
Strings.nullToEmpty(password));
auth = Base64.encodeToString(auth.getBytes(Charsets.ISO_8859_1));
auth = Base64.encodeToString(auth.getBytes(StandardCharsets.ISO_8859_1));
headers.put("Authorization", "Basic ".concat(auth));
return self();
@@ -129,7 +127,7 @@ public abstract class BaseHttpRequest<T extends BaseHttpRequest>
*
*
* @param disableCertificateValidation true to disable certificate validation
*
*
* @return request instance
*/
public T disableCertificateValidation(boolean disableCertificateValidation)
@@ -246,6 +244,19 @@ public abstract class BaseHttpRequest<T extends BaseHttpRequest>
return self();
}
/**
* Sets the kind of span for tracing api.
*
* @param spanKind kind of span
* @return request instance
*
* @since 2.9.0
*/
public T spanKind(String spanKind) {
this.spanKind = spanKind;
return self();
}
//~--- get methods ----------------------------------------------------------
/**
@@ -281,6 +292,17 @@ public abstract class BaseHttpRequest<T extends BaseHttpRequest>
return url;
}
/**
* Returns the kind of span which is used for the trace api.
*
* @return kind of span
*
* @since 2.9.0
*/
public String getSpanKind() {
return spanKind;
}
/**
* Returns true if the request decodes gzip compression.
*
@@ -317,7 +339,7 @@ public abstract class BaseHttpRequest<T extends BaseHttpRequest>
/**
* Returns true if the proxy settings are ignored.
*
*
*
* @return true if the proxy settings are ignored
*/
public boolean isIgnoreProxySettings()
@@ -341,7 +363,7 @@ public abstract class BaseHttpRequest<T extends BaseHttpRequest>
}
/**
* Returns string representation of the given object or {@code null}, if the
* Returns string representation of the given object or {@code null}, if the
* object is {@code null}.
*
*
@@ -398,4 +420,7 @@ public abstract class BaseHttpRequest<T extends BaseHttpRequest>
/** url of request */
private String url;
/** kind of span for trace api */
private String spanKind = "http-request";
}

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.net.ahc;
//~--- non-JDK imports --------------------------------------------------------
@@ -38,8 +38,11 @@ import sonia.scm.config.ScmConfiguration;
import sonia.scm.net.Proxies;
import sonia.scm.net.TrustAllHostnameVerifier;
import sonia.scm.net.TrustAllTrustManager;
import sonia.scm.trace.Span;
import sonia.scm.trace.Tracer;
import sonia.scm.util.HttpUtil;
import javax.annotation.Nonnull;
import javax.inject.Provider;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
@@ -99,9 +102,10 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
*/
@Inject
public DefaultAdvancedHttpClient(ScmConfiguration configuration,
Set<ContentTransformer> contentTransformers, Provider<SSLContext> sslContextProvider)
Tracer tracer, Set<ContentTransformer> contentTransformers, Provider<SSLContext> sslContextProvider)
{
this.configuration = configuration;
this.tracer = tracer;
this.contentTransformers = contentTransformers;
this.sslContextProvider = sslContextProvider;
}
@@ -185,45 +189,48 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
* @throws IOException
*/
@Override
protected AdvancedHttpResponse request(BaseHttpRequest<?> request)
throws IOException
{
HttpURLConnection connection = openConnection(request,
new URL(request.getUrl()));
protected AdvancedHttpResponse request(BaseHttpRequest<?> request) throws IOException {
try (Span span = tracer.span(request.getSpanKind())) {
span.label("url", request.getUrl());
span.label("method", request.getMethod());
DefaultAdvancedHttpResponse response = doRequest(request);
span.label("status", response.getStatus());
if (!response.isSuccessful()) {
span.failed();
}
return response;
}
}
@Nonnull
private DefaultAdvancedHttpResponse doRequest(BaseHttpRequest<?> request) throws IOException {
HttpURLConnection connection = openConnection(request, new URL(request.getUrl()));
applyBaseSettings(request, connection);
if (connection instanceof HttpsURLConnection)
{
if (connection instanceof HttpsURLConnection) {
applySSLSettings(request, (HttpsURLConnection) connection);
}
Content content = null;
if (request instanceof AdvancedHttpRequestWithBody)
{
if (request instanceof AdvancedHttpRequestWithBody) {
AdvancedHttpRequestWithBody ahrwb = (AdvancedHttpRequestWithBody) request;
content = ahrwb.getContent();
if (content != null)
{
if (content != null) {
content.prepare(ahrwb);
}
else
{
} else {
request.header(HttpUtil.HEADER_CONTENT_LENGTH, "0");
}
}
else
{
} else {
request.header(HttpUtil.HEADER_CONTENT_LENGTH, "0");
}
applyHeaders(request, connection);
if (content != null)
{
if (content != null) {
applyContent(connection, content);
}
@@ -309,8 +316,8 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
{
logger.error("could not disable certificate validation", ex);
}
}
else
}
else
{
logger.trace("set ssl socker factory from provider");
connection.setSSLSocketFactory(sslContextProvider.get().getSocketFactory());
@@ -380,7 +387,10 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
/** set of content transformers */
private final Set<ContentTransformer> contentTransformers;
/** ssl context provider */
private final Provider<SSLContext> sslContextProvider;
/** tracer used for request tracing */
private Tracer tracer;
}

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.net.ahc;
//~--- non-JDK imports --------------------------------------------------------
@@ -30,11 +30,14 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.net.TrustAllHostnameVerifier;
import sonia.scm.trace.Span;
import sonia.scm.trace.Tracer;
import sonia.scm.util.HttpUtil;
import static org.junit.Assert.*;
@@ -82,12 +85,12 @@ public class DefaultAdvancedHttpClientTest
DefaultAdvancedHttpClient.TIMEOUT_CONNECTION);
verify(connection).addRequestProperty(HttpUtil.HEADER_CONTENT_LENGTH, "0");
}
@Test(expected = ContentTransformerNotFoundException.class)
public void testContentTransformerNotFound(){
client.createTransformer(String.class, "text/plain");
}
@Test
public void testContentTransformer(){
ContentTransformer transformer = mock(ContentTransformer.class);
@@ -265,6 +268,32 @@ public class DefaultAdvancedHttpClientTest
"Basic dHJpY2lhOnRyaWNpYXMgc2VjcmV0");
}
@Test
public void shouldCreateTracingSpan() throws IOException {
when(connection.getResponseCode()).thenReturn(200);
new AdvancedHttpRequest(client, HttpMethod.GET, "https://www.scm-manager.org").spanKind("spaceships").request();
verify(tracer).span("spaceships");
verify(span).label("url", "https://www.scm-manager.org");
verify(span).label("method", "GET");
verify(span).label("status", 200);
verify(span, never()).failed();
verify(span).close();
}
@Test
public void shouldCreateFailedTracingSpan() throws IOException {
when(connection.getResponseCode()).thenReturn(500);
new AdvancedHttpRequest(client, HttpMethod.GET, "https://www.scm-manager.org").request();
verify(tracer).span("http-request");
verify(span).label("url", "https://www.scm-manager.org");
verify(span).label("method", "GET");
verify(span).label("status", 500);
verify(span).failed();
verify(span).close();
}
//~--- set methods ----------------------------------------------------------
/**
@@ -277,6 +306,7 @@ public class DefaultAdvancedHttpClientTest
configuration = new ScmConfiguration();
transformers = new HashSet<ContentTransformer>();
client = new TestingAdvacedHttpClient(configuration, transformers);
when(tracer.span(anyString())).thenReturn(span);
}
//~--- inner classes --------------------------------------------------------
@@ -298,10 +328,9 @@ public class DefaultAdvancedHttpClientTest
* @param configuration
* @param transformers
*/
public TestingAdvacedHttpClient(ScmConfiguration configuration,
Set<ContentTransformer> transformers)
public TestingAdvacedHttpClient(ScmConfiguration configuration, Set<ContentTransformer> transformers)
{
super(configuration, transformers, new SSLContextProvider());
super(configuration, tracer, transformers, new SSLContextProvider());
}
//~--- methods ------------------------------------------------------------
@@ -364,4 +393,10 @@ public class DefaultAdvancedHttpClientTest
/** Field description */
private Set<ContentTransformer> transformers;
@Mock
private Tracer tracer;
@Mock
private Span span;
}

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.net.ahc;
//~--- non-JDK imports --------------------------------------------------------
@@ -33,9 +33,11 @@ import com.google.common.collect.Multimap;
import com.google.common.io.ByteSource;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@@ -56,6 +58,7 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import sonia.scm.net.SSLContextProvider;
import sonia.scm.trace.Tracer;
/**
*
@@ -65,6 +68,13 @@ import sonia.scm.net.SSLContextProvider;
public class DefaultAdvancedHttpResponseTest
{
private DefaultAdvancedHttpClient client;
@Before
public void setUpClient() {
client = new DefaultAdvancedHttpClient(new ScmConfiguration(), tracer, new HashSet<>(), new SSLContextProvider());
}
/**
* Method description
*
@@ -130,13 +140,10 @@ public class DefaultAdvancedHttpResponseTest
assertTrue(headers.get("Test-2").isEmpty());
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private final DefaultAdvancedHttpClient client =
new DefaultAdvancedHttpClient(new ScmConfiguration(), new HashSet<>(), new SSLContextProvider());
/** Field description */
@Mock
private HttpURLConnection connection;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Tracer tracer;
}