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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@

public class NettyUtil {

// This config is added so customers can also start netty transactions at a lower level.
// However, this risks netty producing transactions with 'unknown' urls.
// Only use it if it provides the coverage you need for your application's use case.
public static final Boolean START_HTTP2_FRAME_READ_LISTENER_TXN =
NewRelic.getAgent().getConfig().getValue("netty.http2.frame_read_listener.start_transaction", false);
public static final Boolean START_HTTP2_FRAME_CODEC_TXN = !START_HTTP2_FRAME_READ_LISTENER_TXN;

public static String getNettyVersion() {
return "4.1.16";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

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;
Expand All @@ -19,7 +20,7 @@ class FrameReadListener_Instrumentation {
// Process HTTP/2 request headers and start txn
public void onHeadersRead(ChannelHandlerContext_Instrumentation ctx, int streamId, Http2Headers headers, int streamDependency, short weight,
boolean exclusive, int padding, boolean endOfStream) {
if (ctx.pipeline().token == null) {
if (NettyUtil.START_HTTP2_FRAME_CODEC_TXN && 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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
*
* * 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, 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) {
if (!NettyUtil.START_HTTP2_FRAME_READ_LISTENER_TXN && 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.headers());
}
}
// 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
// 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;
}
}
// Order matters here!!! Weaver.callOriginal() must come after the call to NettyUtil.processResponse.
Weaver.callOriginal();
}
}
Loading