Fix date format in headers to use 2-digit day of month (#10259)

Motivation:

`Date`, `Expires`, and `Set-Cookie` headers are being generated with a 1-digit day of month,
e.g. `Sun, 6 Nov 1994 08:49:37 GMT`. RFC 2616 specifies that `Date` and `Expires` headers should
use "a fixed-length subset of that defined by RFC 1123" which includes a 2-digit day of month.
RFC6265 is lax in it's specification of the `Set-Cookie` header and permits a 2-digit day of month.

See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
See: https://tools.ietf.org/html/rfc1123#page-55
See: https://tools.ietf.org/html/rfc6265#section-5.1.1

Modifications:

- Update `DateFormatter` to correctly implement RFC 2616 headers

Result:

```
Date: Sun, 06 Nov 1994 08:49:37 GMT
Expires: Sun, 06 Nov 1994 08:49:37 GMT
Set-Cookie: id=a3fWa; Expires=Sun, 06 Nov 1994 08:49:37 GMT
```
This commit is contained in:
Richard Nguyen 2020-05-10 23:57:17 -07:00 committed by GitHub
parent 09d38c87df
commit 322d96ef92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 45 deletions

View File

@ -38,10 +38,12 @@ import java.util.TimeZone;
* If you're looking for a date format that validates day of week, or supports other timezones, consider using
* java.util.DateTimeFormatter.RFC_1123_DATE_TIME.
*
* On the formatting side, it uses RFC1123 format.
* On the formatting side, it uses a subset of RFC1123 (2 digit day-of-month and 4 digit year) as per RFC2616.
* This subset supports RFC6265.
*
* @see <a href="https://tools.ietf.org/html/rfc6265#section-5.1.1">RFC6265</a> for the parsing side
* @see <a href="https://tools.ietf.org/html/rfc1123#page-55">RFC1123</a> for the encoding side.
* @see <a href="https://tools.ietf.org/html/rfc1123#page-55">RFC1123</a> and
* <a href="https://tools.ietf.org/html/rfc2616#section-3.3.1">RFC2616</a> for the encoding side.
*/
public final class DateFormatter {
@ -429,7 +431,7 @@ public final class DateFormatter {
cal.setTime(date);
sb.append(DAY_OF_WEEK_TO_SHORT_NAME[cal.get(Calendar.DAY_OF_WEEK) - 1]).append(", ");
sb.append(cal.get(Calendar.DAY_OF_MONTH)).append(' ');
appendZeroLeftPadded(cal.get(Calendar.DAY_OF_MONTH), sb).append(' ');
sb.append(CALENDAR_MONTH_TO_SHORT_NAME[cal.get(Calendar.MONTH)]).append(' ');
sb.append(cal.get(Calendar.YEAR)).append(' ');
appendZeroLeftPadded(cal.get(Calendar.HOUR_OF_DAY), sb).append(':');

View File

@ -43,90 +43,97 @@ public class DateFormatterTest {
@Test
public void testParseWithDashSeparatorSingleDigitDay() {
assertEquals(DATE, parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"));
}
@Test
public void testParseWithSingleDoubleDigitDay() {
assertEquals(DATE, parseHttpDate("Sunday, 6-Nov-94 08:49:37 GMT"));
}
@Test
public void testParseWithDashSeparatorDoubleDigitDay() {
assertEquals(DATE, parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"));
}
@Test
public void testParseWithoutGMT() {
assertEquals(DATE, parseHttpDate("Sun Nov 6 08:49:37 1994"));
assertEquals(DATE, parseHttpDate("Sun Nov 06 08:49:37 1994"));
}
@Test
public void testParseWithFunkyTimezone() {
assertEquals(DATE, parseHttpDate("Sun Nov 6 08:49:37 1994 -0000"));
assertEquals(DATE, parseHttpDate("Sun Nov 06 08:49:37 1994 -0000"));
}
@Test
public void testParseWithSingleDigitHourMinutesAndSecond() {
assertEquals(DATE, parseHttpDate("Sunday, 6-Nov-94 8:49:37 GMT"));
assertEquals(DATE, parseHttpDate("Sunday, 06-Nov-94 8:49:37 GMT"));
}
@Test
public void testParseWithSingleDigitTime() {
assertEquals(DATE, parseHttpDate("Sunday, 6 Nov 1994 8:49:37 GMT"));
assertEquals(DATE, parseHttpDate("Sunday, 06 Nov 1994 8:49:37 GMT"));
Date _08_09_37 = new Date(TIMESTAMP - 40 * 60 * 1000);
assertEquals(_08_09_37, parseHttpDate("Sunday, 6 Nov 1994 8:9:37 GMT"));
assertEquals(_08_09_37, parseHttpDate("Sunday, 6 Nov 1994 8:09:37 GMT"));
assertEquals(_08_09_37, parseHttpDate("Sunday, 06 Nov 1994 8:9:37 GMT"));
assertEquals(_08_09_37, parseHttpDate("Sunday, 06 Nov 1994 8:09:37 GMT"));
Date _08_09_07 = new Date(TIMESTAMP - (40 * 60 + 30) * 1000);
assertEquals(_08_09_07, parseHttpDate("Sunday, 6 Nov 1994 8:9:7 GMT"));
assertEquals(_08_09_07, parseHttpDate("Sunday, 6 Nov 1994 8:9:07 GMT"));
assertEquals(_08_09_07, parseHttpDate("Sunday, 06 Nov 1994 8:9:7 GMT"));
assertEquals(_08_09_07, parseHttpDate("Sunday, 06 Nov 1994 8:9:07 GMT"));
}
@Test
public void testParseMidnight() {
assertEquals(new Date(784080000000L), parseHttpDate("Sunday, 6 Nov 1994 00:00:00 GMT"));
assertEquals(new Date(784080000000L), parseHttpDate("Sunday, 06 Nov 1994 00:00:00 GMT"));
}
@Test
public void testParseInvalidInput() {
// missing field
assertNull(parseHttpDate("Sun, Nov 1994 08:49:37 GMT"));
assertNull(parseHttpDate("Sun, 6 1994 08:49:37 GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 08:49:37 GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 1994 :49:37 GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 1994 49:37 GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 1994 08::37 GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 1994 08:37 GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 1994 08:49: GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 1994 08:49 GMT"));
assertNull(parseHttpDate("Sun, 06 1994 08:49:37 GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 08:49:37 GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 1994 :49:37 GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 1994 49:37 GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 1994 08::37 GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 1994 08:37 GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 1994 08:49: GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 1994 08:49 GMT"));
//invalid value
assertNull(parseHttpDate("Sun, 6 FOO 1994 08:49:37 GMT"));
assertNull(parseHttpDate("Sun, 06 FOO 1994 08:49:37 GMT"));
assertNull(parseHttpDate("Sun, 36 Nov 1994 08:49:37 GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 1994 28:49:37 GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 1994 08:69:37 GMT"));
assertNull(parseHttpDate("Sun, 6 Nov 1994 08:49:67 GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 1994 28:49:37 GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 1994 08:69:37 GMT"));
assertNull(parseHttpDate("Sun, 06 Nov 1994 08:49:67 GMT"));
//wrong number of digits in timestamp
assertNull(parseHttpDate("Sunday, 6 Nov 1994 0:0:000 GMT"));
assertNull(parseHttpDate("Sunday, 6 Nov 1994 0:000:0 GMT"));
assertNull(parseHttpDate("Sunday, 6 Nov 1994 000:0:0 GMT"));
assertNull(parseHttpDate("Sunday, 06 Nov 1994 0:0:000 GMT"));
assertNull(parseHttpDate("Sunday, 06 Nov 1994 0:000:0 GMT"));
assertNull(parseHttpDate("Sunday, 06 Nov 1994 000:0:0 GMT"));
}
@Test
public void testFormat() {
assertEquals("Sun, 6 Nov 1994 08:49:37 GMT", format(DATE));
assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", format(DATE));
}
@Test
public void testAppend() {
StringBuilder sb = new StringBuilder();
append(DATE, sb);
assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", sb.toString());
}
@Test
public void testParseAllMonths() {
assertEquals(Calendar.JANUARY, getMonth(parseHttpDate("Sun, 6 Jan 1994 08:49:37 GMT")));
assertEquals(Calendar.FEBRUARY, getMonth(parseHttpDate("Sun, 6 Feb 1994 08:49:37 GMT")));
assertEquals(Calendar.MARCH, getMonth(parseHttpDate("Sun, 6 Mar 1994 08:49:37 GMT")));
assertEquals(Calendar.APRIL, getMonth(parseHttpDate("Sun, 6 Apr 1994 08:49:37 GMT")));
assertEquals(Calendar.MAY, getMonth(parseHttpDate("Sun, 6 May 1994 08:49:37 GMT")));
assertEquals(Calendar.JUNE, getMonth(parseHttpDate("Sun, 6 Jun 1994 08:49:37 GMT")));
assertEquals(Calendar.JULY, getMonth(parseHttpDate("Sun, 6 Jul 1994 08:49:37 GMT")));
assertEquals(Calendar.AUGUST, getMonth(parseHttpDate("Sun, 6 Aug 1994 08:49:37 GMT")));
assertEquals(Calendar.SEPTEMBER, getMonth(parseHttpDate("Sun, 6 Sep 1994 08:49:37 GMT")));
assertEquals(Calendar.OCTOBER, getMonth(parseHttpDate("Sun Oct 6 08:49:37 1994")));
assertEquals(Calendar.NOVEMBER, getMonth(parseHttpDate("Sun Nov 6 08:49:37 1994")));
assertEquals(Calendar.DECEMBER, getMonth(parseHttpDate("Sun Dec 6 08:49:37 1994")));
assertEquals(Calendar.JANUARY, getMonth(parseHttpDate("Sun, 06 Jan 1994 08:49:37 GMT")));
assertEquals(Calendar.FEBRUARY, getMonth(parseHttpDate("Sun, 06 Feb 1994 08:49:37 GMT")));
assertEquals(Calendar.MARCH, getMonth(parseHttpDate("Sun, 06 Mar 1994 08:49:37 GMT")));
assertEquals(Calendar.APRIL, getMonth(parseHttpDate("Sun, 06 Apr 1994 08:49:37 GMT")));
assertEquals(Calendar.MAY, getMonth(parseHttpDate("Sun, 06 May 1994 08:49:37 GMT")));
assertEquals(Calendar.JUNE, getMonth(parseHttpDate("Sun, 06 Jun 1994 08:49:37 GMT")));
assertEquals(Calendar.JULY, getMonth(parseHttpDate("Sun, 06 Jul 1994 08:49:37 GMT")));
assertEquals(Calendar.AUGUST, getMonth(parseHttpDate("Sun, 06 Aug 1994 08:49:37 GMT")));
assertEquals(Calendar.SEPTEMBER, getMonth(parseHttpDate("Sun, 06 Sep 1994 08:49:37 GMT")));
assertEquals(Calendar.OCTOBER, getMonth(parseHttpDate("Sun Oct 06 08:49:37 1994")));
assertEquals(Calendar.NOVEMBER, getMonth(parseHttpDate("Sun Nov 06 08:49:37 1994")));
assertEquals(Calendar.DECEMBER, getMonth(parseHttpDate("Sun Dec 06 08:49:37 1994")));
}
private static int getMonth(Date referenceDate) {