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

com.google.apphosting.runtime.jetty.CacheControlHeader Maven / Gradle / Ivy

There is a newer version: 2.0.31
Show newest version
/*
 * Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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 com.google.apphosting.runtime.jetty;

import com.google.common.base.Ascii;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.GoogleLogger;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.regex.Pattern;

/**
 * Wrapper for cache-control header value strings. Also includes logic to parse expiration time
 * strings provided in application config files.
 */
public final class CacheControlHeader {
  private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
  private static final String DEFAULT_BASE_VALUE = "public, max-age=";
  // Default max age is 10 minutes, per GAE documentation
  private static final String DEFAULT_MAX_AGE = "600";

  private static final ImmutableMap EXPIRATION_TIME_UNITS =
      ImmutableMap.of(
          "s", ChronoUnit.SECONDS,
          "m", ChronoUnit.MINUTES,
          "h", ChronoUnit.HOURS,
          "d", ChronoUnit.DAYS);

  private final String value;

  private CacheControlHeader(String value) {
    this.value = value;
  }

  public static CacheControlHeader getDefaultInstance() {
    return new CacheControlHeader(DEFAULT_BASE_VALUE + DEFAULT_MAX_AGE);
  }

  /**
   * Parse formatted expiration time (e.g., "1d 2h 3m") and convert to seconds. If there is no
   * expiration time set, avoid setting max age parameter.
   */
  public static CacheControlHeader fromExpirationTime(String expirationTime) {
    String maxAge = DEFAULT_MAX_AGE;

    if (expirationTime != null) {
      if (expirationTimeIsValid(expirationTime)) {
        Duration totalTime = Duration.ZERO;
        for (String timeString : Splitter.on(" ").split(expirationTime)) {
          String timeUnitShort = Ascii.toLowerCase(timeString.substring(timeString.length() - 1));
          TemporalUnit timeUnit = EXPIRATION_TIME_UNITS.get(timeUnitShort);
          String timeValue = timeString.substring(0, timeString.length() - 1);
          totalTime = totalTime.plus(Long.parseLong(timeValue), timeUnit);
        }
        maxAge = String.valueOf(totalTime.getSeconds());
      } else {
          logger.atWarning().log(
            "Failed to parse expiration time: \"%s\". Using default value instead.",
            expirationTime
        );
      }
    }

    String output = DEFAULT_BASE_VALUE + maxAge;
    return new CacheControlHeader(output);
  }

  public String getValue() {
    return value;
  }

  /**
   * Validate that expiration time string is a space-delineated collection of expiration tokens (a
   * number followed by a valid unit character).
   */
  private static boolean expirationTimeIsValid(String expirationTime) {
    String expirationTokenPattern = "\\d+[smhd]";
    Pattern pattern =
        Pattern.compile("^" + expirationTokenPattern + "(\\s" + expirationTokenPattern + ")*$");
    return pattern.matcher(expirationTime).matches();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy