Skip to content

Commit 0847044

Browse files
author
Egor Martsynkovsky
committed
Fix opened connections outside ResourceRetriever
DEVSIX-5037
1 parent 22758a9 commit 0847044

File tree

4 files changed

+103
-20
lines changed

4 files changed

+103
-20
lines changed

io/src/main/java/com/itextpdf/io/util/UrlUtil.java

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,21 +103,21 @@ public static InputStream openStream(URL url) throws IOException {
103103

104104
/**
105105
* This method gets the last redirected url.
106-
* @param initialUrl an initial URL
107-
* @return the last redirected url
108-
* @throws IOException
106+
*
107+
* @param initialUrl an initial URL.
108+
*
109+
* @return the last redirected url.
110+
*
111+
* @throws IOException signals that an I/O exception has occurred.
112+
*
113+
* @deprecated {@link UrlUtil#getInputStreamOfFinalConnection(URL)} can be used to get input stream from final
114+
* connection.
109115
*/
116+
@Deprecated
110117
public static URL getFinalURL(URL initialUrl) throws IOException {
111-
URL finalUrl = null;
112-
URL nextUrl = initialUrl;
113-
while (nextUrl != null) {
114-
finalUrl = nextUrl;
115-
URLConnection connection = finalUrl.openConnection();
116-
String location = connection.getHeaderField("location");
117-
// Close input stream deliberately to close the handle which is created during getHeaderField invocation
118-
connection.getInputStream().close();
119-
nextUrl = location != null ? new URL(location) : null;
120-
}
118+
final URLConnection finalConnection = getFinalConnection(initialUrl);
119+
final URL finalUrl = finalConnection.getURL();
120+
finalConnection.getInputStream().close();
121121
return finalUrl;
122122
}
123123

@@ -138,4 +138,44 @@ public static String getFileUriString(String filename) throws MalformedURLExcept
138138
public static String getNormalizedFileUriString(String filename) {
139139
return "file://" + UrlUtil.toNormalizedURI(filename).getPath();
140140
}
141+
142+
/**
143+
* Gets the input stream of connection related to last redirected url. You should manually close input stream after
144+
* calling this method to not hold any open resources.
145+
*
146+
* @param initialUrl an initial URL.
147+
*
148+
* @return an input stream of connection related to the last redirected url.
149+
*
150+
* @throws IOException signals that an I/O exception has occurred.
151+
*/
152+
public static InputStream getInputStreamOfFinalConnection(URL initialUrl) throws IOException {
153+
final URLConnection finalConnection = getFinalConnection(initialUrl);
154+
return finalConnection.getInputStream();
155+
}
156+
157+
/**
158+
* Gets the connection related to the last redirected url. You should close connection manually after calling
159+
* this method, to not hold any open resources.
160+
*
161+
* @param initialUrl an initial URL.
162+
*
163+
* @return connection related to the last redirected url.
164+
*
165+
* @throws IOException signals that an I/O exception has occurred.
166+
*/
167+
static URLConnection getFinalConnection(URL initialUrl) throws IOException {
168+
URL nextUrl = initialUrl;
169+
URLConnection connection = null;
170+
while (nextUrl != null) {
171+
connection = nextUrl.openConnection();
172+
final String location = connection.getHeaderField("location");
173+
nextUrl = location == null ? null : new URL(location);
174+
if (nextUrl != null) {
175+
// close input stream deliberately to close the handle which is created during getHeaderField invocation
176+
connection.getInputStream().close();
177+
}
178+
}
179+
return connection;
180+
}
141181
}

io/src/test/java/com/itextpdf/io/util/UrlUtilTest.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,13 @@ This file is part of the iText (R) project.
4545
import com.itextpdf.commons.utils.FileUtil;
4646
import com.itextpdf.test.ExtendedITextTest;
4747
import com.itextpdf.test.annotations.type.UnitTest;
48+
4849
import java.io.File;
4950
import java.io.IOException;
5051
import java.io.InputStream;
52+
import java.net.URL;
53+
import java.net.URLConnection;
54+
import java.net.UnknownHostException;
5155
import java.nio.charset.StandardCharsets;
5256
import java.nio.file.Paths;
5357
import org.junit.Assert;
@@ -65,7 +69,9 @@ public static void beforeClass() {
6569
createDestinationFolder(destinationFolder);
6670
}
6771

