Skip to content

Commit

Permalink
feat: add httpclient: java.net.HttpURLConnection support (#320)
Browse files Browse the repository at this point in the history
* feat: add httpclient: java.net.HttpURLConnection support

* fix: HttpURLConnectionPlugin moved to package com.megaease.easeagent.plugin.httpurlconnection;

* feat: add httpURLConnection plugin config

* feat: add forwarded plugin for independent from Trace
  • Loading branch information
lanxenet authored Sep 25, 2023
1 parent 9614240 commit d309007
Show file tree
Hide file tree
Showing 13 changed files with 651 additions and 0 deletions.
2 changes: 2 additions & 0 deletions build/src/main/resources/agent.properties
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ plugin.observability.redis.metric.url=/platform-metrics
# plugin.observability.feignClient.tracing.enabled=true
## restTemplate tracing
# plugin.observability.restTemplate.tracing.enabled=true
## httpURLConnection tracing
# plugin.observability.httpURLConnection.tracing.enabled=true

# -------------------- service name ---------------------
## add service name to header by name for easemesh. default name: X-Mesh-RPC-Service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ interface Namespace {
String WEB_CLIENT = "webclient";
String FEIGN_CLIENT = "feignClient";
String REST_TEMPLATE = "resTemplate";
String HTTPURLCONNECTION = "httpURLConnection";

String FORWARDED = "forwarded";
}
Expand Down
50 changes: 50 additions & 0 deletions plugins/httpurlconnection/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2023, MegaEase
~ All rights reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<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>
<artifactId>plugins</artifactId>
<groupId>com.megaease.easeagent</groupId>
<version>2.2.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>httpurlconnection</artifactId>


<dependencies>
<dependency>
<groupId>com.megaease.easeagent</groupId>
<artifactId>plugin-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.megaease.easeagent</groupId>
<artifactId>plugin-api-mock</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2023, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.megaease.easeagent.plugin.httpurlconnection;

import com.megaease.easeagent.plugin.AgentPlugin;
import com.megaease.easeagent.plugin.api.config.ConfigConst;

