Skip to content

Commit 692d23f

Browse files
authored
Stop DateFormatter trailing token from running past the parse end (#16958)
## Problem `DateFormatter.parseHttpDate(txt, start, end)` is documented to parse only the `[start, end)` substring. Its tokenizer loop correctly stops at `end`, but the **trailing token** — the one still open when the loop finishes — was terminated at `txt.length()` instead of `end`: ```java // terminate trailing token return tokenStart != -1 && parseToken(txt, tokenStart, txt.length()); ``` When `end < txt.length()` and the date's *last* token is the one that completes the parse, the trailing token swallows the bytes after `end` and fails to parse. This is reachable in practice through cookies. A `Set-Cookie` header like `foo=bar; Expires=<date>; Path=/` makes `ClientCookieDecoder` call `parseHttpDate(header, start, end)` with `end` pointing at the `;` before `Path`. RFC 6265 §5.1.1 cookie-date tokens are **order-independent**, so a valid date whose year (or day) is the last token — e.g. `Sun 08:49:37 06 Nov 1994` — parses fine in isolation but returns `null` as a substring, silently dropping the expiry and downgrading the cookie to a session cookie. Standard `Sun, 06 Nov 1994 08:49:37 GMT` ordering does not trigger it (the time token completes the parse mid-loop, before the trailing `GMT`), which is why existing tests miss it. ## Fix Terminate the trailing token at `end` rather than `txt.length()`. Since `end <= txt.length()` always, this only ever shrinks the trailing token to the intended bound; the full-string `parseHttpDate(txt)` path (where `end == txt.length()`) is unaffected. Added a `DateFormatterTest` case that parses such a date both in isolation and as a substring and asserts they agree. ## Result `parseHttpDate` honours the `end` bound for the trailing token; valid order-independent cookie dates followed by other attributes now parse correctly. Full `DateFormatterTest` (14) passes.
1 parent ba8e9e6 commit 692d23f

2 files changed

Lines changed: 10 additions & 2 deletions

File tree

codec-base/src/main/java/io/netty/handler/codec/DateFormatter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,8 +388,8 @@ private boolean parse1(CharSequence txt, int start, int end) {
388388
}
389389
}
390390

391-
// terminate trailing token
392-
return tokenStart != -1 && parseToken(txt, tokenStart, txt.length());
391+
// terminate trailing token at end, not txt.length(), so a substring parse doesn't overrun
392+
return tokenStart != -1 && parseToken(txt, tokenStart, end);
393393
}
394394

395395
private boolean normalizeAndValidate() {

codec-base/src/test/java/io/netty/handler/codec/DateFormatterTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ public void testParseWithSingleDigitDay() {
3737
assertEquals(DATE, parseHttpDate("Sun, 6 Nov 1994 08:49:37 GMT"));
3838
}
3939

40+
@Test
41+
public void testParseHttpDateSubstringDoesNotOverrunEnd() {
42+
// Set-Cookie header format with the date anywhere but last (RFC 6265 tokens are unordered).
43+
String dateText = "Sun 08:49:37 06 Nov 1994";
44+
assertEquals(DATE, parseHttpDate(dateText));
45+
assertEquals(DATE, parseHttpDate(dateText + "; Path=/", 0, dateText.length()));
46+
}
47+
4048
@Test
4149
public void testParseWithDoubleDigitDay() {
4250
assertEquals(DATE, parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"));

0 commit comments

Comments
 (0)