src/main/scala/s3/website/CloudFront.scala in s3_website-2.8.3 vs src/main/scala/s3/website/CloudFront.scala in s3_website-2.8.4
- old
+ new
@@ -6,11 +6,11 @@
import com.amazonaws.services.cloudfront.model.{TooManyInvalidationsInProgressException, Paths, InvalidationBatch, CreateInvalidationRequest}
import scala.collection.JavaConversions._
import scala.concurrent.duration._
import s3.website.S3.{SuccessfulDelete, PushSuccessReport, SuccessfulUpload}
import com.amazonaws.auth.BasicAWSCredentials
-import java.net.URI
+import java.net.{URLEncoder, URI}
import scala.concurrent.{ExecutionContextExecutor, Future}
import s3.website.model.Config.awsCredentials
object CloudFront {
def invalidate(invalidationBatch: InvalidationBatch, distributionId: String, attempt: Attempt = 1)
@@ -66,33 +66,38 @@
}
def awsCloudFrontClient(config: Config) = new AmazonCloudFrontClient(awsCredentials(config))
def toInvalidationBatches(pushSuccessReports: Seq[PushSuccessReport])(implicit config: Config): Seq[InvalidationBatch] = {
+ def defaultPath(paths: Seq[String]): Option[String] = {
+ // This is how we support the Default Root Object @ CloudFront (http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DefaultRootObject.html)
+ // We could do this more accurately by fetching the distribution config (http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/GetConfig.html)
+ // and reading the Default Root Object from there.
+ val containsPotentialDefaultRootObject = paths
+ .exists(
+ _
+ .replaceFirst("^/", "") // S3 keys do not begin with a slash
+ .contains("/") == false // See if the S3 key is a top-level key (i.e., it is not within a directory)
+ )
+ if (containsPotentialDefaultRootObject) Some("/") else None
+ }
+ val indexPath = config.cloudfront_invalidate_root collect {
+ case true if pushSuccessReports.nonEmpty => "/index.html"
+ }
+
val invalidationPaths: Seq[String] = {
- def withDefaultPathIfNeeded(paths: Seq[String]) = {
- // This is how we support the Default Root Object @ CloudFront (http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DefaultRootObject.html)
- // We do this more accurately by fetching the distribution config (http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/GetConfig.html)
- // and reading the Default Root Object from there.
- val containsPotentialDefaultRootObject = paths
- .exists(
- _
- .replaceFirst("^/", "") // S3 keys do not begin with a slash
- .contains("/") == false // See if the S3 key is a top-level key (i.e., it is not within a directory)
- )
- if (containsPotentialDefaultRootObject) paths :+ "/" else paths
- }
- def withIndexPathIfNeeded(paths: Seq[String]) =
- if (config.cloudfront_invalidate_root.contains(true) && pushSuccessReports.nonEmpty)
- paths :+ "/index.html"
- else
- paths
val paths = pushSuccessReports
- .filter(needsInvalidation) // Assume that redirect objects are never cached.
+ .filter(needsInvalidation)
.map(toInvalidationPath)
+ .map(encodeUnsafeChars)
.map(applyInvalidateRootSetting)
- withIndexPathIfNeeded(withDefaultPathIfNeeded(paths))
+
+ val extraPathItems = defaultPath(paths) :: indexPath :: Nil collect {
+ case Some(path) => path
+ }
+
+ paths ++ extraPathItems
}
invalidationPaths
.grouped(1000) // CloudFront supports max 1000 invalidations in one request (http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#InvalidationLimits)
.map { batchKeys =>
@@ -107,20 +112,14 @@
if (config.cloudfront_invalidate_root.contains(true))
path.replaceFirst("/index.html$", "/")
else
path
- def toInvalidationPath(report: PushSuccessReport) = {
- def encodeUnsafeChars(path: String) =
- new URI(
- "http",
- "cloudfront", // We want to use the encoder in the URI class. These must be passed in.
- "/" + report.s3Key, // CloudFront keys have the slash in front
- path
- ).toURL.getPath // The URL class encodes the unsafe characters
- val invalidationPath = "/" + report.s3Key // CloudFront keys have the slash in front
- encodeUnsafeChars(invalidationPath)
- }
+ def toInvalidationPath(report: PushSuccessReport) = "/" + report.s3Key
+
+ def encodeUnsafeChars(invalidationPath: String) =
+ new URI("http", "cloudfront", invalidationPath, "").toURL.getPath
+ .replaceAll("'", URLEncoder.encode("'", "UTF-8")) // CloudFront does not accept ' in invalidation path
def needsInvalidation: PartialFunction[PushSuccessReport, Boolean] = {
case succ: SuccessfulUpload => succ.details.fold(_.uploadType, _.uploadType) == FileUpdate
case SuccessfulDelete(_) => true