1
18
19 package io.undertow.util;
20
21 import io.undertow.UndertowOptions;
22 import io.undertow.server.HttpServerExchange;
23
24 import java.text.ParsePosition;
25 import java.text.SimpleDateFormat;
26 import java.util.Date;
27 import java.util.Locale;
28 import java.util.TimeZone;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.atomic.AtomicReference;
31
32
37 public class DateUtils {
38
39 private static final Locale LOCALE_US = Locale.US;
40
41 private static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
42
43 private static final String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";
44
45 private static final AtomicReference<String> cachedDateString = new AtomicReference<>();
46
47
53 private static final ThreadLocal<SimpleDateFormat> RFC1123_PATTERN_FORMAT = new ThreadLocal<SimpleDateFormat>() {
54 @Override
55 protected SimpleDateFormat initialValue() {
56 SimpleDateFormat df = new SimpleDateFormat(RFC1123_PATTERN, LOCALE_US);
57 return df;
58 }
59 };
60
61
64 private static final Runnable INVALIDATE_TASK = new Runnable() {
65 @Override
66 public void run() {
67 cachedDateString.set(null);
68 }
69 };
70
71 private static final String RFC1036_PATTERN = "EEEEEEEEE, dd-MMM-yy HH:mm:ss z";
72
73 private static final String ASCITIME_PATTERN = "EEE MMM d HH:mm:ss yyyyy";
74
75 private static final String OLD_COOKIE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z";
76
77 private static final String COMMON_LOG_PATTERN = "[dd/MMM/yyyy:HH:mm:ss Z]";
78
79 private static final ThreadLocal<SimpleDateFormat> COMMON_LOG_PATTERN_FORMAT = new ThreadLocal<SimpleDateFormat>() {
80 @Override
81 protected SimpleDateFormat initialValue() {
82 SimpleDateFormat df = new SimpleDateFormat(COMMON_LOG_PATTERN, LOCALE_US);
83 return df;
84 }
85 };
86
87 private static final ThreadLocal<SimpleDateFormat> OLD_COOKIE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
88 @Override
89 protected SimpleDateFormat initialValue() {
90 SimpleDateFormat df = new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US);
91 df.setTimeZone(GMT_ZONE);
92 return df;
93 }
94 };
95
96
102 public static String toDateString(final Date date) {
103 SimpleDateFormat df = RFC1123_PATTERN_FORMAT.get();
104
105
106
107 df.setTimeZone(GMT_ZONE);
108 return df.format(date);
109 }
110
111
112 public static String toOldCookieDateString(final Date date) {
113 return OLD_COOKIE_FORMAT.get().format(date);
114 }
115
116 public static String toCommonLogFormat(final Date date) {
117 return COMMON_LOG_PATTERN_FORMAT.get().format(date);
118 }
119
120
126 public static Date parseDate(final String date) {
127
128
134
135 final int semicolonIndex = date.indexOf(';');
136 final String trimmedDate = semicolonIndex >= 0 ? date.substring(0, semicolonIndex) : date;
137
138 ParsePosition pp = new ParsePosition(0);
139 SimpleDateFormat dateFormat = RFC1123_PATTERN_FORMAT.get();
140 dateFormat.setTimeZone(GMT_ZONE);
141 Date val = dateFormat.parse(trimmedDate, pp);
142 if (val != null && pp.getIndex() == trimmedDate.length()) {
143 return val;
144 }
145
146 pp = new ParsePosition(0);
147 dateFormat = new SimpleDateFormat(RFC1036_PATTERN, LOCALE_US);
148 dateFormat.setTimeZone(GMT_ZONE);
149 val = dateFormat.parse(trimmedDate, pp);
150 if (val != null && pp.getIndex() == trimmedDate.length()) {
151 return val;
152 }
153
154 pp = new ParsePosition(0);
155 dateFormat = new SimpleDateFormat(ASCITIME_PATTERN, LOCALE_US);
156 dateFormat.setTimeZone(GMT_ZONE);
157 val = dateFormat.parse(trimmedDate, pp);
158 if (val != null && pp.getIndex() == trimmedDate.length()) {
159 return val;
160 }
161
162 pp = new ParsePosition(0);
163 dateFormat = new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US);
164 dateFormat.setTimeZone(GMT_ZONE);
165 val = dateFormat.parse(trimmedDate, pp);
166 if (val != null && pp.getIndex() == trimmedDate.length()) {
167 return val;
168 }
169
170 return null;
171 }
172
173
180 public static boolean handleIfModifiedSince(final HttpServerExchange exchange, final Date lastModified) {
181 return handleIfModifiedSince(exchange.getRequestHeaders().getFirst(Headers.IF_MODIFIED_SINCE), lastModified);
182 }
183
184
191 public static boolean handleIfModifiedSince(final String modifiedSince, final Date lastModified) {
192 if (lastModified == null) {
193 return true;
194 }
195 if (modifiedSince == null) {
196 return true;
197 }
198 Date modDate = parseDate(modifiedSince);
199 if (modDate == null) {
200 return true;
201 }
202 return lastModified.getTime() > (modDate.getTime() + 999);
203 }
204
205
212 public static boolean handleIfUnmodifiedSince(final HttpServerExchange exchange, final Date lastModified) {
213 return handleIfUnmodifiedSince(exchange.getRequestHeaders().getFirst(Headers.IF_UNMODIFIED_SINCE), lastModified);
214 }
215
216
223 public static boolean handleIfUnmodifiedSince(final String modifiedSince, final Date lastModified) {
224 if (lastModified == null) {
225 return true;
226 }
227 if (modifiedSince == null) {
228 return true;
229 }
230 Date modDate = parseDate(modifiedSince);
231 if (modDate == null) {
232 return true;
233 }
234 return lastModified.getTime() < (modDate.getTime() + 999);
235 }
236
237 public static void addDateHeaderIfRequired(HttpServerExchange exchange) {
238 HeaderMap responseHeaders = exchange.getResponseHeaders();
239 if (exchange.getConnection().getUndertowOptions().get(UndertowOptions.ALWAYS_SET_DATE, true) && !responseHeaders.contains(Headers.DATE)) {
240 String dateString = getCurrentDateTime(exchange);
241 responseHeaders.put(Headers.DATE, dateString);
242 }
243 }
244
245 public static String getCurrentDateTime(HttpServerExchange exchange) {
246 String dateString = cachedDateString.get();
247 if (dateString == null) {
248
249
250
251 long realTime = System.currentTimeMillis();
252 long mod = realTime % 1000;
253 long toGo = 1000 - mod;
254 dateString = DateUtils.toDateString(new Date(realTime));
255 if (cachedDateString.compareAndSet(null, dateString)) {
256 WorkerUtils.executeAfter(exchange.getIoThread(), INVALIDATE_TASK, toGo, TimeUnit.MILLISECONDS);
257 }
258 }
259 return dateString;
260 }
261
262 private DateUtils() {
263
264 }
265
266 }
267