From 91d94fb9fa5333d43e92aec1d0b99eed80a28800 Mon Sep 17 00:00:00 2001 From: Jason Keller Date: Thu, 1 Feb 2024 13:47:20 -0800 Subject: [PATCH 1/7] Add custom logging around Netty transaction start point --- .../netty/bootstrap/NettyDispatcher.java | 17 ++++++++++++---- .../codec/http/HttpMessageDecoder.java | 20 +++++++++++++------ .../netty/bootstrap/NettyDispatcher.java | 13 ++++++++++-- .../codec/http/HttpMessageDecoder.java | 20 +++++++++++++------ .../io/netty/bootstrap/NettyDispatcher.java | 12 ++++++++++- .../handler/codec/http/HttpObjectDecoder.java | 14 +++++++++++-- .../io/netty/bootstrap/NettyDispatcher.java | 11 +++++++++- .../handler/codec/http/HttpObjectDecoder.java | 15 ++++++++++++-- 8 files changed, 98 insertions(+), 24 deletions(-) diff --git a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java index 900d994adf..7192204308 100644 --- a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java @@ -7,8 +7,6 @@ package org.jboss.netty.bootstrap; -import java.util.logging.Level; - import com.agent.instrumentation.netty34.RequestWrapper; import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.Transaction; @@ -20,10 +18,12 @@ import org.jboss.netty.channel.ChannelHandlerContext_Instrumentation; import org.jboss.netty.handler.codec.http.DefaultHttpRequest; +import java.util.logging.Level; + /** * This isn't a netty class. This is an agent ChannelUpstreamHandler which will start a transaction on i/o read. It's * best to put this handler in front of "interesting" (e.g. ServerBootstrap) pipelines. - * + *

* Since this class creates a tracer, its class+method name will show in the TT, hence the class name. */ public class NettyDispatcher { @@ -58,14 +58,23 @@ public static void upstreamDispatcher(ChannelHandlerContext_Instrumentation ctx, AgentBridge.currentApiSource.set(WeavePackageType.INTERNAL); AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.SERVLET_NAME, true, "NettyDispatcher", "NettyDispatcher"); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, "Netty Debug: Set transaction name to NettyDispatcher for transaction: " + AgentBridge.getAgent().getTransaction()); } Transaction tx = AgentBridge.getAgent().getTransaction(false); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.getPipeline().token); + if (tx != null) { tx.setWebRequest(new RequestWrapper((DefaultHttpRequest) msg)); } - } catch(Throwable t) { + } catch (Throwable t) { AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle()); } finally { AgentBridge.currentApiSource.remove(); diff --git a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java index 97daeeee2b..1d58913838 100644 --- a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java +++ b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java @@ -7,23 +7,31 @@ package org.jboss.netty.handler.codec.http; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; import org.jboss.netty.bootstrap.NettyDispatcher; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelHandlerContext_Instrumentation; -import com.agent.instrumentation.netty34.NettyUtil; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; +import java.util.logging.Level; @Weave(type = MatchType.BaseClass) public class HttpMessageDecoder { @Trace protected Object decode(ChannelHandlerContext_Instrumentation ctx, Channel channel, ChannelBuffer buffer, State state) { Object request = Weaver.callOriginal(); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, + "Netty Debug: Called HttpObjectDecoder.decode with request of type: " + request.getClass() + " for transaction: " + + AgentBridge.getAgent().getTransaction() + ". Token: " + + ctx.getPipeline().token); + if (request instanceof HttpRequest && ctx.getPipeline().token == null) { NettyDispatcher.upstreamDispatcher(ctx, request); } diff --git a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java index 4991fec4c3..9b97cc4ca6 100644 --- a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java @@ -7,8 +7,6 @@ package org.jboss.netty.bootstrap; -import java.util.logging.Level; - import com.agent.instrumentation.netty38.RequestWrapper; import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.agent.bridge.Transaction; @@ -20,6 +18,8 @@ import org.jboss.netty.channel.ChannelHandlerContext_Instrumentation; import org.jboss.netty.handler.codec.http.DefaultHttpRequest; +import java.util.logging.Level; + /** * This isn't a netty class. This is an agent ChannelUpstreamHandler which will start a transaction on i/o read. It's * best to put this handler in front of "interesting" (e.g. ServerBootstrap) pipelines. @@ -58,9 +58,18 @@ public static void upstreamDispatcher(ChannelHandlerContext_Instrumentation ctx, AgentBridge.currentApiSource.set(WeavePackageType.INTERNAL); AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.SERVLET_NAME, true, "NettyDispatcher", "NettyDispatcher"); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, "Netty Debug: Set transaction name to NettyDispatcher for transaction: " + AgentBridge.getAgent().getTransaction()); } Transaction tx = AgentBridge.getAgent().getTransaction(false); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.getPipeline().token); + if (tx != null) { tx.setWebRequest(new RequestWrapper((DefaultHttpRequest) msg)); } diff --git a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java index 1394a4fe02..1d58913838 100644 --- a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java +++ b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java @@ -7,23 +7,31 @@ package org.jboss.netty.handler.codec.http; -import org.jboss.netty.bootstrap.NettyDispatcher; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelHandlerContext; - -import com.agent.instrumentation.netty38.NettyUtil; +import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import org.jboss.netty.bootstrap.NettyDispatcher; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext_Instrumentation; +import java.util.logging.Level; + @Weave(type = MatchType.BaseClass) public class HttpMessageDecoder { @Trace protected Object decode(ChannelHandlerContext_Instrumentation ctx, Channel channel, ChannelBuffer buffer, State state) { Object request = Weaver.callOriginal(); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, + "Netty Debug: Called HttpObjectDecoder.decode with request of type: " + request.getClass() + " for transaction: " + + AgentBridge.getAgent().getTransaction() + ". Token: " + + ctx.getPipeline().token); + if (request instanceof HttpRequest && ctx.getPipeline().token == null) { NettyDispatcher.upstreamDispatcher(ctx, request); } diff --git a/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java index efbed142f5..f45b8789fd 100644 --- a/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java @@ -20,7 +20,7 @@ /** * This isn't a netty class. This is an agent class which will start a transaction on i/o read. - * + *

* Since this class creates a tracer, its class+method name will show in the TT, hence the class name. */ public class NettyDispatcher { @@ -53,9 +53,19 @@ public static void channelRead(ChannelHandlerContext_Instrumentation ctx, Object tracer.setMetricName("NettyUpstreamDispatcher"); AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.SERVLET_NAME, true, "NettyDispatcher", "NettyDispatcher"); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, "Netty Debug: Set transaction name to NettyDispatcher for transaction: " + AgentBridge.getAgent().getTransaction()); + } Transaction tx = AgentBridge.getAgent().getTransaction(false); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.pipeline().token); + if (tx != null) { tx.setWebRequest(new RequestWrapper((DefaultHttpRequest) msg)); } diff --git a/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index e26c3c79b8..d31ab7624c 100644 --- a/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -7,8 +7,7 @@ package io.netty.handler.codec.http; -import java.util.List; - +import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -16,6 +15,9 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext_Instrumentation; +import java.util.List; +import java.util.logging.Level; + @Weave(type = MatchType.BaseClass) public class HttpObjectDecoder { @@ -23,6 +25,14 @@ public class HttpObjectDecoder { protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, List out) { Weaver.callOriginal(); for (Object msg : out) { + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, + "Netty Debug: Called HttpObjectDecoder.decode with msg of type: " + msg.getClass() + " for transaction: " + + AgentBridge.getAgent().getTransaction() + ". Token: " + + ctx.pipeline().token); + if (msg instanceof HttpRequest && ctx.pipeline().token == null) { NettyDispatcher.channelRead(ctx, msg); } diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java index 820412091d..09f3fb7e09 100644 --- a/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java @@ -21,7 +21,7 @@ /** * This isn't a netty class. This is an agent class which will start a transaction on i/o read. - * + *

* Since this class creates a tracer, its class+method name will show in the TT, hence the class name. */ public class NettyDispatcher { @@ -56,9 +56,18 @@ public static void channelRead(ChannelHandlerContext_Instrumentation ctx, Object tracer.setMetricName("NettyUpstreamDispatcher"); AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.SERVLET_NAME, true, "NettyDispatcher", "NettyDispatcher"); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, "Netty Debug: Set transaction name to NettyDispatcher for transaction: " + AgentBridge.getAgent().getTransaction()); } Transaction tx = AgentBridge.getAgent().getTransaction(false); + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.pipeline().token); + if (tx != null) { tx.setWebRequest(new RequestWrapper((HttpRequest) msg)); } diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 91de1b45dd..8ec471e90e 100644 --- a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -7,8 +7,8 @@ package io.netty.handler.codec.http; -import java.util.List; - +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -16,6 +16,9 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext_Instrumentation; +import java.util.List; +import java.util.logging.Level; + @Weave(type = MatchType.BaseClass) public class HttpObjectDecoder { @@ -23,6 +26,14 @@ public class HttpObjectDecoder { protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, List out) { Weaver.callOriginal(); for (Object msg : out) { + + AgentBridge.getAgent() + .getLogger() + .log(Level.INFO, + "Netty Debug: Called HttpObjectDecoder.decode with msg of type: " + msg.getClass() + " for transaction: " + + AgentBridge.getAgent().getTransaction() + ". Token: " + + ctx.pipeline().token); + if (msg instanceof HttpRequest && ctx.pipeline().token == null) { // NettyDispatcher class is usually initialized in AbstractBootstrap; however, // that code is not always invoked when using recent Netty versions (4.1.54) From cf834cf389dc97239b7adee889fb3391cb156114 Mon Sep 17 00:00:00 2001 From: Jason Keller Date: Thu, 1 Feb 2024 16:45:30 -0800 Subject: [PATCH 2/7] Add some additional context to logging --- .../org/jboss/netty/bootstrap/NettyDispatcher.java | 4 +++- .../netty/handler/codec/http/HttpMessageDecoder.java | 5 ++++- .../org/jboss/netty/bootstrap/NettyDispatcher.java | 10 ++++++---- .../netty/handler/codec/http/HttpMessageDecoder.java | 5 ++++- .../main/java/io/netty/bootstrap/NettyDispatcher.java | 3 ++- .../io/netty/handler/codec/http/HttpObjectDecoder.java | 5 ++++- .../main/java/io/netty/bootstrap/NettyDispatcher.java | 3 ++- .../io/netty/handler/codec/http/HttpObjectDecoder.java | 6 ++++-- 8 files changed, 29 insertions(+), 12 deletions(-) diff --git a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java index 7192204308..3809c1e347 100644 --- a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java @@ -68,7 +68,9 @@ public static void upstreamDispatcher(ChannelHandlerContext_Instrumentation ctx, AgentBridge.getAgent() .getLogger() - .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.getPipeline().token); + .log(Level.INFO, + "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.getPipeline().token + ". ctx: " + + ctx); if (tx != null) { tx.setWebRequest(new RequestWrapper((DefaultHttpRequest) msg)); diff --git a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java index 1d58913838..f2ab43d7d0 100644 --- a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java +++ b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java @@ -25,12 +25,15 @@ public class HttpMessageDecoder { protected Object decode(ChannelHandlerContext_Instrumentation ctx, Channel channel, ChannelBuffer buffer, State state) { Object request = Weaver.callOriginal(); + boolean typeCheck = (request instanceof HttpRequest); + AgentBridge.getAgent() .getLogger() .log(Level.INFO, "Netty Debug: Called HttpObjectDecoder.decode with request of type: " + request.getClass() + " for transaction: " + AgentBridge.getAgent().getTransaction() + ". Token: " + - ctx.getPipeline().token); + ctx.getPipeline().token + ". ctx: " + + ctx + ". instanceof HttpRequest = " + typeCheck); if (request instanceof HttpRequest && ctx.getPipeline().token == null) { NettyDispatcher.upstreamDispatcher(ctx, request); diff --git a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java index 9b97cc4ca6..f0476d242b 100644 --- a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java @@ -23,7 +23,7 @@ /** * This isn't a netty class. This is an agent ChannelUpstreamHandler which will start a transaction on i/o read. It's * best to put this handler in front of "interesting" (e.g. ServerBootstrap) pipelines. - * + *

* Since this class creates a tracer, its class+method name will show in the TT, hence the class name. */ public class NettyDispatcher { @@ -68,13 +68,15 @@ public static void upstreamDispatcher(ChannelHandlerContext_Instrumentation ctx, AgentBridge.getAgent() .getLogger() - .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.getPipeline().token); + .log(Level.INFO, + "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.getPipeline().token + ". ctx: " + + ctx); if (tx != null) { tx.setWebRequest(new RequestWrapper((DefaultHttpRequest) msg)); } - - } catch(Throwable t) { + + } catch (Throwable t) { AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle()); } finally { AgentBridge.currentApiSource.remove(); diff --git a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java index 1d58913838..f2ab43d7d0 100644 --- a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java +++ b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java @@ -25,12 +25,15 @@ public class HttpMessageDecoder { protected Object decode(ChannelHandlerContext_Instrumentation ctx, Channel channel, ChannelBuffer buffer, State state) { Object request = Weaver.callOriginal(); + boolean typeCheck = (request instanceof HttpRequest); + AgentBridge.getAgent() .getLogger() .log(Level.INFO, "Netty Debug: Called HttpObjectDecoder.decode with request of type: " + request.getClass() + " for transaction: " + AgentBridge.getAgent().getTransaction() + ". Token: " + - ctx.getPipeline().token); + ctx.getPipeline().token + ". ctx: " + + ctx + ". instanceof HttpRequest = " + typeCheck); if (request instanceof HttpRequest && ctx.getPipeline().token == null) { NettyDispatcher.upstreamDispatcher(ctx, request); diff --git a/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java index f45b8789fd..8d50eca5f0 100644 --- a/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java @@ -64,7 +64,8 @@ public static void channelRead(ChannelHandlerContext_Instrumentation ctx, Object AgentBridge.getAgent() .getLogger() - .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.pipeline().token); + .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.pipeline().token + ". ctx: " + + ctx); if (tx != null) { tx.setWebRequest(new RequestWrapper((DefaultHttpRequest) msg)); diff --git a/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index d31ab7624c..8bd772f7a7 100644 --- a/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -26,12 +26,15 @@ protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, Weaver.callOriginal(); for (Object msg : out) { + boolean typeCheck = (msg instanceof HttpRequest); + AgentBridge.getAgent() .getLogger() .log(Level.INFO, "Netty Debug: Called HttpObjectDecoder.decode with msg of type: " + msg.getClass() + " for transaction: " + AgentBridge.getAgent().getTransaction() + ". Token: " + - ctx.pipeline().token); + ctx.pipeline().token + ". ctx: " + + ctx + ". instanceof HttpRequest = " + typeCheck); if (msg instanceof HttpRequest && ctx.pipeline().token == null) { NettyDispatcher.channelRead(ctx, msg); diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java index 09f3fb7e09..14b40d27d1 100644 --- a/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java @@ -66,7 +66,8 @@ public static void channelRead(ChannelHandlerContext_Instrumentation ctx, Object AgentBridge.getAgent() .getLogger() - .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.pipeline().token); + .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.pipeline().token + ". ctx: " + + ctx); if (tx != null) { tx.setWebRequest(new RequestWrapper((HttpRequest) msg)); diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 8ec471e90e..d4810d10a1 100644 --- a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -8,7 +8,6 @@ package io.netty.handler.codec.http; import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -27,12 +26,15 @@ protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, Weaver.callOriginal(); for (Object msg : out) { + boolean typeCheck = (msg instanceof HttpRequest); + AgentBridge.getAgent() .getLogger() .log(Level.INFO, "Netty Debug: Called HttpObjectDecoder.decode with msg of type: " + msg.getClass() + " for transaction: " + AgentBridge.getAgent().getTransaction() + ". Token: " + - ctx.pipeline().token); + ctx.pipeline().token + ". ctx: " + + ctx + ". instanceof HttpRequest = " + typeCheck); if (msg instanceof HttpRequest && ctx.pipeline().token == null) { // NettyDispatcher class is usually initialized in AbstractBootstrap; however, From f909d1332cf2c6dcf02a1dfdc92564a6dcdd2d91 Mon Sep 17 00:00:00 2001 From: Jason Keller Date: Thu, 21 Mar 2024 15:37:50 -0700 Subject: [PATCH 3/7] Add netty-4.1.16 module with HTTP/2 support --- .../netty/bootstrap/NettyDispatcher.java | 11 -- .../codec/http/HttpMessageDecoder.java | 14 -- .../netty/bootstrap/NettyDispatcher.java | 11 -- .../codec/http/HttpMessageDecoder.java | 14 -- .../io/netty/bootstrap/NettyDispatcher.java | 11 -- .../handler/codec/http/HttpObjectDecoder.java | 14 -- instrumentation/netty-4.0.8/build.gradle | 13 +- .../io/netty/bootstrap/NettyDispatcher.java | 10 -- .../handler/codec/http/HttpObjectDecoder.java | 14 -- .../http2/DefaultHttp2Connection_Skip.java | 11 ++ .../java/netty408/RequestWrapperTest.java | 9 +- instrumentation/netty-4.1.16/build.gradle | 24 +++ .../netty4116/Http2RequestWrapper.java | 147 ++++++++++++++++++ .../netty4116/Http2ResponseWrapper.java | 88 +++++++++++ .../instrumentation/netty4116/NettyUtil.java | 62 ++++++++ .../netty4116/RequestWrapper.java | 119 ++++++++++++++ .../netty4116/ResponseWrapper.java | 57 +++++++ .../io/netty/bootstrap/AbstractBootstrap.java | 32 ++++ .../io/netty/bootstrap/NettyDispatcher.java | 72 +++++++++ ...ChannelHandlerContext_Instrumentation.java | 17 ++ .../ChannelHandler_Instrumentation.java | 47 ++++++ ...ChannelInboundHandler_Instrumentation.java | 25 +++ .../ChannelPipeline_Instrumentation.java | 20 +++ .../handler/codec/http/HttpObjectDecoder.java | 38 +++++ .../handler/codec/http/HttpObjectEncoder.java | 29 ++++ .../handler/codec/http2/Http2FrameCodec.java | 50 ++++++ .../java/netty4116/RequestWrapperTest.java | 41 +++++ settings.gradle | 1 + 28 files changed, 896 insertions(+), 105 deletions(-) create mode 100644 instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection_Skip.java create mode 100644 instrumentation/netty-4.1.16/build.gradle create mode 100644 instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/Http2RequestWrapper.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/Http2ResponseWrapper.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/NettyUtil.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/RequestWrapper.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/ResponseWrapper.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/NettyDispatcher.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandlerContext_Instrumentation.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandler_Instrumentation.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelInboundHandler_Instrumentation.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelPipeline_Instrumentation.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java create mode 100644 instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java create mode 100644 instrumentation/netty-4.1.16/src/test/java/netty4116/RequestWrapperTest.java diff --git a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java index 3809c1e347..c50fe4519d 100644 --- a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java @@ -58,20 +58,10 @@ public static void upstreamDispatcher(ChannelHandlerContext_Instrumentation ctx, AgentBridge.currentApiSource.set(WeavePackageType.INTERNAL); AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.SERVLET_NAME, true, "NettyDispatcher", "NettyDispatcher"); - - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, "Netty Debug: Set transaction name to NettyDispatcher for transaction: " + AgentBridge.getAgent().getTransaction()); } Transaction tx = AgentBridge.getAgent().getTransaction(false); - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, - "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.getPipeline().token + ". ctx: " + - ctx); - if (tx != null) { tx.setWebRequest(new RequestWrapper((DefaultHttpRequest) msg)); } @@ -82,5 +72,4 @@ public static void upstreamDispatcher(ChannelHandlerContext_Instrumentation ctx, AgentBridge.currentApiSource.remove(); } } - } diff --git a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java index f2ab43d7d0..da5ebdb9a9 100644 --- a/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java +++ b/instrumentation/netty-3.4/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java @@ -7,7 +7,6 @@ package org.jboss.netty.handler.codec.http; -import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; @@ -17,24 +16,11 @@ import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext_Instrumentation; -import java.util.logging.Level; - @Weave(type = MatchType.BaseClass) public class HttpMessageDecoder { @Trace protected Object decode(ChannelHandlerContext_Instrumentation ctx, Channel channel, ChannelBuffer buffer, State state) { Object request = Weaver.callOriginal(); - - boolean typeCheck = (request instanceof HttpRequest); - - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, - "Netty Debug: Called HttpObjectDecoder.decode with request of type: " + request.getClass() + " for transaction: " + - AgentBridge.getAgent().getTransaction() + ". Token: " + - ctx.getPipeline().token + ". ctx: " + - ctx + ". instanceof HttpRequest = " + typeCheck); - if (request instanceof HttpRequest && ctx.getPipeline().token == null) { NettyDispatcher.upstreamDispatcher(ctx, request); } diff --git a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java index f0476d242b..c0717bb95d 100644 --- a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/bootstrap/NettyDispatcher.java @@ -58,20 +58,10 @@ public static void upstreamDispatcher(ChannelHandlerContext_Instrumentation ctx, AgentBridge.currentApiSource.set(WeavePackageType.INTERNAL); AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.SERVLET_NAME, true, "NettyDispatcher", "NettyDispatcher"); - - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, "Netty Debug: Set transaction name to NettyDispatcher for transaction: " + AgentBridge.getAgent().getTransaction()); } Transaction tx = AgentBridge.getAgent().getTransaction(false); - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, - "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.getPipeline().token + ". ctx: " + - ctx); - if (tx != null) { tx.setWebRequest(new RequestWrapper((DefaultHttpRequest) msg)); } @@ -82,5 +72,4 @@ public static void upstreamDispatcher(ChannelHandlerContext_Instrumentation ctx, AgentBridge.currentApiSource.remove(); } } - } diff --git a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java index f2ab43d7d0..da5ebdb9a9 100644 --- a/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java +++ b/instrumentation/netty-3.8/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java @@ -7,7 +7,6 @@ package org.jboss.netty.handler.codec.http; -import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; @@ -17,24 +16,11 @@ import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext_Instrumentation; -import java.util.logging.Level; - @Weave(type = MatchType.BaseClass) public class HttpMessageDecoder { @Trace protected Object decode(ChannelHandlerContext_Instrumentation ctx, Channel channel, ChannelBuffer buffer, State state) { Object request = Weaver.callOriginal(); - - boolean typeCheck = (request instanceof HttpRequest); - - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, - "Netty Debug: Called HttpObjectDecoder.decode with request of type: " + request.getClass() + " for transaction: " + - AgentBridge.getAgent().getTransaction() + ". Token: " + - ctx.getPipeline().token + ". ctx: " + - ctx + ". instanceof HttpRequest = " + typeCheck); - if (request instanceof HttpRequest && ctx.getPipeline().token == null) { NettyDispatcher.upstreamDispatcher(ctx, request); } diff --git a/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java index 8d50eca5f0..447bee09ac 100644 --- a/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-4.0.0/src/main/java/io/netty/bootstrap/NettyDispatcher.java @@ -53,23 +53,12 @@ public static void channelRead(ChannelHandlerContext_Instrumentation ctx, Object tracer.setMetricName("NettyUpstreamDispatcher"); AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.SERVLET_NAME, true, "NettyDispatcher", "NettyDispatcher"); - - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, "Netty Debug: Set transaction name to NettyDispatcher for transaction: " + AgentBridge.getAgent().getTransaction()); - } Transaction tx = AgentBridge.getAgent().getTransaction(false); - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.pipeline().token + ". ctx: " + - ctx); - if (tx != null) { tx.setWebRequest(new RequestWrapper((DefaultHttpRequest) msg)); } } - } diff --git a/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 8bd772f7a7..211b8e3300 100644 --- a/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/instrumentation/netty-4.0.0/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -7,7 +7,6 @@ package io.netty.handler.codec.http; -import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -16,7 +15,6 @@ import io.netty.channel.ChannelHandlerContext_Instrumentation; import java.util.List; -import java.util.logging.Level; @Weave(type = MatchType.BaseClass) public class HttpObjectDecoder { @@ -25,21 +23,9 @@ public class HttpObjectDecoder { protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, List out) { Weaver.callOriginal(); for (Object msg : out) { - - boolean typeCheck = (msg instanceof HttpRequest); - - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, - "Netty Debug: Called HttpObjectDecoder.decode with msg of type: " + msg.getClass() + " for transaction: " + - AgentBridge.getAgent().getTransaction() + ". Token: " + - ctx.pipeline().token + ". ctx: " + - ctx + ". instanceof HttpRequest = " + typeCheck); - if (msg instanceof HttpRequest && ctx.pipeline().token == null) { NettyDispatcher.channelRead(ctx, msg); } } } - } diff --git a/instrumentation/netty-4.0.8/build.gradle b/instrumentation/netty-4.0.8/build.gradle index af909e29a5..416753933f 100644 --- a/instrumentation/netty-4.0.8/build.gradle +++ b/instrumentation/netty-4.0.8/build.gradle @@ -1,15 +1,18 @@ dependencies { - implementation(project(":agent-bridge")) - implementation("io.netty:netty-all:4.0.8.Final") + implementation(project(":agent-bridge")) + implementation("io.netty:netty-all:4.0.8.Final") } jar { - manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.netty-4.0.8', - 'Implementation-Title-Alias': 'netty_instrumentation' } + manifest { + attributes 'Implementation-Title': 'com.newrelic.instrumentation.netty-4.0.8', + 'Implementation-Title-Alias': 'netty_instrumentation' + } } +// This module doesn't support HTTP/2 which was introduced in 4.1.0.Beta4 verifyInstrumentation { - passesOnly 'io.netty:netty-all:[4.0.8.Final,5.0.0.Alpha1)' + passesOnly 'io.netty:netty-all:[4.0.8.Final,4.1.0.Beta4)' } site { diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java index 14b40d27d1..3c76d3b89a 100644 --- a/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java +++ b/instrumentation/netty-4.0.8/src/main/java/io/netty/bootstrap/NettyDispatcher.java @@ -56,22 +56,12 @@ public static void channelRead(ChannelHandlerContext_Instrumentation ctx, Object tracer.setMetricName("NettyUpstreamDispatcher"); AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.SERVLET_NAME, true, "NettyDispatcher", "NettyDispatcher"); - - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, "Netty Debug: Set transaction name to NettyDispatcher for transaction: " + AgentBridge.getAgent().getTransaction()); } Transaction tx = AgentBridge.getAgent().getTransaction(false); - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, "Netty Debug: Called: NettyDispatcher.channelRead for transaction: " + tx + ". Token: " + ctx.pipeline().token + ". ctx: " + - ctx); - if (tx != null) { tx.setWebRequest(new RequestWrapper((HttpRequest) msg)); } } - } diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index d4810d10a1..76161018af 100644 --- a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -7,7 +7,6 @@ package io.netty.handler.codec.http; -import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -16,7 +15,6 @@ import io.netty.channel.ChannelHandlerContext_Instrumentation; import java.util.List; -import java.util.logging.Level; @Weave(type = MatchType.BaseClass) public class HttpObjectDecoder { @@ -25,17 +23,6 @@ public class HttpObjectDecoder { protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, List out) { Weaver.callOriginal(); for (Object msg : out) { - - boolean typeCheck = (msg instanceof HttpRequest); - - AgentBridge.getAgent() - .getLogger() - .log(Level.INFO, - "Netty Debug: Called HttpObjectDecoder.decode with msg of type: " + msg.getClass() + " for transaction: " + - AgentBridge.getAgent().getTransaction() + ". Token: " + - ctx.pipeline().token + ". ctx: " + - ctx + ". instanceof HttpRequest = " + typeCheck); - if (msg instanceof HttpRequest && ctx.pipeline().token == null) { // NettyDispatcher class is usually initialized in AbstractBootstrap; however, // that code is not always invoked when using recent Netty versions (4.1.54) @@ -47,5 +34,4 @@ protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, } } } - } diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection_Skip.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection_Skip.java new file mode 100644 index 0000000000..398e02f3a8 --- /dev/null +++ b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection_Skip.java @@ -0,0 +1,11 @@ +package io.netty.handler.codec.http2; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +/* + * Stops this instrumentation from applying to versions + * io.netty:netty-all:4.1.0.Beta4 and above that support HTTP/2 + */ +@SkipIfPresent(originalName = "io.netty.handler.codec.http2.DefaultHttp2Connection") +public class DefaultHttp2Connection_Skip { +} diff --git a/instrumentation/netty-4.0.8/src/test/java/netty408/RequestWrapperTest.java b/instrumentation/netty-4.0.8/src/test/java/netty408/RequestWrapperTest.java index 8a72ffc6f4..e60469503b 100644 --- a/instrumentation/netty-4.0.8/src/test/java/netty408/RequestWrapperTest.java +++ b/instrumentation/netty-4.0.8/src/test/java/netty408/RequestWrapperTest.java @@ -1,3 +1,10 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + package netty408; import com.agent.instrumentation.netty40.RequestWrapper; @@ -32,4 +39,4 @@ public void testPercentageEscaping() { Assert.assertEquals(expected, requestWrapper.getParameterValues("asdf")[0]); } } -} \ No newline at end of file +} diff --git a/instrumentation/netty-4.1.16/build.gradle b/instrumentation/netty-4.1.16/build.gradle new file mode 100644 index 0000000000..cd62f637d9 --- /dev/null +++ b/instrumentation/netty-4.1.16/build.gradle @@ -0,0 +1,24 @@ +dependencies { + implementation(project(":agent-bridge")) +// implementation 'io.netty:netty-all:4.1.107.Final' + implementation 'io.netty:netty-all:4.1.48.Final' +// implementation 'io.netty:netty-all:4.1.0.Beta4' +} + +jar { + manifest { + attributes 'Implementation-Title': 'com.newrelic.instrumentation.netty-4.1.16', + 'Implementation-Title-Alias': 'netty_instrumentation' + } +} + +verifyInstrumentation { + // http2 support was added in 4.1.0.Beta4 + // this module only supports from 4.1.16.Final + passesOnly 'io.netty:netty-all:[4.1.16.Final,5.0.0.Alpha1)' +} + +site { + title 'Netty' + type 'Appserver' +} \ No newline at end of file diff --git a/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/Http2RequestWrapper.java b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/Http2RequestWrapper.java new file mode 100644 index 0000000000..1d7653c839 --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/Http2RequestWrapper.java @@ -0,0 +1,147 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.netty4116; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.ExtendedRequest; +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.ServerCookieDecoder; +import io.netty.handler.codec.http2.Http2Exception; +import io.netty.handler.codec.http2.Http2HeadersFrame; +import io.netty.handler.codec.http2.HttpConversionUtil; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.regex.Pattern; + +public class Http2RequestWrapper extends ExtendedRequest { + private static final Pattern URL_REPLACEMENT_PATTERN = Pattern.compile("(?i)%(?![\\da-f]{2})"); + private final HttpRequest request; + private final Set cookies; + private final Map> parameters; + + public Http2RequestWrapper(Http2HeadersFrame http2HeadersFrame) { + super(); + // There's no reason to mutate the incoming request but the Http2HeadersFrame could be used for that if need be. + + // Use the HttpRequest for getting info from the request + this.request = getHttpRequest(http2HeadersFrame); + + Set rawCookies = null; + if (request.headers().contains(HttpHeaderNames.COOKIE)) { + try { + rawCookies = ServerCookieDecoder.STRICT.decode(request.headers().get(HttpHeaderNames.COOKIE)); + } catch (Exception e) { + AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to decode cookie: {0}", + request.headers().get(HttpHeaderNames.COOKIE)); + rawCookies = Collections.emptySet(); + } + } + this.cookies = rawCookies; + + Map> params; + try { + String uri = request.uri(); + uri = URL_REPLACEMENT_PATTERN.matcher(uri).replaceAll("%25"); // Escape any percent signs in the URI + QueryStringDecoder decoderQuery = new QueryStringDecoder(uri); + params = decoderQuery.parameters(); + } catch (Exception e) { + AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to decode URI: {0}", request.uri()); + params = new LinkedHashMap<>(); + } + this.parameters = params; + } + + @Override + public String getRequestURI() { + return request.uri(); + } + + @Override + public String getHeader(String name) { + return request.headers().get(name); + } + + @Override + public String getRemoteUser() { + return null; + } + + @SuppressWarnings("rawtypes") + @Override + public Enumeration getParameterNames() { + return Collections.enumeration(parameters.keySet()); + } + + @Override + public String[] getParameterValues(String name) { + List result = parameters.get(name); + return (result == null ? null : result.toArray(new String[0])); + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public String getCookieValue(String name) { + for (Cookie cookie : cookies) { + if (cookie.name().equals(name)) { + return cookie.value(); + } + } + return null; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public String getMethod() { + return request.method().name(); + } + + @Override + public List getHeaders(String name) { + return request.headers().getAll(name); + } + + /** + * Converts an Http2HeadersFrame into an HttpRequest. + *

+ * Note: Mutating the resulting HttpRequest does not + * mutate the Http2HeadersFrame used in the conversion. + * + * @param msg Http2HeadersFrame + * @return HttpRequest + */ + public static HttpRequest getHttpRequest(Http2HeadersFrame msg) { + HttpRequest httpRequest = null; + try { + // Setting validateHttpHeaders to false will mean that Netty won't + // validate & protect against user-supplied header values that are malicious. + httpRequest = HttpConversionUtil.toHttpRequest(msg.stream().id(), msg.headers(), true); + } catch (Http2Exception e) { + AgentBridge.instrumentation.noticeInstrumentationError(e, Weaver.getImplementationTitle()); + } + return httpRequest; + } +} diff --git a/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/Http2ResponseWrapper.java b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/Http2ResponseWrapper.java new file mode 100644 index 0000000000..6873534be0 --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/Http2ResponseWrapper.java @@ -0,0 +1,88 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.netty4116; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.ExtendedResponse; +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http2.Http2Exception; +import io.netty.handler.codec.http2.Http2HeadersFrame; +import io.netty.handler.codec.http2.HttpConversionUtil; + +public class Http2ResponseWrapper extends ExtendedResponse { + private final HttpResponse response; + private final Http2HeadersFrame http2HeadersFrame; + + public Http2ResponseWrapper(Http2HeadersFrame http2HeadersFrame) { + // Use the Http2HeadersFrame for mutating the outgoing response + this.http2HeadersFrame = http2HeadersFrame; + // Use the HttpResponse for getting info from the response + this.response = getHttpResponse(http2HeadersFrame); + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public void setHeader(String name, String value) { + // If we're mutating the outgoing HTTP/2 response it needs + // to be done on the actual frame that is being streamed. + http2HeadersFrame.headers().set(name, value); + } + + @Override + public int getStatus() throws Exception { + return response.status().code(); + } + + @Override + public String getStatusMessage() throws Exception { + return response.status().reasonPhrase(); + } + + @Override + public String getContentType() { + return response.headers().get(HttpHeaderNames.CONTENT_TYPE); + } + + @Override + public long getContentLength() { + String contentLengthHeader = response.headers().get(HttpHeaderNames.CONTENT_LENGTH); + try { + return contentLengthHeader != null ? Long.parseLong(contentLengthHeader) : -1; + } catch (NumberFormatException e) { + return -1; + } + } + + /** + * Converts an Http2HeadersFrame into an HttpResponse. + *

+ * Note: Mutating the resulting HttpResponse does not + * mutate the Http2HeadersFrame used in the conversion. + * + * @param frame Http2HeadersFrame + * @return HttpResponse + */ + public static HttpResponse getHttpResponse(Http2HeadersFrame frame) { + HttpResponse httpResponse = null; + try { + // Setting validateHttpHeaders to false will mean that Netty won't + // validate & protect against user-supplied header values that are malicious. + httpResponse = HttpConversionUtil.toHttpResponse(frame.stream().id(), frame.headers(), true); + } catch (Http2Exception e) { + AgentBridge.instrumentation.noticeInstrumentationError(e, Weaver.getImplementationTitle()); + } + return httpResponse; + } +} diff --git a/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/NettyUtil.java b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/NettyUtil.java new file mode 100644 index 0000000000..bea825847c --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/NettyUtil.java @@ -0,0 +1,62 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.netty4116; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Token; +import com.newrelic.api.agent.NewRelic; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http2.Http2HeadersFrame; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.logging.Level; + +public class NettyUtil { + + public static String getNettyVersion() { + return "4.1.16"; + } + + public static void setAppServerPort(SocketAddress localAddress) { + if (localAddress instanceof InetSocketAddress) { + int port = ((InetSocketAddress) localAddress).getPort(); + NewRelic.setAppServerPort(port); + } else { + AgentBridge.getAgent().getLogger().log(Level.FINE, "Unable to get Netty port number"); + } + } + + public static void setServerInfo() { + AgentBridge.publicApi.setServerInfo("Netty", getNettyVersion()); + } + + public static boolean processResponse(Object msg, Token token) { + if (token != null) { + if (msg instanceof HttpResponse || msg instanceof Http2HeadersFrame) { + com.newrelic.api.agent.Transaction tx = token.getTransaction(); + if (tx != null) { + try { + if (msg instanceof HttpResponse) { + tx.setWebResponse(new ResponseWrapper((HttpResponse) msg)); + } else { + tx.setWebResponse(new Http2ResponseWrapper((Http2HeadersFrame) msg)); + } + tx.addOutboundResponseHeaders(); + tx.markResponseSent(); + } catch (Exception e) { + AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to set web request on transaction: {0}", tx); + } + } + token.expire(); + return true; + } + } + return false; + } +} diff --git a/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/RequestWrapper.java b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/RequestWrapper.java new file mode 100644 index 0000000000..028de34155 --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/RequestWrapper.java @@ -0,0 +1,119 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.netty4116; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.ExtendedRequest; +import com.newrelic.api.agent.HeaderType; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.ServerCookieDecoder; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.regex.Pattern; + +public class RequestWrapper extends ExtendedRequest { + private static final Pattern URL_REPLACEMENT_PATTERN = Pattern.compile("(?i)%(?![\\da-f]{2})"); + private final HttpRequest request; + private final Set cookies; + private final Map> parameters; + + public RequestWrapper(HttpRequest request) { + super(); + this.request = request; + + Set rawCookies = null; + if (request.headers().contains(HttpHeaderNames.COOKIE)) { + try { + rawCookies = ServerCookieDecoder.STRICT.decode(request.headers().get(HttpHeaderNames.COOKIE)); + } catch (Exception e) { + AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to decode cookie: {0}", + request.headers().get(HttpHeaderNames.COOKIE)); + rawCookies = Collections.emptySet(); + } + } + this.cookies = rawCookies; + + Map> params; + try { + String uri = request.uri(); + uri = URL_REPLACEMENT_PATTERN.matcher(uri).replaceAll("%25"); // Escape any percent signs in the URI + QueryStringDecoder decoderQuery = new QueryStringDecoder(uri); + params = decoderQuery.parameters(); + } catch (Exception e) { + AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to decode URI: {0}", request.uri()); + params = new LinkedHashMap<>(); + } + this.parameters = params; + } + + @Override + public String getRequestURI() { + return request.uri(); + } + + @Override + public String getHeader(String name) { + return request.headers().get(name); + } + + @Override + public String getRemoteUser() { + return null; + } + + @SuppressWarnings("rawtypes") + @Override + public Enumeration getParameterNames() { + return Collections.enumeration(parameters.keySet()); + } + + @Override + public String[] getParameterValues(String name) { + List result = parameters.get(name); + return (result == null ? null : result.toArray(new String[0])); + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public String getCookieValue(String name) { + for (Cookie cookie : cookies) { + if (cookie.name().equals(name)) { + return cookie.value(); + } + } + return null; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public String getMethod() { + return request.method().name(); + } + + @Override + public List getHeaders(String name) { + return request.headers().getAll(name); + } +} diff --git a/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/ResponseWrapper.java b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/ResponseWrapper.java new file mode 100644 index 0000000000..5b8addab88 --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/ResponseWrapper.java @@ -0,0 +1,57 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.netty4116; + +import com.newrelic.api.agent.ExtendedResponse; +import com.newrelic.api.agent.HeaderType; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpResponse; + +public class ResponseWrapper extends ExtendedResponse { + private final HttpResponse response; + + public ResponseWrapper(HttpResponse msg) { + this.response = msg; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public void setHeader(String name, String value) { + response.headers().set(name, value); + } + + @Override + public int getStatus() throws Exception { + return response.status().code(); + } + + @Override + public String getStatusMessage() throws Exception { + return response.status().reasonPhrase(); + } + + @Override + public String getContentType() { + return response.headers().get(HttpHeaderNames.CONTENT_TYPE); + } + + @Override + public long getContentLength() { + String contentLengthHeader = response.headers().get(HttpHeaderNames.CONTENT_LENGTH); + try { + return contentLengthHeader != null ? Long.parseLong(contentLengthHeader) : -1; + } catch (NumberFormatException e) { + return -1; + } + } + +} diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap.java new file mode 100644 index 0000000000..325fad4155 --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.netty.bootstrap; + +import com.agent.instrumentation.netty4116.NettyUtil; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.WeaveAllConstructors; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelFuture; + +import java.net.SocketAddress; + +@Weave +public abstract class AbstractBootstrap { + + @WeaveAllConstructors + AbstractBootstrap() { + // initialize NettyDispatcher here to avoid classloader deadlocks + NettyDispatcher.get(); + } + + private ChannelFuture doBind(final SocketAddress localAddress) { + NettyUtil.setAppServerPort(localAddress); + NettyUtil.setServerInfo(); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/NettyDispatcher.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/NettyDispatcher.java new file mode 100644 index 0000000000..59044c39df --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/NettyDispatcher.java @@ -0,0 +1,72 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.netty.bootstrap; + +import com.agent.instrumentation.netty4116.Http2RequestWrapper; +import com.agent.instrumentation.netty4116.RequestWrapper; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; +import com.newrelic.agent.bridge.TransactionNamePriority; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.TracedMethod; +import io.netty.channel.ChannelHandlerContext_Instrumentation; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http2.Http2HeadersFrame; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; + +/** + * This isn't a netty class. This is an agent class which will start a transaction on i/o read. + *

+ * Since this class creates a tracer, its class+method name will show in the TT, hence the class name. + */ +public class NettyDispatcher { + + private static volatile NettyDispatcher instance = null; + public static final AtomicBoolean instrumented = new AtomicBoolean(false); + + public static NettyDispatcher get() { + if (null == instance) { + synchronized (NettyDispatcher.class) { + if (null == instance) { + instance = new NettyDispatcher(); + instrumented.set(true); + } + } + } + return instance; + } + + private NettyDispatcher() { + AgentBridge.instrumentation.retransformUninstrumentedClass(NettyDispatcher.class); + } + + @Trace(dispatcher = true) + public static void channelRead(ChannelHandlerContext_Instrumentation ctx, Object msg) { + ctx.pipeline().token = AgentBridge.getAgent().getTransaction().getToken(); + TracedMethod tracer = AgentBridge.getAgent().getTransaction().getTracedMethod(); + if (tracer == null) { + AgentBridge.getAgent().getLogger().log(Level.FINEST, "Unable to dispatch netty tx. No tracer."); // it happens. + } else { + tracer.setMetricName("NettyUpstreamDispatcher"); + AgentBridge.getAgent().getTransaction().setTransactionName(TransactionNamePriority.SERVLET_NAME, true, + "NettyDispatcher", "NettyDispatcher"); + } + + Transaction tx = AgentBridge.getAgent().getTransaction(false); + + if (tx != null) { + if (msg instanceof HttpRequest) { + tx.setWebRequest(new RequestWrapper((HttpRequest) msg)); + } else if (msg instanceof Http2HeadersFrame) { + tx.setWebRequest(new Http2RequestWrapper((Http2HeadersFrame) msg)); + } + } + } +} diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandlerContext_Instrumentation.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandlerContext_Instrumentation.java new file mode 100644 index 0000000000..2b2cc6d043 --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandlerContext_Instrumentation.java @@ -0,0 +1,17 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.netty.channel; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; + +@Weave(type = MatchType.Interface, originalName = "io.netty.channel.ChannelHandlerContext") +public abstract class ChannelHandlerContext_Instrumentation { + + public abstract ChannelPipeline_Instrumentation pipeline(); +} diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandler_Instrumentation.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandler_Instrumentation.java new file mode 100644 index 0000000000..2886dabf2c --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandler_Instrumentation.java @@ -0,0 +1,47 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package io.netty.channel; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(type = MatchType.Interface, originalName = "io.netty.channel.ChannelHandler") +public class ChannelHandler_Instrumentation { + + /* + * This is to solve a bug where the transaction is lost when spring webclient times out and throws an error + * using the io.netty.handler.timeout.ReadTimeoutHandler class from netty. + * + * Any extra handlers used by netty will now link a transaction if available. + * + * ----------------------------------- + * WARNING + * ----------------------------------- + * + * Netty has marked this method as deprecated since 4.1 + * + * If instrumentation verification fails for because of this class, + * then in the new instrumentation module try instrumenting the class: + * + * io.netty.channel.AbstractChannelHandlerContext + * + * and its method: + * + * static void invokeExceptionCaught(final AbstractChannelHandlerContext next, final Throwable cause) + * + * */ + @Trace(async = true, excludeFromTransactionTrace = true) + public void exceptionCaught(ChannelHandlerContext_Instrumentation ctx, Throwable cause) throws Exception { + if (ctx != null && + ctx.pipeline().token != null && ctx.pipeline().token.isActive()) { + ctx.pipeline().token.link(); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelInboundHandler_Instrumentation.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelInboundHandler_Instrumentation.java new file mode 100644 index 0000000000..2118ae3bfa --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelInboundHandler_Instrumentation.java @@ -0,0 +1,25 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.netty.channel; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(type = MatchType.Interface, originalName = "io.netty.channel.ChannelInboundHandler") +public abstract class ChannelInboundHandler_Instrumentation { + + @Trace(async = true, excludeFromTransactionTrace = true) + public void channelRead(ChannelHandlerContext_Instrumentation ctx, Object msg) throws Exception { + if (ctx.pipeline().token != null) { + ctx.pipeline().token.link(); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelPipeline_Instrumentation.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelPipeline_Instrumentation.java new file mode 100644 index 0000000000..47d1f106c9 --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelPipeline_Instrumentation.java @@ -0,0 +1,20 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.netty.channel; + +import com.newrelic.agent.bridge.Token; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; + +@Weave(type = MatchType.BaseClass, originalName = "io.netty.channel.ChannelPipeline") +public class ChannelPipeline_Instrumentation { + + @NewField + public Token token; +} diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java new file mode 100644 index 0000000000..2955937b9a --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -0,0 +1,38 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.netty.handler.codec.http; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.bootstrap.NettyDispatcher; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext_Instrumentation; + +import java.util.List; + +@Weave(type = MatchType.BaseClass) +public class HttpObjectDecoder { + + // heading upstream + protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, List out) { + Weaver.callOriginal(); + for (Object msg : out) { + if (msg instanceof HttpRequest && ctx.pipeline().token == null) { + // NettyDispatcher class is usually initialized in AbstractBootstrap; however, + // that code is not always invoked when using recent Netty versions (4.1.54) + // so we check here and initialize if we haven't yet. + if (!NettyDispatcher.instrumented.get()) { + NettyDispatcher.get(); + } + NettyDispatcher.channelRead(ctx, msg); + } + } + } + +} diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java new file mode 100644 index 0000000000..414fdefbfb --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java @@ -0,0 +1,29 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.netty.handler.codec.http; + +import com.agent.instrumentation.netty4116.NettyUtil; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelHandlerContext_Instrumentation; + +import java.util.List; + +@Weave(type = MatchType.BaseClass) +public class HttpObjectEncoder { + + // heading downstream + protected void encode(ChannelHandlerContext_Instrumentation ctx, Object msg, List out) { + boolean expired = NettyUtil.processResponse(msg, ctx.pipeline().token); + if (expired) { + ctx.pipeline().token = null; + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java new file mode 100644 index 0000000000..771fe9ef9d --- /dev/null +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java @@ -0,0 +1,50 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.netty.handler.codec.http2; + +import com.agent.instrumentation.netty4116.NettyUtil; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.bootstrap.NettyDispatcher; +import io.netty.channel.ChannelHandlerContext_Instrumentation; +import io.netty.channel.ChannelPromise; + +@Weave(type = MatchType.BaseClass) +public class Http2FrameCodec { + + // Handle the incoming request. For HTTP/2 there is no HttpRequest object + // but rather a stream of Http2Frame objects that make up the full request. + void onHttp2Frame(ChannelHandlerContext_Instrumentation ctx, Http2Frame frame) { + Weaver.callOriginal(); + if (frame instanceof Http2HeadersFrame && ctx.pipeline().token == null) { + Http2HeadersFrame msg = (Http2HeadersFrame) frame; + if (msg.isEndStream()) { + // NettyDispatcher class is usually initialized in AbstractBootstrap; however, + // that code is not always invoked when using recent Netty versions (4.1.54) + // so we check here and initialize if we haven't yet. + if (!NettyDispatcher.instrumented.get()) { + NettyDispatcher.get(); + } + NettyDispatcher.channelRead(ctx, msg); + } + } + } + + // Handle the outgoing response. For HTTP/2 there is no HttpResponse object + // but rather a stream of Http2Frame objects that make up the full response. + public void write(ChannelHandlerContext_Instrumentation ctx, Object msg, ChannelPromise promise) { + if (msg instanceof Http2HeadersFrame) { + boolean expired = NettyUtil.processResponse(msg, ctx.pipeline().token); + if (expired) { + ctx.pipeline().token = null; + } + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-4.1.16/src/test/java/netty4116/RequestWrapperTest.java b/instrumentation/netty-4.1.16/src/test/java/netty4116/RequestWrapperTest.java new file mode 100644 index 0000000000..7a3b3988fa --- /dev/null +++ b/instrumentation/netty-4.1.16/src/test/java/netty4116/RequestWrapperTest.java @@ -0,0 +1,41 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package netty4116; + +import com.agent.instrumentation.netty4116.RequestWrapper; +import io.netty.handler.codec.http.EmptyHttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class RequestWrapperTest { + @Test + public void testPercentageEscaping() { + Map inputExpectedMap = new HashMap<>(); + inputExpectedMap.put("http://example.com?asdf=%qwer", "%qwer"); + inputExpectedMap.put("http://example.com?asdf=%20", " "); + inputExpectedMap.put("http://example.com?asdf=%2b", "+"); + inputExpectedMap.put("http://example.com?asdf=qwer", "qwer"); + + for (Map.Entry inputExpectedEntry : inputExpectedMap.entrySet()) { + String input = inputExpectedEntry.getKey(); + String expected = inputExpectedEntry.getValue(); + HttpRequest request = mock(HttpRequest.class); + when(request.headers()).thenReturn(EmptyHttpHeaders.INSTANCE); + when(request.uri()).thenReturn(input); + RequestWrapper requestWrapper = new RequestWrapper(request); + Assert.assertEquals(expected, requestWrapper.getParameterValues("asdf")[0]); + } + } +} diff --git a/settings.gradle b/settings.gradle index 25c83ddc99..08a75afefa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -257,6 +257,7 @@ include 'instrumentation:netty-3.4' include 'instrumentation:netty-3.8' include 'instrumentation:netty-4.0.0' include 'instrumentation:netty-4.0.8' +include 'instrumentation:netty-4.1.16' include 'instrumentation:netty-reactor-0.7.0' include 'instrumentation:netty-reactor-0.8.0' include 'instrumentation:netty-reactor-0.9.0' From 353b2cc9b68117e7fd65c47412bc4b1cb7791d0e Mon Sep 17 00:00:00 2001 From: Jason Keller Date: Thu, 21 Mar 2024 16:00:45 -0700 Subject: [PATCH 4/7] Update build.gradle --- instrumentation/netty-4.1.16/build.gradle | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/instrumentation/netty-4.1.16/build.gradle b/instrumentation/netty-4.1.16/build.gradle index cd62f637d9..dff463b536 100644 --- a/instrumentation/netty-4.1.16/build.gradle +++ b/instrumentation/netty-4.1.16/build.gradle @@ -1,8 +1,6 @@ dependencies { implementation(project(":agent-bridge")) -// implementation 'io.netty:netty-all:4.1.107.Final' - implementation 'io.netty:netty-all:4.1.48.Final' -// implementation 'io.netty:netty-all:4.1.0.Beta4' + implementation 'io.netty:netty-all:4.1.107.Final' } jar { From e4491989151f1a28ce6e23758688982e02013d44 Mon Sep 17 00:00:00 2001 From: Jason Keller Date: Thu, 21 Mar 2024 16:02:56 -0700 Subject: [PATCH 5/7] Formatting --- .../java/io/netty/channel/ChannelHandler_Instrumentation.java | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandler_Instrumentation.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandler_Instrumentation.java index 2886dabf2c..f003f01857 100644 --- a/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandler_Instrumentation.java +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/channel/ChannelHandler_Instrumentation.java @@ -4,6 +4,7 @@ * * SPDX-License-Identifier: Apache-2.0 * */ + package io.netty.channel; import com.newrelic.api.agent.Trace; From 0b18d8ea811c22f081da0e459b792859f1ca50c8 Mon Sep 17 00:00:00 2001 From: Jason Keller Date: Fri, 22 Mar 2024 17:45:27 -0700 Subject: [PATCH 6/7] Some cleanup and documentation --- instrumentation/netty-4.0.8/build.gradle | 6 +-- .../http2/DefaultHttp2Connection_Skip.java | 11 ----- ...ttp2StreamFrameToHttpObjectCodec_Skip.java | 10 ++++ instrumentation/netty-4.1.16/README.md | 47 +++++++++++++++++++ instrumentation/netty-4.1.16/build.gradle | 3 +- ...=> AbstractBootstrap_Instrumentation.java} | 7 +-- ...=> HttpObjectDecoder_Instrumentation.java} | 5 +- ...=> HttpObjectEncoder_Instrumentation.java} | 4 +- ...a => Http2FrameCodec_Instrumentation.java} | 8 ++-- 9 files changed, 74 insertions(+), 27 deletions(-) delete mode 100644 instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection_Skip.java create mode 100644 instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec_Skip.java create mode 100644 instrumentation/netty-4.1.16/README.md rename instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/{AbstractBootstrap.java => AbstractBootstrap_Instrumentation.java} (76%) rename instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/{HttpObjectDecoder.java => HttpObjectDecoder_Instrumentation.java} (88%) rename instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/{HttpObjectEncoder.java => HttpObjectEncoder_Instrumentation.java} (83%) rename instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/{Http2FrameCodec.java => Http2FrameCodec_Instrumentation.java} (84%) diff --git a/instrumentation/netty-4.0.8/build.gradle b/instrumentation/netty-4.0.8/build.gradle index 416753933f..5e31469b8d 100644 --- a/instrumentation/netty-4.0.8/build.gradle +++ b/instrumentation/netty-4.0.8/build.gradle @@ -1,6 +1,6 @@ dependencies { implementation(project(":agent-bridge")) - implementation("io.netty:netty-all:4.0.8.Final") + implementation("io.netty:netty-all:4.1.15.Final") } jar { @@ -10,9 +10,9 @@ jar { } } -// This module doesn't support HTTP/2 which was introduced in 4.1.0.Beta4 +// This module doesn't support HTTP/2 verifyInstrumentation { - passesOnly 'io.netty:netty-all:[4.0.8.Final,4.1.0.Beta4)' + passesOnly 'io.netty:netty-all:[4.0.8.Final,4.1.16.Final)' } site { diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection_Skip.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection_Skip.java deleted file mode 100644 index 398e02f3a8..0000000000 --- a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection_Skip.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.netty.handler.codec.http2; - -import com.newrelic.api.agent.weaver.SkipIfPresent; - -/* - * Stops this instrumentation from applying to versions - * io.netty:netty-all:4.1.0.Beta4 and above that support HTTP/2 - */ -@SkipIfPresent(originalName = "io.netty.handler.codec.http2.DefaultHttp2Connection") -public class DefaultHttp2Connection_Skip { -} diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec_Skip.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec_Skip.java new file mode 100644 index 0000000000..00e3e72388 --- /dev/null +++ b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec_Skip.java @@ -0,0 +1,10 @@ +package io.netty.handler.codec.http2; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +/* + * Stops this instrumentation from applying when the netty-4.1.16 module applies + */ +@SkipIfPresent(originalName = "io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec") +public class Http2StreamFrameToHttpObjectCodec_Skip { +} diff --git a/instrumentation/netty-4.1.16/README.md b/instrumentation/netty-4.1.16/README.md new file mode 100644 index 0000000000..a8943d763e --- /dev/null +++ b/instrumentation/netty-4.1.16/README.md @@ -0,0 +1,47 @@ +# Netty + +In addition to supporting HTTP/1, this instrumentation module adds support for +HTTP/2 starting with Netty version `io.netty:netty-all:4.1.16.Final`. + +At the time this instrumentation was added the most recent version of Netty was +`io.netty:netty-all:4.1.107.Final` and virtually all the HTTP/2 APIs in the +project are annotated as unstable. + +## HTTP/2 + +HTTP/2 works in a fundamentally different manner than HTTP/1. + +> At the core of all performance enhancements of HTTP/2 is the new binary framing layer, which dictates how the HTTP messages are encapsulated and transferred +> between the client and server. +> +>The "layer" refers to a design choice to introduce a new optimized encoding mechanism between the socket interface and the higher HTTP API exposed to our +> applications: the HTTP semantics, such as verbs, methods, and headers, are unaffected, but the way they are encoded while in transit is different. +> +>In short, HTTP/2 breaks down the HTTP protocol communication into an exchange of binary-encoded frames, which are then mapped to messages that belong to a +> particular stream, all of which are multiplexed within a single TCP connection. +> +>* Stream: A bidirectional flow of bytes within an established connection, which may carry one or more messages. +>* Message: A complete sequence of frames that map to a logical request or response message. +>* Frame: The smallest unit of communication in HTTP/2, each containing a frame header, which at a minimum identifies the stream to which the frame belongs. +> +>The relation of these terms can be summarized as follows: +>* All communication is performed over a single TCP connection that can carry any number of bidirectional streams. +>* Each stream has a unique identifier and optional priority information that is used to carry bidirectional messages. +>* Each message is a logical HTTP message, such as a request, or response, which consists of one or more frames. +>* The frame is the smallest unit of communication that carries a specific type of data—e.g., HTTP headers, message payload, and so on. Frames from different + streams may be interleaved and then reassembled via the embedded stream identifier in the header of each frame. + +## HTTP/2 Instrumentation + +To instrument HTTP/2 the `io.netty.handler.codec.http2.Http2FrameCodec` class is weaved and new wrappers are used to handle working +with HTTP/2 frames instead of request/response objects. + +### Request + +When `Http2FrameCodec.onHttp2Frame` is invoked on a `Http2HeadersFrame` representing the request headers, `NettyDispatcher.channelRead` is called which stores +a `token` in the context pipeline, starts a transaction, and sets/wraps the request on the transaction. + +### Response + +When `Http2FrameCodec.write` is invoked on a `Http2HeadersFrame` representing the response headers, `NettyUtil.processResponse` is called which uses the +`token` in the context pipeline to get the transaction, sets/wraps the response on the transaction, and expires the `token`. diff --git a/instrumentation/netty-4.1.16/build.gradle b/instrumentation/netty-4.1.16/build.gradle index dff463b536..53f00498e8 100644 --- a/instrumentation/netty-4.1.16/build.gradle +++ b/instrumentation/netty-4.1.16/build.gradle @@ -11,8 +11,7 @@ jar { } verifyInstrumentation { - // http2 support was added in 4.1.0.Beta4 - // this module only supports from 4.1.16.Final + // This module supports HTTP/2 starting from netty 4.1.16.Final passesOnly 'io.netty:netty-all:[4.1.16.Final,5.0.0.Alpha1)' } diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap_Instrumentation.java similarity index 76% rename from instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap.java rename to instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap_Instrumentation.java index 325fad4155..17d5888d96 100644 --- a/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap.java +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/bootstrap/AbstractBootstrap_Instrumentation.java @@ -8,6 +8,7 @@ package io.netty.bootstrap; import com.agent.instrumentation.netty4116.NettyUtil; +import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.WeaveAllConstructors; import com.newrelic.api.agent.weaver.Weaver; @@ -15,11 +16,11 @@ import java.net.SocketAddress; -@Weave -public abstract class AbstractBootstrap { +@Weave(type = MatchType.ExactClass, originalName = "io.netty.bootstrap.AbstractBootstrap") +public abstract class AbstractBootstrap_Instrumentation { @WeaveAllConstructors - AbstractBootstrap() { + AbstractBootstrap_Instrumentation() { // initialize NettyDispatcher here to avoid classloader deadlocks NettyDispatcher.get(); } diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder_Instrumentation.java similarity index 88% rename from instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java rename to instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder_Instrumentation.java index 2955937b9a..4811536deb 100644 --- a/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder_Instrumentation.java @@ -16,8 +16,8 @@ import java.util.List; -@Weave(type = MatchType.BaseClass) -public class HttpObjectDecoder { +@Weave(type = MatchType.BaseClass, originalName = "io.netty.handler.codec.http.HttpObjectDecoder") +public class HttpObjectDecoder_Instrumentation { // heading upstream protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, List out) { @@ -34,5 +34,4 @@ protected void decode(ChannelHandlerContext_Instrumentation ctx, ByteBuf buffer, } } } - } diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder_Instrumentation.java similarity index 83% rename from instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java rename to instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder_Instrumentation.java index 414fdefbfb..0688a02d4f 100644 --- a/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder_Instrumentation.java @@ -15,8 +15,8 @@ import java.util.List; -@Weave(type = MatchType.BaseClass) -public class HttpObjectEncoder { +@Weave(type = MatchType.BaseClass, originalName = "io.netty.handler.codec.http.HttpObjectEncoder") +public class HttpObjectEncoder_Instrumentation { // heading downstream protected void encode(ChannelHandlerContext_Instrumentation ctx, Object msg, List out) { diff --git a/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec_Instrumentation.java similarity index 84% rename from instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java rename to instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec_Instrumentation.java index 771fe9ef9d..b97fce16eb 100644 --- a/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java +++ b/instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec_Instrumentation.java @@ -15,13 +15,12 @@ import io.netty.channel.ChannelHandlerContext_Instrumentation; import io.netty.channel.ChannelPromise; -@Weave(type = MatchType.BaseClass) -public class Http2FrameCodec { +@Weave(type = MatchType.BaseClass, originalName = "io.netty.handler.codec.http2.Http2FrameCodec") +public class Http2FrameCodec_Instrumentation { // Handle the incoming request. For HTTP/2 there is no HttpRequest object // but rather a stream of Http2Frame objects that make up the full request. void onHttp2Frame(ChannelHandlerContext_Instrumentation ctx, Http2Frame frame) { - Weaver.callOriginal(); if (frame instanceof Http2HeadersFrame && ctx.pipeline().token == null) { Http2HeadersFrame msg = (Http2HeadersFrame) frame; if (msg.isEndStream()) { @@ -34,6 +33,8 @@ void onHttp2Frame(ChannelHandlerContext_Instrumentation ctx, Http2Frame frame) { NettyDispatcher.channelRead(ctx, msg); } } + // Order matters here!!! Weaver.callOriginal() must come after the call to NettyDispatcher.channelRead. + Weaver.callOriginal(); } // Handle the outgoing response. For HTTP/2 there is no HttpResponse object @@ -45,6 +46,7 @@ public void write(ChannelHandlerContext_Instrumentation ctx, Object msg, Channel ctx.pipeline().token = null; } } + // Order matters here!!! Weaver.callOriginal() must come after the call to NettyUtil.processResponse. Weaver.callOriginal(); } } From 392384781848fd78c7b4a834b3b0673455b30afa Mon Sep 17 00:00:00 2001 From: Jason Keller Date: Mon, 25 Mar 2024 10:15:48 -0700 Subject: [PATCH 7/7] Add copyright header --- .../http2/Http2StreamFrameToHttpObjectCodec_Skip.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec_Skip.java b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec_Skip.java index 00e3e72388..1b9bce33fd 100644 --- a/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec_Skip.java +++ b/instrumentation/netty-4.0.8/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec_Skip.java @@ -1,3 +1,10 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + package io.netty.handler.codec.http2; import com.newrelic.api.agent.weaver.SkipIfPresent;