From 83c569462ddcdc9e9443dc79a6f41ebed6396a10 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 10 Jun 2022 21:23:18 -0700 Subject: [PATCH] Fix #763 --- release-notes/VERSION-2.x | 1 + .../fasterxml/jackson/core/JsonFactory.java | 17 ++++- .../core/json/InputStreamInitTest.java | 69 +++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/core/json/InputStreamInitTest.java diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 403870b89a..5127f15404 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -32,6 +32,7 @@ JSON library. (contributed by @pjfanning) #759: JsonGenerator to provide current value to the context before starting objects (reported by Illia O) +#763: `JsonFactory.createParser()` with `File` may leak `InputStream`s 2.13.3 (14-May-2022) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index c732fe9c29..302e50b0bd 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -1651,9 +1651,20 @@ public JsonGenerator createJsonGenerator(OutputStream out) throws IOException { * @since 2.1 */ protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException { - // As per [JACKSON-259], may want to fully disable canonicalization: - return new ByteSourceJsonBootstrapper(ctxt, in).constructParser(_parserFeatures, - _objectCodec, _byteSymbolCanonicalizer, _rootCharSymbols, _factoryFeatures); + try { + return new ByteSourceJsonBootstrapper(ctxt, in).constructParser(_parserFeatures, + _objectCodec, _byteSymbolCanonicalizer, _rootCharSymbols, _factoryFeatures); + } catch (IOException | RuntimeException e) { + // 10-Jun-2022, tatu: For [core#763] may need to close InputStream here + if (ctxt.isResourceManaged()) { + try { + in.close(); + } catch (Exception e2) { + e.addSuppressed(e2); + } + } + throw e; + } } /** diff --git a/src/test/java/com/fasterxml/jackson/core/json/InputStreamInitTest.java b/src/test/java/com/fasterxml/jackson/core/json/InputStreamInitTest.java new file mode 100644 index 0000000000..01f09adbad --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/core/json/InputStreamInitTest.java @@ -0,0 +1,69 @@ +package com.fasterxml.jackson.core.json; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import com.fasterxml.jackson.core.*; + +// [core#763] (and [databind#3455] +public class InputStreamInitTest + extends com.fasterxml.jackson.core.BaseTest +{ + static class FailingInputStream extends InputStream { + public boolean closed = false; + + @Override + public void close() { + closed = true; + } + + @Override + public int read() throws IOException { + throw new IOException("Will not read, ever!"); + } + } + + static class FailingJsonFactory extends JsonFactory { + private static final long serialVersionUID = 1L; + + public FailingInputStream lastStream; + + @Override + protected InputStream _fileInputStream(File f) throws IOException { + return (lastStream = new FailingInputStream()); + } + + @Override + protected InputStream _optimizedStreamFromURL(URL url) throws IOException { + return (lastStream = new FailingInputStream()); + } + } + + public void testForFile() throws Exception + { + final FailingJsonFactory jsonF = new FailingJsonFactory(); + try { + /*JsonParser p =*/ jsonF.createParser(new File("/tmp/test.json")); + fail("Should not pass"); + } catch (IOException e) { + verifyException(e, "Will not read"); + } + assertNotNull(jsonF.lastStream); + assertTrue(jsonF.lastStream.closed); + } + + public void testForURL() throws Exception + { + final FailingJsonFactory jsonF = new FailingJsonFactory(); + try { + /*JsonParser p =*/ jsonF.createParser(new URL("http://localhost:80/")); + fail("Should not pass"); + } catch (IOException e) { + verifyException(e, "Will not read"); + } + assertNotNull(jsonF.lastStream); + assertTrue(jsonF.lastStream.closed); + } +}