Skip to content

Source is not closed when a JSON tester read fails - #51399

Open
dlwldn30 wants to merge 2 commits into
spring-projects:mainfrom
dlwldn30:close-json-tester-source
Open

Source is not closed when a JSON tester read fails#51399
dlwldn30 wants to merge 2 commits into
spring-projects:mainfrom
dlwldn30:close-json-tester-source

Conversation

@dlwldn30

Copy link
Copy Markdown

Re-submitted from a user-account fork, replacing #51384. Carries your review: the
close is verified with a spy rather than an AtomicBoolean.

AbstractJsonMarshalTester.read(Resource) and read(Reader) take ownership of a
source, hand it to readObject(...) and close it afterwards, so the close is
skipped when readObject throws, most commonly on malformed JSON.

Whether that leaks depends on the delegate, which the base class cannot control.
Measured rather than assumed: JacksonTester, Jackson2Tester and JsonbTester
close the source themselves, GsonTester does not, so GsonTester.read(...)
leaks a file handle on every failed read. Three of its four public entry points
open a real file.

The fix closes the source in a finally block in both methods. For the three
delegates that already close it, close() runs a second time. That is a no-op by
contract, since Closeable specifies that closing an already closed stream has
no effect, so the tests verify with atLeastOnce() rather than pinning the
count. If you would rather the tester close exactly once, that needs a wrapper
tracking what the delegate already did, and I am happy to go that way instead.

Verified: both new tests fail for GsonTesterTests without the production change
and pass for all four testers with it, :core:spring-boot-test:test 755 tests
with 0 failures, and checkFormatMain, checkFormatTest, checkstyleMain and
checkstyleTest pass.

Contributed on behalf of Goatshave.

AbstractJsonMarshalTester.read(Resource) and read(Reader) close their
source only after readObject has returned. When the delegate throws,
for example on malformed JSON, closeQuietly is skipped and the source
is left open.

Whether this leaks depends on the delegate: Jackson and JSON-B close
the source themselves, but Gson does not, so GsonTester leaks a file
handle for every failed read from a Resource, File or classpath path.

Close in a finally block so the tester honours its own contract
regardless of the delegate.

See spring-projectsgh-51384

Signed-off-by: dlwldn30 <dlwldn30@naver.com>
Spy the source and verify close() with Mockito, as suggested in review.
Verification is atLeastOnce() rather than the default times(1): Jackson,
Jackson2 and JSON-B close the source themselves, so the finally block
makes close() run a second time. That is a no-op by contract, since
Closeable specifies that closing an already closed stream has no effect.
The count is not the property under test; being closed at all is.

See spring-projectsgh-51384

Signed-off-by: dlwldn30 <dlwldn30@naver.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants