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

io.gatling.http.cache.ExpiresSupport.scala Maven / Gradle / Ivy

There is a newer version: 3.13.1
Show newest version
/*
 * Copyright 2011-2024 GatlingCorp (https://gatling.io)
 *
 * 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.gatling.http.cache

import io.gatling.commons.util.Clock

import io.netty.handler.codec.DateFormatter
import io.netty.handler.codec.http.{ HttpHeaderNames, HttpHeaderValues, HttpHeaders }

object ExpiresSupport {
  private[cache] def extractLongValue(s: String, start: Int): Long = {
    require(start >= 0 && start < s.length, s"Start=$start is not an acceptable starting index for the string=$s")

    var value = 0L
    var k = start
    var c = ' '
    while (
      k < s.length && {
        c = s.charAt(k)
        c.isDigit
      }
    ) {
      value = value * 10L + c.getNumericValue
      k += 1
    }
    value
  }
}

private[cache] trait ExpiresSupport {
  def clock: Clock

  private val MaxAgePrefix = "max-age="
  private val MaxAgeZero = MaxAgePrefix + "0"

  def extractMaxAgeValue(s: String): Option[Long] = {
    val index = s.indexOf(MaxAgePrefix)
    val start = MaxAgePrefix.length + index
    if (index >= 0 && start <= s.length) {
      s.charAt(start) match {
        case '-'            => Some(-1)
        case c if c.isDigit => Some(ExpiresSupport.extractLongValue(s, start))
        case _              => None
      }
    } else {
      None
    }
  }

  def extractExpiresValue(timestring: String): Option[Long] = {
    def removeQuote(s: String) =
      if (s.isEmpty) {
        s
      } else {
        var start = 0
        var end = s.length

        if (s.charAt(0) == '"')
          start += 1

        if (s.charAt(s.length() - 1) == '"')
          end -= 1

        s.substring(start, end)
      }

    // FIXME use offset instead of 2 substrings
    val trimmedTimeString = removeQuote(timestring.trim)

    Option(DateFormatter.parseHttpDate(trimmedTimeString)).map(_.getTime)
  }

  private def cacheControlNoCache(cacheControlHeader: String): Boolean =
    cacheControlHeader.contains(HttpHeaderValues.NO_CACHE.toString) || cacheControlHeader.contains(HttpHeaderValues.NO_STORE.toString) || cacheControlHeader
      .contains(MaxAgeZero)

  private def maxAgeAsExpiresValue(cacheControlHeader: String): Option[Long] =
    extractMaxAgeValue(cacheControlHeader).flatMap { maxAge =>
      if (maxAge < 0) {
        None
      } else {
        val updatedMaxAge = maxAge * 1000 + clock.nowMillis
        if (updatedMaxAge < 0) {
          None
        } else {
          Some(maxAge * 1000 + clock.nowMillis)
        }
      }
    }

  private def expiresValue(responseHeaders: HttpHeaders): Option[Long] = {
    val expiresHeader = responseHeaders.get(HttpHeaderNames.EXPIRES)
    if (expiresHeader != null) {
      extractExpiresValue(expiresHeader).filter(_ > clock.nowMillis)
    } else {
      None
    }
  }

  def getResponseExpires(responseHeaders: HttpHeaders): Option[Long] = {
    val pragmaHeader = responseHeaders.get(HttpHeaderNames.PRAGMA)
    if (pragmaHeader != null && pragmaHeader.contains(HttpHeaderValues.NO_CACHE.toString)) {
      None
    } else {
      val cacheControlHeader = responseHeaders.get(HttpHeaderNames.CACHE_CONTROL)
      if (cacheControlHeader != null && cacheControlNoCache(cacheControlHeader)) {
        None
      } else {
        // If a response includes both an Expires header and a max-age directive, the max-age directive overrides the Expires header,
        // even if the Expires header is more restrictive. (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3)
        if (cacheControlHeader != null) {
          maxAgeAsExpiresValue(cacheControlHeader)
        } else {
          expiresValue(responseHeaders)
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy