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

org.swisspush.gateleen.queue.expiry.ExpiryCheckHandler Maven / Gradle / Ivy

There is a newer version: 2.1.12
Show newest version
package org.swisspush.gateleen.queue.expiry;

import io.vertx.core.MultiMap;

import io.vertx.core.http.HttpServerRequest;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Instant;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.swisspush.gateleen.core.http.HttpRequest;

import java.util.Optional;

/**
 * The ExpiryCheckHandler allows you to check the expiry
 * of a request.
 * It also offers methods for setting default
 * values for expiration and timestamps to the request header.
 *
 * @author https://github.com/ljucam [Mario Ljuca]
 */
public final class ExpiryCheckHandler {
    public static final String SERVER_TIMESTAMP_HEADER = "X-Server-Timestamp";
    public static final String EXPIRE_AFTER_HEADER = "X-Expire-After";
    public static final String QUEUE_EXPIRE_AFTER_HEADER = "x-queue-expire-after";

    private static Logger log = LoggerFactory.getLogger(ExpiryCheckHandler.class);

    private static DateTimeFormatter dfISO8601 = ISODateTimeFormat.dateTime().withZone(DateTimeZone.forID("Europe/Zurich"));
    private static DateTimeFormatter dfISO8601Parser = ISODateTimeFormat.dateTimeParser().withZone(DateTimeZone.forID("Europe/Zurich"));

    private ExpiryCheckHandler() {
        // Prevent instantiation
    }

    /**
     * Checks if a "X-Server-Timestamp" header is set or not.
     * If no valid value is found, the current timestamp is set.
     * 
     * @param request request
     */
    public static void updateServerTimestampHeader(HttpRequest request) {
        if (request.getHeaders() == null) {
            request.setHeaders(MultiMap.caseInsensitiveMultiMap());
        }

        updateServerTimestampHeader(request.getHeaders());
    }

    /**
     * Checks if a "X-Server-Timestamp" header is set or not.
     * If no valid value is found, the current timestamp is set.
     * 
     * @param headers headers
     */
    public static void updateServerTimestampHeader(MultiMap headers) {
        String serverTimestamp = headers.get(SERVER_TIMESTAMP_HEADER);

        if (serverTimestamp == null) {
            String nowAsISO = dfISO8601.print(Instant.now());

            log.debug("Setting {} value to {} since header {} is not defined", SERVER_TIMESTAMP_HEADER, nowAsISO, SERVER_TIMESTAMP_HEADER);

            headers.set(SERVER_TIMESTAMP_HEADER, nowAsISO);
        } else {
            headers.remove(SERVER_TIMESTAMP_HEADER);
            headers.set(SERVER_TIMESTAMP_HEADER, printDateTime(DateTime.now()));
        }
    }

    /**
     * Extracts the value of the "X-Expire-After" header.
     * If the value can't be extracted (not found, invalid, and so on),
     * null is returned.
     * 
     * @param headers headers
     * @return expire-after time in seconds or null if nothing is found
     */
    public static Integer getExpireAfter(MultiMap headers) {
        try {
            Integer ans = getExpireValue(headers.get(EXPIRE_AFTER_HEADER));
            // Convert -1 to null
            return (ans != null && ans == -1) ? null : ans;
        } catch (NumberFormatException e) {
            // Treat corrupt header same as it were not set at all.
            // This is to keep backward compatibility to previous version of getExpireValue(MultiMap,String).
            return null;
        }
    }

    /**
     * Delegates to {@link #getExpireValue(String)}
     *
     * This is extended version of {@link #getExpireAfter(MultiMap)} but also
     * returning -1 for infinite.
     *
     * @param headers
     *      Headers to fetch value from.
     * @return
     *      

The parsed expire-after value.

*

Returns empty in case {@link #getExpireValue(String)} returned null.

*/ public static Optional getExpireAfterConcerningCaseOfCorruptHeaderAndInfinite(MultiMap headers) { try { final Integer expireValue = getExpireValue(headers.get(EXPIRE_AFTER_HEADER)); return Optional.ofNullable(expireValue); } catch (NumberFormatException e) { // Throwing exceptions is not allowed by design. // See: "https://github.com/swisspush/gateleen/pull/209#discussion_r177712751" log.warn("Conceal exception (because we're not allowed to throw by design): {}: {}", e.getClass().getSimpleName(), e.getMessage()); return Optional.empty(); } } /** * Extracts the value of the "x-queue-expire-after" header. * If the value can't be extracted (not found, invalid, and so on), * null is returned. * * @param headers headers * @return queue-expire-after time in seconds or null if nothing is found */ public static Integer getQueueExpireAfter(MultiMap headers) { try { final Integer ans = getExpireValue(headers.get(QUEUE_EXPIRE_AFTER_HEADER)); // Convert -1 to null return (ans != null && ans == -1) ? null : ans; } catch (NumberFormatException e) { // Treat corrupt header same as it were not set at all. // This is to keep backward compatibility to previous version of getExpireValue(MultiMap,String). return null; } } /** * @param expirationTimeout * Value as provided in the X-Expire-After header for example. * @return * Expire-after time in seconds or null if header not present. This * possibly returns value -1 indicating "never expires". * @throws NumberFormatException * In case the value contained in the header is not a valid * 'X-Expire-After' value. A negative number or a non-numeric value * for example. */ private static Integer getExpireValue( final String expirationTimeout ) throws NumberFormatException { final Integer ans; if (expirationTimeout == null) { // If we get null we'll answer with null. ans = null; } else { // Value exists. Try parsing it (this will throw specified exception on failure). ans = Integer.parseInt(expirationTimeout); // Further, treat numbers below -1 as invalid. -1 itself is accepted indicating // "infinite". if (ans < -1) { throw new NumberFormatException("Values less than -1 aren't valid expireAfter values."); } } return ans; } /** * Checks the expiration based on the given headers and a timstamp in milliseconds. * * @param headers headers * @return true if the request has expired, false otherwise */ public static boolean isExpired(MultiMap headers, Long timestamp) { if (headers != null && timestamp != null) { Integer queueExpireAfter = getQueueExpireAfter(headers); Integer expireAfter = getExpireAfter(headers); // override expireAfter if x-queue-expire-after header is set if ( queueExpireAfter != null ) { expireAfter = queueExpireAfter; } if (expireAfter != null) { long expiredSince = System.currentTimeMillis() - ( timestamp + expireAfter * 1000L ); if(expiredSince > 0) { log.debug(" > isExpired - Request expired since {} milliseconds.", expiredSince); return true; } else { log.debug(" > isExpired - Request not expired (would expire in {} milliseconds).", -expiredSince); return false; } } } return false; } /** * Returns the expiration time, based on the current time (now) * in addition with the expireAfter value. * * @param expireAfter - in seconds * @return expiration time */ public static DateTime getExpirationTime(int expireAfter) { return getExpirationTime(DateTime.now(), expireAfter); } /** * @return * Expiration time based on passed expireAfter or empty in case of an infinite * expiration. */ public static Optional getExpirationTimeAsString(int expireAfter) { final String expirationTime; if (expireAfter == -1) { expirationTime = null; } else { expirationTime = printDateTime(getExpirationTime(expireAfter)); } return Optional.ofNullable(expirationTime); } /** * Parses the given string to a DateTime object. * * @param datetime datetime as string * @return DateTime */ public static DateTime parseDateTime(String datetime) { return dfISO8601Parser.parseDateTime(datetime); } /** * Prints the given datetime as a string. * * @param datetime datetime * @return String */ public static String printDateTime(DateTime datetime) { return dfISO8601.print(datetime); } /** * Returns the expiration time. * * @param timestamp timestamp * @param expireAfter expireAfter * @return expiration time. */ private static DateTime getExpirationTime(DateTime timestamp, int expireAfter) { DateTime expirationTime = timestamp.plusSeconds(expireAfter); log.debug("getExpirationTime: {}", expirationTime); return expirationTime; } /** * Sets an "X-Expire-After" header. * If such a header already exist, it's overridden by the new value. * * @param request request * @param expireAfter expireAfter */ public static void setExpireAfter(HttpServerRequest request, int expireAfter) { setFieldValue(request.headers(), EXPIRE_AFTER_HEADER, expireAfter); } /** * Sets an "x-queue-expire-after" header. * If such a header already exist, it's overridden by the new value. * * @param headers headers * @param queueExpireAfter queueExpireAfter */ public static void setQueueExpireAfter(MultiMap headers, int queueExpireAfter) { setFieldValue(headers, QUEUE_EXPIRE_AFTER_HEADER, queueExpireAfter); } /** * Sets a header with the given field name and the given expire value. * If such a header already exist, it's overridden by the new value. * @param headers headers * @param field the name of the header field * @param expireValue expireValue */ private static void setFieldValue( MultiMap headers, String field, int expireValue ) { if (headers.get(field) != null) { headers.remove(field); } headers.set(field, String.valueOf(expireValue)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy