-
-
Notifications
You must be signed in to change notification settings - Fork 795
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
FilteringParserDelegate
can go into an infinite loop if underlying parser is non-blocking
#1144
Comments
Can you provide reproducible test cases? Volunteers appreciate time savers like that. |
Sure! Here is a testcase, probably needs some refinement (especially around asserting timeout with assertj). public void testFilteringNonBlockingParser() throws IOException {
JsonFactory factory = new JsonFactoryBuilder().build();
JsonParser nonBlockingParser = factory.createNonBlockingByteArrayParser();
ByteArrayFeeder inputFeeder = (ByteArrayFeeder) nonBlockingParser.getNonBlockingInputFeeder();
String json = "{\"first\":1,\"second\":2}";
byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
JsonParser filteringParser = new FilteringParserDelegate(nonBlockingParser, new JsonPointerBasedFilter("/second"),
TokenFilter.Inclusion.ONLY_INCLUDE_ALL, false);
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.schedule(() -> {
try {
inputFeeder.feedInput(jsonBytes, 0, jsonBytes.length);
} catch (IOException e) {
throw new RuntimeException(e);
}
}, 500, TimeUnit.MILLISECONDS);
Future<JsonToken> future = Executors.newSingleThreadExecutor().submit(() -> filteringParser.nextToken());
Assertions.assertThat(future)
.succeedsWithin(Duration.ofSeconds(5))
.isNotNull()
.isNotEqualTo(JsonToken.NOT_AVAILABLE);
} |
Quick note: yes, first 2 seem like easy enough oversights to fix (alas, I don't know of a way to properly prevent the issue of missing overrides). But I suspect you may be right in that handling Let's start with #1146 tho, add overrides where they belong ( |
Ok, so... my first instinct is that handling of The reason I think handling is requires all over the place is that basically But... I am not convinced it would be impossible to do this. :) @simonbasle If you have time to try to add changes, I'd be happy to review code. One thing that would be very important, I think, would be to add test cases that feed input (byte-by-byte, in bigger chunks); support for this exists, see tests in |
hey @cowtowncoder, thanks for the analysis. unfortunately I don't think I will have the possibility to try to add changes anytime soon 😞 hopefully there is some kind of low hanging fruit where the parsing doesn't work but fails fast ? (e.g. throwing an exception if the underlying parser is returning |
@simonbasle All legit, and also bit challenging to do... esp. since patch release typically should not change behavior minus bug fixes. And while there are tests, I think due to complexity of filtering delegation, there are still gaps. For patch release, ideally I do think I'd like to improve things here, and maybe I can. But it does need to fight wrt all other priorities. But I'll keep this on my ever-expanding todo-list. |
Quick notes:
|
Not sure what you mean by that. That said I can understand the apprehensiveness of failing the constructor and how to document that. If you feel the risk is too big in terms of impact on existing users, I guess a big WARN logging would perhaps be the next best thing 🤷♂️
👍 |
Ok, let me start by explaining one possibly valid use case for Async parser: if the whole content is indeed read into buffer, it seems possible to use filtering parser without issue (in this case Looking at tests, there is:
which actually verifies that usage works (assuming content is indeed all available). As to logging WARNING: Jackson does zero-logging, as matter of principle (well, some modules like Afterburner do log one line for specific failure) so that is not available as a technique. But I am not sure it'd be good way even if we could do that. But I think there might be another way: create method for validating given parser -- overridable -- which will by default fail on async parser. I think I;ll go with that. |
Forgot to say: thank you @simonbasle for both reporting this and going through with some of the options. Beyond initial blocking of usage, it'd be nice to add handling of |
Ok. So, as per This does lead to the obvious problem: should we block supported usage (as per test) to prevent rather nasty issues for "bad" usage? I guess I should add test from here to at least reproduce failure case. |
Adding test which does reproduce problem, but cannot quite pinpoint where. Need to see how to debug where the infinite loop occurs (I realize there are probably a few places... but easier to find one etc) |
Jackson version (BOM):
2.15.2
While experimenting with
JsonPointer
in Spring Framework, I tried to build up on the existingFilteringParserDelegate
and associatedJsonPointerBasedTokenFilter
. Unfortunately, this doesn't seem to work with a non-blockingJsonParser
delegate, for several reasons:canParseAsync()
(ends up returning the default interface implem =>false
)getNonBlockingInputFeeder()
(same as above =>null
)JsonToken.NOT_AVAILABLE
While the first two are easily circumvented by extending
FilteringParserDelegate
, the last one is the truly problematic one. It results in going down the code path of a "scalar value", and will continuously calldelegate.nextToken()
which continues to result inNOT_AVAILABLE
.This leads to an infinite loop when attempting to filter based on a
JsonPointer
on top of a non-blocking parser.Is there anything fundamentally preventing
FilteringParserDelegate
from being compatible with non-blocking parsers that I might have overlooked? Otherwise I think it's pretty close, hopefully that can be fixed 😄The text was updated successfully, but these errors were encountered: