From: TheSaminator Date: Sat, 21 May 2022 17:18:45 +0000 (-0400) Subject: Add ETag hashes of files X-Git-Url: https://gitweb.starshipfights.net/?a=commitdiff_plain;h=2cb57a3563129d56cdf5922f3b20a5f84dd17069;p=starship-fights Add ETag hashes of files --- diff --git a/build.gradle.kts b/build.gradle.kts index f211d10..265eda0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,8 @@ import com.nixxcode.jvmbrotli.common.BrotliLoader import com.nixxcode.jvmbrotli.enc.BrotliOutputStream import com.nixxcode.jvmbrotli.enc.Encoder - +import java.security.MessageDigest +import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors import java.util.zip.GZIPOutputStream @@ -119,13 +120,21 @@ tasks.named("jvmProcessResources") { doLast { val pool = Executors.newWorkStealingPool() + val encoderParams = if (BrotliLoader.isBrotliAvailable()) Encoder.Parameters().setQuality(8) else null - val resourceTree = fileTree(mapOf("dir" to outputs.files.asPath + "/static/", "exclude" to listOf("*.gz", "*.br"))) + val base64 = Base64.getUrlEncoder() + val hashDigest = ThreadLocal.withInitial { MessageDigest.getInstance("SHA-256") } + + val resourceTree = fileTree(mapOf("dir" to outputs.files.asPath + "/static/", "exclude" to listOf("*.gz", "*.br", "*.sha256"))) val countDownLatch = CountDownLatch(resourceTree.count()) for (file in resourceTree) { pool.execute { val bytes = file.readBytes() + val hashFile = File("${file.absolutePath}.sha256").bufferedWriter() + hashFile.write(base64.encodeToString(hashDigest.get().digest(bytes)).trimEnd('=')) + hashFile.close() + val result = File("${file.absolutePath}.gz").outputStream() val gzipStream = GZIPOutputStream(result) gzipStream.write(bytes) diff --git a/src/jvmMain/kotlin/starshipfights/server.kt b/src/jvmMain/kotlin/starshipfights/server.kt index 7f096a3..2383309 100644 --- a/src/jvmMain/kotlin/starshipfights/server.kt +++ b/src/jvmMain/kotlin/starshipfights/server.kt @@ -12,6 +12,7 @@ import io.ktor.response.* import io.ktor.routing.* import io.ktor.server.engine.* import io.ktor.server.netty.* +import io.ktor.util.* import io.ktor.websocket.* import org.slf4j.event.Level import starshipfights.auth.AuthProvider @@ -24,6 +25,8 @@ import java.util.concurrent.atomic.AtomicLong object ResourceLoader { fun getResource(resource: String): InputStream? = javaClass.getResourceAsStream(resource) + + val SHA256AttributeKey = AttributeKey("SHA256Hash") } fun main() { @@ -55,6 +58,14 @@ fun main() { } } + install(ConditionalHeaders) { + version { outgoingContent -> + outgoingContent.getProperty(ResourceLoader.SHA256AttributeKey)?.let { hash -> + listOf(EntityTagVersion(hash)) + }.orEmpty() + } + } + install(StatusPages) { status(HttpStatusCode.NotFound) { call.respondHtml(HttpStatusCode.NotFound, call.error404()) @@ -111,6 +122,10 @@ fun main() { val staticContentPath = call.parameters.getAll("static-content")?.joinToString("/") ?: return@get val contentPath = "/static/$staticContentPath" + val hashContentPath = "$contentPath.sha256" + val sha256Hash = ResourceLoader.getResource(hashContentPath)?.reader()?.readText() + val configureContent: OutgoingContent.() -> Unit = { setProperty(ResourceLoader.SHA256AttributeKey, sha256Hash) } + val brContentPath = "$contentPath.br" val gzContentPath = "$contentPath.gz" @@ -125,7 +140,7 @@ fun main() { call.response.header(HttpHeaders.ContentEncoding, CompressedFileType.BROTLI.encoding) - call.respondBytes(brContent.readBytes(), contentType) + call.respondBytes(brContent.readBytes(), contentType, configure = configureContent) return@get } @@ -138,13 +153,13 @@ fun main() { call.response.header(HttpHeaders.ContentEncoding, CompressedFileType.GZIP.encoding) - call.respondBytes(gzContent.readBytes(), contentType) + call.respondBytes(gzContent.readBytes(), contentType, configure = configureContent) return@get } } - ResourceLoader.getResource(contentPath)?.let { call.respondBytes(it.readBytes(), contentType) } + ResourceLoader.getResource(contentPath)?.let { call.respondBytes(it.readBytes(), contentType, configure = configureContent) } } } }