Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies {
compileOnly group: 'io.ktor', name: 'ktor-client-cio', version: '1.5.2'
compileOnly group: 'com.typesafe.akka', name: 'akka-http-core_2.13', version: '10.2.4'
compileOnly group: 'com.typesafe.akka', name: 'akka-actor_2.13', version: '2.6.13'
compileOnly group: 'io.vertx', name: 'vertx-core', version: '4.2.2'

// Test deps:
testImplementation group: 'io.kotest', name: 'kotest-runner-junit5-jvm', version: '4.4.0'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package tech.httptoolkit.javaagent.advice.vertxclient;

import io.vertx.core.net.ProxyOptions;
import net.bytebuddy.asm.Advice;
import tech.httptoolkit.javaagent.HttpProxyAgent;

public class VertxHttpClientReturnProxyConfigurationAdvice {

@Advice.OnMethodExit
public static void getProxyConfiguration(@Advice.Return(readOnly = false) ProxyOptions returnValue) {
returnValue = new ProxyOptions().setHost(HttpProxyAgent.getAgentProxyHost()).setPort(HttpProxyAgent.getAgentProxyPort());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package tech.httptoolkit.javaagent.advice.vertxclient;

import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.net.ClientOptionsBase;
import io.vertx.core.net.NetClientOptions;
import io.vertx.core.net.TrustOptions;
import net.bytebuddy.asm.Advice;
import tech.httptoolkit.javaagent.HttpProxyAgent;

public class VertxNetClientOptionsSetTrustOptionsAdvice {

@Advice.OnMethodExit
public static void afterConstructor(
@Advice.This NetClientOptions thisNetClientOptions,
@Advice.Argument(value = 0) ClientOptionsBase other
) {
if (other instanceof HttpClientOptions) {
thisNetClientOptions.setTrustOptions(TrustOptions.wrap(HttpProxyAgent.getInterceptedTrustManagerFactory().getTrustManagers()[0]));
}
}
}
6 changes: 4 additions & 2 deletions src/main/kotlin/tech/httptoolkit/javaagent/AgentMain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ fun interceptAllHttps(config: Config, instrumentation: Instrumentation) {
AkkaHttpTransformer(logger),
AkkaPoolSettingsTransformer(logger),
AkkaPoolTransformer(logger),
AkkaGatewayTransformer(logger)
AkkaGatewayTransformer(logger),
VertxHttpClientTransformer(logger),
VertxNetClientOptionsTransformer(logger),
).forEach { matchingAgentTransformer ->
agentBuilder = matchingAgentTransformer.register(agentBuilder)
}
Expand Down Expand Up @@ -171,4 +173,4 @@ private fun setDefaultProxy(proxyHost: String, proxyPort: Int) {
private fun setDefaultSslContext(context: SSLContext) {
SSLContext.setDefault(context)
HttpsURLConnection.setDefaultSSLSocketFactory(context.socketFactory)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package tech.httptoolkit.javaagent

import net.bytebuddy.agent.builder.AgentBuilder
import net.bytebuddy.asm.Advice
import net.bytebuddy.dynamic.DynamicType
import net.bytebuddy.matcher.ElementMatchers.*

// Ensures that the proxy is used by overriding the getProxyOptions method of HttpClientImpl
// to always return our proxy information
class VertxHttpClientTransformer(logger: TransformationLogger): MatchingAgentTransformer(logger) {
override fun register(builder: AgentBuilder): AgentBuilder {
return builder
.type(
named("io.vertx.core.http.impl.HttpClientImpl")
).transform(this)
}

override fun transform(builder: DynamicType.Builder<*>, loadAdvice: (String) -> Advice): DynamicType.Builder<*> {
return builder
.visit(loadAdvice("tech.httptoolkit.javaagent.advice.vertxclient.VertxHttpClientReturnProxyConfigurationAdvice")
.on(hasMethodName("getProxyOptions")))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package tech.httptoolkit.javaagent

import net.bytebuddy.agent.builder.AgentBuilder
import net.bytebuddy.asm.Advice
import net.bytebuddy.description.method.MethodDescription
import net.bytebuddy.dynamic.DynamicType
import net.bytebuddy.matcher.ElementMatchers.*

// Ensures that the proxy is trusted by setting the Vert'x TrustOptions based on the TrustManager
// created by the agent
class VertxNetClientOptionsTransformer(logger: TransformationLogger): MatchingAgentTransformer(logger) {
override fun register(builder: AgentBuilder): AgentBuilder {
return builder
.type(
named("io.vertx.core.net.NetClientOptions")
).transform(this)
}

override fun transform(builder: DynamicType.Builder<*>, loadAdvice: (String) -> Advice): DynamicType.Builder<*> {
return builder
.visit(loadAdvice("tech.httptoolkit.javaagent.advice.vertxclient.VertxNetClientOptionsSetTrustOptionsAdvice")
.on(
isConstructor<MethodDescription>()
.and(takesArguments(1))
.and(takesArgument(0, named("io.vertx.core.net.ClientOptionsBase")))))
}
}
5 changes: 4 additions & 1 deletion test-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ dependencies {
implementation group: 'com.typesafe.akka', name: 'akka-actor_2.13', version: '2.6.13'
implementation group: 'com.typesafe.akka', name: 'akka-http_2.13', version: '10.2.4'
implementation group: 'com.typesafe.akka', name: 'akka-stream_2.13', version: '2.6.13'

implementation group: 'io.vertx', name: 'vertx-core', version: '4.2.2'
implementation group: 'io.vertx', name: 'vertx-web-client', version: '4.2.2'
}

test {
Expand All @@ -56,4 +59,4 @@ shadowJar {
resource = 'reference.conf' // Required for akka
}
with jar
}
}
4 changes: 3 additions & 1 deletion test-app/src/main/java/tech/httptoolkit/testapp/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ public class Main {
entry("spring-web", new SpringWebClientCase()),
entry("ktor-cio", new KtorCioCase()),
entry("akka-req-http", new AkkaRequestClientCase()),
entry("akka-host-http", new AkkaHostClientCase())
entry("akka-host-http", new AkkaHostClientCase()),
entry("vertx-httpclient", new VertxHttpClientCase()),
entry("vertx-webclient", new VertxWebClientCase())
);

public static void main(String[] args) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package tech.httptoolkit.testapp.cases;

import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpClientResponse;
import io.vertx.core.http.HttpMethod;

import java.net.URISyntaxException;
import java.util.concurrent.ExecutionException;

public class VertxHttpClientCase extends ClientCase<HttpClient> {
@Override
public HttpClient newClient(String url) throws Exception {
return Vertx.vertx().createHttpClient();
}

@Override
public int test(String url, HttpClient client) throws URISyntaxException, InterruptedException, ExecutionException {
HttpClientResponse response = client
.request(HttpMethod.GET, url)
.compose(HttpClientRequest::send)
.toCompletionStage().toCompletableFuture().get();
return response.statusCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package tech.httptoolkit.testapp.cases;

import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutionException;

public class VertxWebClientCase extends ClientCase<WebClient> {
@Override
public WebClient newClient(String url) throws Exception {
return WebClient.create(Vertx.vertx());
}

@Override
public int test(String url, WebClient client) throws URISyntaxException, InterruptedException, ExecutionException {
HttpResponse<Buffer> response = client
.request(HttpMethod.GET, url)
.send()
.toCompletionStage().toCompletableFuture().get();
return response.statusCode();
}
}