mirror of
https://github.com/jlengrand/ktor.git
synced 2026-03-10 08:31:20 +00:00
Added tests for clients and servers to check that Headers.getAll returns null when the key is not found
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
*/
|
||||
|
||||
package io.ktor.client.tests
|
||||
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.client.tests.utils.*
|
||||
import io.ktor.http.*
|
||||
import kotlin.test.*
|
||||
|
||||
class HeadersTest : ClientLoader() {
|
||||
|
||||
@Test
|
||||
fun headersReturnNullWhenMissing(): Unit = clientTests {
|
||||
config {}
|
||||
test { client ->
|
||||
client.get<HttpResponse>("$TEST_SERVER/headers/").let {
|
||||
assertEquals(HttpStatusCode.OK, it.status)
|
||||
assertEquals("OK", it.readText())
|
||||
|
||||
assertNull(it.headers["X-Nonexistent-Header"])
|
||||
assertNull(it.headers.getAll("X-Nonexistent-Header"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ internal fun Application.tests() {
|
||||
featuresTest()
|
||||
webSockets()
|
||||
multiPartFormDataTest()
|
||||
headersTestServer()
|
||||
|
||||
routing {
|
||||
post("/echo") {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
*/
|
||||
|
||||
package io.ktor.client.tests.utils.tests
|
||||
|
||||
import io.ktor.application.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.response.*
|
||||
import io.ktor.routing.*
|
||||
|
||||
internal fun Application.headersTestServer() {
|
||||
routing {
|
||||
route("/headers") {
|
||||
get("/") {
|
||||
call.response.header("X-Header-Single-Value", "foo")
|
||||
call.response.header("X-Header-Double-Value", "foo")
|
||||
call.response.header("X-Header-Double-Value", "bar")
|
||||
call.respond(HttpStatusCode.OK, "OK")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,8 @@ class HeadersTest {
|
||||
|
||||
@Test
|
||||
fun parseAcceptHeaderWithExtraParametersAndFallback() {
|
||||
val items = parseAndSortContentTypeHeader("text/*;q=0.3, text/html;q=0.7, text/html;level=1,text/html;level=2;q=0.4, */*;q=0.5")
|
||||
val items =
|
||||
parseAndSortContentTypeHeader("text/*;q=0.3, text/html;q=0.7, text/html;level=1,text/html;level=2;q=0.4, */*;q=0.5")
|
||||
val item0 = HeaderValue("text/html", listOf(HeaderValueParam("level", "1")))
|
||||
val item1 = HeaderValue("text/html", listOf(HeaderValueParam("q", "0.7")))
|
||||
val item2 = HeaderValue("*/*", listOf(HeaderValueParam("q", "0.5")))
|
||||
@@ -68,14 +69,16 @@ class HeadersTest {
|
||||
@Test
|
||||
fun parseJustValueWithSingleParameterWithValue() {
|
||||
val headerValue = parseHeaderValue("justValue;a=b")
|
||||
assertEquals(listOf(HeaderValue("justValue", listOf(HeaderValueParam("a", "b")))), headerValue
|
||||
assertEquals(
|
||||
listOf(HeaderValue("justValue", listOf(HeaderValueParam("a", "b")))), headerValue
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseJustValueWithSingleParameter() {
|
||||
val headerValue = parseHeaderValue("justValue;implicit")
|
||||
assertEquals(listOf(HeaderValue("justValue", listOf(HeaderValueParam("implicit", "")))), headerValue
|
||||
assertEquals(
|
||||
listOf(HeaderValue("justValue", listOf(HeaderValueParam("implicit", "")))), headerValue
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,32 +94,46 @@ class HeadersTest {
|
||||
@Test
|
||||
fun parseJustValueWithMultipleParameters() {
|
||||
val headerValue = parseHeaderValue("justValue; a=b; c=d")
|
||||
assertEquals(listOf(HeaderValue("justValue", listOf(
|
||||
HeaderValueParam("a", "b"),
|
||||
HeaderValueParam("c", "d")
|
||||
))),
|
||||
headerValue)
|
||||
assertEquals(
|
||||
listOf(
|
||||
HeaderValue(
|
||||
"justValue", listOf(
|
||||
HeaderValueParam("a", "b"),
|
||||
HeaderValueParam("c", "d")
|
||||
)
|
||||
)
|
||||
),
|
||||
headerValue
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseJustValueWithQuotedParameter() {
|
||||
assertEquals(
|
||||
listOf(HeaderValue("justValue", listOf(
|
||||
listOf(
|
||||
HeaderValue(
|
||||
"justValue", listOf(
|
||||
HeaderValueParam("a", "quoted;=,\"value")
|
||||
))),
|
||||
parseHeaderValue("justValue; a=\"quoted;=,\\\"value\"")
|
||||
)
|
||||
)
|
||||
),
|
||||
parseHeaderValue("justValue; a=\"quoted;=,\\\"value\"")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseJustValueWithQuotedAndSimpleParameters() {
|
||||
assertEquals(
|
||||
listOf(HeaderValue("justValue", listOf(
|
||||
listOf(
|
||||
HeaderValue(
|
||||
"justValue", listOf(
|
||||
HeaderValueParam("a", "quoted;=,\"value"),
|
||||
HeaderValueParam("b", "3"),
|
||||
HeaderValueParam("q", "0.1")
|
||||
))),
|
||||
parseHeaderValue("justValue; a=\"quoted;=,\\\"value\"; b=3; q=0.1")
|
||||
)
|
||||
)
|
||||
),
|
||||
parseHeaderValue("justValue; a=\"quoted;=,\\\"value\"; b=3; q=0.1")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -131,19 +148,25 @@ class HeadersTest {
|
||||
assertEquals(listOf(HeaderValue("justValue")), parseHeaderValue("justValue;=33"))
|
||||
assertEquals(listOf(HeaderValue("justValue")), parseHeaderValue("justValue;====33"))
|
||||
assertEquals(listOf(HeaderValue("justValue")), parseHeaderValue("justValue;=\""))
|
||||
assertEquals(listOf(HeaderValue("justValue", listOf(HeaderValueParam("x", "")))), parseHeaderValue("justValue;x=\"\""))
|
||||
assertEquals(listOf(HeaderValue("justValue", listOf(HeaderValueParam("x", "abc\\")))), parseHeaderValue("justValue;x=\"abc\\"))
|
||||
assertEquals(
|
||||
listOf(HeaderValue("justValue", listOf(HeaderValueParam("x", "")))),
|
||||
parseHeaderValue("justValue;x=\"\"")
|
||||
)
|
||||
assertEquals(
|
||||
listOf(HeaderValue("justValue", listOf(HeaderValueParam("x", "abc\\")))),
|
||||
parseHeaderValue("justValue;x=\"abc\\")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseRealLifeHeadersShouldntFail() {
|
||||
val examples = listOf(
|
||||
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"ru,en-US;q=0.7,en;q=0.3",
|
||||
"gzip, deflate",
|
||||
"""If-Match: "strong", W/"weak", "oops, a \"comma\""""",
|
||||
"""WWW-Authenticate: Newauth realm="newauth";test="oh, a \"comma\""; foo=a'b'c, Basic realm="basic"""",
|
||||
"remixlang=0; remixflash=11.2.202; remixscreen_depth=24; remixdt=0; audio_vol=35; remixrefkey=836214a50b5b18f112; audio_time_left=0; remixtst=483196cd; remixsid=63476f202634a7b7f6e9975e8b446b126c1d9c82a94e38801bcc3; remixsslsid=1"
|
||||
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"ru,en-US;q=0.7,en;q=0.3",
|
||||
"gzip, deflate",
|
||||
"""If-Match: "strong", W/"weak", "oops, a \"comma\""""",
|
||||
"""WWW-Authenticate: Newauth realm="newauth";test="oh, a \"comma\""; foo=a'b'c, Basic realm="basic"""",
|
||||
"remixlang=0; remixflash=11.2.202; remixscreen_depth=24; remixdt=0; audio_vol=35; remixrefkey=836214a50b5b18f112; audio_time_left=0; remixtst=483196cd; remixsid=63476f202634a7b7f6e9975e8b446b126c1d9c82a94e38801bcc3; remixsslsid=1"
|
||||
)
|
||||
|
||||
examples.forEach {
|
||||
@@ -153,10 +176,20 @@ class HeadersTest {
|
||||
|
||||
@Test
|
||||
fun parseParametersOnly() {
|
||||
assertEquals(listOf(HeaderValue("", listOf(HeaderValueParam("k", "v")))), parseHeaderValue("k=v", parametersOnly = true))
|
||||
assertEquals(listOf(HeaderValue("", listOf(HeaderValueParam("k", "v"), HeaderValueParam("k2", "v2")))), parseHeaderValue("k=v;k2=v2", parametersOnly = true))
|
||||
assertEquals(listOf(HeaderValue("", listOf(HeaderValueParam("k", "v"))),
|
||||
HeaderValue("", listOf(HeaderValueParam("k2", "v2")))), parseHeaderValue("k=v,k2=v2", parametersOnly = true))
|
||||
assertEquals(
|
||||
listOf(HeaderValue("", listOf(HeaderValueParam("k", "v")))),
|
||||
parseHeaderValue("k=v", parametersOnly = true)
|
||||
)
|
||||
assertEquals(
|
||||
listOf(HeaderValue("", listOf(HeaderValueParam("k", "v"), HeaderValueParam("k2", "v2")))),
|
||||
parseHeaderValue("k=v;k2=v2", parametersOnly = true)
|
||||
)
|
||||
assertEquals(
|
||||
listOf(
|
||||
HeaderValue("", listOf(HeaderValueParam("k", "v"))),
|
||||
HeaderValue("", listOf(HeaderValueParam("k2", "v2")))
|
||||
), parseHeaderValue("k=v,k2=v2", parametersOnly = true)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -171,17 +204,27 @@ class HeadersTest {
|
||||
|
||||
@Test
|
||||
fun testRenderSimpleWithMultipleParameters() {
|
||||
assertEquals("file; k1=v1; k2=v2", ContentDisposition.File.withParameters(listOf(
|
||||
HeaderValueParam("k1", "v1"),
|
||||
HeaderValueParam("k2", "v2")
|
||||
)).toString())
|
||||
assertEquals(
|
||||
"file; k1=v1; k2=v2", ContentDisposition.File.withParameters(
|
||||
listOf(
|
||||
HeaderValueParam("k1", "v1"),
|
||||
HeaderValueParam("k2", "v2")
|
||||
)
|
||||
).toString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRenderEscaped() {
|
||||
assertEquals("file; k=\"v,v\"", ContentDisposition.File.withParameter("k", "v,v").toString())
|
||||
assertEquals("file; k=\"v,v\"; k2=\"=\"", ContentDisposition.File.withParameter("k", "v,v").withParameter("k2", "=").toString())
|
||||
assertEquals("file; k=\"v,v\"; k2=v2", ContentDisposition.File.withParameter("k", "v,v").withParameter("k2", "v2").toString())
|
||||
assertEquals(
|
||||
"file; k=\"v,v\"; k2=\"=\"",
|
||||
ContentDisposition.File.withParameter("k", "v,v").withParameter("k2", "=").toString()
|
||||
)
|
||||
assertEquals(
|
||||
"file; k=\"v,v\"; k2=v2",
|
||||
ContentDisposition.File.withParameter("k", "v,v").withParameter("k2", "v2").toString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -210,4 +253,16 @@ class HeadersTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun headersReturnNullWhenMissing() {
|
||||
val value = "world"
|
||||
val headers1 = headersOf("hello", value)
|
||||
val headers2 = headersOf("hello" to listOf(value))
|
||||
|
||||
assertNull(headers1["foo"])
|
||||
assertNull(headers2["foo"])
|
||||
|
||||
assertNull(headers1.getAll("foo"))
|
||||
assertNull(headers2.getAll("foo"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1848,6 +1848,33 @@ abstract class EngineTestSuite<TEngine : ApplicationEngine, TConfiguration : App
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHeadersReturnCorrectly() {
|
||||
createAndStartServer {
|
||||
|
||||
get("/") {
|
||||
assertEquals("foo", call.request.headers["X-Single-Value"])
|
||||
assertEquals("foo;bar", call.request.headers["X-Double-Value"])
|
||||
|
||||
assertNull(call.request.headers["X-Nonexistent-Header"])
|
||||
assertNull(call.request.headers.getAll("X-Nonexistent-Header"))
|
||||
|
||||
call.respond(HttpStatusCode.OK, "OK")
|
||||
}
|
||||
}
|
||||
|
||||
withUrl("/", {
|
||||
headers {
|
||||
append("X-Single-Value", "foo")
|
||||
append("X-Double-Value", "foo")
|
||||
append("X-Double-Value", "bar")
|
||||
}
|
||||
}) {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals("OK", readText())
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.urlPath() = replace("\\", "/")
|
||||
private class ExpectedException(message: String) : RuntimeException(message)
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
*/
|
||||
|
||||
package io.ktor.tests.server.http
|
||||
|
||||
import io.ktor.application.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.response.*
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.server.testing.*
|
||||
import io.ktor.util.*
|
||||
import org.junit.Test
|
||||
import kotlin.test.*
|
||||
|
||||
class HeadersTest {
|
||||
|
||||
@Test
|
||||
fun headersReturnNullWhenEmpty(): Unit = withTestApplication {
|
||||
application.routing {
|
||||
get("/") {
|
||||
assertNull(call.request.headers["X-Nonexistent-Header"])
|
||||
assertNull(call.request.headers.getAll("X-Nonexistent-Header"))
|
||||
|
||||
call.respond(HttpStatusCode.OK, "OK")
|
||||
}
|
||||
}
|
||||
|
||||
handleRequest(HttpMethod.Get, "/").let { call ->
|
||||
assertEquals(HttpStatusCode.OK, call.response.status())
|
||||
assertEquals("OK", call.response.content)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user