All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.undertow.util.DateUtils Maven / Gradle / Ivy

There is a newer version: 62
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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.undertow.util;

import io.undertow.UndertowOptions;
import io.undertow.server.HttpServerExchange;

import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Utility for parsing and generating dates
 *
 * @author Stuart Douglas
 */
public class DateUtils {

    private static final Locale LOCALE_US = Locale.US;

    private static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");

    private static final String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";

    private static final AtomicReference cachedDateString = new AtomicReference<>();

    /**
     * Thread local cache of this date format. This is technically a small memory leak, however
     * in practice it is fine, as it will only be used by server threads.
     * 

* This is the most common date format, which is why we cache it. */ private static final ThreadLocal RFC1123_PATTERN_FORMAT = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat df = new SimpleDateFormat(RFC1123_PATTERN, LOCALE_US); return df; } }; /** * Invalidates the current date */ private static final Runnable INVALIDATE_TASK = new Runnable() { @Override public void run() { cachedDateString.set(null); } }; private static final String RFC1036_PATTERN = "EEEEEEEEE, dd-MMM-yy HH:mm:ss z"; private static final String ASCITIME_PATTERN = "EEE MMM d HH:mm:ss yyyyy"; private static final String OLD_COOKIE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z"; private static final String COMMON_LOG_PATTERN = "[dd/MMM/yyyy:HH:mm:ss Z]"; private static final ThreadLocal COMMON_LOG_PATTERN_FORMAT = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat df = new SimpleDateFormat(COMMON_LOG_PATTERN, LOCALE_US); return df; } }; private static final ThreadLocal OLD_COOKIE_FORMAT = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat df = new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US); df.setTimeZone(GMT_ZONE); return df; } }; /** * Converts a date to a format suitable for use in a HTTP request * * @param date The date * @return The RFC-1123 formatted date */ public static String toDateString(final Date date) { SimpleDateFormat df = RFC1123_PATTERN_FORMAT.get(); //we always need to set the time zone //because date format is stupid, and calling parse() can mutate the timezone //see UNDERTOW-458 df.setTimeZone(GMT_ZONE); return df.format(date); } public static String toOldCookieDateString(final Date date) { return OLD_COOKIE_FORMAT.get().format(date); } public static String toCommonLogFormat(final Date date) { return COMMON_LOG_PATTERN_FORMAT.get().format(date); } /** * Attempts to pass a HTTP date. * * @param date The date to parse * @return The parsed date, or null if parsing failed */ public static Date parseDate(final String date) { /* IE9 sends a superflous lenght parameter after date in the If-Modified-Since header, which needs to be stripped before parsing. */ final int semicolonIndex = date.indexOf(';'); final String trimmedDate = semicolonIndex >= 0 ? date.substring(0, semicolonIndex) : date; ParsePosition pp = new ParsePosition(0); SimpleDateFormat dateFormat = RFC1123_PATTERN_FORMAT.get(); dateFormat.setTimeZone(GMT_ZONE); Date val = dateFormat.parse(trimmedDate, pp); if (val != null && pp.getIndex() == trimmedDate.length()) { return val; } pp = new ParsePosition(0); dateFormat = new SimpleDateFormat(RFC1036_PATTERN, LOCALE_US); dateFormat.setTimeZone(GMT_ZONE); val = dateFormat.parse(trimmedDate, pp); if (val != null && pp.getIndex() == trimmedDate.length()) { return val; } pp = new ParsePosition(0); dateFormat = new SimpleDateFormat(ASCITIME_PATTERN, LOCALE_US); dateFormat.setTimeZone(GMT_ZONE); val = dateFormat.parse(trimmedDate, pp); if (val != null && pp.getIndex() == trimmedDate.length()) { return val; } pp = new ParsePosition(0); dateFormat = new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US); dateFormat.setTimeZone(GMT_ZONE); val = dateFormat.parse(trimmedDate, pp); if (val != null && pp.getIndex() == trimmedDate.length()) { return val; } return null; } /** * Handles the if-modified-since header. returns true if the request should proceed, false otherwise * * @param exchange the exchange * @param lastModified The last modified date * @return */ public static boolean handleIfModifiedSince(final HttpServerExchange exchange, final Date lastModified) { return handleIfModifiedSince(exchange.getRequestHeaders().getFirst(Headers.IF_MODIFIED_SINCE), lastModified); } /** * Handles the if-modified-since header. returns true if the request should proceed, false otherwise * * @param modifiedSince the modified since date * @param lastModified The last modified date * @return */ public static boolean handleIfModifiedSince(final String modifiedSince, final Date lastModified) { if (lastModified == null) { return true; } if (modifiedSince == null) { return true; } Date modDate = parseDate(modifiedSince); if (modDate == null) { return true; } return lastModified.getTime() > (modDate.getTime() + 999); //UNDERTOW-341 +999 as there is no millisecond part in the if-modified-since } /** * Handles the if-unmodified-since header. returns true if the request should proceed, false otherwise * * @param exchange the exchange * @param lastModified The last modified date * @return */ public static boolean handleIfUnmodifiedSince(final HttpServerExchange exchange, final Date lastModified) { return handleIfUnmodifiedSince(exchange.getRequestHeaders().getFirst(Headers.IF_UNMODIFIED_SINCE), lastModified); } /** * Handles the if-unmodified-since header. returns true if the request should proceed, false otherwise * * @param modifiedSince the if unmodified since date * @param lastModified The last modified date * @return */ public static boolean handleIfUnmodifiedSince(final String modifiedSince, final Date lastModified) { if (lastModified == null) { return true; } if (modifiedSince == null) { return true; } Date modDate = parseDate(modifiedSince); if (modDate == null) { return true; } return lastModified.getTime() < (modDate.getTime() + 999); //UNDERTOW-341 +999 as there is no millisecond part in the if-unmodified-since } public static void addDateHeaderIfRequired(HttpServerExchange exchange) { HeaderMap responseHeaders = exchange.getResponseHeaders(); if (exchange.getConnection().getUndertowOptions().get(UndertowOptions.ALWAYS_SET_DATE, true) && !responseHeaders.contains(Headers.DATE)) { String dateString = getCurrentDateTime(exchange); responseHeaders.put(Headers.DATE, dateString); } } public static String getCurrentDateTime(HttpServerExchange exchange) { String dateString = cachedDateString.get(); if (dateString == null) { //set the time and register a timer to invalidate it //note that this is racey, it does not matter if multiple threads do this //the perf cost of synchronizing would be more than the perf cost of multiple threads running it long realTime = System.currentTimeMillis(); long mod = realTime % 1000; long toGo = 1000 - mod; dateString = DateUtils.toDateString(new Date(realTime)); if (cachedDateString.compareAndSet(null, dateString)) { WorkerUtils.executeAfter(exchange.getIoThread(), INVALIDATE_TASK, toGo, TimeUnit.MILLISECONDS); } } return dateString; } private DateUtils() { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy