Grizzly-http and grizzly-client instrumentation (DataDog/dd-trace-java#1365)
This commit is contained in:
parent
ebebbc7dae
commit
d8c9639fbb
|
@ -25,8 +25,10 @@ public class InstrumentationContext {
|
||||||
* <p>Conceptually this can be thought of as a map lookup to fetch a second level map given
|
* <p>Conceptually this can be thought of as a map lookup to fetch a second level map given
|
||||||
* keyClass.
|
* keyClass.
|
||||||
*
|
*
|
||||||
* <p>However, the implementation is actually provided by bytecode transformation for performance
|
* <p>In reality, the <em>calls</em> to this method are re-written to something more performant
|
||||||
* reasons.
|
* while injecting advice into a method.
|
||||||
|
*
|
||||||
|
* <p>This method must only be called within an Advice class.
|
||||||
*
|
*
|
||||||
* @param keyClass The key class context is attached to.
|
* @param keyClass The key class context is attached to.
|
||||||
* @param contextClass The context class attached to the user class.
|
* @param contextClass The context class attached to the user class.
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.bootstrap.instrumentation.api;
|
||||||
|
|
||||||
|
public final class Pair<T, U> {
|
||||||
|
|
||||||
|
public static <T, U> Pair<T, U> of(final T left, final U right) {
|
||||||
|
return new Pair<>(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final T left;
|
||||||
|
private final U right;
|
||||||
|
|
||||||
|
Pair(final T left, final U right) {
|
||||||
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getLeft() {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public U getRight() {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasLeft() {
|
||||||
|
return null != left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasRight() {
|
||||||
|
return null != right;
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class HttpServerDecorator<REQUEST, CONNECTION, RESPONSE> extends ServerDecorator {
|
public abstract class HttpServerDecorator<REQUEST, CONNECTION, RESPONSE> extends ServerDecorator {
|
||||||
public static final String SPAN_ATTRIBUTE = "io.opentelemetry.auto.span";
|
public static final String SPAN_ATTRIBUTE = "io.opentelemetry.auto.span";
|
||||||
|
public static final String RESPONSE_ATTRIBUTE = "io.opentelemetry.auto.response";
|
||||||
public static final String DEFAULT_SPAN_NAME = "HTTP request";
|
public static final String DEFAULT_SPAN_NAME = "HTTP request";
|
||||||
|
|
||||||
protected abstract String method(REQUEST request);
|
protected abstract String method(REQUEST request);
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
ext {
|
||||||
|
minJavaVersionForTests = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "${rootDir}/gradle/instrumentation.gradle"
|
||||||
|
apply plugin: 'org.unbroken-dome.test-sets'
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group = "org.glassfish.grizzly"
|
||||||
|
module = "grizzly-http-client"
|
||||||
|
versions = "[1.9,1.16]"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
pass {
|
||||||
|
group = "com.ning"
|
||||||
|
module = "async-http-client"
|
||||||
|
versions = "[1.9.0,)"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testSets {
|
||||||
|
latestDepTest {
|
||||||
|
dirName = 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly group: 'org.glassfish.grizzly', name: 'grizzly-http-client', version: '1.9'
|
||||||
|
// for some reason, the tests don't *load* until 1.12, but muzzles works as far back as 1.9
|
||||||
|
testCompile group: 'org.glassfish.grizzly', name: 'grizzly-http-client', version: '1.12'
|
||||||
|
|
||||||
|
latestDepTestCompile group: 'org.glassfish.grizzly', name: 'grizzly-http-client', version: '1.16'
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.client;
|
||||||
|
|
||||||
|
import com.ning.http.client.Request;
|
||||||
|
import com.ning.http.client.Response;
|
||||||
|
import io.opentelemetry.OpenTelemetry;
|
||||||
|
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpClientDecorator;
|
||||||
|
import io.opentelemetry.trace.Tracer;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
public class ClientDecorator extends HttpClientDecorator<Request, Response> {
|
||||||
|
|
||||||
|
public static final ClientDecorator DECORATE = new ClientDecorator();
|
||||||
|
|
||||||
|
public static final Tracer TRACER =
|
||||||
|
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.grizzly-client-1.9");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String method(final Request request) {
|
||||||
|
return request.getMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected URI url(final Request request) throws URISyntaxException {
|
||||||
|
return request.getUri().toJavaNetURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer status(final Response response) {
|
||||||
|
return response.getStatusCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.client;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.instrumentation.grizzly.client.ClientDecorator.DECORATE;
|
||||||
|
import static io.opentelemetry.auto.instrumentation.grizzly.client.ClientDecorator.TRACER;
|
||||||
|
import static io.opentelemetry.auto.instrumentation.grizzly.client.InjectAdapter.SETTER;
|
||||||
|
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
||||||
|
import static io.opentelemetry.trace.TracingContextUtils.withSpan;
|
||||||
|
|
||||||
|
import com.ning.http.client.AsyncHandler;
|
||||||
|
import com.ning.http.client.Request;
|
||||||
|
import io.grpc.Context;
|
||||||
|
import io.opentelemetry.OpenTelemetry;
|
||||||
|
import io.opentelemetry.auto.bootstrap.InstrumentationContext;
|
||||||
|
import io.opentelemetry.auto.bootstrap.instrumentation.api.Pair;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
public class ClientRequestAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static Scope onEnter(
|
||||||
|
@Advice.Argument(0) final Request request,
|
||||||
|
@Advice.Argument(1) final AsyncHandler<?> handler) {
|
||||||
|
final Span parentSpan = TRACER.getCurrentSpan();
|
||||||
|
final Span span =
|
||||||
|
TRACER.spanBuilder(DECORATE.spanNameForRequest(request)).setSpanKind(CLIENT).startSpan();
|
||||||
|
DECORATE.afterStart(span);
|
||||||
|
DECORATE.onRequest(span, request);
|
||||||
|
|
||||||
|
final Context context = withSpan(span, Context.current());
|
||||||
|
OpenTelemetry.getPropagators().getHttpTextFormat().inject(context, request, SETTER);
|
||||||
|
InstrumentationContext.get(AsyncHandler.class, Pair.class)
|
||||||
|
.put(handler, Pair.of(parentSpan, span));
|
||||||
|
return TRACER.withSpan(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void onExit(@Advice.Enter final Scope scope) {
|
||||||
|
// span closed in ClientResponseAdvice
|
||||||
|
scope.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.client;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.bootstrap.instrumentation.api.Pair;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public final class ClientRequestInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public ClientRequestInstrumentation() {
|
||||||
|
super("grizzly-client", "ning");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> contextStore() {
|
||||||
|
return singletonMap("com.ning.http.client.AsyncHandler", Pair.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return named("com.ning.http.client.AsyncHttpClient");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {packageName + ".ClientDecorator", packageName + ".InjectAdapter"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean defaultEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
named("executeRequest")
|
||||||
|
.and(takesArgument(0, named("com.ning.http.client.Request")))
|
||||||
|
.and(takesArgument(1, named("com.ning.http.client.AsyncHandler")))
|
||||||
|
.and(isPublic()),
|
||||||
|
packageName + ".ClientRequestAdvice");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.client;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.instrumentation.grizzly.client.ClientDecorator.DECORATE;
|
||||||
|
import static io.opentelemetry.auto.instrumentation.grizzly.client.ClientDecorator.TRACER;
|
||||||
|
|
||||||
|
import com.ning.http.client.AsyncCompletionHandler;
|
||||||
|
import com.ning.http.client.AsyncHandler;
|
||||||
|
import com.ning.http.client.Response;
|
||||||
|
import io.opentelemetry.auto.bootstrap.ContextStore;
|
||||||
|
import io.opentelemetry.auto.bootstrap.InstrumentationContext;
|
||||||
|
import io.opentelemetry.auto.bootstrap.instrumentation.api.Pair;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
public class ClientResponseAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static Scope onEnter(
|
||||||
|
@Advice.This final AsyncCompletionHandler<?> handler,
|
||||||
|
@Advice.Argument(0) final Response response) {
|
||||||
|
final ContextStore<AsyncHandler, Pair> contextStore =
|
||||||
|
InstrumentationContext.get(AsyncHandler.class, Pair.class);
|
||||||
|
final Pair<Span, Span> spanWithParent = contextStore.get(handler);
|
||||||
|
if (null != spanWithParent) {
|
||||||
|
contextStore.put(handler, null);
|
||||||
|
}
|
||||||
|
if (spanWithParent.hasRight()) {
|
||||||
|
DECORATE.onResponse(spanWithParent.getRight(), response);
|
||||||
|
DECORATE.beforeFinish(spanWithParent.getRight());
|
||||||
|
spanWithParent.getRight().end();
|
||||||
|
}
|
||||||
|
return spanWithParent.hasLeft() ? TRACER.withSpan(spanWithParent.getLeft()) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void onExit(@Advice.Enter final Scope scope) {
|
||||||
|
if (null != scope) {
|
||||||
|
scope.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.client;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.bootstrap.instrumentation.api.Pair;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public final class ClientResponseInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public ClientResponseInstrumentation() {
|
||||||
|
super("grizzly-client", "ning");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> contextStore() {
|
||||||
|
return singletonMap("com.ning.http.client.AsyncHandler", Pair.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<ClassLoader> classLoaderMatcher() {
|
||||||
|
return hasClassesNamed("com.ning.http.client.AsyncCompletionHandler");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean defaultEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return hasSuperClass(named("com.ning.http.client.AsyncCompletionHandler"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {packageName + ".ClientDecorator"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
named("onCompleted")
|
||||||
|
.and(takesArgument(0, named("com.ning.http.client.Response")))
|
||||||
|
.and(isPublic()),
|
||||||
|
packageName + ".ClientResponseAdvice");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.client;
|
||||||
|
|
||||||
|
import com.ning.http.client.Request;
|
||||||
|
import io.opentelemetry.context.propagation.HttpTextFormat;
|
||||||
|
|
||||||
|
public class InjectAdapter implements HttpTextFormat.Setter<Request> {
|
||||||
|
|
||||||
|
public static final InjectAdapter SETTER = new InjectAdapter();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(final Request carrier, final String key, final String value) {
|
||||||
|
carrier.getHeaders().replaceWith(key, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import com.ning.http.client.AsyncCompletionHandler
|
||||||
|
import com.ning.http.client.AsyncHttpClient
|
||||||
|
import com.ning.http.client.Request
|
||||||
|
import com.ning.http.client.RequestBuilder
|
||||||
|
import com.ning.http.client.Response
|
||||||
|
import com.ning.http.client.uri.Uri
|
||||||
|
import io.opentelemetry.auto.test.base.HttpClientTest
|
||||||
|
import spock.lang.AutoCleanup
|
||||||
|
import spock.lang.Shared
|
||||||
|
|
||||||
|
class GrizzlyAsyncHttpClientTest extends HttpClientTest {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("ota.integration.grizzly-client.enabled", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
@AutoCleanup
|
||||||
|
@Shared
|
||||||
|
def client = new AsyncHttpClient()
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
|
||||||
|
|
||||||
|
RequestBuilder requestBuilder = new RequestBuilder(method)
|
||||||
|
.setUri(Uri.create(uri.toString()))
|
||||||
|
headers.entrySet().each {
|
||||||
|
requestBuilder.addHeader(it.key, it.value)
|
||||||
|
}
|
||||||
|
Request request = requestBuilder.build()
|
||||||
|
|
||||||
|
def handler = new AsyncCompletionHandlerMock(callback)
|
||||||
|
|
||||||
|
def response = client.executeRequest(request, handler).get()
|
||||||
|
response.statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
class AsyncCompletionHandlerMock extends AsyncCompletionHandler<Response> {
|
||||||
|
|
||||||
|
private Closure callback
|
||||||
|
|
||||||
|
AsyncCompletionHandlerMock(Closure callback) {
|
||||||
|
this.callback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Response onCompleted(Response response) throws Exception {
|
||||||
|
if (callback != null) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testRedirects() {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testConnectionFailure() {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testRemoteConnection() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
apply from: "${rootDir}/gradle/instrumentation.gradle"
|
||||||
|
apply plugin: 'org.unbroken-dome.test-sets'
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group = "org.glassfish.grizzly"
|
||||||
|
module = 'grizzly-http'
|
||||||
|
versions = "[2.3,)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testSets {
|
||||||
|
latestDepTest {
|
||||||
|
dirName = 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
compileOnly group: 'org.glassfish.grizzly', name: 'grizzly-http', version: '2.3'
|
||||||
|
|
||||||
|
testCompile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.2.3'
|
||||||
|
testCompile group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
|
||||||
|
testCompile group: 'org.glassfish.jersey.containers', name: 'jersey-container-grizzly2-http', version: '2.0'
|
||||||
|
|
||||||
|
latestDepTestCompile group: 'org.glassfish.jersey.containers', name: 'jersey-container-grizzly2-http', version: '2.+'
|
||||||
|
latestDepTestCompile group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.+'
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import org.glassfish.grizzly.filterchain.FilterChainContext;
|
||||||
|
|
||||||
|
public class DefaultFilterChainAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void onFail(
|
||||||
|
@Advice.Argument(0) final FilterChainContext ctx,
|
||||||
|
@Advice.Argument(1) final Throwable throwable) {
|
||||||
|
GrizzlyDecorator.onFilterChainFail(ctx, throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPrivate;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class DefaultFilterChainInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public DefaultFilterChainInstrumentation() {
|
||||||
|
super("grizzly-filterchain");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return named("org.glassfish.grizzly.filterchain.DefaultFilterChain");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {packageName + ".GrizzlyDecorator", packageName + ".ExtractAdapter"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean defaultEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return Collections.singletonMap(
|
||||||
|
isMethod()
|
||||||
|
.and(isPrivate())
|
||||||
|
.and(named("notifyFailure"))
|
||||||
|
.and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext")))
|
||||||
|
.and(takesArgument(1, named("java.lang.Throwable"))),
|
||||||
|
packageName + ".DefaultFilterChainAdvice");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import io.opentelemetry.context.propagation.HttpTextFormat;
|
||||||
|
import org.glassfish.grizzly.http.HttpHeader;
|
||||||
|
|
||||||
|
public class ExtractAdapter implements HttpTextFormat.Getter<HttpHeader> {
|
||||||
|
public static final ExtractAdapter GETTER = new ExtractAdapter();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(final HttpHeader carrier, final String key) {
|
||||||
|
return carrier.getHeader(key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpServerDecorator.SPAN_ATTRIBUTE;
|
||||||
|
import static io.opentelemetry.auto.instrumentation.grizzly.http.v2_3.GrizzlyDecorator.TRACER;
|
||||||
|
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import org.glassfish.grizzly.filterchain.BaseFilter;
|
||||||
|
import org.glassfish.grizzly.filterchain.FilterChainContext;
|
||||||
|
|
||||||
|
public class FilterAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static Scope onEnter(
|
||||||
|
@Advice.This final BaseFilter it, @Advice.Argument(0) final FilterChainContext ctx) {
|
||||||
|
if (TRACER.getCurrentSpan().getContext().isValid()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Span span = (Span) ctx.getAttributes().getAttribute(SPAN_ATTRIBUTE);
|
||||||
|
if (span == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return TRACER.withSpan(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void onExit(@Advice.This final BaseFilter it, @Advice.Enter final Scope scope) {
|
||||||
|
if (scope != null) {
|
||||||
|
scope.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public final class FilterInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public FilterInstrumentation() {
|
||||||
|
super("grizzly-filterchain");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<ClassLoader> classLoaderMatcher() {
|
||||||
|
return hasClassesNamed("org.glassfish.grizzly.filterchain.BaseFilter");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return hasSuperClass(named("org.glassfish.grizzly.filterchain.BaseFilter"))
|
||||||
|
// HttpCodecFilter is instrumented in the server instrumentation
|
||||||
|
.and(
|
||||||
|
not(
|
||||||
|
ElementMatchers.<TypeDescription>named(
|
||||||
|
"org.glassfish.grizzly.http.HttpCodecFilter")))
|
||||||
|
.and(
|
||||||
|
not(
|
||||||
|
ElementMatchers.<TypeDescription>named(
|
||||||
|
"org.glassfish.grizzly.http.HttpServerFilter")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {packageName + ".GrizzlyDecorator", packageName + ".ExtractAdapter"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
named("handleRead")
|
||||||
|
.and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext")))
|
||||||
|
.and(isPublic()),
|
||||||
|
packageName + ".FilterAdvice");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import static io.opentelemetry.trace.Span.Kind.SERVER;
|
||||||
|
|
||||||
|
import io.opentelemetry.OpenTelemetry;
|
||||||
|
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpServerDecorator;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import io.opentelemetry.trace.Tracer;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import org.glassfish.grizzly.filterchain.FilterChainContext;
|
||||||
|
import org.glassfish.grizzly.http.HttpHeader;
|
||||||
|
import org.glassfish.grizzly.http.HttpRequestPacket;
|
||||||
|
import org.glassfish.grizzly.http.HttpResponsePacket;
|
||||||
|
|
||||||
|
public class GrizzlyDecorator
|
||||||
|
extends HttpServerDecorator<HttpRequestPacket, HttpRequestPacket, HttpResponsePacket> {
|
||||||
|
|
||||||
|
public static final GrizzlyDecorator DECORATE = new GrizzlyDecorator();
|
||||||
|
|
||||||
|
public static final Tracer TRACER =
|
||||||
|
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.grizzly-http-2.3");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String method(final HttpRequestPacket httpRequest) {
|
||||||
|
return httpRequest.getMethod().getMethodString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected URI url(final HttpRequestPacket httpRequest) throws URISyntaxException {
|
||||||
|
return new URI(
|
||||||
|
(httpRequest.isSecure() ? "https://" : "http://")
|
||||||
|
+ httpRequest.getRemoteHost()
|
||||||
|
+ ":"
|
||||||
|
+ httpRequest.getLocalPort()
|
||||||
|
+ httpRequest.getRequestURI()
|
||||||
|
+ (httpRequest.getQueryString() != null ? "?" + httpRequest.getQueryString() : ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String peerHostIP(final HttpRequestPacket httpRequest) {
|
||||||
|
return httpRequest.getLocalHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer peerPort(final HttpRequestPacket httpRequest) {
|
||||||
|
return httpRequest.getLocalPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer status(final HttpResponsePacket httpResponse) {
|
||||||
|
return httpResponse.getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onHttpServerFilterPrepareResponseExit(
|
||||||
|
final FilterChainContext ctx, final HttpResponsePacket responsePacket) {
|
||||||
|
final Span span = (Span) ctx.getAttributes().getAttribute(SPAN_ATTRIBUTE);
|
||||||
|
DECORATE.onResponse(span, responsePacket);
|
||||||
|
span.end();
|
||||||
|
ctx.getAttributes().removeAttribute(SPAN_ATTRIBUTE);
|
||||||
|
ctx.getAttributes().removeAttribute(RESPONSE_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onHttpCodecFilterExit(
|
||||||
|
final FilterChainContext ctx, final HttpHeader httpHeader) {
|
||||||
|
// only create a span if there isn't another one attached to the current ctx
|
||||||
|
// and if the httpHeader has been parsed into a HttpRequestPacket
|
||||||
|
if (ctx.getAttributes().getAttribute(SPAN_ATTRIBUTE) != null
|
||||||
|
|| !(httpHeader instanceof HttpRequestPacket)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final HttpRequestPacket httpRequest = (HttpRequestPacket) httpHeader;
|
||||||
|
final HttpResponsePacket httpResponse = httpRequest.getResponse();
|
||||||
|
final Span span =
|
||||||
|
TRACER
|
||||||
|
.spanBuilder(DECORATE.spanNameForRequest(httpRequest))
|
||||||
|
.setSpanKind(SERVER)
|
||||||
|
.setParent(extract(httpHeader, ExtractAdapter.GETTER))
|
||||||
|
.startSpan();
|
||||||
|
try (final Scope ignored = TRACER.withSpan(span)) {
|
||||||
|
DECORATE.afterStart(span);
|
||||||
|
ctx.getAttributes().setAttribute(SPAN_ATTRIBUTE, span);
|
||||||
|
ctx.getAttributes().setAttribute(RESPONSE_ATTRIBUTE, httpResponse);
|
||||||
|
DECORATE.onConnection(span, httpRequest);
|
||||||
|
DECORATE.onRequest(span, httpRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onFilterChainFail(final FilterChainContext ctx, final Throwable throwable) {
|
||||||
|
final Span span = (Span) ctx.getAttributes().getAttribute(SPAN_ATTRIBUTE);
|
||||||
|
if (null != span) {
|
||||||
|
DECORATE.onError(span, throwable);
|
||||||
|
DECORATE.beforeFinish(span);
|
||||||
|
span.end();
|
||||||
|
}
|
||||||
|
ctx.getAttributes().removeAttribute(SPAN_ATTRIBUTE);
|
||||||
|
ctx.getAttributes().removeAttribute(RESPONSE_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import org.glassfish.grizzly.filterchain.FilterChainContext;
|
||||||
|
import org.glassfish.grizzly.http.HttpHeader;
|
||||||
|
|
||||||
|
public class HttpCodecFilterAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void onExit(
|
||||||
|
@Advice.Argument(0) final FilterChainContext ctx,
|
||||||
|
@Advice.Argument(1) final HttpHeader httpHeader) {
|
||||||
|
GrizzlyDecorator.onHttpCodecFilterExit(ctx, httpHeader);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public final class HttpCodecFilterInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public HttpCodecFilterInstrumentation() {
|
||||||
|
super("grizzly-filterchain");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return named("org.glassfish.grizzly.http.HttpCodecFilter");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean defaultEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {packageName + ".GrizzlyDecorator", packageName + ".ExtractAdapter"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||||
|
// this is for 2.3 through 2.3.19
|
||||||
|
transformers.put(
|
||||||
|
named("handleRead")
|
||||||
|
.and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext")))
|
||||||
|
.and(takesArgument(1, named("org.glassfish.grizzly.http.HttpPacketParsing")))
|
||||||
|
.and(isPublic()),
|
||||||
|
packageName + ".HttpCodecFilterOldAdvice");
|
||||||
|
// this is for 2.3.20+
|
||||||
|
transformers.put(
|
||||||
|
named("handleRead")
|
||||||
|
.and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext")))
|
||||||
|
.and(takesArgument(1, named("org.glassfish.grizzly.http.HttpHeader")))
|
||||||
|
.and(isPublic()),
|
||||||
|
packageName + ".HttpCodecFilterAdvice");
|
||||||
|
return transformers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import org.glassfish.grizzly.filterchain.FilterChainContext;
|
||||||
|
import org.glassfish.grizzly.http.HttpHeader;
|
||||||
|
import org.glassfish.grizzly.http.HttpPacketParsing;
|
||||||
|
|
||||||
|
public class HttpCodecFilterOldAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void onExit(
|
||||||
|
@Advice.Argument(0) final FilterChainContext ctx,
|
||||||
|
@Advice.Argument(1) final HttpPacketParsing httpHeader) {
|
||||||
|
GrizzlyDecorator.onHttpCodecFilterExit(ctx, (HttpHeader) httpHeader);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import org.glassfish.grizzly.filterchain.FilterChainContext;
|
||||||
|
import org.glassfish.grizzly.http.HttpResponsePacket;
|
||||||
|
|
||||||
|
public class HttpServerFilterAdvice {
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void onExit(
|
||||||
|
@Advice.Argument(0) final FilterChainContext ctx,
|
||||||
|
@Advice.Argument(2) final HttpResponsePacket response) {
|
||||||
|
GrizzlyDecorator.onHttpServerFilterPrepareResponseExit(ctx, response);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.opentelemetry.auto.instrumentation.grizzly.http.v2_3;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPrivate;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class HttpServerFilterInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public HttpServerFilterInstrumentation() {
|
||||||
|
super("grizzly-filterchain");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return named("org.glassfish.grizzly.http.HttpServerFilter");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean defaultEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {packageName + ".GrizzlyDecorator", packageName + ".ExtractAdapter"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return Collections.singletonMap(
|
||||||
|
named("prepareResponse")
|
||||||
|
.and(takesArgument(0, named("org.glassfish.grizzly.filterchain.FilterChainContext")))
|
||||||
|
.and(takesArgument(1, named("org.glassfish.grizzly.http.HttpRequestPacket")))
|
||||||
|
.and(takesArgument(2, named("org.glassfish.grizzly.http.HttpResponsePacket")))
|
||||||
|
.and(takesArgument(3, named("org.glassfish.grizzly.http.HttpContent")))
|
||||||
|
.and(isPrivate()),
|
||||||
|
packageName + ".HttpServerFilterAdvice");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import org.glassfish.grizzly.http.server.HttpServer
|
||||||
|
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig
|
||||||
|
|
||||||
|
import javax.ws.rs.GET
|
||||||
|
import javax.ws.rs.Path
|
||||||
|
import javax.ws.rs.QueryParam
|
||||||
|
import javax.ws.rs.container.AsyncResponse
|
||||||
|
import javax.ws.rs.container.Suspended
|
||||||
|
import javax.ws.rs.core.Response
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||||
|
|
||||||
|
class GrizzlyAsyncTest extends GrizzlyTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
HttpServer startServer(int port) {
|
||||||
|
ResourceConfig rc = new ResourceConfig()
|
||||||
|
rc.register(SimpleExceptionMapper)
|
||||||
|
rc.register(AsyncServiceResource)
|
||||||
|
GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("/")
|
||||||
|
static class AsyncServiceResource {
|
||||||
|
private ExecutorService executor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("success")
|
||||||
|
void success(@Suspended AsyncResponse ar) {
|
||||||
|
executor.execute {
|
||||||
|
controller(SUCCESS) {
|
||||||
|
ar.resume(Response.status(SUCCESS.status).entity(SUCCESS.body).build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("query")
|
||||||
|
Response query_param(@QueryParam("some") String param, @Suspended AsyncResponse ar) {
|
||||||
|
controller(QUERY_PARAM) {
|
||||||
|
ar.resume(Response.status(QUERY_PARAM.status).entity("some=$param".toString()).build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("redirect")
|
||||||
|
void redirect(@Suspended AsyncResponse ar) {
|
||||||
|
executor.execute {
|
||||||
|
controller(REDIRECT) {
|
||||||
|
ar.resume(Response.status(REDIRECT.status).location(new URI(REDIRECT.body)).build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("error-status")
|
||||||
|
void error(@Suspended AsyncResponse ar) {
|
||||||
|
executor.execute {
|
||||||
|
controller(ERROR) {
|
||||||
|
ar.resume(Response.status(ERROR.status).entity(ERROR.body).build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("exception")
|
||||||
|
void exception(@Suspended AsyncResponse ar) {
|
||||||
|
executor.execute {
|
||||||
|
try {
|
||||||
|
controller(EXCEPTION) {
|
||||||
|
throw new Exception(EXCEPTION.body)
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
ar.resume(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import io.opentelemetry.auto.test.base.HttpServerTest
|
||||||
|
import org.glassfish.grizzly.filterchain.BaseFilter
|
||||||
|
import org.glassfish.grizzly.filterchain.FilterChain
|
||||||
|
import org.glassfish.grizzly.filterchain.FilterChainBuilder
|
||||||
|
import org.glassfish.grizzly.filterchain.FilterChainContext
|
||||||
|
import org.glassfish.grizzly.filterchain.NextAction
|
||||||
|
import org.glassfish.grizzly.filterchain.TransportFilter
|
||||||
|
import org.glassfish.grizzly.http.HttpContent
|
||||||
|
import org.glassfish.grizzly.http.HttpHeader
|
||||||
|
import org.glassfish.grizzly.http.HttpRequestPacket
|
||||||
|
import org.glassfish.grizzly.http.HttpResponsePacket
|
||||||
|
import org.glassfish.grizzly.http.HttpServerFilter
|
||||||
|
import org.glassfish.grizzly.http.server.HttpServer
|
||||||
|
import org.glassfish.grizzly.nio.transport.TCPNIOServerConnection
|
||||||
|
import org.glassfish.grizzly.nio.transport.TCPNIOTransport
|
||||||
|
import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder
|
||||||
|
import org.glassfish.grizzly.utils.DelayedExecutor
|
||||||
|
import org.glassfish.grizzly.utils.IdleTimeoutFilter
|
||||||
|
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.AUTH_REQUIRED
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||||
|
import static java.lang.String.valueOf
|
||||||
|
import static java.nio.charset.Charset.defaultCharset
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
|
import static org.glassfish.grizzly.memory.Buffers.wrap
|
||||||
|
|
||||||
|
class GrizzlyFilterchainServerTest extends HttpServerTest<HttpServer> {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("ota.integration.grizzly-filterchain.enabled", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
private TCPNIOTransport transport
|
||||||
|
private TCPNIOServerConnection serverConnection
|
||||||
|
|
||||||
|
@Override
|
||||||
|
HttpServer startServer(int port) {
|
||||||
|
FilterChain filterChain = setUpFilterChain()
|
||||||
|
setUpTransport(filterChain)
|
||||||
|
|
||||||
|
serverConnection = transport.bind("127.0.0.1", port)
|
||||||
|
transport.start()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void stopServer(HttpServer httpServer) {
|
||||||
|
transport.shutdownNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testException() {
|
||||||
|
// justification: grizzly async closes the channel which
|
||||||
|
// looks like a ConnectException to the client when this happens
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUpTransport(FilterChain filterChain) {
|
||||||
|
TCPNIOTransportBuilder transportBuilder = TCPNIOTransportBuilder.newInstance()
|
||||||
|
.setOptimizedForMultiplexing(true)
|
||||||
|
|
||||||
|
transportBuilder.setTcpNoDelay(true)
|
||||||
|
transportBuilder.setKeepAlive(false)
|
||||||
|
transportBuilder.setReuseAddress(true)
|
||||||
|
transportBuilder.setServerConnectionBackLog(50)
|
||||||
|
transportBuilder.setServerSocketSoTimeout(80000)
|
||||||
|
|
||||||
|
transport = transportBuilder.build()
|
||||||
|
transport.setProcessor(filterChain)
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterChain setUpFilterChain() {
|
||||||
|
return FilterChainBuilder.stateless()
|
||||||
|
.add(createTransportFilter())
|
||||||
|
.add(createIdleTimeoutFilter())
|
||||||
|
.add(new HttpServerFilter())
|
||||||
|
.add(new LastFilter())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
TransportFilter createTransportFilter() {
|
||||||
|
return new TransportFilter()
|
||||||
|
}
|
||||||
|
|
||||||
|
IdleTimeoutFilter createIdleTimeoutFilter() {
|
||||||
|
return new IdleTimeoutFilter(new DelayedExecutor(Executors.newCachedThreadPool()), 80000, MILLISECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
class LastFilter extends BaseFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
NextAction handleRead(final FilterChainContext ctx) throws IOException {
|
||||||
|
if (ctx.getMessage() instanceof HttpContent) {
|
||||||
|
final HttpContent httpContent = ctx.getMessage()
|
||||||
|
final HttpHeader httpHeader = httpContent.getHttpHeader()
|
||||||
|
if (httpHeader instanceof HttpRequestPacket) {
|
||||||
|
HttpRequestPacket request = (HttpRequestPacket) httpContent.getHttpHeader()
|
||||||
|
ResponseParameters responseParameters = buildResponse(request)
|
||||||
|
HttpResponsePacket.Builder builder = HttpResponsePacket.builder(request)
|
||||||
|
.status(responseParameters.getStatus())
|
||||||
|
.header("Content-Length", valueOf(responseParameters.getResponseBody().length))
|
||||||
|
responseParameters.fillHeaders(builder)
|
||||||
|
HttpResponsePacket responsePacket = builder.build()
|
||||||
|
controller(responseParameters.getEndpoint()) {
|
||||||
|
ctx.write(HttpContent.builder(responsePacket)
|
||||||
|
.content(wrap(ctx.getMemoryManager(), responseParameters.getResponseBody()))
|
||||||
|
.build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx.getStopAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseParameters buildResponse(HttpRequestPacket request) {
|
||||||
|
final String uri = request.getRequestURI()
|
||||||
|
final String requestParams = request.getQueryString()
|
||||||
|
final String fullPath = uri + (requestParams != null ? "?" + requestParams : "")
|
||||||
|
|
||||||
|
Map<String, String> headers = new HashMap<>()
|
||||||
|
|
||||||
|
HttpServerTest.ServerEndpoint endpoint
|
||||||
|
switch (fullPath) {
|
||||||
|
case "/success":
|
||||||
|
endpoint = SUCCESS
|
||||||
|
break
|
||||||
|
case "/redirect":
|
||||||
|
endpoint = REDIRECT
|
||||||
|
headers.put("location", REDIRECT.body)
|
||||||
|
break
|
||||||
|
case "/error-status":
|
||||||
|
endpoint = ERROR
|
||||||
|
break
|
||||||
|
case "/exception":
|
||||||
|
throw new Exception(EXCEPTION.body)
|
||||||
|
case "/notFound":
|
||||||
|
endpoint = NOT_FOUND
|
||||||
|
break
|
||||||
|
case "/query?some=query":
|
||||||
|
endpoint = QUERY_PARAM
|
||||||
|
break
|
||||||
|
case "/path/123/param":
|
||||||
|
endpoint = PATH_PARAM
|
||||||
|
break
|
||||||
|
case "/authRequired":
|
||||||
|
endpoint = AUTH_REQUIRED
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
endpoint = NOT_FOUND
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = endpoint.status
|
||||||
|
String responseBody = endpoint == REDIRECT ? "" : endpoint.body
|
||||||
|
|
||||||
|
final byte[] responseBodyBytes = responseBody.getBytes(defaultCharset())
|
||||||
|
return new ResponseParameters(endpoint, status, responseBodyBytes, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResponseParameters {
|
||||||
|
Map<String, String> headers
|
||||||
|
HttpServerTest.ServerEndpoint endpoint
|
||||||
|
int status
|
||||||
|
byte[] responseBody
|
||||||
|
|
||||||
|
ResponseParameters(HttpServerTest.ServerEndpoint endpoint,
|
||||||
|
int status,
|
||||||
|
byte[] responseBody,
|
||||||
|
Map<String, String> headers) {
|
||||||
|
this.endpoint = endpoint
|
||||||
|
this.status = status
|
||||||
|
this.responseBody = responseBody
|
||||||
|
this.headers = headers
|
||||||
|
}
|
||||||
|
|
||||||
|
int getStatus() {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getResponseBody() {
|
||||||
|
return responseBody
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpServerTest.ServerEndpoint getEndpoint() {
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillHeaders(HttpResponsePacket.Builder builder) {
|
||||||
|
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||||
|
builder.header(header.getKey(), header.getValue())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.test.base.HttpServerTestAdvice;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class GrizzlyFilterchainServerTestInstrumentation implements Instrumenter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
|
||||||
|
return agentBuilder
|
||||||
|
.type(named("org.glassfish.grizzly.http.HttpCodecFilter"))
|
||||||
|
.transform(
|
||||||
|
new AgentBuilder.Transformer.ForAdvice()
|
||||||
|
.advice(
|
||||||
|
named("handleRead")
|
||||||
|
.and(
|
||||||
|
takesArgument(
|
||||||
|
0, named("org.glassfish.grizzly.filterchain.FilterChainContext"))),
|
||||||
|
HttpServerTestAdvice.ServerEntryAdvice.class.getName()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import org.glassfish.grizzly.IOStrategy
|
||||||
|
import org.glassfish.grizzly.http.server.HttpServer
|
||||||
|
import org.glassfish.grizzly.strategies.LeaderFollowerNIOStrategy
|
||||||
|
import org.glassfish.grizzly.strategies.SameThreadIOStrategy
|
||||||
|
import org.glassfish.grizzly.strategies.SimpleDynamicNIOStrategy
|
||||||
|
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig
|
||||||
|
|
||||||
|
abstract class GrizzlyIOStrategyTest extends GrizzlyTest {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("ota.integration.grizzly-http.enabled", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
HttpServer startServer(int port) {
|
||||||
|
ResourceConfig rc = new ResourceConfig()
|
||||||
|
rc.register(SimpleExceptionMapper)
|
||||||
|
rc.register(ServiceResource)
|
||||||
|
def server = GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc)
|
||||||
|
server.getListener("grizzly").getTransport().setIOStrategy(strategy())
|
||||||
|
// Default in NIOTransportBuilder is WorkerThreadIOStrategy, so don't need to retest that.s
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract IOStrategy strategy()
|
||||||
|
}
|
||||||
|
|
||||||
|
class LeaderFollowerTest extends GrizzlyIOStrategyTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
IOStrategy strategy() {
|
||||||
|
return LeaderFollowerNIOStrategy.instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SameThreadTest extends GrizzlyIOStrategyTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
IOStrategy strategy() {
|
||||||
|
return SameThreadIOStrategy.instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleDynamicTest extends GrizzlyIOStrategyTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
IOStrategy strategy() {
|
||||||
|
return SimpleDynamicNIOStrategy.instance
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import io.opentelemetry.auto.test.base.HttpServerTest
|
||||||
|
import org.glassfish.grizzly.http.server.HttpServer
|
||||||
|
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig
|
||||||
|
|
||||||
|
import javax.ws.rs.GET
|
||||||
|
import javax.ws.rs.NotFoundException
|
||||||
|
import javax.ws.rs.Path
|
||||||
|
import javax.ws.rs.QueryParam
|
||||||
|
import javax.ws.rs.core.Response
|
||||||
|
import javax.ws.rs.ext.ExceptionMapper
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.REDIRECT
|
||||||
|
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||||
|
|
||||||
|
class GrizzlyTest extends HttpServerTest<HttpServer> {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("ota.integration.grizzly-filterchain.enabled", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
HttpServer startServer(int port) {
|
||||||
|
ResourceConfig rc = new ResourceConfig()
|
||||||
|
rc.register(SimpleExceptionMapper)
|
||||||
|
rc.register(ServiceResource)
|
||||||
|
GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void stopServer(HttpServer server) {
|
||||||
|
server.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SimpleExceptionMapper implements ExceptionMapper<Throwable> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Response toResponse(Throwable exception) {
|
||||||
|
if (exception instanceof NotFoundException) {
|
||||||
|
return exception.getResponse()
|
||||||
|
}
|
||||||
|
Response.status(500).entity(exception.message).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("/")
|
||||||
|
static class ServiceResource {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("success")
|
||||||
|
Response success() {
|
||||||
|
controller(SUCCESS) {
|
||||||
|
Response.status(SUCCESS.status).entity(SUCCESS.body).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("query")
|
||||||
|
Response query_param(@QueryParam("some") String param) {
|
||||||
|
controller(QUERY_PARAM) {
|
||||||
|
Response.status(QUERY_PARAM.status).entity("some=$param".toString()).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("redirect")
|
||||||
|
Response redirect() {
|
||||||
|
controller(REDIRECT) {
|
||||||
|
Response.status(REDIRECT.status).location(new URI(REDIRECT.body)).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("error-status")
|
||||||
|
Response error() {
|
||||||
|
controller(ERROR) {
|
||||||
|
Response.status(ERROR.status).entity(ERROR.body).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("exception")
|
||||||
|
Response exception() {
|
||||||
|
controller(EXCEPTION) {
|
||||||
|
throw new Exception(EXCEPTION.body)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,6 +65,8 @@ include ':instrumentation:finatra-2.9'
|
||||||
include ':instrumentation:geode-1.4'
|
include ':instrumentation:geode-1.4'
|
||||||
include ':instrumentation:google-http-client-1.19'
|
include ':instrumentation:google-http-client-1.19'
|
||||||
include ':instrumentation:grizzly-2.0'
|
include ':instrumentation:grizzly-2.0'
|
||||||
|
include ':instrumentation:grizzly-client-1.9'
|
||||||
|
include ':instrumentation:grizzly-http-2.3'
|
||||||
include ':instrumentation:grpc-1.5'
|
include ':instrumentation:grpc-1.5'
|
||||||
include ':instrumentation:hibernate:hibernate-3.3'
|
include ':instrumentation:hibernate:hibernate-3.3'
|
||||||
include ':instrumentation:hibernate:hibernate-4.0'
|
include ':instrumentation:hibernate:hibernate-4.0'
|
||||||
|
|
|
@ -124,6 +124,10 @@ abstract class HttpServerTest<SERVER> extends AgentTestRunner {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean testException() {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
enum ServerEndpoint {
|
enum ServerEndpoint {
|
||||||
SUCCESS("success", 200, "success"),
|
SUCCESS("success", 200, "success"),
|
||||||
REDIRECT("redirect", 302, "/redirected"),
|
REDIRECT("redirect", 302, "/redirected"),
|
||||||
|
@ -304,6 +308,7 @@ abstract class HttpServerTest<SERVER> extends AgentTestRunner {
|
||||||
|
|
||||||
def "test exception"() {
|
def "test exception"() {
|
||||||
setup:
|
setup:
|
||||||
|
assumeTrue(testException())
|
||||||
def request = request(EXCEPTION, method, body).build()
|
def request = request(EXCEPTION, method, body).build()
|
||||||
def response = client.newCall(request).execute()
|
def response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue