summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
authorThomas Wolf <thomas.wolf@paranor.ch>2021-03-01 08:30:09 +0100
committerThomas Wolf <thomas.wolf@paranor.ch>2021-03-03 00:26:51 +0100
commit40d6eda3f16f24db20776d33e586737efeddc725 (patch)
tree6b3fc17727c51651e323dcc52a8a1c61b2cb0fec /org.eclipse.jgit
parent535f446a2e570ff769348af5a3e7b1b5df9889ea (diff)
downloadjgit-40d6eda3f16f24db20776d33e586737efeddc725.tar.gz
jgit-40d6eda3f16f24db20776d33e586737efeddc725.zip
HTTP: cookie file stores expiration in seconds
A cookie file stores the expiration in seconds since the Linux Epoch, not in milliseconds. Correct reading and writing cookie files; with a backwards-compatibility hack to read files that contain a millisecond timestamp. Add a test, and fix tests not to rely on the actual current time so that they will also run successfully after 2030-01-01 noon. Bug: 571574 Change-Id: If3ba68391e574520701cdee119544eedc42a1ff2 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java54
1 files changed, 30 insertions, 24 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java
index 49f26c7885..dae7173059 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java
@@ -22,11 +22,12 @@ import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.text.MessageFormat;
+import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
@@ -53,6 +54,7 @@ import org.slf4j.LoggerFactory;
* In general this class is not thread-safe. So any consumer needs to take care
* of synchronization!
*
+ * @see <a href="https://curl.se/docs/http-cookies.html">Cookie file format</a>
* @see <a href="http://www.cookiecentral.com/faq/#3.5">Netscape Cookie File
* Format</a>
* @see <a href=
@@ -92,7 +94,7 @@ public final class NetscapeCookieFile {
private byte[] hash;
- final Date creationDate;
+ private final Instant createdAt;
private Set<HttpCookie> cookies = null;
@@ -104,13 +106,13 @@ public final class NetscapeCookieFile {
* where to find the cookie file
*/
public NetscapeCookieFile(Path path) {
- this(path, new Date());
+ this(path, Instant.now());
}
- NetscapeCookieFile(Path path, Date creationDate) {
+ NetscapeCookieFile(Path path, Instant createdAt) {
this.path = path;
this.snapshot = FileSnapshot.DIRTY;
- this.creationDate = creationDate;
+ this.createdAt = createdAt;
}
/**
@@ -142,7 +144,7 @@ public final class NetscapeCookieFile {
if (cookies == null || refresh) {
try {
byte[] in = getFileContentIfModified();
- Set<HttpCookie> newCookies = parseCookieFile(in, creationDate);
+ Set<HttpCookie> newCookies = parseCookieFile(in, createdAt);
if (cookies != null) {
cookies = mergeCookies(newCookies, cookies);
} else {
@@ -168,9 +170,9 @@ public final class NetscapeCookieFile {
*
* @param input
* the file content to parse
- * @param creationDate
- * the date for the creation of the cookies (used to calculate
- * the maxAge based on the expiration date given within the file)
+ * @param createdAt
+ * cookie creation time; used to calculate the maxAge based on
+ * the expiration date given within the file
* @return the set of parsed cookies from the given file (even expired
* ones). If there is more than one cookie with the same name in
* this file the last one overwrites the first one!
@@ -180,7 +182,7 @@ public final class NetscapeCookieFile {
* if the given file does not have a proper format
*/
private static Set<HttpCookie> parseCookieFile(@NonNull byte[] input,
- @NonNull Date creationDate)
+ @NonNull Instant createdAt)
throws IOException, IllegalArgumentException {
String decoded = RawParseUtils.decode(StandardCharsets.US_ASCII, input);
@@ -190,7 +192,7 @@ public final class NetscapeCookieFile {
new StringReader(decoded))) {
String line;
while ((line = reader.readLine()) != null) {
- HttpCookie cookie = parseLine(line, creationDate);
+ HttpCookie cookie = parseLine(line, createdAt);
if (cookie != null) {
cookies.add(cookie);
}
@@ -200,7 +202,7 @@ public final class NetscapeCookieFile {
}
private static HttpCookie parseLine(@NonNull String line,
- @NonNull Date creationDate) {
+ @NonNull Instant createdAt) {
if (line.isEmpty() || (line.startsWith("#") //$NON-NLS-1$
&& !line.startsWith(HTTP_ONLY_PREAMBLE))) {
return null;
@@ -236,7 +238,12 @@ public final class NetscapeCookieFile {
cookie.setSecure(Boolean.parseBoolean(cookieLineParts[3]));
long expires = Long.parseLong(cookieLineParts[4]);
- long maxAge = (expires - creationDate.getTime()) / 1000;
+ // Older versions stored milliseconds. This heuristic to detect that
+ // will cause trouble in the year 33658. :-)
+ if (cookieLineParts[4].length() == 13) {
+ expires = TimeUnit.MILLISECONDS.toSeconds(expires);
+ }
+ long maxAge = expires - createdAt.getEpochSecond();
if (maxAge <= 0) {
return null; // skip expired cookies
}
@@ -245,7 +252,7 @@ public final class NetscapeCookieFile {
}
/**
- * Read the underying file and return its content but only in case it has
+ * Read the underlying file and return its content but only in case it has
* been modified since the last access.
* <p>
* Internally calculates the hash and maintains {@link FileSnapshot}s to
@@ -333,7 +340,7 @@ public final class NetscapeCookieFile {
path);
// reread new changes if necessary
Set<HttpCookie> cookiesFromFile = NetscapeCookieFile
- .parseCookieFile(cookieFileContent, creationDate);
+ .parseCookieFile(cookieFileContent, createdAt);
this.cookies = mergeCookies(cookiesFromFile, cookies);
}
} catch (FileNotFoundException e) {
@@ -343,7 +350,7 @@ public final class NetscapeCookieFile {
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (Writer writer = new OutputStreamWriter(output,
StandardCharsets.US_ASCII)) {
- write(writer, cookies, url, creationDate);
+ write(writer, cookies, url, createdAt);
}
LockFile lockFile = new LockFile(path.toFile());
for (int retryCount = 0; retryCount < LOCK_ACQUIRE_MAX_RETRY_COUNT; retryCount++) {
@@ -377,24 +384,23 @@ public final class NetscapeCookieFile {
* @param url
* the url for which to write the cookie (to derive the default
* values for certain cookie attributes)
- * @param creationDate
- * the date when the cookie has been created. Important for
- * calculation the cookie expiration time (calculated from
- * cookie's maxAge and this creation time)
+ * @param createdAt
+ * cookie creation time; used to calculate a cookie's expiration
+ * time
* @throws IOException
* if an I/O error occurs
*/
static void write(@NonNull Writer writer,
@NonNull Collection<HttpCookie> cookies, @NonNull URL url,
- @NonNull Date creationDate) throws IOException {
+ @NonNull Instant createdAt) throws IOException {
for (HttpCookie cookie : cookies) {
- writeCookie(writer, cookie, url, creationDate);
+ writeCookie(writer, cookie, url, createdAt);
}
}
private static void writeCookie(@NonNull Writer writer,
@NonNull HttpCookie cookie, @NonNull URL url,
- @NonNull Date creationDate) throws IOException {
+ @NonNull Instant createdAt) throws IOException {
if (cookie.getMaxAge() <= 0) {
return; // skip expired cookies
}
@@ -422,7 +428,7 @@ public final class NetscapeCookieFile {
final String expirationDate;
// whenCreated field is not accessible in HttpCookie
expirationDate = String
- .valueOf(creationDate.getTime() + (cookie.getMaxAge() * 1000));
+ .valueOf(createdAt.getEpochSecond() + cookie.getMaxAge());
writer.write(expirationDate);
writer.write(COLUMN_SEPARATOR);
writer.write(cookie.getName());