Skip to content

Commit 022316a

Browse files
committed
Fix Jetty client cannot receive the HTTP response body
1 parent 762d31f commit 022316a

File tree

7 files changed

+206
-4
lines changed

7 files changed

+206
-4
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Release Notes.
1616
* Update Maven to 3.6.3 in mvnw.
1717
* Fix OOM due to too many span logs.
1818
* Fix ClassLoader cache OOM issue with WeakHashMap.
19+
* Fix Jetty client cannot receive the HTTP response body
1920

2021
All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/242?closed=1)
2122

apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.0-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v90/client/AsyncHttpRequestSendInterceptor.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,13 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr
5858

5959
span.prepareForAsync();
6060
request.attribute(Constants.SW_JETTY_EXIT_SPAN_KEY, span);
61-
Response.CompleteListener callback = (Response.CompleteListener) allArguments[0];
62-
allArguments[0] = new CompleteListenerWrapper(callback, ContextManager.capture());
61+
if (allArguments[0] instanceof Response.Listener) {
62+
Response.Listener callback = (Response.Listener) allArguments[0];
63+
allArguments[0] = new ResponseListenerWrapper(callback, ContextManager.capture());
64+
} else {
65+
Response.CompleteListener callback = (Response.CompleteListener) allArguments[0];
66+
allArguments[0] = new CompleteListenerWrapper(callback, ContextManager.capture());
67+
}
6368
}
6469

6570
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.apm.plugin.jetty.v90.client;
20+
21+
import org.apache.skywalking.apm.agent.core.context.ContextManager;
22+
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
23+
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
24+
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
25+
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
26+
import org.eclipse.jetty.client.api.Response;
27+
import org.eclipse.jetty.client.api.Result;
28+
import org.eclipse.jetty.http.HttpField;
29+
import java.nio.ByteBuffer;
30+
31+
public class ResponseListenerWrapper implements Response.Listener {
32+
33+
private final Response.Listener callback;
34+
35+
private final ContextSnapshot context;
36+
37+
public ResponseListenerWrapper(Response.Listener callback, ContextSnapshot context) {
38+
this.callback = callback;
39+
this.context = context;
40+
}
41+
@Override
42+
public void onComplete(Result result) {
43+
AbstractSpan span = ContextManager.createLocalSpan(Constants.PLUGIN_NAME + "/CompleteListener/onComplete");
44+
span.setComponent(ComponentsDefine.JETTY_CLIENT);
45+
SpanLayer.asHttp(span);
46+
if (context != null) {
47+
ContextManager.continued(context);
48+
}
49+
if (callback != null) {
50+
callback.onComplete(result);
51+
}
52+
ContextManager.stopSpan();
53+
}
54+
55+
@Override
56+
public void onHeaders(Response response) {
57+
callback.onHeaders(response);
58+
}
59+
60+
@Override
61+
public void onContent(Response response, ByteBuffer content) {
62+
callback.onContent(response, content);
63+
}
64+
65+
@Override
66+
public void onBegin(Response response) {
67+
callback.onBegin(response);
68+
}
69+
70+
@Override
71+
public boolean onHeader(Response response, HttpField field) {
72+
return callback.onHeader(response, field);
73+
}
74+
75+
@Override
76+
public void onSuccess(Response response) {
77+
callback.onSuccess(response);
78+
}
79+
80+
@Override
81+
public void onFailure(Response response, Throwable failure) {
82+
callback.onFailure(response, failure);
83+
}
84+
}

apm-sniffer/apm-sdk-plugin/jetty-plugin/jetty-client-9.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jetty/v9/client/AsyncHttpRequestSendInterceptor.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,13 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr
5757

5858
span.prepareForAsync();
5959
request.attribute(Constants.SW_JETTY_EXIT_SPAN_KEY, span);
60-
Response.CompleteListener callback = (Response.CompleteListener) allArguments[0];
61-
allArguments[0] = new CompleteListenerWrapper(callback, ContextManager.capture());
60+
if (allArguments[0] instanceof Response.Listener) {
61+
Response.Listener callback = (Response.Listener) allArguments[0];
62+
allArguments[0] = new ResponseListenerWrapper(callback, ContextManager.capture());
63+
} else {
64+
Response.CompleteListener callback = (Response.CompleteListener) allArguments[0];
65+
allArguments[0] = new CompleteListenerWrapper(callback, ContextManager.capture());
66+
}
6267
}
6368

