Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e619f2a
Implemented WebSocket support for Netty 4.1
ValentinZakharov Mar 28, 2025
1ab91db
Let propagate unhandled events and fix tests
amarziali Mar 28, 2025
7f96ea2
Refactoring
ValentinZakharov Mar 30, 2025
b2d45a7
Refactor netty test and fix instrumentation
amarziali Apr 3, 2025
0d7330c
Improved pipeline processing - now you can insert handler in any place
ValentinZakharov Apr 3, 2025
7f1138f
Merge branch 'master' into vzakharov/websockets_netty
ValentinZakharov Apr 4, 2025
ee2c5fd
Merge branch 'master' into vzakharov/websockets_netty
ValentinZakharov Apr 7, 2025
203e468
Fixed helper
ValentinZakharov Apr 8, 2025
d753e90
Refactoring
ValentinZakharov Apr 8, 2025
af33164
WebSocket Server support for netty-4.0
ValentinZakharov Apr 9, 2025
e79c661
Missing handlers use cases for netty-4.1
ValentinZakharov Apr 10, 2025
19417b9
Fixed handlers for netty-4.0
ValentinZakharov Apr 10, 2025
6e94f0f
Tests for netty-4.0
ValentinZakharov Apr 10, 2025
c9a7c20
Refactoring
ValentinZakharov Apr 10, 2025
86715ec
WebSocket Server support for netty-3.8
ValentinZakharov Apr 11, 2025
dabfe2d
Tests for netty-3.8
ValentinZakharov Apr 15, 2025
e7f4d7f
Spotless
ValentinZakharov Apr 15, 2025
5fd4cf2
Fixed tests
ValentinZakharov Apr 16, 2025
091ee80
Add profiler env check command to AgentCLI (#8671)
jbachorik Apr 7, 2025
eb18875
Remove dependency on bash from crash/oome uploder scripts (#8652)
jbachorik Apr 7, 2025
66f7b9f
Do not apply JUnit 4 instrumentation to MUnit runners (#8675)
nikita-tkachenko-datadog Apr 7, 2025
d851aa9
Shutdown CI Visibility test event handlers before tracer (#8677)
nikita-tkachenko-datadog Apr 7, 2025
c7ec7ce
Prevent double reporting of Scalatest events when using SBT with test…
nikita-tkachenko-datadog Apr 8, 2025
ba3f346
Fix In-Product when config is empty (#8679)
jpbempel Apr 9, 2025
2203ec4
Expand MUnit runners filter to catch munit.MUnitRunner in JUnit 4 ins…
daniel-mohedano Apr 9, 2025
fef00c7
Remove unused TestEventsHandler methods (#8674)
nikita-tkachenko-datadog Apr 9, 2025
15889fc
Delete print line (#8686)
sarahchen6 Apr 9, 2025
b838ed1
Exclude ProxyLeakTask exception from exception profiling (#8666)
jbachorik Apr 10, 2025
690dd95
Use jvmstat for JDKs 9+ programmatically (#8641)
Apr 10, 2025
6510fa4
Update test.retry_reason to use full name of the feature (#8689)
daniel-mohedano Apr 10, 2025
b8b8dab
Allow dogstatsd port to be configurable with DD_DOGSTATSD_PORT (#8693)
randomanderson Apr 14, 2025
5922b70
wait the client handshake
amarziali Apr 17, 2025
78e9c9c
move netty ws client to interested modules
amarziali Apr 17, 2025
bc4b6f0
Merge remote-tracking branch 'origin/master' into vzakharov/websocket…
amarziali Apr 17, 2025
97e7d2f
Added WebSocket tracing check
ValentinZakharov Apr 28, 2025
c817fc9
Merge branch 'master' into vzakharov/websockets_netty
ValentinZakharov Apr 28, 2025
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
Prev Previous commit
Next Next commit
Refactor netty test and fix instrumentation
  • Loading branch information
amarziali committed Apr 3, 2025
commit b2d45a70529fb4b1cc99c2513004364378116644
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ public final class AttributeKeys {
public static final AttributeKey<Boolean> BLOCKED_RESPONSE_KEY =
attributeKey("datadog.server.blocked_response");

public static final AttributeKey<HandlerContext.Sender> WEBSOCKET_HANDLER_CONTEXT =
attributeKey("datadog.server.websocket.handler_context");
public static final AttributeKey<HandlerContext.Sender> WEBSOCKET_SENDER_HANDLER_CONTEXT =
attributeKey("datadog.server.websocket.sender.handler_context");

public static final AttributeKey<HandlerContext.Receiver> WEBSOCKET_RECEIVER_HANDLER_CONTEXT =
attributeKey("datadog.server.websocket.receiver.handler_context");

/**
* Generate an attribute key or reuse the one existing in the global app map. This implementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public void methodAdvice(MethodTransformer transformer) {
}

public static class OperationCompleteAdvice {
@Advice.OnMethodEnter
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AgentScope activateScope(@Advice.Argument(0) final ChannelFuture future) {
/*
Idea here is:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import datadog.trace.instrumentation.netty41.server.HttpServerResponseTracingHandler;
import datadog.trace.instrumentation.netty41.server.HttpServerTracingHandler;
import datadog.trace.instrumentation.netty41.server.MaybeBlockResponseHandler;
import datadog.trace.instrumentation.netty41.server.websocket.WebSocketProtocolHandshakeHandler;
import datadog.trace.instrumentation.netty41.server.websocket.WebSocketServerTracingHandler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
Expand All @@ -33,7 +32,6 @@
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.util.Attribute;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
Expand Down Expand Up @@ -80,7 +78,6 @@ public String[] helperClassNames() {
packageName + ".server.HttpServerResponseTracingHandler",
packageName + ".server.HttpServerTracingHandler",
packageName + ".server.MaybeBlockResponseHandler",
packageName + ".server.websocket.WebSocketProtocolHandshakeHandler",
packageName + ".server.websocket.WebSocketServerTracingHandler",
packageName + ".server.websocket.WebSocketServerResponseTracingHandler",
packageName + ".server.websocket.WebSocketServerRequestTracingHandler",
Expand Down Expand Up @@ -157,8 +154,6 @@ public static void addHandler(
} else if (handler instanceof HttpResponseEncoder) {
toAdd = HttpServerResponseTracingHandler.INSTANCE;
toAdd2 = MaybeBlockResponseHandler.INSTANCE;
} else if (handler instanceof WebSocketServerProtocolHandler) {
toAdd = WebSocketProtocolHandshakeHandler.INSTANCE;
} else
// Client pipeline handlers
if (handler instanceof HttpClientCodec) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.instrumentation.netty41.AttributeKeys.SPAN_ATTRIBUTE_KEY;
import static datadog.trace.instrumentation.netty41.AttributeKeys.WEBSOCKET_SENDER_HANDLER_CONTEXT;
import static datadog.trace.instrumentation.netty41.server.NettyHttpServerDecorator.DECORATE;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.websocket.HandlerContext;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
Expand Down Expand Up @@ -39,9 +41,16 @@ public void write(final ChannelHandlerContext ctx, final Object msg, final Chann
ctx.channel().attr(SPAN_ATTRIBUTE_KEY).remove();
throw throwable;
}
final boolean isWebsocketUpgrade =
response.status() == HttpResponseStatus.SWITCHING_PROTOCOLS
&& "websocket".equals(response.headers().get(HttpHeaderNames.UPGRADE));
if (isWebsocketUpgrade) {
ctx.channel()
.attr(WEBSOCKET_SENDER_HANDLER_CONTEXT)
.set(new HandlerContext.Sender(span, ctx.channel().id().asShortText()));
}
if (response.status() != HttpResponseStatus.CONTINUE
&& (response.status() != HttpResponseStatus.SWITCHING_PROTOCOLS
|| "websocket".equals(response.headers().get(HttpHeaderNames.UPGRADE)))) {
&& (response.status() != HttpResponseStatus.SWITCHING_PROTOCOLS || isWebsocketUpgrade)) {
DECORATE.onResponse(span, response);
DECORATE.beforeFinish(span);
span.finish(); // Finish the span manually since finishSpanOnClose was false
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.decorator.WebsocketDecorator.DECORATE;
import static datadog.trace.instrumentation.netty41.AttributeKeys.WEBSOCKET_HANDLER_CONTEXT;
import static datadog.trace.bootstrap.instrumentation.websocket.HandlersExtractor.MESSAGE_TYPE_TEXT;
import static datadog.trace.instrumentation.netty41.AttributeKeys.WEBSOCKET_RECEIVER_HANDLER_CONTEXT;
import static datadog.trace.instrumentation.netty41.AttributeKeys.WEBSOCKET_SENDER_HANDLER_CONTEXT;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.websocket.HandlerContext;
import io.netty.channel.*;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;

Expand All @@ -23,29 +26,32 @@ public void channelRead(ChannelHandlerContext ctx, Object frame) {

if (frame instanceof WebSocketFrame) {
Channel channel = ctx.channel();
final HandlerContext.Sender sessionState = channel.attr(WEBSOCKET_HANDLER_CONTEXT).get();
if (sessionState != null) {
final HandlerContext.Receiver handlerContext =
new HandlerContext.Receiver(
sessionState.getHandshakeSpan(), ctx.channel().id().asShortText());

HandlerContext.Receiver receiverContext =
channel.attr(WEBSOCKET_RECEIVER_HANDLER_CONTEXT).get();
if (receiverContext == null) {
HandlerContext.Sender sessionState = channel.attr(WEBSOCKET_SENDER_HANDLER_CONTEXT).get();
if (sessionState != null) {
receiverContext =
new HandlerContext.Receiver(
sessionState.getHandshakeSpan(), ctx.channel().id().asShortText());
channel.attr(WEBSOCKET_RECEIVER_HANDLER_CONTEXT).set(receiverContext);
}
}
if (receiverContext != null) {
if (frame instanceof TextWebSocketFrame) {
// WebSocket Read Text Start
TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;

final AgentSpan span =
DECORATE.onReceiveFrameStart(
handlerContext, textFrame.text(), textFrame.isFinalFragment());
if (span == null) {
receiverContext, textFrame.text(), textFrame.isFinalFragment());
try (final AgentScope scope = activateSpan(span)) {
ctx.fireChannelRead(textFrame);
} else {
try (final AgentScope scope = activateSpan(span)) {
ctx.fireChannelRead(textFrame);

// WebSocket Read Text Start
if (textFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
// WebSocket Read Text Start
} finally {
if (textFrame.isFinalFragment()) {
channel.attr(WEBSOCKET_RECEIVER_HANDLER_CONTEXT).remove();
DECORATE.onFrameEnd(receiverContext);
}
}
return;
Expand All @@ -56,17 +62,38 @@ public void channelRead(ChannelHandlerContext ctx, Object frame) {
BinaryWebSocketFrame binaryFrame = (BinaryWebSocketFrame) frame;
final AgentSpan span =
DECORATE.onReceiveFrameStart(
handlerContext, binaryFrame.content().nioBuffer(), binaryFrame.isFinalFragment());
if (span == null) {
receiverContext,
binaryFrame.content().nioBuffer(),
binaryFrame.isFinalFragment());
try (final AgentScope scope = activateSpan(span)) {
ctx.fireChannelRead(binaryFrame);
} else {
try (final AgentScope scope = activateSpan(span)) {
ctx.fireChannelRead(binaryFrame);
} finally {
// WebSocket Read Binary End
if (binaryFrame.isFinalFragment()) {
channel.attr(WEBSOCKET_RECEIVER_HANDLER_CONTEXT).remove();
DECORATE.onFrameEnd(receiverContext);
}
}

// WebSocket Read Binary End
if (binaryFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
return;
}

if (frame instanceof ContinuationWebSocketFrame) {
ContinuationWebSocketFrame continuationWebSocketFrame =
(ContinuationWebSocketFrame) frame;
final AgentSpan span =
DECORATE.onReceiveFrameStart(
receiverContext,
MESSAGE_TYPE_TEXT.equals(receiverContext.getMessageType())
? continuationWebSocketFrame.text()
: continuationWebSocketFrame.content().nioBuffer(),
continuationWebSocketFrame.isFinalFragment());
try (final AgentScope scope = activateSpan(span)) {
ctx.fireChannelRead(continuationWebSocketFrame);
} finally {
if (continuationWebSocketFrame.isFinalFragment()) {
channel.attr(WEBSOCKET_RECEIVER_HANDLER_CONTEXT).remove();
DECORATE.onFrameEnd(receiverContext);
}
}
return;
Expand All @@ -77,25 +104,21 @@ public void channelRead(ChannelHandlerContext ctx, Object frame) {
CloseWebSocketFrame closeFrame = (CloseWebSocketFrame) frame;
int statusCode = closeFrame.statusCode();
String reasonText = closeFrame.reasonText();
channel.attr(WEBSOCKET_HANDLER_CONTEXT).remove();
channel.attr(WEBSOCKET_SENDER_HANDLER_CONTEXT).remove();
channel.attr(WEBSOCKET_RECEIVER_HANDLER_CONTEXT).remove();
final AgentSpan span =
DECORATE.onSessionCloseReceived(handlerContext, reasonText, statusCode);
if (span == null) {
DECORATE.onSessionCloseReceived(receiverContext, reasonText, statusCode);
try (final AgentScope scope = activateSpan(span)) {
ctx.fireChannelRead(closeFrame);
} else {
try (final AgentScope scope = activateSpan(span)) {
ctx.fireChannelRead(closeFrame);

if (closeFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
if (closeFrame.isFinalFragment()) {
DECORATE.onFrameEnd(receiverContext);
}
}
return;
}
}
}
// can be other messages we do not handle like ping, pong or continuations
// can be other messages we do not handle like ping, pong
ctx.fireChannelRead(frame);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import static datadog.trace.bootstrap.instrumentation.decorator.WebsocketDecorator.DECORATE;
import static datadog.trace.bootstrap.instrumentation.websocket.HandlersExtractor.MESSAGE_TYPE_BINARY;
import static datadog.trace.bootstrap.instrumentation.websocket.HandlersExtractor.MESSAGE_TYPE_TEXT;
import static datadog.trace.instrumentation.netty41.AttributeKeys.WEBSOCKET_HANDLER_CONTEXT;
import static datadog.trace.instrumentation.netty41.AttributeKeys.WEBSOCKET_SENDER_HANDLER_CONTEXT;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.websocket.HandlerContext;
import io.netty.channel.*;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;

Expand All @@ -26,7 +27,7 @@ public void write(ChannelHandlerContext ctx, Object frame, ChannelPromise promis

if (frame instanceof WebSocketFrame) {
Channel channel = ctx.channel();
HandlerContext.Sender handlerContext = channel.attr(WEBSOCKET_HANDLER_CONTEXT).get();
HandlerContext.Sender handlerContext = channel.attr(WEBSOCKET_SENDER_HANDLER_CONTEXT).get();
if (handlerContext != null) {

if (frame instanceof TextWebSocketFrame) {
Expand All @@ -35,16 +36,12 @@ public void write(ChannelHandlerContext ctx, Object frame, ChannelPromise promis
final AgentSpan span =
DECORATE.onSendFrameStart(
handlerContext, MESSAGE_TYPE_TEXT, textFrame.text().length());
if (span == null) {
try (final AgentScope scope = activateSpan(span)) {
ctx.write(frame, promise);
} else {
try (final AgentScope scope = activateSpan(span)) {
ctx.write(frame, promise);

// WebSocket Write Text End
if (textFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
} finally {
// WebSocket Write Text End
if (textFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
}
return;
Expand All @@ -56,16 +53,33 @@ public void write(ChannelHandlerContext ctx, Object frame, ChannelPromise promis
final AgentSpan span =
DECORATE.onSendFrameStart(
handlerContext, MESSAGE_TYPE_BINARY, binaryFrame.content().readableBytes());
if (span == null) {
try (final AgentScope scope = activateSpan(span)) {
ctx.write(frame, promise);
} else {
try (final AgentScope scope = activateSpan(span)) {
ctx.write(frame, promise);
} finally {
// WebSocket Write Binary End
if (binaryFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
}
return;
}

// WebSocket Write Binary End
if (binaryFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
if (frame instanceof ContinuationWebSocketFrame) {
ContinuationWebSocketFrame continuationWebSocketFrame =
(ContinuationWebSocketFrame) frame;
final AgentSpan span =
DECORATE.onSendFrameStart(
handlerContext,
handlerContext.getMessageType(),
MESSAGE_TYPE_TEXT.equals(handlerContext.getMessageType())
? continuationWebSocketFrame.text().length()
: continuationWebSocketFrame.content().readableBytes());
try (final AgentScope scope = activateSpan(span)) {
ctx.write(frame, promise);
} finally {
// WebSocket Write Binary End
if (continuationWebSocketFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
}
return;
Expand All @@ -76,24 +90,21 @@ public void write(ChannelHandlerContext ctx, Object frame, ChannelPromise promis
CloseWebSocketFrame closeFrame = (CloseWebSocketFrame) frame;
int statusCode = closeFrame.statusCode();
String reasonText = closeFrame.reasonText();
channel.attr(WEBSOCKET_HANDLER_CONTEXT).remove();
channel.attr(WEBSOCKET_SENDER_HANDLER_CONTEXT).remove();
final AgentSpan span =
DECORATE.onSessionCloseIssued(handlerContext, reasonText, statusCode);
if (span == null) {
try (final AgentScope scope = activateSpan(span)) {
ctx.write(frame, promise);
} else {
try (final AgentScope scope = activateSpan(span)) {
ctx.write(frame, promise);
if (closeFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
} finally {
if (closeFrame.isFinalFragment()) {
DECORATE.onFrameEnd(handlerContext);
}
}
return;
}
}
}
// can be other messages we do not handle like ping, pong or continuations
// can be other messages we do not handle like ping, pong
ctx.write(frame, promise);
}
}
Loading