|
4 | 4 |
|
5 | 5 | package io.ktor.tests.server.netty |
6 | 6 |
|
| 7 | +import io.ktor.client.HttpClient |
| 8 | +import io.ktor.client.engine.cio.CIO |
| 9 | +import io.ktor.client.plugins.DefaultRequest |
| 10 | +import io.ktor.client.request.get |
| 11 | +import io.ktor.http.HttpHeaders |
| 12 | +import io.ktor.http.HttpStatusCode |
| 13 | +import io.ktor.http.content.OutgoingContent |
7 | 14 | import io.ktor.server.application.* |
| 15 | +import io.ktor.server.application.hooks.ResponseSent |
8 | 16 | import io.ktor.server.engine.* |
9 | 17 | import io.ktor.server.netty.* |
| 18 | +import io.ktor.server.response.respond |
| 19 | +import io.ktor.server.response.respondBytesWriter |
| 20 | +import io.ktor.server.routing.get |
| 21 | +import io.ktor.server.routing.routing |
| 22 | +import io.ktor.utils.io.ByteReadChannel |
| 23 | +import kotlinx.coroutines.CompletableDeferred |
| 24 | +import kotlinx.coroutines.CoroutineScope |
| 25 | +import kotlinx.coroutines.Dispatchers |
| 26 | +import kotlinx.coroutines.launch |
| 27 | +import kotlinx.coroutines.test.runTest |
10 | 28 | import java.net.* |
11 | 29 | import java.util.concurrent.* |
12 | 30 | import kotlin.test.* |
@@ -68,4 +86,87 @@ class NettySpecificTest { |
68 | 86 | assertTrue(server.engine.bootstraps.all { (it.config().group() as ExecutorService).isTerminated }) |
69 | 87 | } |
70 | 88 | } |
| 89 | + |
| 90 | + @Test |
| 91 | + fun contentLengthAndTransferEncodingAreSafelyRemoved() = runTest { |
| 92 | + val appStarted = CompletableDeferred<Application>() |
| 93 | + val testScope = CoroutineScope(coroutineContext) |
| 94 | + val earlyHints = HttpStatusCode(103, "Early Hints") |
| 95 | + |
| 96 | + val serverJob = launch(Dispatchers.IO) { |
| 97 | + val server = embeddedServer(Netty, port = 0) { |
| 98 | + install( |
| 99 | + createApplicationPlugin("CallLogging") { |
| 100 | + on(ResponseSent) { call -> |
| 101 | + testScope.launch { |
| 102 | + val headers = call.response.headers.allValues() |
| 103 | + assertNull(headers[HttpHeaders.ContentLength]) |
| 104 | + assertNull(headers[HttpHeaders.TransferEncoding]) |
| 105 | + } |
| 106 | + } |
| 107 | + }, |
| 108 | + ) |
| 109 | + |
| 110 | + routing { |
| 111 | + get("/no-content") { |
| 112 | + call.respond(HttpStatusCode.NoContent) |
| 113 | + } |
| 114 | + |
| 115 | + get("no-content-channel-writer") { |
| 116 | + call.respondBytesWriter(status = HttpStatusCode.NoContent) {} |
| 117 | + } |
| 118 | + |
| 119 | + get("no-content-read-channel") { |
| 120 | + call.respond(object : OutgoingContent.ReadChannelContent() { |
| 121 | + override val status: HttpStatusCode = HttpStatusCode.NoContent |
| 122 | + override fun readFrom(): ByteReadChannel = ByteReadChannel.Empty |
| 123 | + }) |
| 124 | + } |
| 125 | + |
| 126 | + get("/info") { |
| 127 | + call.respond(earlyHints) |
| 128 | + } |
| 129 | + |
| 130 | + get("info-channel-writer") { |
| 131 | + call.respondBytesWriter(status = earlyHints) {} |
| 132 | + } |
| 133 | + |
| 134 | + get("info-read-channel") { |
| 135 | + call.respond(object : OutgoingContent.ReadChannelContent() { |
| 136 | + override val status: HttpStatusCode = earlyHints |
| 137 | + override fun readFrom(): ByteReadChannel = ByteReadChannel.Empty |
| 138 | + }) |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + server.monitor.subscribe(ApplicationStarted) { app -> |
| 144 | + appStarted.complete(app) |
| 145 | + } |
| 146 | + |
| 147 | + server.start(wait = true) |
| 148 | + } |
| 149 | + |
| 150 | + try { |
| 151 | + val serverApp = appStarted.await() |
| 152 | + val connector = serverApp.engine.resolvedConnectors()[0] |
| 153 | + val host = connector.host |
| 154 | + val port = connector.port |
| 155 | + |
| 156 | + HttpClient(CIO) { |
| 157 | + install(DefaultRequest) { |
| 158 | + url("http://$host:$port/") |
| 159 | + } |
| 160 | + }.use { client -> |
| 161 | + assertEquals(HttpStatusCode.NoContent, client.get("/no-content").status) |
| 162 | + assertEquals(HttpStatusCode.NoContent, client.get("/no-content-channel-writer").status) |
| 163 | + assertEquals(HttpStatusCode.NoContent, client.get("/no-content-read-channel").status) |
| 164 | + assertEquals(earlyHints, client.get("/info").status) |
| 165 | + assertEquals(earlyHints, client.get("/info-channel-writer").status) |
| 166 | + assertEquals(earlyHints, client.get("/info-read-channel").status) |
| 167 | + } |
| 168 | + } finally { |
| 169 | + serverJob.cancel() |
| 170 | + } |
| 171 | + } |
71 | 172 | } |
0 commit comments