Skip to content

Commit

Permalink
KTOR-4943 NettyHttp2ApplicationResponse should not expose pseudo head…
Browse files Browse the repository at this point in the history
…ers (#3186)
  • Loading branch information
rsinukov authored Oct 10, 2022
1 parent 2423cd2 commit a5a1ade
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import io.netty.channel.*
import io.netty.handler.codec.http2.*
import kotlin.coroutines.*

@OptIn(InternalAPI::class)
internal class NettyHttp2ApplicationResponse constructor(
call: NettyApplicationCall,
val handler: NettyHttp2Handler,
Expand Down Expand Up @@ -76,13 +75,17 @@ internal class NettyHttp2ApplicationResponse constructor(
}
}

private class Http2ResponseHeaders(private val underlying: DefaultHttp2Headers) : ResponseHeaders() {
internal class Http2ResponseHeaders(private val underlying: DefaultHttp2Headers) : ResponseHeaders() {
override fun engineAppendHeader(name: String, value: String) {
underlying.add(name.toLowerCasePreservingASCIIRules(), value)
}

override fun get(name: String): String? = underlying[name]?.toString()
override fun getEngineHeaderNames(): List<String> = underlying.names().map { it.toString() }
override fun getEngineHeaderValues(name: String): List<String> = underlying.getAll(name).map { it.toString() }
override fun get(name: String): String? = if (name.startsWith(':')) null else underlying[name]?.toString()

override fun getEngineHeaderNames(): List<String> = underlying.names()
.filter { !it.startsWith(':') }.map { it.toString() }

override fun getEngineHeaderValues(name: String): List<String> =
if (name.startsWith(':')) emptyList() else underlying.getAll(name).map { it.toString() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.tests.server.netty

import io.ktor.http.*
import io.ktor.server.netty.http2.*
import io.netty.handler.codec.http2.*
import kotlin.test.*

class NettyHttp2ApplicationResponseTest {

@Test
fun testAllHeadersSkipPseudoHeaders() {
val nettyHeaders = DefaultHttp2Headers()
val headers = NettyHttp2ApplicationResponse.Http2ResponseHeaders(nettyHeaders)
nettyHeaders.status("200")
headers.append(HttpHeaders.ContentType, "text/plain")
assertEquals(headersOf(HttpHeaders.ContentType.lowercase(), "text/plain"), headers.allValues())
}

@Test
fun testGetHeaderSkipPseudoHeaders() {
val nettyHeaders = DefaultHttp2Headers()
val headers = NettyHttp2ApplicationResponse.Http2ResponseHeaders(nettyHeaders)
nettyHeaders.status("200")
headers.append(HttpHeaders.ContentType, "text/plain")
assertEquals("text/plain", headers[HttpHeaders.ContentType.lowercase()])
assertNull(headers[":status"])
}

@Test
fun testGetHeaderValuesSkipPseudoHeaders() {
val nettyHeaders = DefaultHttp2Headers()
val headers = NettyHttp2ApplicationResponse.Http2ResponseHeaders(nettyHeaders)
nettyHeaders.status("200")
headers.append(HttpHeaders.ContentType, "text/plain")
assertEquals(listOf("text/plain"), headers.values(HttpHeaders.ContentType.lowercase()))
assertEquals(emptyList(), headers.values(":status"))
}

@Test
fun testAppendThrowsOnPseudoHeaders() {
val nettyHeaders = DefaultHttp2Headers()
val headers = NettyHttp2ApplicationResponse.Http2ResponseHeaders(nettyHeaders)
headers.append(HttpHeaders.ContentType, "text/plain")
assertFailsWith<IllegalHeaderNameException> { headers.append(":status", "200") }
}
}

0 comments on commit a5a1ade

Please sign in to comment.