Return getLastModified result from JarUrlConnection
Update `JarUrlConnection` and `NestedUrlConnection` so that calls to `getLastModified()` and `getHeaderFieldDate("last-modified", 0)` always return a result. Fixes gh-38204
This commit is contained in:
parent
d6c28b3fc7
commit
c0f8b90d31
|
@ -154,6 +154,11 @@ final class JarUrlConnection extends java.net.JarURLConnection {
|
|||
return guessContentTypeFromName(this.entryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
return (this.jarFileConnection != null) ? this.jarFileConnection.getLastModified() : super.getLastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeaderField(String name) {
|
||||
return (this.jarFileConnection != null) ? this.jarFileConnection.getHeaderField(name) : null;
|
||||
|
|
|
@ -28,6 +28,15 @@ import java.net.URL;
|
|||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.security.Permission;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.boot.loader.ref.Cleaner;
|
||||
|
||||
|
@ -39,6 +48,9 @@ import org.springframework.boot.loader.ref.Cleaner;
|
|||
*/
|
||||
class NestedUrlConnection extends URLConnection {
|
||||
|
||||
private static final DateTimeFormatter RFC_1123_DATE_TIME = DateTimeFormatter.RFC_1123_DATE_TIME
|
||||
.withZone(ZoneId.of("GMT"));
|
||||
|
||||
private static final String CONTENT_TYPE = "x-java/jar";
|
||||
|
||||
private final NestedUrlConnectionResources resources;
|
||||
|
@ -49,6 +61,8 @@ class NestedUrlConnection extends URLConnection {
|
|||
|
||||
private FilePermission permission;
|
||||
|
||||
private Map<String, List<String>> headerFields;
|
||||
|
||||
NestedUrlConnection(URL url) throws MalformedURLException {
|
||||
this(url, Cleaner.instance);
|
||||
}
|
||||
|
@ -69,6 +83,60 @@ class NestedUrlConnection extends URLConnection {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeaderField(String name) {
|
||||
List<String> values = getHeaderFields().get(name);
|
||||
return (values != null && !values.isEmpty()) ? values.get(0) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeaderField(int n) {
|
||||
Entry<String, List<String>> entry = getHeaderEntry(n);
|
||||
List<String> values = (entry != null) ? entry.getValue() : null;
|
||||
return (values != null && !values.isEmpty()) ? values.get(0) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeaderFieldKey(int n) {
|
||||
Entry<String, List<String>> entry = getHeaderEntry(n);
|
||||
return (entry != null) ? entry.getKey() : null;
|
||||
}
|
||||
|
||||
private Entry<String, List<String>> getHeaderEntry(int n) {
|
||||
Iterator<Entry<String, List<String>>> iterator = getHeaderFields().entrySet().iterator();
|
||||
Entry<String, List<String>> entry = null;
|
||||
for (int i = 0; i < n; i++) {
|
||||
entry = (!iterator.hasNext()) ? null : iterator.next();
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getHeaderFields() {
|
||||
try {
|
||||
connect();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, List<String>> headerFields = this.headerFields;
|
||||
if (headerFields == null) {
|
||||
headerFields = new LinkedHashMap<>();
|
||||
long contentLength = getContentLengthLong();
|
||||
long lastModified = getLastModified();
|
||||
if (contentLength > 0) {
|
||||
headerFields.put("content-length", List.of(String.valueOf(contentLength)));
|
||||
}
|
||||
if (getLastModified() > 0) {
|
||||
headerFields.put("last-modified",
|
||||
List.of(RFC_1123_DATE_TIME.format(Instant.ofEpochMilli(lastModified))));
|
||||
}
|
||||
headerFields = Collections.unmodifiableMap(headerFields);
|
||||
this.headerFields = headerFields;
|
||||
}
|
||||
return headerFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContentLength() {
|
||||
long contentLength = getContentLengthLong();
|
||||
|
|
|
@ -27,6 +27,8 @@ import java.net.URL;
|
|||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Permission;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarEntry;
|
||||
|
@ -490,4 +492,22 @@ class JarUrlConnectionTests {
|
|||
assertThat(connection).isNotNull();
|
||||
}
|
||||
|
||||
@Test // gh-38204
|
||||
void getLastModifiedReturnsFileModifiedTime() throws Exception {
|
||||
JarUrlConnection connection = JarUrlConnection.open(this.url);
|
||||
assertThat(connection.getLastModified()).isEqualTo(this.file.lastModified());
|
||||
}
|
||||
|
||||
@Test // gh-38204
|
||||
void getLastModifiedHeaderReturnsFileModifiedTime() throws IOException {
|
||||
JarUrlConnection connection = JarUrlConnection.open(this.url);
|
||||
URLConnection fileConnection = this.file.toURI().toURL().openConnection();
|
||||
assertThat(connection.getHeaderFieldDate("last-modified", 0)).isEqualTo(withoutNanos(this.file.lastModified()))
|
||||
.isEqualTo(fileConnection.getHeaderFieldDate("last-modified", 0));
|
||||
}
|
||||
|
||||
private long withoutNanos(long epochMilli) {
|
||||
return Instant.ofEpochMilli(epochMilli).with(ChronoField.NANO_OF_SECOND, 0).toEpochMilli();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,11 +18,15 @@ package org.springframework.boot.loader.net.protocol.nested;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FilePermission;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.Cleaner.Cleanable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.Permission;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoField;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -148,4 +152,23 @@ class NestedUrlConnectionTests {
|
|||
actionCaptor.getValue().run();
|
||||
}
|
||||
|
||||
@Test // gh-38204
|
||||
void getLastModifiedReturnsFileModifiedTime() throws Exception {
|
||||
NestedUrlConnection connection = new NestedUrlConnection(this.url);
|
||||
assertThat(connection.getLastModified()).isEqualTo(this.jarFile.lastModified());
|
||||
}
|
||||
|
||||
@Test // gh-38204
|
||||
void getLastModifiedHeaderReturnsFileModifiedTime() throws IOException {
|
||||
NestedUrlConnection connection = new NestedUrlConnection(this.url);
|
||||
URLConnection fileConnection = this.jarFile.toURI().toURL().openConnection();
|
||||
assertThat(connection.getHeaderFieldDate("last-modified", 0))
|
||||
.isEqualTo(withoutNanos(this.jarFile.lastModified()))
|
||||
.isEqualTo(fileConnection.getHeaderFieldDate("last-modified", 0));
|
||||
}
|
||||
|
||||
private long withoutNanos(long epochMilli) {
|
||||
return Instant.ofEpochMilli(epochMilli).with(ChronoField.NANO_OF_SECOND, 0).toEpochMilli();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue