Skip to content

Commit 4e33494

Browse files
committed
Introduce support for Vert.x HttpClient and WebClient
1 parent cfb08d8 commit 4e33494

File tree

10 files changed

+147
-4
lines changed

10 files changed

+147
-4
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ dependencies {
4444
compileOnly group: 'io.ktor', name: 'ktor-client-cio', version: '1.5.2'
4545
compileOnly group: 'com.typesafe.akka', name: 'akka-http-core_2.13', version: '10.2.4'
4646
compileOnly group: 'com.typesafe.akka', name: 'akka-actor_2.13', version: '2.6.13'
47+
compileOnly group: 'io.vertx', name: 'vertx-core', version: '4.2.2'
4748

4849
// Test deps:
4950
testImplementation group: 'io.kotest', name: 'kotest-runner-junit5-jvm', version: '4.4.0'
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package tech.httptoolkit.javaagent.advice.vertxclient;
2+
3+
import io.vertx.core.net.ProxyOptions;
4+
import net.bytebuddy.asm.Advice;
5+
import tech.httptoolkit.javaagent.HttpProxyAgent;
6+
7+
public class VertxHttpClientReturnProxyConfigurationAdvice {
8+
9+
@Advice.OnMethodExit
10+
public static void getProxyConfiguration(@Advice.Return(readOnly = false) ProxyOptions returnValue) {
11+
returnValue = new ProxyOptions().setHost(HttpProxyAgent.getAgentProxyHost()).setPort(HttpProxyAgent.getAgentProxyPort());
12+
}
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package tech.httptoolkit.javaagent.advice.vertxclient;
2+
3+
import io.vertx.core.http.HttpClientOptions;
4+
import io.vertx.core.net.ClientOptionsBase;
5+
import io.vertx.core.net.NetClientOptions;
6+
import io.vertx.core.net.TrustOptions;
7+
import net.bytebuddy.asm.Advice;
8+
import tech.httptoolkit.javaagent.HttpProxyAgent;
9+
10+
public class VertxNetClientOptionsSetTrustOptionsAdvice {
11+
12+
@Advice.OnMethodExit
13+
public static void afterConstructor(
14+
@Advice.This NetClientOptions thisNetClientOptions,
15+
@Advice.Argument(value = 0) ClientOptionsBase other
16+
) {
17+
if (other instanceof HttpClientOptions) {
18+
thisNetClientOptions.setTrustOptions(TrustOptions.wrap(HttpProxyAgent.getInterceptedTrustManagerFactory().getTrustManagers()[0]));
19+
}
20+
}
21+
}

src/main/kotlin/tech/httptoolkit/javaagent/AgentMain.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ fun interceptAllHttps(config: Config, instrumentation: Instrumentation) {
117117
AkkaHttpTransformer(logger),
118118
AkkaPoolSettingsTransformer(logger),
119119
AkkaPoolTransformer(logger),
120-
AkkaGatewayTransformer(logger)
120+
AkkaGatewayTransformer(logger),
121+
VertxHttpClientTransformer(logger),
122+
VertxNetClientOptionsTransformer(logger),
121123
).forEach { matchingAgentTransformer ->
122124
agentBuilder = matchingAgentTransformer.register(agentBuilder)
123125
}
@@ -171,4 +173,4 @@ private fun setDefaultProxy(proxyHost: String, proxyPort: Int) {
171173
private fun setDefaultSslContext(context: SSLContext) {
172174
SSLContext.setDefault(context)
173175
HttpsURLConnection.setDefaultSSLSocketFactory(context.socketFactory)
174-
}
176+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package tech.httptoolkit.javaagent
2+
3+
import net.bytebuddy.agent.builder.AgentBuilder
4+
import net.bytebuddy.asm.Advice
5+
import net.bytebuddy.dynamic.DynamicType
6+
import net.bytebuddy.matcher.ElementMatchers.*
7+
8+
// Ensures that the proxy is used by overriding the getProxyOptions method of HttpClientImpl
9+
// to always return our proxy information
10+
class VertxHttpClientTransformer(logger: TransformationLogger): MatchingAgentTransformer(logger) {
11+
override fun register(builder: AgentBuilder): AgentBuilder {
12+
return builder
13+
.type(
14+
named("io.vertx.core.http.impl.HttpClientImpl")
15+
).transform(this)
16+
}
17+
18+
override fun transform(builder: DynamicType.Builder<*>, loadAdvice: (String) -> Advice): DynamicType.Builder<*> {
19+
return builder
20+
.visit(loadAdvice("tech.httptoolkit.javaagent.advice.vertxclient.VertxHttpClientReturnProxyConfigurationAdvice")
21+
.on(hasMethodName("getProxyOptions")))
22+
}
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package tech.httptoolkit.javaagent
2+
3+
import net.bytebuddy.agent.builder.AgentBuilder
4+
import net.bytebuddy.asm.Advice
5+
import net.bytebuddy.description.method.MethodDescription
6+
import net.bytebuddy.dynamic.DynamicType
7+
import net.bytebuddy.matcher.ElementMatchers.*
8+
9+
// Ensures that the proxy is trusted by setting the Vert'x TrustOptions based on the TrustManager
10+
// created by the agent
11+
class VertxNetClientOptionsTransformer(logger: TransformationLogger): MatchingAgentTransformer(logger) {
12+
override fun register(builder: AgentBuilder): AgentBuilder {
13+
return builder
14+
.type(
15+
named("io.vertx.core.net.NetClientOptions")
16+
).transform(this)
17+
}
18+
19+
override fun transform(builder: DynamicType.Builder<*>, loadAdvice: (String) -> Advice): DynamicType.Builder<*> {
20+
return builder
21+
.visit(loadAdvice("tech.httptoolkit.javaagent.advice.vertxclient.VertxNetClientOptionsSetTrustOptionsAdvice")
22+
.on(
23+
isConstructor<MethodDescription>()
24+
.and(takesArguments(1))
25+
.and(takesArgument(0, named("io.vertx.core.net.ClientOptionsBase")))))
26+
}
27+
}

test-app/build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ dependencies {
3232
implementation group: 'com.typesafe.akka', name: 'akka-actor_2.13', version: '2.6.13'
3333
implementation group: 'com.typesafe.akka', name: 'akka-http_2.13', version: '10.2.4'
3434
implementation group: 'com.typesafe.akka', name: 'akka-stream_2.13', version: '2.6.13'
35+
36+
implementation group: 'io.vertx', name: 'vertx-core', version: '4.2.2'
37+
implementation group: 'io.vertx', name: 'vertx-web-client', version: '4.2.2'
3538
}
3639

3740
test {
@@ -56,4 +59,4 @@ shadowJar {
5659
resource = 'reference.conf' // Required for akka
5760
}
5861
with jar
59-
}
62+
}

test-app/src/main/java/tech/httptoolkit/testapp/Main.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ public class Main {
3030
entry("spring-web", new SpringWebClientCase()),
3131
entry("ktor-cio", new KtorCioCase()),
3232
entry("akka-req-http", new AkkaRequestClientCase()),
33-
entry("akka-host-http", new AkkaHostClientCase())
33+
entry("akka-host-http", new AkkaHostClientCase()),
34+
entry("vertx-httpclient", new VertxHttpClientCase()),
35+
entry("vertx-webclient", new VertxWebClientCase())
3436
);
3537

3638
public static void main(String[] args) throws Exception {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package tech.httptoolkit.testapp.cases;
2+
3+
import io.vertx.core.Vertx;
4+
import io.vertx.core.http.HttpClient;
5+
import io.vertx.core.http.HttpClientRequest;
6+
import io.vertx.core.http.HttpClientResponse;
7+
import io.vertx.core.http.HttpMethod;
8+
9+
import java.net.URISyntaxException;
10+
import java.util.concurrent.ExecutionException;
11+
12+
public class VertxHttpClientCase extends ClientCase<HttpClient> {
13+
@Override
14+
public HttpClient newClient(String url) throws Exception {
15+
return Vertx.vertx().createHttpClient();
16+
}
17+
18+
@Override
19+
public int test(String url, HttpClient client) throws URISyntaxException, InterruptedException, ExecutionException {
20+
HttpClientResponse response = client
21+
.request(HttpMethod.GET, url)
22+
.compose(HttpClientRequest::send)
23+
.toCompletionStage().toCompletableFuture().get();
24+
return response.statusCode();
25+
}
26+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package tech.httptoolkit.testapp.cases;
2+
3+
import io.vertx.core.Vertx;
4+
import io.vertx.core.buffer.Buffer;
5+
import io.vertx.core.http.HttpMethod;
6+
import io.vertx.ext.web.client.HttpResponse;
7+
import io.vertx.ext.web.client.WebClient;
8+
import java.net.URISyntaxException;
9+
import java.util.concurrent.ExecutionException;
10+
11+
public class VertxWebClientCase extends ClientCase<WebClient> {
12+
@Override
13+
public WebClient newClient(String url) throws Exception {
14+
return WebClient.create(Vertx.vertx());
15+
}
16+
17+
@Override
18+
public int test(String url, WebClient client) throws URISyntaxException, InterruptedException, ExecutionException {
19+
HttpResponse<Buffer> response = client
20+
.request(HttpMethod.GET, url)
21+
.send()
22+
.toCompletionStage().toCompletableFuture().get();
23+
return response.statusCode();
24+
}
25+
}

0 commit comments

Comments
 (0)