6469
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.apm.plugin.jetty.v9.client;
20+
21+
import org.apache.skywalking.apm.agent.core.context.ContextManager;
22+
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
23+
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
24+
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
25+
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
26+
import org.eclipse.jetty.client.api.Response;
27+
import org.eclipse.jetty.client.api.Result;
28+
import org.eclipse.jetty.http.HttpField;
29+
30+
import java.nio.ByteBuffer;
31+
32+
public class ResponseListenerWrapper implements Response.Listener {
33+
34+
private final Response.Listener callback;
35+
36+
private final ContextSnapshot context;
37+
38+
public ResponseListenerWrapper(Response.Listener callback, ContextSnapshot context) {
39+
this.callback = callback;
40+
this.context = context;
41+
}
42+
43+
@Override
44+
public void onComplete(Result result) {
45+
AbstractSpan span = ContextManager.createLocalSpan(Constants.PLUGIN_NAME + "/CompleteListener/onComplete");
46+
span.setComponent(ComponentsDefine.JETTY_CLIENT);
47+
SpanLayer.asHttp(span);
48+
if (context != null) {
49+
ContextManager.continued(context);
50+
}
51+
if (callback != null) {
52+
callback.onComplete(result);
53+
}
54+
ContextManager.stopSpan();
55+
}
56+
57+
@Override
58+
public void onHeaders(Response response) {
59+
callback.onHeaders(response);
60+
}
61+
62+
@Override
63+
public void onContent(Response response, ByteBuffer content) {
64+
callback.onContent(response, content);
65+
}
66+
67+
@Override
68+
public void onBegin(Response response) {
69+
callback.onBegin(response);
70+
}
71+
72+
@Override
73+
public boolean onHeader(Response response, HttpField field) {
74+
return callback.onHeader(response, field);
75+
}
76+
77+
@Override
78+
public void onSuccess(Response response) {
79+
callback.onSuccess(response);
80+
}
81+
82+
@Override
83+
public void onFailure(Response response, Throwable failure) {
84+
callback.onFailure(response, failure);
85+
}
86+
}

test/plugin/scenarios/jetty-scenario/jettyclient-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyclient/controller/CaseController.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
import org.apache.http.impl.client.CloseableHttpClient;
2626
import org.apache.http.impl.client.HttpClients;
2727
import org.eclipse.jetty.client.HttpClient;
28+
import org.eclipse.jetty.client.api.Request;
2829
import org.eclipse.jetty.client.api.Response;
30+
import org.eclipse.jetty.client.api.Result;
31+
import org.eclipse.jetty.client.util.BufferingResponseListener;
32+
import org.eclipse.jetty.http.HttpMethod;
2933
import org.springframework.beans.factory.annotation.Value;
3034
import org.springframework.context.annotation.PropertySource;
3135
import org.springframework.stereotype.Controller;
@@ -61,6 +65,19 @@ public String jettyClientScenario() throws Exception {
6165
}
6266
};
6367
client.newRequest("http://" + jettyServerHost + ":18080/jettyserver-case/case/receiveContext-1").send(listener);
68+
69+
//When using BufferingResponseListener, check getContent
70+
Request request = client.newRequest("http://" + jettyServerHost + ":18080/jettyserver-case/case/receiveContext-0").method(HttpMethod.GET);
71+
request.send(new BufferingResponseListener() {
72+
@Override
73+
public void onComplete(Result result) {
74+
byte[] bytes = getContent();
75+
if (bytes == null || bytes.length == 0) {
76+
throw new RuntimeException("content cant empty");
77+
}
78+
}
79+
});
80+
6481
return "Success";
6582
}
6683

test/plugin/scenarios/jetty-scenario/jettyserver-scenario/src/main/java/org/apache/skywalking/apm/testcase/jettyserver/servlet/CaseServlet.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.apache.skywalking.apm.testcase.jettyserver.servlet;
2020

2121
import java.io.IOException;
22+
import java.io.PrintWriter;
2223
import javax.servlet.ServletException;
2324
import javax.servlet.http.HttpServlet;
2425
import javax.servlet.http.HttpServletRequest;
@@ -31,6 +32,9 @@ public class CaseServlet extends HttpServlet {
3132
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
3233
try {
3334
Thread.sleep(2000);
35+
resp.setContentType("text/plain;charset=UTF-8");
36+
PrintWriter out = resp.getWriter();
37+
out.print("Success");
3438
} catch (InterruptedException e) {
3539
}
3640
}

0 commit comments

Comments
 (0)