public class ForwardedPlugin implements AgentPlugin {
@Override
public String getNamespace() {
return ConfigConst.Namespace.FORWARDED;
}

@Override
public String getDomain() {
return ConfigConst.INTEGRABILITY;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2023, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.megaease.easeagent.plugin.httpurlconnection;

import com.megaease.easeagent.plugin.AgentPlugin;
import com.megaease.easeagent.plugin.api.config.ConfigConst;

public class HttpURLConnectionPlugin implements AgentPlugin {
@Override
public String getNamespace() {
return ConfigConst.Namespace.HTTPURLCONNECTION;
}

@Override
public String getDomain() {
return ConfigConst.OBSERVABILITY;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2023, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.megaease.easeagent.plugin.httpurlconnection.advice;

import com.megaease.easeagent.plugin.Points;
import com.megaease.easeagent.plugin.matcher.ClassMatcher;
import com.megaease.easeagent.plugin.matcher.IClassMatcher;
import com.megaease.easeagent.plugin.matcher.IMethodMatcher;
import com.megaease.easeagent.plugin.matcher.MethodMatcher;

import java.util.Set;

public class HttpURLConnectionGetResponseCodeAdvice implements Points {

@Override
public IClassMatcher getClassMatcher() {
return ClassMatcher.builder()
.hasSuperClass("java.net.HttpURLConnection")
.build();
}

@Override
public Set<IMethodMatcher> getMethodMatcher() {
return MethodMatcher.multiBuilder()
.match(MethodMatcher.builder().isPublic().named("getResponseCode").build())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2023, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.megaease.easeagent.plugin.httpurlconnection.interceptor;

import com.megaease.easeagent.plugin.annotation.AdviceTo;
import com.megaease.easeagent.plugin.api.Context;
import com.megaease.easeagent.plugin.api.config.ConfigConst;
import com.megaease.easeagent.plugin.enums.Order;
import com.megaease.easeagent.plugin.httpurlconnection.ForwardedPlugin;
import com.megaease.easeagent.plugin.httpurlconnection.HttpURLConnectionPlugin;
import com.megaease.easeagent.plugin.httpurlconnection.advice.HttpURLConnectionGetResponseCodeAdvice;
import com.megaease.easeagent.plugin.interceptor.Interceptor;
import com.megaease.easeagent.plugin.interceptor.MethodInfo;

import java.net.HttpURLConnection;

@AdviceTo(value = HttpURLConnectionGetResponseCodeAdvice.class, qualifier = "default", plugin = ForwardedPlugin.class)
public class HttpURLConnectionGetResponseCodeForwardedInterceptor implements Interceptor {

@Override
public void before(MethodInfo methodInfo, Context context) {
HttpURLConnection httpURLConnection = (HttpURLConnection) methodInfo.getInvoker();
context.injectForwardedHeaders(httpURLConnection::setRequestProperty);
}

@Override
public String getType() {
return ConfigConst.PluginID.FORWARDED;
}

@Override
public int order() {
return Order.FORWARDED.getOrder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright (c) 2023, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.megaease.easeagent.plugin.httpurlconnection.interceptor;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.megaease.easeagent.plugin.annotation.AdviceTo;
import com.megaease.easeagent.plugin.api.Context;
import com.megaease.easeagent.plugin.api.trace.Span;
import com.megaease.easeagent.plugin.httpurlconnection.HttpURLConnectionPlugin;
import com.megaease.easeagent.plugin.httpurlconnection.advice.HttpURLConnectionGetResponseCodeAdvice;
import com.megaease.easeagent.plugin.interceptor.MethodInfo;
import com.megaease.easeagent.plugin.tools.trace.BaseHttpClientTracingInterceptor;
import com.megaease.easeagent.plugin.tools.trace.HttpRequest;
import com.megaease.easeagent.plugin.tools.trace.HttpResponse;
import lombok.SneakyThrows;

import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;


@AdviceTo(value = HttpURLConnectionGetResponseCodeAdvice.class, qualifier = "default", plugin = HttpURLConnectionPlugin.class)
public class HttpURLConnectionGetResponseCodeInterceptor extends BaseHttpClientTracingInterceptor {
@Override
public Object getProgressKey() {
return HttpURLConnectionGetResponseCodeInterceptor.class;
}

@Override
protected HttpRequest getRequest(MethodInfo methodInfo, Context context) {
return new InternalRequest((HttpURLConnection) methodInfo.getInvoker());
}


@Override
protected HttpResponse getResponse(MethodInfo methodInfo, Context context) {
return new InternalResponse(methodInfo.getThrowable(), (HttpURLConnection) methodInfo.getInvoker());
}


final static class InternalRequest implements HttpRequest {

private final HttpURLConnection httpURLConn;

public InternalRequest(HttpURLConnection httpURLConn) {
this.httpURLConn = httpURLConn;
}


@Override
public String method() {
return httpURLConn.getRequestMethod();
}

@Override
public String path() {
return httpURLConn.getURL().toString();
}

@Override
public String route() {
return null;
}

@Override
public String getRemoteAddr() {
return null;
}

@Override
public int getRemotePort() {
return 0;
}

@Override
public Span.Kind kind() {
return Span.Kind.CLIENT;
}

@Override
public String header(String name) {
return httpURLConn.getRequestProperty(name);
}

@Override
public boolean cacheScope() {
return false;
}

@Override
public void setHeader(String name, String value) {
httpURLConn.setRequestProperty(name, value);
}

}

final static class InternalResponse implements HttpResponse {
private final Throwable caught;
private final HttpURLConnection httpURLConn;
private final Multimap<String, String> headers;

public InternalResponse(Throwable caught, HttpURLConnection httpURLConn) {
this.caught = caught;
this.httpURLConn = httpURLConn;
this.headers = getResponseHeaders(httpURLConn);
}

@Override
public String method() {
return httpURLConn.getRequestMethod();
}

@Override
public String route() {
return null;
}

@SneakyThrows
@Override
public int statusCode() {
return this.httpURLConn.getResponseCode();
}

@Override
public Throwable maybeError() {
return caught;
}


@Override
public String header(String name) {
return this.headers.get(name).stream().findFirst().orElse(null);

}

private Multimap<String, String> getResponseHeaders(HttpURLConnection uc) {
Multimap<String, String> headers = ArrayListMultimap.create();
for (Map.Entry<String, List<String>> e : uc.getHeaderFields().entrySet()) {
if (e.getKey() != null) {
headers.putAll(e.getKey(), e.getValue());
}
}
return headers;
}
}
}
Loading

0 comments on commit d309007

Please sign in to comment.