Skip to content

Commit 6dbd635

Browse files
amarzialiAlexeyKuznetsov-DDdevflow.devflow-routing-intake
authored
Fix Jetty 9.0 continuation form-body test race (#12904)
Fix Jetty 9.0 continuation form-body test race Update dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-9.0/src/test/groovy/datadog/trace/instrumentation/jetty9/JettyContinuationHandlerTest.groovy Co-authored-by: Alexey Kuznetsov <alexey.kuznetsov@datadoghq.com> Improve comment. Co-authored-by: AlexeyKuznetsov-DD <alexey.kuznetsov@datadoghq.com> Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
1 parent 7e1f64e commit 6dbd635

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

‎dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-9.0/src/test/groovy/datadog/trace/instrumentation/jetty9/JettyContinuationHandlerTest.groovy‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import datadog.trace.agent.test.asserts.TraceAssert
44
import datadog.trace.agent.test.naming.TestingGenericHttpNamingConventions
55
import datadog.trace.bootstrap.instrumentation.api.Tags
66
import datadog.trace.instrumentation.servlet3.AsyncDispatcherDecorator
7+
import okhttp3.MediaType
8+
import okhttp3.RequestBody
79
import org.eclipse.jetty.continuation.Continuation
810
import org.eclipse.jetty.continuation.ContinuationSupport
911
import org.eclipse.jetty.server.Request
@@ -18,13 +20,46 @@ import java.util.concurrent.ExecutorService
1820
import java.util.concurrent.Executors
1921

2022
import static test.TestHandler.handleRequest
23+
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.BODY_URLENCODED
2124
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS
2225
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.TIMEOUT
2326
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.TIMEOUT_ERROR
2427
import static datadog.trace.instrumentation.servlet3.TestServlet3.SERVLET_TIMEOUT
2528

2629
abstract class JettyContinuationHandlerTest extends Jetty9Test {
2730

31+
def 'form requests survive continuation redispatch without HTTP retries'() {
32+
setup:
33+
def noRetryClient = client.newBuilder().retryOnConnectionFailure(false).build()
34+
int attempts = 100
35+
36+
when:
37+
attempts.times {
38+
def formRequest = request(BODY_URLENCODED, 'POST',
39+
RequestBody.create(MediaType.get('application/x-www-form-urlencoded'), 'a=x')).build()
40+
try (def response = noRetryClient.newCall(formRequest).execute()) {
41+
assert response.code() == 200
42+
assert response.body().string() == '[a:[x]]'
43+
}
44+
}
45+
TEST_WRITER.waitForTraces(attempts)
46+
47+
then:
48+
TEST_WRITER.size() == attempts
49+
TEST_WRITER.every {
50+
trace ->
51+
def root = trace.find {
52+
it.parentId == 0
53+
}
54+
trace.size() == 3 &&
55+
root.getTag('http.status_code') == 200 &&
56+
root.getTag('request.body.converted') == '[a:[x]]' &&
57+
trace.any {
58+
it.operationName == 'servlet.dispatch'
59+
}
60+
}
61+
}
62+
2863
@Override
2964
AbstractHandler handler() {
3065
def ret = new SessionHandler()
@@ -48,6 +83,16 @@ abstract class JettyContinuationHandlerTest extends Jetty9Test {
4883
// this happens in the /exception endpoint
4984
if (!request.getAttribute('javax.servlet.error.status_code')) {
5085
if (continuation.initial) {
86+
if (request.contentType?.startsWith('application/x-www-form-urlencoded')) {
87+
// Parse the form before suspending the request. When the client waits for `HTTP 100 Continue`
88+
// before sending the body, parsing after resume can overlap with Jetty 9.0.0's pending connection read
89+
// and cause ReadPendingException.
90+
request.parameterMap
91+
if (response.committed) {
92+
baseRequest.handled = true
93+
return
94+
}
95+
}
5196
continuation.suspend()
5297
executorService.execute {
5398
continuation.resume()

0 commit comments

Comments
 (0)