Clean up following #6016
Motivation: * DefaultHeaders from netty-codec has some duplicated logic for header date parsing * Several classes keep on using deprecated HttpHeaderDateFormat Modifications: * Move HttpHeaderDateFormatter to netty-codec and rename it into HeaderDateFormatter * Make DefaultHeaders use HeaderDateFormatter * Replace HttpHeaderDateFormat usage with HeaderDateFormatter Result: Faster and more consistent code
This commit is contained in:
parent
930633350d
commit
f755e58463
@ -18,13 +18,15 @@ package io.netty.handler.codec.http;
|
||||
import static io.netty.handler.codec.http.CookieUtil.firstInvalidCookieNameOctet;
|
||||
import static io.netty.handler.codec.http.CookieUtil.firstInvalidCookieValueOctet;
|
||||
import static io.netty.handler.codec.http.CookieUtil.unwrapValue;
|
||||
|
||||
import io.netty.handler.codec.DateFormatter;
|
||||
import io.netty.handler.codec.http.cookie.CookieHeaderNames;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@ -152,14 +154,10 @@ public final class CookieDecoder {
|
||||
} else if (CookieHeaderNames.PATH.equalsIgnoreCase(name)) {
|
||||
path = value;
|
||||
} else if (CookieHeaderNames.EXPIRES.equalsIgnoreCase(name)) {
|
||||
try {
|
||||
long maxAgeMillis =
|
||||
HttpHeaderDateFormat.get().parse(value).getTime() -
|
||||
System.currentTimeMillis();
|
||||
|
||||
Date date = DateFormatter.parseHttpDate(value);
|
||||
if (date != null) {
|
||||
long maxAgeMillis = date.getTime() - System.currentTimeMillis();
|
||||
maxAge = maxAgeMillis / 1000 + (maxAgeMillis % 1000 != 0? 1 : 0);
|
||||
} catch (ParseException e) {
|
||||
// Ignore.
|
||||
}
|
||||
} else if (CookieHeaderNames.MAX_AGE.equalsIgnoreCase(name)) {
|
||||
maxAge = Integer.parseInt(value);
|
||||
|
@ -16,6 +16,7 @@
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.handler.codec.CharSequenceValueConverter;
|
||||
import io.netty.handler.codec.DateFormatter;
|
||||
import io.netty.handler.codec.DefaultHeaders;
|
||||
import io.netty.handler.codec.DefaultHeaders.NameValidator;
|
||||
import io.netty.handler.codec.DefaultHeadersImpl;
|
||||
@ -391,10 +392,10 @@ public class DefaultHttpHeaders extends HttpHeaders {
|
||||
return (CharSequence) value;
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
return HttpHeaderDateFormat.get().format((Date) value);
|
||||
return DateFormatter.format((Date) value);
|
||||
}
|
||||
if (value instanceof Calendar) {
|
||||
return HttpHeaderDateFormat.get().format(((Calendar) value).getTime());
|
||||
return DateFormatter.format(((Calendar) value).getTime());
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.util.concurrent.FastThreadLocal;
|
||||
import io.netty.handler.codec.DateFormatter;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -32,7 +33,7 @@ import java.util.TimeZone;
|
||||
* <li>Sunday, 06-Nov-94 08:49:37 GMT: obsolete specification</li>
|
||||
* <li>Sun Nov 6 08:49:37 1994: obsolete specification</li>
|
||||
* </ul>
|
||||
* @deprecated Use {@link HttpHeaderDateFormatter} instead
|
||||
* @deprecated Use {@link DateFormatter} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class HttpHeaderDateFormat extends SimpleDateFormat {
|
||||
|
@ -17,6 +17,7 @@ package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.handler.codec.DateFormatter;
|
||||
import io.netty.handler.codec.Headers;
|
||||
import io.netty.util.AsciiString;
|
||||
|
||||
@ -839,7 +840,11 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
|
||||
if (value == null) {
|
||||
throw new ParseException("header not found: " + name, 0);
|
||||
}
|
||||
return HttpHeaderDateFormat.get().parse(value);
|
||||
Date date = DateFormatter.parseHttpDate(value);
|
||||
if (date == null) {
|
||||
throw new ParseException("header can't be parsed into a Date: " + value, 0);
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -865,15 +870,8 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
|
||||
@Deprecated
|
||||
public static Date getDateHeader(HttpMessage message, CharSequence name, Date defaultValue) {
|
||||
final String value = getHeader(message, name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
return HttpHeaderDateFormat.get().parse(value);
|
||||
} catch (ParseException ignored) {
|
||||
return defaultValue;
|
||||
}
|
||||
Date date = DateFormatter.parseHttpDate(value);
|
||||
return date != null ? date : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -897,7 +895,7 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
|
||||
@Deprecated
|
||||
public static void setDateHeader(HttpMessage message, CharSequence name, Date value) {
|
||||
if (value != null) {
|
||||
message.headers().set(name, HttpHeaderDateFormat.get().format(value));
|
||||
message.headers().set(name, DateFormatter.format(value));
|
||||
} else {
|
||||
message.headers().set(name, null);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http.cookie;
|
||||
|
||||
import io.netty.handler.codec.http.HttpHeaderDateFormatter;
|
||||
import io.netty.handler.codec.DateFormatter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ -168,7 +168,7 @@ public final class ClientCookieDecoder extends CookieDecoder {
|
||||
if (maxAge != Long.MIN_VALUE) {
|
||||
return maxAge;
|
||||
} else if (isValueDefined(expiresStart, expiresEnd)) {
|
||||
Date expiresDate = HttpHeaderDateFormatter.parse(header, expiresStart, expiresEnd);
|
||||
Date expiresDate = DateFormatter.parseHttpDate(header, expiresStart, expiresEnd);
|
||||
if (expiresDate != null) {
|
||||
long maxAgeMillis = expiresDate.getTime() - System.currentTimeMillis();
|
||||
return maxAgeMillis / 1000 + (maxAgeMillis % 1000 != 0 ? 1 : 0);
|
||||
|
@ -21,7 +21,7 @@ import static io.netty.handler.codec.http.cookie.CookieUtil.stringBuilder;
|
||||
import static io.netty.handler.codec.http.cookie.CookieUtil.stripTrailingSeparator;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
import io.netty.handler.codec.http.HttpHeaderDateFormatter;
|
||||
import io.netty.handler.codec.DateFormatter;
|
||||
import io.netty.handler.codec.http.HttpConstants;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
|
||||
@ -106,7 +106,7 @@ public final class ServerCookieEncoder extends CookieEncoder {
|
||||
Date expires = new Date(cookie.maxAge() * 1000 + System.currentTimeMillis());
|
||||
buf.append(CookieHeaderNames.EXPIRES);
|
||||
buf.append((char) HttpConstants.EQUALS);
|
||||
HttpHeaderDateFormatter.append(expires, buf);
|
||||
DateFormatter.append(expires, buf);
|
||||
buf.append((char) HttpConstants.SEMICOLON);
|
||||
buf.append((char) HttpConstants.SP);
|
||||
}
|
||||
|
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static io.netty.handler.codec.http.HttpHeaderDateFormatter.*;
|
||||
|
||||
public class HttpHeaderDateFormatterTest {
|
||||
/**
|
||||
* This date is set at "06 Nov 1994 08:49:37 GMT", from
|
||||
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html">examples in RFC documentation</a>
|
||||
*/
|
||||
private static final long TIMESTAMP = 784111777000L;
|
||||
private static final Date DATE = new Date(TIMESTAMP);
|
||||
|
||||
@Test
|
||||
public void testParseWithSingleDigitDay() {
|
||||
assertEquals(DATE, parse("Sun, 6 Nov 1994 08:49:37 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithDoubleDigitDay() {
|
||||
assertEquals(DATE, parse("Sun, 06 Nov 1994 08:49:37 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithDashSeparatorSingleDigitDay() {
|
||||
assertEquals(DATE, parse("Sunday, 06-Nov-94 08:49:37 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithSingleDoubleDigitDay() {
|
||||
assertEquals(DATE, parse("Sunday, 6-Nov-94 08:49:37 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithoutGMT() {
|
||||
assertEquals(DATE, parse("Sun Nov 6 08:49:37 1994"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithFunkyTimezone() {
|
||||
assertEquals(DATE, parse("Sun Nov 6 08:49:37 1994 -0000"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithSingleDigitHourMinutesAndSecond() {
|
||||
assertEquals(DATE, parse("Sunday, 6-Nov-94 8:49:37 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithSingleDigitTime() {
|
||||
assertEquals(DATE, parse("Sunday, 6 Nov 1994 8:49:37 GMT"));
|
||||
|
||||
Date _08_09_37 = new Date(TIMESTAMP - 40 * 60 * 1000);
|
||||
assertEquals(_08_09_37, parse("Sunday, 6 Nov 1994 8:9:37 GMT"));
|
||||
assertEquals(_08_09_37, parse("Sunday, 6 Nov 1994 8:09:37 GMT"));
|
||||
|
||||
Date _08_09_07 = new Date(TIMESTAMP - (40 * 60 + 30) * 1000);
|
||||
assertEquals(_08_09_07, parse("Sunday, 6 Nov 1994 8:9:7 GMT"));
|
||||
assertEquals(_08_09_07, parse("Sunday, 6 Nov 1994 8:9:07 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMidnight() {
|
||||
assertEquals(new Date(784080000000L), parse("Sunday, 6 Nov 1994 00:00:00 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseInvalidInput() {
|
||||
// missing field
|
||||
assertNull(parse("Sun, Nov 1994 08:49:37 GMT"));
|
||||
assertNull(parse("Sun, 6 1994 08:49:37 GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 08:49:37 GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 1994 :49:37 GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 1994 49:37 GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 1994 08::37 GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 1994 08:37 GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 1994 08:49: GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 1994 08:49 GMT"));
|
||||
//invalid value
|
||||
assertNull(parse("Sun, 6 FOO 1994 08:49:37 GMT"));
|
||||
assertNull(parse("Sun, 36 Nov 1994 08:49:37 GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 1994 28:49:37 GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 1994 08:69:37 GMT"));
|
||||
assertNull(parse("Sun, 6 Nov 1994 08:49:67 GMT"));
|
||||
//wrong number of digits in timestamp
|
||||
assertNull(parse("Sunday, 6 Nov 1994 0:0:000 GMT"));
|
||||
assertNull(parse("Sunday, 6 Nov 1994 0:000:0 GMT"));
|
||||
assertNull(parse("Sunday, 6 Nov 1994 000:0:0 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormat() {
|
||||
assertEquals("Sun, 6 Nov 1994 08:49:37 GMT", format(DATE));
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http.cookie;
|
||||
|
||||
import io.netty.handler.codec.http.HttpHeaderDateFormatter;
|
||||
import io.netty.handler.codec.DateFormatter;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -30,26 +30,18 @@ import static org.junit.Assert.*;
|
||||
public class ClientCookieDecoderTest {
|
||||
@Test
|
||||
public void testDecodingSingleCookieV0() {
|
||||
String cookieString = "myCookie=myValue;expires=XXX;path=/apathsomewhere;domain=.adomainsomewhere;secure;";
|
||||
cookieString = cookieString.replace("XXX",
|
||||
HttpHeaderDateFormatter.format(new Date(System.currentTimeMillis() + 50000)));
|
||||
String cookieString = "myCookie=myValue;expires="
|
||||
+ DateFormatter.format(new Date(System.currentTimeMillis() + 50000))
|
||||
+ ";path=/apathsomewhere;domain=.adomainsomewhere;secure;";
|
||||
|
||||
Cookie cookie = ClientCookieDecoder.STRICT.decode(cookieString);
|
||||
assertNotNull(cookie);
|
||||
assertEquals("myValue", cookie.value());
|
||||
assertEquals(".adomainsomewhere", cookie.domain());
|
||||
|
||||
boolean fail = true;
|
||||
for (int i = 40; i <= 60; i++) {
|
||||
if (cookie.maxAge() == i) {
|
||||
fail = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fail) {
|
||||
fail("expected: 50, actual: " + cookie.maxAge());
|
||||
}
|
||||
|
||||
assertNotEquals("maxAge should be defined when parsing cookie " + cookieString,
|
||||
Long.MIN_VALUE, cookie.maxAge());
|
||||
assertTrue("maxAge should be about 50ms when parsing cookie " + cookieString,
|
||||
cookie.maxAge() >= 40 && cookie.maxAge() <= 60);
|
||||
assertEquals("/apathsomewhere", cookie.path());
|
||||
assertTrue(cookie.isSecure());
|
||||
}
|
||||
|
@ -17,9 +17,6 @@ package io.netty.handler.codec.http.cookie;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import io.netty.handler.codec.http.HttpHeaderDateFormat;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
@ -29,9 +26,6 @@ public class ServerCookieDecoderTest {
|
||||
@Test
|
||||
public void testDecodingSingleCookie() {
|
||||
String cookieString = "myCookie=myValue";
|
||||
cookieString = cookieString.replace("XXX",
|
||||
HttpHeaderDateFormat.get().format(new Date(System.currentTimeMillis() + 50000)));
|
||||
|
||||
Set<Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieString);
|
||||
assertEquals(1, cookies.size());
|
||||
Cookie cookie = cookies.iterator().next();
|
||||
|
@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import io.netty.handler.codec.http.HttpHeaderDateFormat;
|
||||
import io.netty.handler.codec.DateFormatter;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
@ -51,7 +51,7 @@ public class ServerCookieEncoderTest {
|
||||
|
||||
Matcher matcher = Pattern.compile(result).matcher(encodedCookie);
|
||||
assertTrue(matcher.find());
|
||||
Date expiresDate = HttpHeaderDateFormat.get().parse(matcher.group(1));
|
||||
Date expiresDate = DateFormatter.parseHttpDate(matcher.group(1));
|
||||
long diff = (expiresDate.getTime() - System.currentTimeMillis()) / 1000;
|
||||
// 2 secs should be fine
|
||||
assertTrue(Math.abs(diff - maxAge) <= 2);
|
||||
|
@ -14,11 +14,11 @@
|
||||
*/
|
||||
package io.netty.handler.codec;
|
||||
|
||||
import io.netty.handler.codec.DefaultHeaders.HeaderDateFormat;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Converts to/from native types, general {@link Object}, and {@link CharSequence}s.
|
||||
@ -126,12 +126,12 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
|
||||
|
||||
@Override
|
||||
public long convertToTimeMillis(CharSequence value) {
|
||||
try {
|
||||
return HeaderDateFormat.get().parse(value.toString());
|
||||
} catch (ParseException e) {
|
||||
PlatformDependent.throwException(e);
|
||||
Date date = DateFormatter.parseHttpDate(value);
|
||||
if (date == null) {
|
||||
PlatformDependent.throwException(new ParseException("header can't be parsed into a Date: " + value, 0));
|
||||
return 0;
|
||||
}
|
||||
return date.getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -13,7 +13,7 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
package io.netty.handler.codec;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
@ -43,7 +43,7 @@ import java.util.TimeZone;
|
||||
* @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.
|
||||
*/
|
||||
public final class HttpHeaderDateFormatter {
|
||||
public final class DateFormatter {
|
||||
|
||||
private static final BitSet DELIMITERS = new BitSet();
|
||||
static {
|
||||
@ -68,11 +68,11 @@ public final class HttpHeaderDateFormatter {
|
||||
private static final String[] CALENDAR_MONTH_TO_SHORT_NAME =
|
||||
new String[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
|
||||
private static final FastThreadLocal<HttpHeaderDateFormatter> INSTANCES =
|
||||
new FastThreadLocal<HttpHeaderDateFormatter>() {
|
||||
private static final FastThreadLocal<DateFormatter> INSTANCES =
|
||||
new FastThreadLocal<DateFormatter>() {
|
||||
@Override
|
||||
protected HttpHeaderDateFormatter initialValue() {
|
||||
return new HttpHeaderDateFormatter();
|
||||
protected DateFormatter initialValue() {
|
||||
return new DateFormatter();
|
||||
}
|
||||
};
|
||||
|
||||
@ -81,8 +81,8 @@ public final class HttpHeaderDateFormatter {
|
||||
* @param txt text to parse
|
||||
* @return a {@link Date}, or null if text couldn't be parsed
|
||||
*/
|
||||
public static Date parse(CharSequence txt) {
|
||||
return parse(txt, 0, txt.length());
|
||||
public static Date parseHttpDate(CharSequence txt) {
|
||||
return parseHttpDate(txt, 0, txt.length());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,7 +92,7 @@ public final class HttpHeaderDateFormatter {
|
||||
* @param end the end index inside <code>txt</code>
|
||||
* @return a {@link Date}, or null if text couldn't be parsed
|
||||
*/
|
||||
public static Date parse(CharSequence txt, int start, int end) {
|
||||
public static Date parseHttpDate(CharSequence txt, int start, int end) {
|
||||
int length = end - start;
|
||||
if (length == 0) {
|
||||
return null;
|
||||
@ -124,8 +124,8 @@ public final class HttpHeaderDateFormatter {
|
||||
return formatter().append0(checkNotNull(date, "date"), checkNotNull(sb, "sb"));
|
||||
}
|
||||
|
||||
private static HttpHeaderDateFormatter formatter() {
|
||||
HttpHeaderDateFormatter formatter = INSTANCES.get();
|
||||
private static DateFormatter formatter() {
|
||||
DateFormatter formatter = INSTANCES.get();
|
||||
formatter.reset();
|
||||
return formatter;
|
||||
}
|
||||
@ -156,7 +156,7 @@ public final class HttpHeaderDateFormatter {
|
||||
private boolean yearFound;
|
||||
private int year;
|
||||
|
||||
private HttpHeaderDateFormatter() {
|
||||
private DateFormatter() {
|
||||
reset();
|
||||
}
|
||||
|
||||
@ -171,7 +171,7 @@ public final class HttpHeaderDateFormatter {
|
||||
month = -1;
|
||||
yearFound = false;
|
||||
year = -1;
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
cal.clear();
|
||||
sb.setLength(0);
|
||||
}
|
||||
|
@ -15,24 +15,17 @@
|
||||
package io.netty.handler.codec;
|
||||
|
||||
import io.netty.util.HashingStrategy;
|
||||
import io.netty.util.concurrent.FastThreadLocal;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static io.netty.util.HashingStrategy.JAVA_HASHER;
|
||||
import static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo;
|
||||
@ -960,77 +953,6 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This {@link DateFormat} decodes 3 formats of {@link Date}.
|
||||
*
|
||||
* <ul>
|
||||
* <li>Sun, 06 Nov 1994 08:49:37 GMT: standard specification, the only one with valid generation</li>
|
||||
* <li>Sun, 06 Nov 1994 08:49:37 GMT: obsolete specification</li>
|
||||
* <li>Sun Nov 6 08:49:37 1994: obsolete specification</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final class HeaderDateFormat {
|
||||
private static final FastThreadLocal<HeaderDateFormat> dateFormatThreadLocal =
|
||||
new FastThreadLocal<HeaderDateFormat>() {
|
||||
@Override
|
||||
protected HeaderDateFormat initialValue() {
|
||||
return new HeaderDateFormat();
|
||||
}
|
||||
};
|
||||
|
||||
static HeaderDateFormat get() {
|
||||
return dateFormatThreadLocal.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard date format:
|
||||
*
|
||||
* <pre>
|
||||
* Sun, 06 Nov 1994 08:49:37 GMT -> E, d MMM yyyy HH:mm:ss z
|
||||
* </pre>
|
||||
*/
|
||||
private final DateFormat dateFormat1 = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
|
||||
|
||||
/**
|
||||
* First obsolete format:
|
||||
*
|
||||
* <pre>
|
||||
* Sunday, 06-Nov-94 08:49:37 GMT -> E, d-MMM-y HH:mm:ss z
|
||||
* </pre>
|
||||
*/
|
||||
private final DateFormat dateFormat2 = new SimpleDateFormat("E, dd-MMM-yy HH:mm:ss z", Locale.ENGLISH);
|
||||
|
||||
/**
|
||||
* Second obsolete format
|
||||
*
|
||||
* <pre>
|
||||
* Sun Nov 6 08:49:37 1994 -> EEE, MMM d HH:mm:ss yyyy
|
||||
* </pre>
|
||||
*/
|
||||
private final DateFormat dateFormat3 = new SimpleDateFormat("E MMM d HH:mm:ss yyyy", Locale.ENGLISH);
|
||||
|
||||
private HeaderDateFormat() {
|
||||
TimeZone tz = TimeZone.getTimeZone("GMT");
|
||||
dateFormat1.setTimeZone(tz);
|
||||
dateFormat2.setTimeZone(tz);
|
||||
dateFormat3.setTimeZone(tz);
|
||||
}
|
||||
|
||||
long parse(String text) throws ParseException {
|
||||
Date date = dateFormat1.parse(text);
|
||||
if (date == null) {
|
||||
date = dateFormat2.parse(text);
|
||||
}
|
||||
if (date == null) {
|
||||
date = dateFormat3.parse(text);
|
||||
}
|
||||
if (date == null) {
|
||||
throw new ParseException(text, 0);
|
||||
}
|
||||
return date.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
private final class HeaderIterator implements Iterator<Map.Entry<K, V>> {
|
||||
private HeaderEntry<K, V> current = head;
|
||||
|
||||
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2016 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.codec;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static io.netty.handler.codec.DateFormatter.*;
|
||||
|
||||
public class DateFormatterTest {
|
||||
/**
|
||||
* This date is set at "06 Nov 1994 08:49:37 GMT", from
|
||||
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html">examples in RFC documentation</a>
|
||||
*/
|
||||
private static final long TIMESTAMP = 784111777000L;
|
||||
private static final Date DATE = new Date(TIMESTAMP);
|
||||
|
||||
@Test
|
||||
public void testParseWithSingleDigitDay() {
|
||||
assertEquals(DATE, parseHttpDate("Sun, 6 Nov 1994 08:49:37 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithDoubleDigitDay() {
|
||||
assertEquals(DATE, parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"));
|
||||
}
|
||||
|
||||
@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 testParseWithoutGMT() {
|
||||
assertEquals(DATE, parseHttpDate("Sun Nov 6 08:49:37 1994"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithFunkyTimezone() {
|
||||
assertEquals(DATE, parseHttpDate("Sun Nov 6 08:49:37 1994 -0000"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithSingleDigitHourMinutesAndSecond() {
|
||||
assertEquals(DATE, parseHttpDate("Sunday, 6-Nov-94 8:49:37 GMT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithSingleDigitTime() {
|
||||
assertEquals(DATE, parseHttpDate("Sunday, 6 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"));
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMidnight() {
|
||||
assertEquals(new Date(784080000000L), parseHttpDate("Sunday, 6 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"));
|
||||
//invalid value
|
||||
assertNull(parseHttpDate("Sun, 6 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"));
|
||||
//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"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormat() {
|
||||
assertEquals("Sun, 6 Nov 1994 08:49:37 GMT", format(DATE));
|
||||
}
|
||||
}
|
@ -13,10 +13,9 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.microbench.http;
|
||||
package io.netty.handler.codec;
|
||||
|
||||
import io.netty.handler.codec.http.HttpHeaderDateFormat;
|
||||
import io.netty.handler.codec.http.HttpHeaderDateFormatter;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
|
||||
@ -24,14 +23,14 @@ import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
public class HttpHeaderDateFormatterBenchmark {
|
||||
public class DateFormatterBenchmark {
|
||||
|
||||
private static final String DATE_STRING = "Sun, 27 Nov 2016 19:18:46 GMT";
|
||||
private static final Date DATE = new Date(784111777000L);
|
||||
|
||||
@Benchmark
|
||||
public Date parseHttpHeaderDateFormatter() {
|
||||
return HttpHeaderDateFormatter.parse(DATE_STRING);
|
||||
return DateFormatter.parseHttpDate(DATE_STRING);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@ -41,7 +40,7 @@ public class HttpHeaderDateFormatterBenchmark {
|
||||
|
||||
@Benchmark
|
||||
public String formatHttpHeaderDateFormatter() {
|
||||
return HttpHeaderDateFormatter.format(DATE);
|
||||
return DateFormatter.format(DATE);
|
||||
}
|
||||
|
||||
@Benchmark
|
Loading…
Reference in New Issue
Block a user