Skip to content

Commit a9c1c7b

Browse files
Use comma to divide headers (Fix #1765).
1 parent 3c54c68 commit a9c1c7b

File tree

6 files changed

+92
-3
lines changed

6 files changed

+92
-3
lines changed

ktor-client/ktor-client-core/common/src/io/ktor/client/engine/Utils.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ fun mergeHeaders(
3333
if (HttpHeaders.ContentLength == key) return@forEach // set later
3434
if (HttpHeaders.ContentType == key) return@forEach // set later
3535

36-
block(key, values.joinToString(";"))
36+
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
37+
block(key, values.joinToString(","))
3738
}
3839

3940
val missingAgent = requestHeaders[HttpHeaders.UserAgent] == null && content.headers[HttpHeaders.UserAgent] == null

ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HeadersTest.kt

+38-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import kotlin.test.*
1313
class HeadersTest : ClientLoader() {
1414

1515
@Test
16-
fun headersReturnNullWhenMissing(): Unit = clientTests {
16+
fun headersReturnNullWhenMissing() = clientTests {
1717
config {}
1818
test { client ->
1919
client.get<HttpResponse>("$TEST_SERVER/headers/").let {
@@ -25,4 +25,41 @@ class HeadersTest : ClientLoader() {
2525
}
2626
}
2727
}
28+
29+
@Test
30+
fun headersMergeTest() = clientTests(listOf("Js")) {
31+
config {}
32+
test { client ->
33+
client.get<HttpResponse>("$TEST_SERVER/headers-merge/") {
34+
accept(ContentType.Text.Html)
35+
accept(ContentType.Application.Json)
36+
}.let {
37+
assertEquals(HttpStatusCode.OK, it.status)
38+
assertEquals("JSON", it.readText())
39+
assertEquals("application/json; charset=UTF-8", it.headers[HttpHeaders.ContentType])
40+
}
41+
42+
client.get<HttpResponse>("$TEST_SERVER/headers-merge/") {
43+
accept(ContentType.Text.Html)
44+
accept(ContentType.Application.Xml)
45+
}.let {
46+
assertEquals("XML", it.readText())
47+
assertEquals("application/xml; charset=UTF-8", it.headers[HttpHeaders.ContentType])
48+
}
49+
}
50+
}
51+
52+
@Test
53+
fun testTest() = clientTests(listOf("Js")) {
54+
config {}
55+
test { client ->
56+
val lines = client.get<String>("$HTTP_PROXY_SERVER/headers-merge") {
57+
accept(ContentType.Application.Xml)
58+
accept(ContentType.Application.Json)
59+
}.split("\n")
60+
61+
val acceptHeaderLine = lines.first { it.startsWith("Accept:") }
62+
assertEquals("Accept: application/xml,application/json", acceptHeaderLine)
63+
}
64+
}
2865
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2014-2020 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.client.tests.engine
6+
7+
import io.ktor.client.engine.*
8+
import io.ktor.client.utils.*
9+
import io.ktor.http.*
10+
import kotlin.test.*
11+
12+
class UtilsTest {
13+
@Test
14+
fun testMergeHeaders() {
15+
val headers = HeadersBuilder().apply {
16+
append("Accept", "application/xml")
17+
append("Accept", "application/json")
18+
}
19+
20+
val result = mutableMapOf<String, String>()
21+
mergeHeaders(headers.build(), EmptyContent) {
22+
key, value -> result[key] = value
23+
}
24+
25+
assertEquals("application/xml,application/json", result["Accept"])
26+
}
27+
}

ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/tests/Headers.kt

+13
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,18 @@ internal fun Application.headersTestServer() {
1919
call.respond(HttpStatusCode.OK, "OK")
2020
}
2121
}
22+
23+
route("/headers-merge") {
24+
accept(ContentType.Application.Json) {
25+
get("/") {
26+
call.respondText("JSON", ContentType.Application.Json, HttpStatusCode.OK)
27+
}
28+
}
29+
accept(ContentType.Application.Xml) {
30+
get("/") {
31+
call.respondText("XML", ContentType.Application.Xml, HttpStatusCode.OK)
32+
}
33+
}
34+
}
2235
}
2336
}

ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/tests/Proxy.kt

+11
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ import io.ktor.client.utils.*
88
import io.ktor.http.*
99
import io.ktor.network.sockets.*
1010
import io.ktor.utils.io.*
11+
import kotlin.text.*
1112

1213
suspend fun proxyHandler(socket: Socket) {
1314
val input = socket.openReadChannel()
1415
val output = socket.openWriteChannel()
1516

1617
val statusLine = input.readUTF8Line()
18+
val requestData = StringBuilder()
19+
requestData.append(statusLine).append("\n")
1720
while (true) {
1821
val line = input.readUTF8Line() ?: ""
22+
requestData.append(line).append("\n")
1923
if (line.isEmpty()) {
2024
break
2125
}
@@ -28,6 +32,13 @@ suspend fun proxyHandler(socket: Socket) {
2832
append(HttpHeaders.ContentType, ContentType.Application.Json)
2933
}, "{\"status\": \"ok\"}"
3034
)
35+
"GET /headers-merge HTTP/1.1" -> buildResponse(
36+
HttpStatusCode.OK,
37+
buildHeaders {
38+
append(HttpHeaders.ContentType, ContentType.Text.Plain)
39+
},
40+
requestData.toString()
41+
)
3142
else -> buildResponse(HttpStatusCode.BadRequest)
3243
}
3344

ktor-server/ktor-server-test-host/jvm/src/io/ktor/server/testing/EngineTestSuite.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1894,7 +1894,7 @@ abstract class EngineTestSuite<TEngine : ApplicationEngine, TConfiguration : App
18941894

18951895
get("/") {
18961896
assertEquals("foo", call.request.headers["X-Single-Value"])
1897-
assertEquals("foo;bar", call.request.headers["X-Double-Value"])
1897+
assertEquals("foo,bar", call.request.headers["X-Double-Value"])
18981898

18991899
assertNull(call.request.headers["X-Nonexistent-Header"])
19001900
assertNull(call.request.headers.getAll("X-Nonexistent-Header"))

0 commit comments

Comments
 (0)