Skip to content

Commit

Permalink
feat: support com.ning.httpclient (#512)
Browse files Browse the repository at this point in the history
  • Loading branch information
YongwuHe authored Jul 1, 2024
1 parent 14687f7 commit d0cf4f6
Show file tree
Hide file tree
Showing 24 changed files with 1,091 additions and 8 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ AREX utilizes the advanced Java technique, Instrument API, and is capable of ins
#### Http Client
- Apache HttpClient [4.0,)
- OkHttp [3.0, 4.11]
- Spring WebClient [5.0,)
- Spring Template
- Feign [9.0,)
- Spring OpenFeign
- Spring RestTemplate
- Spring WebClient [5.0,)
- [ning/async-http-client](https://github.com/ning/async-http-client)
- Elasticsearch Client [7.x,)
#### Redis Client
- RedisTemplate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,21 @@ public static <V> List<V> filterNull(List<V> originalList) {
}
return filterList;
}

public static byte[] listToByteArray(List<byte[]> list) {
int totalLength = 0;
for (byte[] array : list) {
totalLength += array.length;
}

byte[] result = new byte[totalLength];

int currentPos = 0;
for (byte[] array : list) {
System.arraycopy(array, 0, result, currentPos, array.length);
currentPos += array.length;
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,14 @@ public static Set<String> splitToSet(String str, char separatorChars) {
return new HashSet<>(Arrays.asList(strs));
}

public static List<String> splitToList(String str, char separatorChars) {
if (isEmpty(str)) {
return Collections.emptyList();
}
String[] strs = split(str, separatorChars);
return Arrays.asList(strs);
}

public static boolean isNumeric(final String cs) {
if (isEmpty(cs)) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,17 @@ void filterNull() {
actualResult = CollectionUtil.filterNull(CollectionUtil.newArrayList("mock"));
assertEquals(1, actualResult.size());
}

@Test
void testListToByteArray() {
List<byte[]> list = new ArrayList<>();
list.add(new byte[]{1, 2, 3});
list.add(new byte[]{4, 5, 6});
list.add(new byte[]{7, 8, 9});

byte[] result = CollectionUtil.listToByteArray(list);

byte[] expected = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
assertArrayEquals(expected, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import static org.junit.jupiter.api.Assertions.*;

import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.*;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -324,6 +322,24 @@ void testSplitToSet() {
assertTrue(set.contains("c"));
}

@Test
void testSplitToList() {
// null string
List<String> nullSet = StringUtil.splitToList(null, ',');
assertEquals(0, nullSet.size());

// empty string
List<String> emptySet = StringUtil.splitToList("", ',');
assertEquals(0, emptySet.size());

String s = "aaa,bb,c";
List<String> set = StringUtil.splitToList(s, ',');
assertEquals(3, set.size());
assertTrue(set.contains("aaa"));
assertTrue(set.contains("bb"));
assertTrue(set.contains("c"));
}

@Test
void testIsNumeric() {
String s = "123";
Expand Down
5 changes: 5 additions & 0 deletions arex-agent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@
<artifactId>arex-httpclient-feign</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>arex-httpclient-ning</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>arex-netty-v3</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.arex.inst.dynamic.common.listener;
package io.arex.inst.runtime.listener;

import java.util.concurrent.Executor;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.arex.inst.runtime.listener;

import org.junit.jupiter.api.Test;

import java.util.concurrent.Executor;

import static org.junit.jupiter.api.Assertions.*;

class DirectExecutorTest {

@Test
void execute() {
Executor executor = DirectExecutor.INSTANCE;
final boolean[] ran = {false};
executor.execute(() -> ran[0] = true);
assertTrue(ran[0]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.google.common.util.concurrent.ListenableFuture;

import io.arex.inst.dynamic.common.DynamicClassExtractor;
import io.arex.inst.runtime.listener.DirectExecutor;


public class ListenableFutureAdapter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ public class HttpResponseWrapper {
private StringTuple locale;
private List<StringTuple> headers;
private String reason;
private int statusCode;
private String typeName;

public String getTypeName() {
return typeName;
}

public void setTypeName(String typeName) {
this.typeName = typeName;
}

public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}

public int getStatusCode() {
return statusCode;
}

public String getReason() {
return reason;
Expand Down Expand Up @@ -91,4 +109,4 @@ public String getS() {
return s;
}
}
}
}
28 changes: 28 additions & 0 deletions arex-instrumentation/httpclient/arex-httpclient-ning/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.arex</groupId>
<artifactId>arex-instrumentation-parent</artifactId>
<version>${revision}</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>arex-httpclient-ning</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>arex-httpclient-common</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
<version>1.9.39</version>
<scope>provided</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.arex.inst.httpclient;

import com.google.auto.service.AutoService;
import io.arex.inst.extension.ModuleInstrumentation;
import io.arex.inst.extension.TypeInstrumentation;
import io.arex.inst.httpclient.ning.AsyncHttpClientInstrumentation;

import java.util.Collections;
import java.util.List;

@AutoService(ModuleInstrumentation.class)
public class AsyncHttpClientModuleInstrumentation extends ModuleInstrumentation {
public AsyncHttpClientModuleInstrumentation() {
super("arex-httpclient-ning");
}

@Override
public List<TypeInstrumentation> instrumentationTypes() {
return Collections.singletonList(new AsyncHttpClientInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package io.arex.inst.httpclient.ning;

import com.ning.http.client.ListenableFuture;
import com.ning.http.client.Request;
import com.ning.http.client.Response;
import io.arex.agent.bootstrap.model.MockResult;
import io.arex.inst.extension.MethodInstrumentation;
import io.arex.inst.extension.TypeInstrumentation;
import io.arex.inst.httpclient.common.HttpClientAdapter;
import io.arex.inst.httpclient.common.HttpClientExtractor;
import io.arex.inst.runtime.context.ContextManager;
import io.arex.inst.runtime.context.RepeatedCollectManager;
import io.arex.inst.runtime.listener.DirectExecutor;
import io.arex.inst.runtime.util.IgnoreUtils;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;

import java.util.Collections;
import java.util.List;

import static net.bytebuddy.matcher.ElementMatchers.*;

public class AsyncHttpClientInstrumentation extends TypeInstrumentation {
@Override
protected ElementMatcher<TypeDescription> typeMatcher() {
return named("com.ning.http.client.AsyncHttpClient");
}

@Override
public List<MethodInstrumentation> methodAdvices() {
return Collections.singletonList(new MethodInstrumentation(
isMethod().and(named("executeRequest").and(takesArguments(2))), ExecuteRequestAdvice.class.getName()));
}

public static class ExecuteRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class)
public static boolean onEnter(@Advice.Argument(0) Request request,
@Advice.Local("mockResult") MockResult mockResult,
@Advice.Local("extractor") HttpClientExtractor<Request, Object> extractor){
if (IgnoreUtils.excludeOperation(request.getUri().getPath())) {
return false;
}
if (ContextManager.needRecord()) {
RepeatedCollectManager.enter();
}
if (ContextManager.needRecordOrReplay()) {
HttpClientAdapter<Request, Object> adapter = new NingHttpClientAdapter(request);
extractor = new HttpClientExtractor<>(adapter);
if (ContextManager.needReplay()) {
mockResult = extractor.replay();
return mockResult != null && mockResult.notIgnoreMockResult();
}
}
return false;
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit(@Advice.Local("mockResult") MockResult mockResult,
@Advice.Local("extractor") HttpClientExtractor<Request, Object> extractor,
@Advice.Return(readOnly = false) ListenableFuture responseFuture,
@Advice.Thrown(readOnly = false) Throwable throwable) {
if (mockResult != null && mockResult.notIgnoreMockResult()) {
if (mockResult.getThrowable() != null) {
throwable = mockResult.getThrowable();
} else {
responseFuture = new ResponseFutureWrapper(mockResult.getResult());
}
return;
}
if (ContextManager.needRecord() && RepeatedCollectManager.exitAndValidate() && extractor != null) {
if (throwable != null) {
extractor.record(throwable);
} else {
responseFuture.addListener(new ResponseFutureListener(extractor, responseFuture), DirectExecutor.INSTANCE);
}
}
}
}
}
Loading

0 comments on commit d0cf4f6

Please sign in to comment.