-
-
Notifications
You must be signed in to change notification settings - Fork 504
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
ArrayIndexOutOfBoundsException in SwaggerUiConfigParameters #2509
Comments
We have encountered this as well, we're using WebFlux and To reproduce this, I open the SwaggerUI page for each API group in multiple tabs, restart the server and then refresh all tabs at once (in Firefox, you can select multiple tabs and hit Ctrl+R to refresh them all at once). It may take a few attempts, but with this methodology I'd say I can reproduce this issue every 3rd time. Here's a screen recording of me doing that: springdoc_aiobe_c.mp4Once the API spec has been loaded once, concurrent requests seem to always work, therefore I think this only happens with concurrent requests to an uninitialized server. |
@GianniGiglio and @kzander91, it's just not reproducible with the provided code. Would you be able to propose PR with a fix for it ? |
I think I found the bug in these two lines: Lines 111 to 112 in 492d542
SwaggerWelcomeFlux is a singleton and concurrent requests will modify and then read the same oauthPrefix field, which is a UriComponentsBuilder which is not thread-safe.
Here's an example stack trace when two threads concurrently call
The exact error message my vary a bit. I could reproduce it with calls to /webjars/swagger-ui/index.html and /v3/api-docs/swagger-config. This is the test: @Slf4j
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ReproTest {
@LocalServerPort
private int port;
@Autowired
private WebClient client;
@ParameterizedTest
@ValueSource(strings = {
"/webjars/swagger-ui/index.html",
"/v3/api-docs/swagger-config",
})
void repro(String path) {
int nConcurrent = 16;
Flux<Void> requests = Flux.defer(() -> request(path).repeat());
List<Flux<Void>> publishers = new ArrayList<>(nConcurrent);
for (int i = 0; i < nConcurrent; i++) {
publishers.add(requests);
}
Flux.merge(publishers).take(Duration.ofSeconds(30L)).blockLast();
}
private Mono<Void> request(String path) {
return client.get()
.uri("http://localhost:{port}" + path, port)
.retrieve()
.toBodilessEntity()
.doOnError(ex -> log.error("Call failed, URL: {}", path))
.then();
}
@TestConfiguration
static class WebClientConfig {
@Bean
WebClient webClient(WebClient.Builder builder) {
return builder.build();
}
}
} It repeatedly performs 16 concurrent requests to these paths for 30 seconds. On my machine, this almost always reproduces the issue. |
Thank you for your analysis which is very helpful. |
@bnasslahsen |
@bnasslahsen The originally reported issue is still present in the I think a safe fix would be to proactively clone this field's value before streaming over it. put(URLS_PROPERTY, Set.copyOf(urls), params); |
Good catch, we iterate over the set here Line 307 in 492d542
and here: Lines 214 to 221 in 492d542
And can concurrently modify it here: Lines 193 to 206 in 492d542
|
Valid point. Perhaps this will require the |
@bnasslahsen I think this issue should be reopened, as the original bug has not been fixed, as identified by @NielsDoucet. |
Can you propose a PR for it or provide a reproducer project ? |
@bnasslahsen I'm not familiar enough with the code base to attempt to properly fix this issue. Maybe simply replacing the Looking at the code suggests that URLs have to be reconstructed based on the incoming request, I suppose that's because during startup, the app can't know the external URL it has been deployed behind. If that's the case and we therefore have to init with the first request, I would suggest to at least guard those critical sections using |
This ticket will be closed as not reproduced anymore, |
@bnasslahsen While I understand your reaction to this, I believe @kzander91's response summarizes why neither him nor I feel comfortable suggesting a fix for this concurrency issue. Is there nothing you can do about this, given all the provided context? |
The issue is still present in springdoc-openapi-starter-common-2.4.0 |
Your comment could be useful if you provided a Minimal, Reproducible Example - with HelloController that reproduces the problem. |
…rUiConfigProperties.urls` only if url is changed. Test to check if the issue is fixed. Fixes springdoc#2509.
@bnasslahsen could you please check here the minimal, reproducible example? I assume the issue is caused by the changes introduced in #2213. I have prepared the PR to fix the issue and will be looking forward to your feedback. Thank you! |
…rUiConfigProperties.urls` only if url is changed. Test to check if the issue is fixed. Fixes springdoc#2509.
I'm using version 2.6.0 Occurance is here https://github.com/springdoc/springdoc-openapi/blob/v2.6.0/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigParameters.java#L311 |
2.7 depends on unreleased spring boot, can you release 2.6.1 which doesn't need spring boot 3.4? |
Description:
An "ArrayIndexOutOfBoundsException" error has been reported within the SwaggerUiConfigParameters class. This exception occurs during the execution of methods invoked by the AbstractSwaggerIndexTransformer class. The issue was raised by one of our customers, and although we've attempted to reproduce it, we have been unsuccessful thus far. However, monitoring of all customer logs indicates that this issue is not isolated and has affected multiple customers.
Methode:
`protected String addParameters(String html) throws JsonProcessingException {
String layout = swaggerUiConfigParameters.getLayout() != null ? swaggerUiConfigParameters.getLayout() : "StandaloneLayout";
StringBuilder stringBuilder = new StringBuilder("layout: "" + layout + "" ,\n");
Stacktrace:
java.lang.ArrayIndexOutOfBoundsException: Index 11 out of bounds for length 11 at java.util.stream.SortedOps$SizedRefSortingSink.accept(Unknown Source) at java.util.HashMap$KeySpliterator.forEachRemaining(Unknown Source) at java.util.stream.AbstractPipeline.copyInto(Unknown Source) at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(Unknown Source) at java.util.stream.AbstractPipeline.evaluate(Unknown Source) at java.util.stream.ReferencePipeline.collect(Unknown Source) at org.springdoc.core.properties.SwaggerUiConfigParameters.put(SwaggerUiConfigParameters.java:307) at org.springdoc.core.properties.SwaggerUiConfigParameters.getConfigParameters(SwaggerUiConfigParameters.java:284) at org.springdoc.ui.AbstractSwaggerIndexTransformer.addParameters(AbstractSwaggerIndexTransformer.java:198) at org.springdoc.ui.AbstractSwaggerIndexTransformer.defaultTransformations(AbstractSwaggerIndexTransformer.java:173)
Possible Explanation:
The ArrayIndexOutOfBoundsException typically occurs when attempting to access an element of an array or collection at an index that is beyond its bounds. In this context, the usage of the sorted operation in a stateful stream operation within the SwaggerUiConfigParameters class might lead to unexpected behavior. Stateful stream operations maintain state during the execution of the stream pipeline, which could potentially alter the order or length of elements being processed. Consequently, if the index being accessed within the SwaggerUiConfigParameters class exceeds the updated length of the array or collection due to the stateful operation, it could result in the ArrayIndexOutOfBoundsException being thrown.
Similar non springdoc-issue:
oracle/graal#5902
To Reproduce
Unable to reproduce locally
What version of spring-boot you are using?
org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0
What modules and versions of springdoc-openapi are you using?
org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0
What is the actual and the expected result using OpenAPI Description (yml or json)?
Index page doesn't load
Provide with a sample code (HelloController) or Test that reproduces the problem
Unable to reproduce
Expected behavior
A clear and concise description of what you expected to happen.
What is the expected result using OpenAPI Description (yml or json)?
Screenshots
If applicable, add screenshots to help explain your problem.
Additional context
Add any other context about the problem here.
The text was updated successfully, but these errors were encountered: