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

com.twelvemonkeys.net.HTTPUtil Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013, Harald Kuhr
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.twelvemonkeys.net;

import com.twelvemonkeys.lang.DateUtil;
import com.twelvemonkeys.lang.StringUtil;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * HTTPUtil
 *
 * @author Harald Kuhr
 * @author last modified by $Author: haraldk$
 * @version $Id: HTTPUtil.java,v 1.0 08.09.13 13:57 haraldk Exp$
 */
public class HTTPUtil {
    /**
     * RFC 1123 date format, as recommended by RFC 2616 (HTTP/1.1), sec 3.3
     * NOTE: All date formats are private, to ensure synchronized access.
     */
    private static final SimpleDateFormat HTTP_RFC1123_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
    static {
        HTTP_RFC1123_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
    }

    /**
     * RFC 850 date format, (almost) as described in RFC 2616 (HTTP/1.1), sec 3.3
     * USE FOR PARSING ONLY (format is not 100% correct, to be more robust).
     */
    private static final SimpleDateFormat HTTP_RFC850_FORMAT = new SimpleDateFormat("EEE, dd-MMM-yy HH:mm:ss z", Locale.US);
    /**
     * ANSI C asctime() date format, (almost) as described in RFC 2616 (HTTP/1.1), sec 3.3.
     * USE FOR PARSING ONLY (format is not 100% correct, to be more robust).
     */
    private static final SimpleDateFormat HTTP_ASCTIME_FORMAT = new SimpleDateFormat("EEE MMM d HH:mm:ss yy", Locale.US);

    private static long sNext50YearWindowChange = DateUtil.currentTimeDay();
    static {
        HTTP_RFC850_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
        HTTP_ASCTIME_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));

        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.3:
        // - HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
        //   which appears to be more than 50 years in the future is in fact
        //   in the past (this helps solve the "year 2000" problem).
        update50YearWindowIfNeeded();
    }

    private static void update50YearWindowIfNeeded() {
        // Avoid class synchronization
        long next = sNext50YearWindowChange;

        if (next < System.currentTimeMillis()) {
            // Next check in one day
            next += DateUtil.DAY;
            sNext50YearWindowChange = next;

            Date startDate = new Date(next - (50l * DateUtil.CALENDAR_YEAR));
            //System.out.println("next test: " + new Date(next) + ", 50 year start: " + startDate);
            synchronized (HTTP_RFC850_FORMAT) {
                HTTP_RFC850_FORMAT.set2DigitYearStart(startDate);
            }
            synchronized (HTTP_ASCTIME_FORMAT) {
                HTTP_ASCTIME_FORMAT.set2DigitYearStart(startDate);
            }
        }
    }

    private HTTPUtil() {}

    /**
     * Formats the time to a HTTP date, using the RFC 1123 format, as described
     * in RFC 2616 (HTTP/1.1), sec. 3.3.
     *
     * @param pTime the time
     * @return a {@code String} representation of the time
     */
    public static String formatHTTPDate(long pTime) {
        return formatHTTPDate(new Date(pTime));
    }

    /**
     * Formats the time to a HTTP date, using the RFC 1123 format, as described
     * in RFC 2616 (HTTP/1.1), sec. 3.3.
     *
     * @param pTime the time
     * @return a {@code String} representation of the time
     */
    public static String formatHTTPDate(Date pTime) {
        synchronized (HTTP_RFC1123_FORMAT) {
            return HTTP_RFC1123_FORMAT.format(pTime);
        }
    }

    /**
     * Parses a HTTP date string into a {@code long} representing milliseconds
     * since January 1, 1970 GMT.
     * 

* Use this method with headers that contain dates, such as * {@code If-Modified-Since} or {@code Last-Modified}. *

* The date string may be in either RFC 1123, RFC 850 or ANSI C asctime() * format, as described in * RFC 2616 (HTTP/1.1), sec. 3.3 * * @param pDate the date to parse * * @return a {@code long} value representing the date, expressed as the * number of milliseconds since January 1, 1970 GMT, * @throws NumberFormatException if the date parameter is not parseable. * @throws IllegalArgumentException if the date paramter is {@code null} */ public static long parseHTTPDate(String pDate) throws NumberFormatException { return parseHTTPDateImpl(pDate).getTime(); } /** * ParseHTTPDate implementation * * @param pDate the date string to parse * * @return a {@code Date} * @throws NumberFormatException if the date parameter is not parseable. * @throws IllegalArgumentException if the date paramter is {@code null} */ private static Date parseHTTPDateImpl(final String pDate) throws NumberFormatException { if (pDate == null) { throw new IllegalArgumentException("date == null"); } if (StringUtil.isEmpty(pDate)) { throw new NumberFormatException("Invalid HTTP date: \"" + pDate + "\""); } DateFormat format; if (pDate.indexOf('-') >= 0) { format = HTTP_RFC850_FORMAT; update50YearWindowIfNeeded(); } else if (pDate.indexOf(',') < 0) { format = HTTP_ASCTIME_FORMAT; update50YearWindowIfNeeded(); } else { format = HTTP_RFC1123_FORMAT; // NOTE: RFC1123 always uses 4-digit years } Date date; try { //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (format) { date = format.parse(pDate); } } catch (ParseException e) { NumberFormatException nfe = new NumberFormatException("Invalid HTTP date: \"" + pDate + "\""); nfe.initCause(e); throw nfe; } if (date == null) { throw new NumberFormatException("Invalid HTTP date: \"" + pDate + "\""); } return date; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy