-
Notifications
You must be signed in to change notification settings - Fork 5
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
Fix interceptor stack overflow error #89
Fix interceptor stack overflow error #89
Conversation
This fixes unbounded adding of interceptors to the OkHttpClient$Builder instance for each Client instance constructed and guarantees a maximum of one of each interceptor type being added to a Client instance.
Just checking in to see if there is anything more I need to do to have this reviewed and merged. |
@NoahGreer Thank you very much for reporting this issue and creating a pull request to fix it. We apologize for taking so long to get back to you on this. We strive to have a much faster turn around, but got caught up in the development of our new golang client. I was able to reproduce the issue with your example code (again, thank you very much for providing a working POC!). I'm verifying the fix now. |
I apologize also. You made it so easy for us. Great work on the minimal reproducible example! |
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.
Everything checks out 👍.
Thank you so much (yes a third time) for such a detailed explanation and fix for this issue!
This will be available with our next release |
Thank you both for reviewing and merging this! I appreciate the compliments. :) |
# Changelog ## [Unreleased](https://github.com/recurly/recurly-client-java/tree/HEAD) [Full Changelog](3.5.0...HEAD) **Implemented enhancements:** - Latest Features [\#93](#93) ([bhelx](https://github.com/bhelx)) - Updating error hierarchy and providing error handling for non-json error responses [\#92](#92) ([douglasmiller](https://github.com/douglasmiller)) **Fixed bugs:** - Fix interceptor stack overflow error [\#89](#89) ([NoahGreer](https://github.com/NoahGreer)) **Merged pull requests:** - Upgrade dependencies [\#91](#91) ([bhelx](https://github.com/bhelx)) - Refactoring tests to make use of shared code [\#90](#90) ([douglasmiller](https://github.com/douglasmiller)) - Encoding path parameters [\#88](#88) ([douglasmiller](https://github.com/douglasmiller)) - Ensure that path parameters are not empty strings [\#87](#87) ([douglasmiller](https://github.com/douglasmiller))
The bug:
After creating enough new Client objects, the header interceptor chain grows so large that it causes a
java.lang.StackOverflowError
error the next time a Client tries to make a request.This means that in a long-lived web application, using this library creates a ticking time bomb that will eventually cause it to break.
Stack trace:
The stack trace continues for thousands of lines repeating the last three lines.
To Reproduce:
Steps:
Enter a loop, and once per iteration, create a new Client object, and after every 100 client creations make an API call with the current client object. Eventually, the number of header interceptors will overflow the JVM's stack and cause a
java.lang.StackOverflowError
.Note that this is not the actual use case, just a simple brute force example of how to reproduce the issue quickly.
Code to reproduce:
Example output:
Followed by a stack trace.
Expected behavior:
Only one of each Interceptor type should be added for each client created, and a java.lang.StackOverflowError should never occur.
Your Environment
3.5.0
Java 8
Root cause and fix:
Cause:
The bug is primarily due to the reuse of the same
OkHttpClient$Builder
object in theBaseClient::newHttpClient
method.The same
OkHttpClient$Builder
instance is being used for eachcom.recurly.v3.Client
being constructed. A newHeaderInterceptor
is mistakenly being created and added to the same builder every time.On the master branch, in the file BaseClient.java, the lines 53-55 contain the following erroneous code:
The return type of
httpClientBuilder.interceptors()
is aList<Interceptor>
.The
List::contains
method uses object comparison, which requires that the objects being compared must override theObject::equals
method to provide a mechanism for determining whether they are the same. Without that method override, all objects are considered unique.Neither the child class
com.recurly.v3.http.HeaderInterceptor
nor the parent classokhttp3.Interceptor
overrides theObject::equals
method, so the above if statement will always evaluate to true and allow a duplicate header interceptor to be added each time.Another flaw exists on lines 57-62 of the same file as above, code:
When the specified environment variables are set, each time a new
com.recurly.v3.Client
is created, a newHttpLoggingInterceptor
will be added to the same builder, and would eventually cause the samejava.lang.StackOverflowError
when a client makes an API call.Fix:
The simplest fix that would resolve both these flaws would be to remove the global
OkHttpClient$Builder
on line 27:private static OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
And refactor the
BaseClient::newHttpClient
method to inline the creation of a new builder.This would also eliminate the need for checking whether the builder's interceptors list already contains the same interceptors because using a new builder each time would guarantee only one of each type of interceptor is added each time.
Code for refactored method: