-
Notifications
You must be signed in to change notification settings - Fork 867
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sun.net.www.protocol.http.HttpURLConnection.getOutputStream should tr… #6053
Conversation
…ansform GET into POST
instrumentation/http-url-connection/javaagent/src/test/groovy/HttpUrlConnectionTest.groovy
Show resolved
Hide resolved
public String method(HttpURLConnection connection) { | ||
String requestMethod = connection.getRequestMethod(); | ||
|
||
if ("GET".equals(requestMethod)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you consider doing this inside HttpUrlHttpAttributesGetter
?
If you did it this way because there isn't an obvious way how to tell that getOutputStream
was called in HttpUrlHttpAttributesGetter
then here's a couple of ideas for you. You could try using HttpUrlState
, add a boolean field to it that would signify that it really is a POST
. Another way would be to use addContextCustomizer
method from InstrumenterBuilder
. The idea is to use addContextCustomizer
to add a holder object into context where you can store info like is this really a POST
. For example see https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyErrorHolder.java. As you don't have access to context in HttpUrlHttpAttributesGetter.method
you'll need to add a custom AttributesExtractor
to your Instrumenter
. Just leave HttpUrlHttpAttributesGetter
as is and overwrite the request method attribute in your AttributesExtractor.onEnd
. If you set the SemanticAttributes.HTTP_METHOD
attribute in onEnd
then it will work even when the span wasn't started from getOutputStream
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another idea would be to change the request type from HttpURLConnection
to a value class that'd contain both HttpURLConnection
and the String methodName
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your suggestions @laurit and @mateuszrzeszutek. In my last commit, I created a new implementation to not depend on the execution order of the connection's methods.
...o/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlConnectionSingletons.java
Outdated
Show resolved
Hide resolved
public String method(HttpURLConnection connection) { | ||
String requestMethod = connection.getRequestMethod(); | ||
|
||
if ("GET".equals(requestMethod)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another idea would be to change the request type from HttpURLConnection
to a value class that'd contain both HttpURLConnection
and the String methodName
|
||
def connectionClassName = connection.getClass().getCanonicalName() | ||
|
||
assert "sun.net.www.protocol.http.HttpURLConnection".equals(connectionClassName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will that work on non-OpenJDK JVMs? Can you add some assumption that will skip this test if this class is not present?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests seem to succeed with the OpenJ9 JVM.
I you want, I could add the following kind of code in case where the sun.net.www.protocol.http.HttpURLConnection
class disappears one day from the JDK:
if (!"sun.net.www.protocol.http.HttpURLConnection".equals(connectionClassName)) {
return;
}
I preferred to add only this kind of code if the problem will appear one day, to try to keep things simple.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nowadays everybody uses class library from openjdk so I wouldn't worry about this class being missing. Though I wouldn't probably build the instrumentation around sun.net.www.protocol.http.HttpURLConnection
but rather instrument getOutpuStream
in all subclasses of java.net.HttpURLConnection
because for this use case, as far as I can tell, it isn't really important on which class getOutpuStream
is called. Only the fact that it was called matters. Handling this in other classes besides sun.net.www.protocol.http.HttpURLConnection
could come handy on weblogic where there is a custom url protocol handler (system property java.protocol.handler.pkgs
can be used to change url protocol handlers).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though I wouldn't probably build the instrumentation around sun.net.www.protocol.http.HttpURLConnection but rather instrument getOutpuStream in all subclasses of java.net.HttpURLConnection because for this use case, as far as I can tell, it isn't really important on which class getOutpuStream is called.
As far as I understand, the #4597 issue was created for the sun.net.www.protocol.http.HttpURLConnection
class. This issue aims at reflecting the getOutputStream()
behavior of the sun.net.www.protocol.http.HttpURLConnection
class, extending java.net.HttpURLConnection
abstract class. Could we be sure that all classes extending java.net.HttpURLConnection
will implement a getOutputStream()
method that will transform the GET to POST, as in the sun.net.www.protocol.http.HttpURLConnection
class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I understand, the #4597 issue was created for the
sun.net.www.protocol.http.HttpURLConnection
class. This issue aims at reflecting thegetOutputStream()
behavior of thesun.net.www.protocol.http.HttpURLConnection
class, extendingjava.net.HttpURLConnection
abstract class. Could we be sure that all classes extendingjava.net.HttpURLConnection
will implement agetOutputStream()
method that will transform the GET to POST, as in thesun.net.www.protocol.http.HttpURLConnection
class?
This is a good question. I believe we can check the value of getRequestMethod
after calling getOutputStream
, if it returns POST
then it was a POST
request even if it returned GET
before calling getOutputStream
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good question. I believe we can check the value of getRequestMethod after calling getOutputStream, if it returns POST then it was a POST request even if it returned GET before calling getOutputStream.
I am not sure to have been rather explicit. If I was misunderstood., my question was about the expected behavior, not about implementation.
we can check the value of getRequestMethod after calling getOutputStream, if it returns POST then it was a POST request even if it returned GET before calling getOutputStream.
The getOutputStream() method of sun... class updates the method field. So, the getRequestMethod() should return POST even if the HTTP call was done with a GET method.
In term of expected behavior, do we want all classes extending java.net.HttpURLConnection
to have an instrumentation behavior for the getOutputStream()
method similar to this of sun.net.www.protocol.http.HttpURLConnection
? We can't be sure that all classes extending java.net.HttpURLConnection
implement the getOutputStream()
method the same way as the sun.net.www.protocol.http.HttpURLConnection
does (that is to say, changing GET into POST). As in #4597, it may be disturbing not to have instrumentation aligned with the code behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments:
-
I committed a new implementation to not depend on the execution order of the connection's methods. Could you please review it?
In term of expected behavior, do we want all classes extending java.net.HttpURLConnection to have an instrumentation behavior for the getOutputStream() method similar to this of sun.net.www.protocol.http.HttpURLConnection? We can't be sure that all classes extending java.net.HttpURLConnection implement the getOutputStream() method the same way as the sun.net.www.protocol.http.HttpURLConnection does (that is to say, changing GET into POST). As in https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/4597, it may be disturbing not to have instrumentation aligned with the code behavior?
The new implementation could be adjusted once the expected behavior is determined.
The new implementation updates the HTTP method attribute but not the span name. Does span name also need to be updated? If yes, please advise for the implementation. The HttpSpanNameExtractor
has no access to Context
. I have noticed that the Instrumenter
class
span name extractor calls the span name extractor and has access to the context. So, a possible implementation would be to add Context
parameter to the existing method of the SpanNameExtractor interface. It may require updating the code in many places. Another option would be to allow the possibility to define a SpanNameExtractor
and call it around here in Instrumenter class. If something needs to be done with the span name, perhaps it could be another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- If you believe it is best to keep the checks for
sun.net.www.protocol.http.HttpURLConnection
then that is ok. - Nice catch about the span name. I think we should change it as it too. You could use
Span span = Span.fromContext(context);
span.udateName("HTTP " + requestMethod);
to update the span name too. You could update the span name at the same place where you change the value for HTTP_METHOD
attribute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code updated to set the span name, thank you for the trick
...o/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpUrlConnectionSingletons.java
Outdated
Show resolved
Hide resolved
Co-authored-by: Mateusz Rzeszutek <[email protected]>
@@ -116,6 +116,11 @@ public static void methodExit( | |||
callDepth.getAndIncrement(); | |||
try { | |||
scope.close(); | |||
Class<? extends HttpURLConnection> connectionClass = connection.getClass(); | |||
|
|||
GetOutputStreamContext getOutputStreamContext = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use the value of connection.getRequestMethod
here. If getOutputStream
got an exception (maybe setDoOutput(true)
) wasn't called then the request method isn't changed and we could still report it as GET
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good point! I haved added a condition on the request method with a comment. I propose to leave the condition on the method name of HttpUrlConnection to try to make the code explicit.
return context.get(KEY); | ||
} | ||
|
||
public void set( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other places that use the same pattern we typically have static methods for setting and getting values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code changed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thx @jeanbisutti!
I tend to agree with @laurit thought about implementing this behavior for all subclasses of HttpURLConnection
. I would hazzard to guess that weblogic's HttpURLConnection
implementation probably had to conform to the sun behavior just to avoid breaking applications which "worked on tomcat" 😄.
But I'm also ok with handling only the sun implementation.
instrumentation/http-url-connection/javaagent/src/test/groovy/HttpUrlConnectionTest.groovy
Outdated
Show resolved
Hide resolved
instrumentation/http-url-connection/javaagent/src/test/groovy/HttpUrlConnectionTest.groovy
Outdated
Show resolved
Hide resolved
.../opentelemetry/javaagent/instrumentation/httpurlconnection/HttpMethodAttributeExtractor.java
Outdated
Show resolved
Hide resolved
Class<? extends HttpURLConnection> connectionClass = connection.getClass(); | ||
|
||
String requestMethod = connection.getRequestMethod(); | ||
GetOutputStreamContext.set( | ||
httpUrlState.context, connectionClass, methodName, requestMethod); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inlining connectionClass
might be nice to get rid of the generic reference above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would hazzard to guess that weblogic's HttpURLConnection implementation probably had to conform to the sun behavior
Maybe , maybe not. We may discuss this point and create a PR outside this issue? This PR was to fix #4597.
...ava/io/opentelemetry/javaagent/instrumentation/httpurlconnection/GetOutputStreamContext.java
Outdated
Show resolved
Hide resolved
...ava/io/opentelemetry/javaagent/instrumentation/httpurlconnection/GetOutputStreamContext.java
Show resolved
Hide resolved
…HttpUrlConnectionTest.groovy Co-authored-by: Trask Stalnaker <[email protected]>
…HttpUrlConnectionTest.groovy Co-authored-by: Trask Stalnaker <[email protected]>
…/opentelemetry/javaagent/instrumentation/httpurlconnection/HttpMethodAttributeExtractor.java Co-authored-by: Trask Stalnaker <[email protected]>
thx @jeanbisutti! |
Thank you very much @laurit, @mateuszrzeszutek and @trask for your help on this first issue! |
The purpose of this PR is to fix #4597