parent
bf0e20e1db
commit
245a9f7cf7
|
@ -1,147 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import com.ning.http.client.AsyncCompletionHandler
|
|
||||||
import com.ning.http.client.AsyncHttpClient
|
|
||||||
import com.ning.http.client.AsyncHttpClientConfig
|
|
||||||
import com.ning.http.client.Request
|
|
||||||
import com.ning.http.client.RequestBuilder
|
|
||||||
import com.ning.http.client.Response
|
|
||||||
import io.opentelemetry.api.common.AttributeKey
|
|
||||||
import io.opentelemetry.context.Context
|
|
||||||
import io.opentelemetry.context.Scope
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
|
||||||
import io.opentelemetry.instrumentation.test.base.HttpClientTest
|
|
||||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult
|
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
|
||||||
import spock.lang.AutoCleanup
|
|
||||||
import spock.lang.Shared
|
|
||||||
|
|
||||||
import java.nio.channels.ClosedChannelException
|
|
||||||
|
|
||||||
class Netty38ClientTest extends HttpClientTest<Request> implements AgentTestTrait {
|
|
||||||
|
|
||||||
@Shared
|
|
||||||
@AutoCleanup
|
|
||||||
AsyncHttpClient client = new AsyncHttpClient(getClientConfig())
|
|
||||||
|
|
||||||
def getClientConfig() {
|
|
||||||
def builder = new AsyncHttpClientConfig.Builder()
|
|
||||||
.setUserAgent("test-user-agent")
|
|
||||||
|
|
||||||
if (builder.metaClass.getMetaMethod("setConnectTimeout", int) != null) {
|
|
||||||
builder.setConnectTimeout(CONNECT_TIMEOUT_MS)
|
|
||||||
} else {
|
|
||||||
builder.setRequestTimeoutInMs(CONNECT_TIMEOUT_MS)
|
|
||||||
}
|
|
||||||
if (builder.metaClass.getMetaMethod("setFollowRedirect", boolean) != null) {
|
|
||||||
builder.setFollowRedirect(true)
|
|
||||||
} else {
|
|
||||||
builder.setFollowRedirects(true)
|
|
||||||
}
|
|
||||||
if (builder.metaClass.getMetaMethod("setMaxRedirects", int) != null) {
|
|
||||||
builder.setMaxRedirects(3)
|
|
||||||
} else {
|
|
||||||
builder.setMaximumNumberOfRedirects(3)
|
|
||||||
}
|
|
||||||
// with connection pooling is enabled there are occasional failures in high concurrency test
|
|
||||||
if (builder.metaClass.getMetaMethod("setAllowPoolingConnections", boolean) != null) {
|
|
||||||
builder.setAllowPoolingConnections(false)
|
|
||||||
} else {
|
|
||||||
builder.setAllowPoolingConnection(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Request buildRequest(String method, URI uri, Map<String, String> headers) {
|
|
||||||
def requestBuilder = new RequestBuilder(method)
|
|
||||||
.setUrl(uri.toString())
|
|
||||||
headers.entrySet().each {
|
|
||||||
requestBuilder.addHeader(it.key, it.value)
|
|
||||||
}
|
|
||||||
return requestBuilder.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
int sendRequest(Request request, String method, URI uri, Map<String, String> headers) {
|
|
||||||
return client.executeRequest(request).get().statusCode
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void sendRequestWithCallback(Request request, String method, URI uri, Map<String, String> headers, HttpClientResult requestResult) {
|
|
||||||
// TODO: context is not automatically propagated into callbacks
|
|
||||||
Context context = Context.current()
|
|
||||||
// TODO(anuraaga): Do we also need to test ListenableFuture callback?
|
|
||||||
client.executeRequest(request, new AsyncCompletionHandler<Void>() {
|
|
||||||
@Override
|
|
||||||
Void onCompleted(Response response) throws Exception {
|
|
||||||
try (Scope scope = context.makeCurrent()) {
|
|
||||||
requestResult.complete(response.statusCode)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void onThrowable(Throwable throwable) {
|
|
||||||
try (Scope scope = context.makeCurrent()) {
|
|
||||||
requestResult.complete(throwable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String userAgent() {
|
|
||||||
return "test-user-agent"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String expectedClientSpanName(URI uri, String method) {
|
|
||||||
switch (uri.toString()) {
|
|
||||||
case "http://localhost:61/": // unopened port
|
|
||||||
case "http://192.0.2.1/": // non routable address
|
|
||||||
return "CONNECT"
|
|
||||||
default:
|
|
||||||
return super.expectedClientSpanName(uri, method)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Throwable clientSpanError(URI uri, Throwable exception) {
|
|
||||||
switch (uri.toString()) {
|
|
||||||
case "http://localhost:61/": // unopened port
|
|
||||||
exception = exception.getCause() != null ? exception.getCause() : new ConnectException("Connection refused: localhost/127.0.0.1:61")
|
|
||||||
break
|
|
||||||
case "http://192.0.2.1/": // non routable address
|
|
||||||
exception = exception.getCause() != null ? exception.getCause() : new ClosedChannelException()
|
|
||||||
}
|
|
||||||
return exception
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Set<AttributeKey<?>> httpAttributes(URI uri) {
|
|
||||||
switch (uri.toString()) {
|
|
||||||
case "http://localhost:61/": // unopened port
|
|
||||||
case "http://192.0.2.1/": // non routable address
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
def attributes = super.httpAttributes(uri)
|
|
||||||
attributes.remove(SemanticAttributes.NET_PEER_NAME)
|
|
||||||
attributes.remove(SemanticAttributes.NET_PEER_PORT)
|
|
||||||
return attributes
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean testRedirects() {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean testHttps() {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,171 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.api.common.AttributeKey
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
|
||||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest
|
|
||||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
|
||||||
import org.jboss.netty.bootstrap.ServerBootstrap
|
|
||||||
import org.jboss.netty.buffer.ChannelBuffer
|
|
||||||
import org.jboss.netty.buffer.ChannelBuffers
|
|
||||||
import org.jboss.netty.channel.ChannelHandlerContext
|
|
||||||
import org.jboss.netty.channel.ChannelPipeline
|
|
||||||
import org.jboss.netty.channel.ChannelPipelineFactory
|
|
||||||
import org.jboss.netty.channel.DefaultChannelPipeline
|
|
||||||
import org.jboss.netty.channel.DownstreamMessageEvent
|
|
||||||
import org.jboss.netty.channel.ExceptionEvent
|
|
||||||
import org.jboss.netty.channel.FailedChannelFuture
|
|
||||||
import org.jboss.netty.channel.MessageEvent
|
|
||||||
import org.jboss.netty.channel.SimpleChannelHandler
|
|
||||||
import org.jboss.netty.channel.SucceededChannelFuture
|
|
||||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
|
|
||||||
import org.jboss.netty.handler.codec.http.DefaultHttpResponse
|
|
||||||
import org.jboss.netty.handler.codec.http.HttpRequest
|
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponse
|
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponseStatus
|
|
||||||
import org.jboss.netty.handler.codec.http.HttpServerCodec
|
|
||||||
import org.jboss.netty.handler.codec.http.QueryStringDecoder
|
|
||||||
import org.jboss.netty.handler.logging.LoggingHandler
|
|
||||||
import org.jboss.netty.logging.InternalLogLevel
|
|
||||||
import org.jboss.netty.logging.InternalLoggerFactory
|
|
||||||
import org.jboss.netty.logging.Slf4JLoggerFactory
|
|
||||||
import org.jboss.netty.util.CharsetUtil
|
|
||||||
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.forPath
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.LOCATION
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1
|
|
||||||
|
|
||||||
class Netty38ServerTest extends HttpServerTest<ServerBootstrap> implements AgentTestTrait {
|
|
||||||
|
|
||||||
static final LoggingHandler LOGGING_HANDLER
|
|
||||||
static {
|
|
||||||
InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory())
|
|
||||||
LOGGING_HANDLER = new LoggingHandler(SERVER_LOGGER.name, InternalLogLevel.DEBUG, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelPipeline channelPipeline() {
|
|
||||||
ChannelPipeline channelPipeline = new DefaultChannelPipeline()
|
|
||||||
channelPipeline.addFirst("logger", LOGGING_HANDLER)
|
|
||||||
|
|
||||||
channelPipeline.addLast("http-codec", new HttpServerCodec())
|
|
||||||
channelPipeline.addLast("controller", new SimpleChannelHandler() {
|
|
||||||
@Override
|
|
||||||
void messageReceived(ChannelHandlerContext ctx, MessageEvent msg) throws Exception {
|
|
||||||
if (msg.getMessage() instanceof HttpRequest) {
|
|
||||||
def request = msg.getMessage() as HttpRequest
|
|
||||||
def uri = URI.create(request.getUri())
|
|
||||||
ServerEndpoint endpoint = forPath(uri.path)
|
|
||||||
ctx.sendDownstream controller(endpoint) {
|
|
||||||
HttpResponse response
|
|
||||||
ChannelBuffer responseContent = null
|
|
||||||
switch (endpoint) {
|
|
||||||
case SUCCESS:
|
|
||||||
case ERROR:
|
|
||||||
responseContent = ChannelBuffers.copiedBuffer(endpoint.body, CharsetUtil.UTF_8)
|
|
||||||
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
|
|
||||||
response.setContent(responseContent)
|
|
||||||
break
|
|
||||||
case INDEXED_CHILD:
|
|
||||||
responseContent = ChannelBuffers.EMPTY_BUFFER
|
|
||||||
endpoint.collectSpanAttributes { new QueryStringDecoder(uri).getParameters().get(it).find() }
|
|
||||||
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
|
|
||||||
response.setContent(responseContent)
|
|
||||||
break
|
|
||||||
case QUERY_PARAM:
|
|
||||||
responseContent = ChannelBuffers.copiedBuffer(uri.query, CharsetUtil.UTF_8)
|
|
||||||
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
|
|
||||||
response.setContent(responseContent)
|
|
||||||
break
|
|
||||||
case REDIRECT:
|
|
||||||
responseContent = ChannelBuffers.EMPTY_BUFFER
|
|
||||||
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
|
|
||||||
response.setContent(responseContent)
|
|
||||||
response.headers().set(LOCATION, endpoint.body)
|
|
||||||
break
|
|
||||||
case CAPTURE_HEADERS:
|
|
||||||
responseContent = ChannelBuffers.copiedBuffer(endpoint.body, CharsetUtil.UTF_8)
|
|
||||||
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
|
|
||||||
response.headers().set("X-Test-Response", request.headers().get("X-Test-Request"))
|
|
||||||
response.setContent(responseContent)
|
|
||||||
break
|
|
||||||
case EXCEPTION:
|
|
||||||
throw new Exception(endpoint.body)
|
|
||||||
default:
|
|
||||||
responseContent = ChannelBuffers.copiedBuffer(NOT_FOUND.body, CharsetUtil.UTF_8)
|
|
||||||
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
|
|
||||||
response.setContent(responseContent)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
response.headers().set(CONTENT_TYPE, "text/plain")
|
|
||||||
if (responseContent) {
|
|
||||||
response.headers().set(CONTENT_LENGTH, responseContent.readableBytes())
|
|
||||||
}
|
|
||||||
return new DownstreamMessageEvent(
|
|
||||||
ctx.getChannel(),
|
|
||||||
new SucceededChannelFuture(ctx.getChannel()),
|
|
||||||
response,
|
|
||||||
ctx.getChannel().getRemoteAddress())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent ex) throws Exception {
|
|
||||||
def message = ex.cause == null ? "<no cause> " + ex.message : ex.cause.message == null ? "<null>" : ex.cause.message
|
|
||||||
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(message, CharsetUtil.UTF_8)
|
|
||||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR)
|
|
||||||
response.setContent(buffer)
|
|
||||||
response.headers().set(CONTENT_TYPE, "text/plain")
|
|
||||||
response.headers().set(CONTENT_LENGTH, buffer.readableBytes())
|
|
||||||
ctx.sendDownstream(new DownstreamMessageEvent(
|
|
||||||
ctx.getChannel(),
|
|
||||||
new FailedChannelFuture(ctx.getChannel(), ex.getCause()),
|
|
||||||
response,
|
|
||||||
ctx.getChannel().getRemoteAddress()))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return channelPipeline
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
ServerBootstrap startServer(int port) {
|
|
||||||
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory())
|
|
||||||
bootstrap.setParentHandler(LOGGING_HANDLER)
|
|
||||||
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
|
|
||||||
@Override
|
|
||||||
ChannelPipeline getPipeline() throws Exception {
|
|
||||||
return channelPipeline()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
InetSocketAddress address = new InetSocketAddress(port)
|
|
||||||
bootstrap.bind(address)
|
|
||||||
return bootstrap
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void stopServer(ServerBootstrap server) {
|
|
||||||
server?.shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Set<AttributeKey<?>> httpAttributes(ServerEndpoint endpoint) {
|
|
||||||
def attributes = super.httpAttributes(endpoint)
|
|
||||||
attributes.remove(SemanticAttributes.HTTP_ROUTE)
|
|
||||||
attributes
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.client;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptySet;
|
||||||
|
|
||||||
|
import com.ning.http.client.AsyncCompletionHandler;
|
||||||
|
import com.ning.http.client.AsyncHttpClient;
|
||||||
|
import com.ning.http.client.AsyncHttpClientConfig;
|
||||||
|
import com.ning.http.client.Request;
|
||||||
|
import com.ning.http.client.RequestBuilder;
|
||||||
|
import com.ning.http.client.Response;
|
||||||
|
import io.opentelemetry.api.common.AttributeKey;
|
||||||
|
import io.opentelemetry.context.Context;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions;
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class Netty38ClientTest extends AbstractHttpClientTest<Request> {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
|
||||||
|
|
||||||
|
static final String USER_AGENT = "test-user-agent";
|
||||||
|
|
||||||
|
AsyncHttpClient client;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws Exception {
|
||||||
|
AsyncHttpClientConfig.Builder builder =
|
||||||
|
new AsyncHttpClientConfig.Builder().setUserAgent(USER_AGENT);
|
||||||
|
|
||||||
|
Method setConnectTimeout;
|
||||||
|
try {
|
||||||
|
setConnectTimeout =
|
||||||
|
AsyncHttpClientConfig.Builder.class.getMethod("setConnectTimeout", int.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
setConnectTimeout =
|
||||||
|
AsyncHttpClientConfig.Builder.class.getMethod("setRequestTimeoutInMs", int.class);
|
||||||
|
}
|
||||||
|
setConnectTimeout.invoke(builder, (int) CONNECTION_TIMEOUT.toMillis());
|
||||||
|
|
||||||
|
Method setFollowRedirect;
|
||||||
|
try {
|
||||||
|
setFollowRedirect =
|
||||||
|
AsyncHttpClientConfig.Builder.class.getMethod("setFollowRedirect", boolean.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
setFollowRedirect =
|
||||||
|
AsyncHttpClientConfig.Builder.class.getMethod("setFollowRedirects", boolean.class);
|
||||||
|
}
|
||||||
|
setFollowRedirect.invoke(builder, true);
|
||||||
|
|
||||||
|
Method setMaxRedirects;
|
||||||
|
try {
|
||||||
|
setMaxRedirects = AsyncHttpClientConfig.Builder.class.getMethod("setMaxRedirects", int.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
setMaxRedirects =
|
||||||
|
AsyncHttpClientConfig.Builder.class.getMethod("setMaximumNumberOfRedirects", int.class);
|
||||||
|
}
|
||||||
|
setMaxRedirects.invoke(builder, 3);
|
||||||
|
|
||||||
|
Method setAllowPoolingConnections;
|
||||||
|
try {
|
||||||
|
setAllowPoolingConnections =
|
||||||
|
AsyncHttpClientConfig.Builder.class.getMethod(
|
||||||
|
"setAllowPoolingConnections", boolean.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
setAllowPoolingConnections =
|
||||||
|
AsyncHttpClientConfig.Builder.class.getMethod("setAllowPoolingConnection", boolean.class);
|
||||||
|
}
|
||||||
|
setAllowPoolingConnections.invoke(builder, false);
|
||||||
|
|
||||||
|
client = new AsyncHttpClient(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Request buildRequest(String method, URI uri, Map<String, String> headers) {
|
||||||
|
RequestBuilder requestBuilder = new RequestBuilder(method).setUrl(uri.toString());
|
||||||
|
headers.forEach(requestBuilder::addHeader);
|
||||||
|
return requestBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendRequest(Request request, String method, URI uri, Map<String, String> headers)
|
||||||
|
throws Exception {
|
||||||
|
return client.executeRequest(request).get().getStatusCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendRequestWithCallback(
|
||||||
|
Request request,
|
||||||
|
String method,
|
||||||
|
URI uri,
|
||||||
|
Map<String, String> headers,
|
||||||
|
HttpClientResult httpClientResult)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
// TODO: context is not automatically propagated into callbacks
|
||||||
|
Context context = Context.current();
|
||||||
|
// TODO(anuraaga): Do we also need to test ListenableFuture callback?
|
||||||
|
client.executeRequest(
|
||||||
|
request,
|
||||||
|
new AsyncCompletionHandler<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void onCompleted(Response response) {
|
||||||
|
try (Scope ignored = context.makeCurrent()) {
|
||||||
|
httpClientResult.complete(response.getStatusCode());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onThrowable(Throwable throwable) {
|
||||||
|
try (Scope ignored = context.makeCurrent()) {
|
||||||
|
httpClientResult.complete(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpClientTestOptions.Builder optionsBuilder) {
|
||||||
|
optionsBuilder.disableTestRedirects();
|
||||||
|
optionsBuilder.disableTestHttps();
|
||||||
|
|
||||||
|
optionsBuilder.setUserAgent(USER_AGENT);
|
||||||
|
|
||||||
|
optionsBuilder.setExpectedClientSpanNameMapper(
|
||||||
|
(uri, method) -> {
|
||||||
|
// unopened port or non routable address
|
||||||
|
if ("http://localhost:61/".equals(uri.toString())
|
||||||
|
|| "http://192.0.2.1/".equals(uri.toString())) {
|
||||||
|
return "CONNECT";
|
||||||
|
}
|
||||||
|
return HttpClientTestOptions.DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER.apply(uri, method);
|
||||||
|
});
|
||||||
|
|
||||||
|
optionsBuilder.setClientSpanErrorMapper(
|
||||||
|
(uri, error) -> {
|
||||||
|
if ("http://localhost:61/".equals(uri.toString())) { // unopened port
|
||||||
|
error =
|
||||||
|
error.getCause() != null
|
||||||
|
? error.getCause()
|
||||||
|
: new ConnectException("Connection refused: localhost/127.0.0.1:61");
|
||||||
|
} else if ("http://192.0.2.1/".equals(uri.toString())) { // non routable address
|
||||||
|
error = error.getCause() != null ? error.getCause() : new ClosedChannelException();
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
});
|
||||||
|
|
||||||
|
optionsBuilder.setHttpAttributes(
|
||||||
|
uri -> {
|
||||||
|
// unopened port or non routable address
|
||||||
|
if ("http://localhost:61/".equals(uri.toString())
|
||||||
|
|| "http://192.0.2.1/".equals(uri.toString())) {
|
||||||
|
return emptySet();
|
||||||
|
}
|
||||||
|
Set<AttributeKey<?>> attributes =
|
||||||
|
new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES);
|
||||||
|
attributes.remove(SemanticAttributes.NET_PEER_NAME);
|
||||||
|
attributes.remove(SemanticAttributes.NET_PEER_PORT);
|
||||||
|
return attributes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND;
|
||||||
|
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.forPath;
|
||||||
|
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
|
||||||
|
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
|
||||||
|
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.LOCATION;
|
||||||
|
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.AttributeKey;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffers;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.channel.ChannelPipeline;
|
||||||
|
import org.jboss.netty.channel.DefaultChannelPipeline;
|
||||||
|
import org.jboss.netty.channel.DownstreamMessageEvent;
|
||||||
|
import org.jboss.netty.channel.ExceptionEvent;
|
||||||
|
import org.jboss.netty.channel.FailedChannelFuture;
|
||||||
|
import org.jboss.netty.channel.MessageEvent;
|
||||||
|
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||||
|
import org.jboss.netty.channel.SucceededChannelFuture;
|
||||||
|
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||||
|
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpServerCodec;
|
||||||
|
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
|
||||||
|
import org.jboss.netty.handler.logging.LoggingHandler;
|
||||||
|
import org.jboss.netty.logging.InternalLogLevel;
|
||||||
|
import org.jboss.netty.logging.InternalLoggerFactory;
|
||||||
|
import org.jboss.netty.logging.Slf4JLoggerFactory;
|
||||||
|
import org.jboss.netty.util.CharsetUtil;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class Netty38ServerTest extends AbstractHttpServerTest<ServerBootstrap> {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent();
|
||||||
|
|
||||||
|
static final LoggingHandler LOGGING_HANDLER;
|
||||||
|
|
||||||
|
static {
|
||||||
|
InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory());
|
||||||
|
LOGGING_HANDLER =
|
||||||
|
new LoggingHandler(Netty38ServerTest.class.getName(), InternalLogLevel.DEBUG, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerBootstrap setupServer() {
|
||||||
|
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory());
|
||||||
|
bootstrap.setParentHandler(LOGGING_HANDLER);
|
||||||
|
bootstrap.setPipelineFactory(Netty38ServerTest::channelPipeline);
|
||||||
|
|
||||||
|
InetSocketAddress address = new InetSocketAddress(port);
|
||||||
|
bootstrap.bind(address);
|
||||||
|
return bootstrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stopServer(ServerBootstrap server) {
|
||||||
|
server.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpServerTestOptions options) {
|
||||||
|
options.setHttpAttributes(
|
||||||
|
serverEndpoint -> {
|
||||||
|
Set<AttributeKey<?>> attributes =
|
||||||
|
new HashSet<>(HttpServerTestOptions.DEFAULT_HTTP_ATTRIBUTES);
|
||||||
|
attributes.remove(SemanticAttributes.HTTP_ROUTE);
|
||||||
|
return attributes;
|
||||||
|
});
|
||||||
|
|
||||||
|
options.setExpectedException(new IllegalArgumentException(ServerEndpoint.EXCEPTION.getBody()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChannelPipeline channelPipeline() {
|
||||||
|
ChannelPipeline channelPipeline = new DefaultChannelPipeline();
|
||||||
|
channelPipeline.addFirst("logger", LOGGING_HANDLER);
|
||||||
|
|
||||||
|
channelPipeline.addLast("http-codec", new HttpServerCodec());
|
||||||
|
channelPipeline.addLast(
|
||||||
|
"controller",
|
||||||
|
new SimpleChannelHandler() {
|
||||||
|
@Override
|
||||||
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent msg) {
|
||||||
|
if (msg.getMessage() instanceof HttpRequest) {
|
||||||
|
HttpRequest request = (HttpRequest) msg.getMessage();
|
||||||
|
URI uri = URI.create(request.getUri());
|
||||||
|
ServerEndpoint endpoint = forPath(uri.getPath());
|
||||||
|
ctx.sendDownstream(
|
||||||
|
controller(
|
||||||
|
endpoint,
|
||||||
|
() -> {
|
||||||
|
HttpResponse response;
|
||||||
|
ChannelBuffer responseContent;
|
||||||
|
switch (endpoint) {
|
||||||
|
case SUCCESS:
|
||||||
|
case ERROR:
|
||||||
|
responseContent =
|
||||||
|
ChannelBuffers.copiedBuffer(endpoint.getBody(), CharsetUtil.UTF_8);
|
||||||
|
response =
|
||||||
|
new DefaultHttpResponse(
|
||||||
|
HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()));
|
||||||
|
response.setContent(responseContent);
|
||||||
|
break;
|
||||||
|
case INDEXED_CHILD:
|
||||||
|
responseContent = ChannelBuffers.EMPTY_BUFFER;
|
||||||
|
endpoint.collectSpanAttributes(
|
||||||
|
name ->
|
||||||
|
new QueryStringDecoder(uri)
|
||||||
|
.getParameters().get(name).stream()
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null));
|
||||||
|
response =
|
||||||
|
new DefaultHttpResponse(
|
||||||
|
HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()));
|
||||||
|
response.setContent(responseContent);
|
||||||
|
break;
|
||||||
|
case QUERY_PARAM:
|
||||||
|
responseContent =
|
||||||
|
ChannelBuffers.copiedBuffer(uri.getQuery(), CharsetUtil.UTF_8);
|
||||||
|
response =
|
||||||
|
new DefaultHttpResponse(
|
||||||
|
HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()));
|
||||||
|
response.setContent(responseContent);
|
||||||
|
break;
|
||||||
|
case REDIRECT:
|
||||||
|
responseContent = ChannelBuffers.EMPTY_BUFFER;
|
||||||
|
response =
|
||||||
|
new DefaultHttpResponse(
|
||||||
|
HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()));
|
||||||
|
response.setContent(responseContent);
|
||||||
|
response.headers().set(LOCATION, endpoint.getBody());
|
||||||
|
break;
|
||||||
|
case CAPTURE_HEADERS:
|
||||||
|
responseContent =
|
||||||
|
ChannelBuffers.copiedBuffer(endpoint.getBody(), CharsetUtil.UTF_8);
|
||||||
|
response =
|
||||||
|
new DefaultHttpResponse(
|
||||||
|
HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()));
|
||||||
|
response
|
||||||
|
.headers()
|
||||||
|
.set("X-Test-Response", request.headers().get("X-Test-Request"));
|
||||||
|
response.setContent(responseContent);
|
||||||
|
break;
|
||||||
|
case EXCEPTION:
|
||||||
|
throw new IllegalArgumentException(endpoint.getBody());
|
||||||
|
default:
|
||||||
|
responseContent =
|
||||||
|
ChannelBuffers.copiedBuffer(NOT_FOUND.getBody(), CharsetUtil.UTF_8);
|
||||||
|
response =
|
||||||
|
new DefaultHttpResponse(
|
||||||
|
HTTP_1_1, HttpResponseStatus.valueOf(endpoint.getStatus()));
|
||||||
|
response.setContent(responseContent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
response.headers().set(CONTENT_TYPE, "text/plain");
|
||||||
|
if (responseContent != null) {
|
||||||
|
response.headers().set(CONTENT_LENGTH, responseContent.readableBytes());
|
||||||
|
}
|
||||||
|
return new DownstreamMessageEvent(
|
||||||
|
ctx.getChannel(),
|
||||||
|
new SucceededChannelFuture(ctx.getChannel()),
|
||||||
|
response,
|
||||||
|
ctx.getChannel().getRemoteAddress());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent ex) {
|
||||||
|
String message =
|
||||||
|
ex.getCause() == null
|
||||||
|
? "<no cause>"
|
||||||
|
: ex.getCause().getMessage() == null ? "<null>" : ex.getCause().getMessage();
|
||||||
|
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(message, CharsetUtil.UTF_8);
|
||||||
|
HttpResponse response =
|
||||||
|
new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
response.setContent(buffer);
|
||||||
|
response.headers().set(CONTENT_TYPE, "text/plain");
|
||||||
|
response.headers().set(CONTENT_LENGTH, buffer.readableBytes());
|
||||||
|
ctx.sendDownstream(
|
||||||
|
new DownstreamMessageEvent(
|
||||||
|
ctx.getChannel(),
|
||||||
|
new FailedChannelFuture(ctx.getChannel(), ex.getCause()),
|
||||||
|
response,
|
||||||
|
ctx.getChannel().getRemoteAddress()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return channelPipeline;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue