Skip to content

Commit 7bc9d1f

Browse files
authored
KTOR-8236: support for 'Vary' header (#4777)
* KTOR-8236: support for 'Vary' header * KTOR-8236: resolve lint issues * KTOR-8236: add tests for 'Vary' header * KTOR-8236: remove check for null expectedVary header branch
1 parent 426bd8f commit 7bc9d1f

File tree

4 files changed

+57
-2
lines changed

4 files changed

+57
-2
lines changed

ktor-http/common/src/io/ktor/http/content/CompressedContent.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ private class CompressedReadChannelResponse(
4646
Headers.build {
4747
appendFiltered(original.headers) { name, _ -> !name.equals(HttpHeaders.ContentLength, true) }
4848
append(HttpHeaders.ContentEncoding, encoder.name)
49+
append(
50+
HttpHeaders.Vary,
51+
original.headers[HttpHeaders.Vary]?.plus(", ${HttpHeaders.AcceptEncoding}")
52+
?: HttpHeaders.AcceptEncoding
53+
)
4954
}
5055
}
5156

@@ -67,6 +72,11 @@ private class CompressedWriteChannelResponse(
6772
Headers.build {
6873
appendFiltered(original.headers) { name, _ -> !name.equals(HttpHeaders.ContentLength, true) }
6974
append(HttpHeaders.ContentEncoding, encoder.name)
75+
append(
76+
HttpHeaders.Vary,
77+
original.headers[HttpHeaders.Vary]?.plus(", ${HttpHeaders.AcceptEncoding}")
78+
?: HttpHeaders.AcceptEncoding
79+
)
7080
}
7181
}
7282

ktor-server/ktor-server-core/jvm/src/io/ktor/server/http/content/PreCompressed.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ internal suspend fun ApplicationCall.respondStaticFile(
135135
suppressCompression()
136136
val compressedFile = File("${requestedFile.absolutePath}.${bestCompressionFit.extension}")
137137
if (cacheControlValues.isNotEmpty()) response.header(HttpHeaders.CacheControl, cacheControlValues)
138+
response.header(
139+
HttpHeaders.Vary,
140+
response.headers[HttpHeaders.Vary]?.plus(", ${HttpHeaders.AcceptEncoding}") ?: HttpHeaders.AcceptEncoding
141+
)
138142
modify(requestedFile, this)
139143
val localFileContent = LocalFileContent(compressedFile, contentType(requestedFile))
140144
respond(PreCompressedResponse(localFileContent, bestCompressionFit.encoding))
@@ -195,6 +199,10 @@ internal suspend fun ApplicationCall.respondStaticResource(
195199
suppressCompression()
196200
val cacheControlValues = cacheControl(bestCompressionFit.url).joinToString(", ")
197201
if (cacheControlValues.isNotEmpty()) response.header(HttpHeaders.CacheControl, cacheControlValues)
202+
response.header(
203+
HttpHeaders.Vary,
204+
response.headers[HttpHeaders.Vary]?.plus(", ${HttpHeaders.AcceptEncoding}") ?: HttpHeaders.AcceptEncoding
205+
)
198206
modifier(bestCompressionFit.url, this)
199207
respond(PreCompressedResponse(bestCompressionFit.content, bestCompressionFit.compression.encoding))
200208
return

ktor-server/ktor-server-tests/jvm/test/io/ktor/server/plugins/CompressionTest.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ class CompressionTest {
3838
private val textToCompress = "text to be compressed\n".repeat(100)
3939
private val textToCompressAsBytes = textToCompress.encodeToByteArray()
4040

41+
@Test
42+
fun testVaryHeaderPresent() = testApplication {
43+
install(Compression)
44+
45+
routing {
46+
get("/") {
47+
call.respondText(textToCompress)
48+
}
49+
}
50+
51+
handleAndAssert("/", "gzip", "gzip", textToCompress, HttpHeaders.AcceptEncoding)
52+
}
53+
4154
@Test
4255
fun testCompressionNotSpecified() = testApplication {
4356
install(Compression)
@@ -837,7 +850,8 @@ class CompressionTest {
837850
url: String,
838851
acceptHeader: String?,
839852
expectedEncoding: String?,
840-
expectedContent: String
853+
expectedContent: String,
854+
expectedVary: String? = null,
841855
): HttpResponse {
842856
val response = client.get(url) {
843857
if (acceptHeader != null) {
@@ -872,6 +886,10 @@ class CompressionTest {
872886
assertNotNull(response.headers[HttpHeaders.ContentLength])
873887
}
874888

889+
expectedVary?.let {
890+
assertEquals(response.headers[HttpHeaders.Vary], expectedVary)
891+
}
892+
875893
return response
876894
}
877895

ktor-server/ktor-server-tests/jvm/test/io/ktor/server/plugins/StaticContentTest.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,26 @@ class StaticContentTest {
3131
.map { File(it, "io/ktor/server") }
3232
.first(File::exists)
3333

34-
private operator fun File.get(relativePath: String) = File(this, relativePath)
34+
@Test
35+
fun testVaryHeaderWithPreCompressedStaticResources() = testApplication {
36+
routing {
37+
staticResources("static", "public") {
38+
preCompressed(CompressedFileType.BROTLI)
39+
}
40+
}
41+
42+
client.get("static/nested/file-nested.txt").let { response ->
43+
assertNull(response.headers[HttpHeaders.Vary])
44+
}
45+
46+
client.get("static/nested/file-nested.txt") {
47+
headers {
48+
append(HttpHeaders.AcceptEncoding, "br")
49+
}
50+
}.let { response ->
51+
assertEquals(HttpHeaders.AcceptEncoding, response.headers[HttpHeaders.Vary])
52+
}
53+
}
3554

3655
@Test
3756
fun testStaticContentBuilder() = testApplication {

0 commit comments

Comments
 (0)