68-
// Tests that after invocation of the getFinalURL method for local files, no handles are left open and the file is free to be removed
72+
73+
// Tests that after invocation of the getFinalURL method for local files, no handles are left open and the file
74+
// is free to be removed.
6975
@Test
7076
public void getFinalURLDoesNotLockFileTest() throws IOException {
7177
File tempFile = FileUtil.createTempFile(destinationFolder);
@@ -75,6 +81,44 @@ public void getFinalURLDoesNotLockFileTest() throws IOException {
7581
Assert.assertTrue(FileUtil.deleteFile(tempFile));
7682
}
7783

84+
// Tests, that getFinalConnection will be redirected some times for other urls, and initialUrl will be different
85+
// from final url.
86+
@Test
87+
public void getFinalConnectionWhileRedirectingTest() throws IOException {
88+
URL initialUrl = new URL("http://itextpdf.com");
89+
URL expectedURL = new URL("https://itextpdf.com/en");
90+
URLConnection finalConnection = null;
91+
92+
try {
93+
finalConnection = UrlUtil.getFinalConnection(initialUrl);
94+
95+
Assert.assertNotNull(finalConnection);
96+
Assert.assertNotEquals(initialUrl, finalConnection.getURL());
97+
Assert.assertEquals(expectedURL, finalConnection.getURL());
98+
} finally {
99+
finalConnection.getInputStream().close();
100+
}
101+
}
102+
103+
// This test checks that when we pass invalid url and trying get stream related to final redirected url,exception
104+
// would be thrown.
105+
@Test
106+
public void getInputStreamOfFinalConnectionThrowExceptionTest() throws IOException {
107+
URL invalidUrl = new URL("http://itextpdf");
108+
109+
Assert.assertThrows(UnknownHostException.class, () -> UrlUtil.getInputStreamOfFinalConnection(invalidUrl));
110+
}
111+
112+
// This test checks that when we pass valid url and trying get stream related to final redirected url, it would
113+
// not be null.
114+
@Test
115+
public void getInputStreamOfFinalConnectionTest() throws IOException {
116+
URL initialUrl = new URL("http://itextpdf.com");
117+
InputStream streamOfFinalConnectionOfInvalidUrl = UrlUtil.getInputStreamOfFinalConnection(initialUrl);
118+
119+
Assert.assertNotNull(streamOfFinalConnectionOfInvalidUrl);
120+
}
121+
78122
@Test
79123
public void getBaseUriTest() throws IOException {
80124
String absolutePathRoot = Paths.get("").toAbsolutePath().toUri().toURL().toExternalForm();

styled-xml-parser/src/main/java/com/itextpdf/styledxmlparser/resolver/resource/DefaultResourceRetriever.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This file is part of the iText (R) project.
2424

2525
import com.itextpdf.commons.utils.MessageFormatUtil;
2626
import com.itextpdf.io.util.StreamUtil;
27+
import com.itextpdf.io.util.UrlUtil;
2728
import com.itextpdf.styledxmlparser.logs.StyledXmlParserLogMessageConstant;
2829
import com.itextpdf.styledxmlparser.exceptions.ReadingByteLimitException;
2930

@@ -90,7 +91,7 @@ public InputStream getInputStreamByUrl(URL url) throws IOException {
9091
url));
9192
return null;
9293
}
93-
return new LimitedInputStream(url.openStream(), resourceSizeByteLimit);
94+
return new LimitedInputStream(UrlUtil.getInputStreamOfFinalConnection(url), resourceSizeByteLimit);
9495
}
9596

9697
/**

styled-xml-parser/src/main/java/com/itextpdf/styledxmlparser/resolver/resource/ResourceResolver.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ This file is part of the iText (R) project.
4545
import com.itextpdf.commons.utils.Base64;
4646
import com.itextpdf.commons.utils.MessageFormatUtil;
4747
import com.itextpdf.io.image.ImageDataFactory;
48-
import com.itextpdf.io.util.UrlUtil;
4948
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;
5049
import com.itextpdf.kernel.pdf.xobject.PdfXObject;
5150
import com.itextpdf.styledxmlparser.logs.StyledXmlParserLogMessageConstant;
@@ -277,7 +276,6 @@ protected PdfXObject tryResolveBase64ImageSource(String src) {
277276
protected PdfXObject tryResolveUrlImageSource(String uri) {
278277
try {
279278
URL url = uriResolver.resolveAgainstBaseUri(uri);
280-
url = UrlUtil.getFinalURL(url);
281279
String imageResolvedSrc = url.toExternalForm();
282280
PdfXObject imageXObject = imageCache.getImage(imageResolvedSrc);
283281
if (imageXObject == null) {
@@ -295,9 +293,9 @@ protected PdfXObject tryResolveUrlImageSource(String uri) {
295293
/**
296294
* Create a iText XObject based on the image stored at the passed location.
297295
*
298-
* @param url location of the Image file
299-
* @return {@link PdfXObject} containing the Image loaded in
300-
* @throws Exception thrown if error occurred during fetching or constructing the image
296+
* @param url location of the Image file.
297+
* @return {@link PdfXObject} containing the Image loaded in.
298+
* @throws Exception thrown if error occurred during fetching or constructing the image.
301299
*/
302300
protected PdfXObject createImageByUrl(URL url) throws Exception {
303301
byte[] bytes = retriever.getByteArrayByUrl(url);

0 commit comments

Comments
 (0)