Skip to content

Commit fb20e5c

Browse files
committed
Merge branch 'main' into zibet27/fix-netty-engine-stop
2 parents 0388470 + c1103e7 commit fb20e5c

File tree

20 files changed

+406
-52
lines changed

20 files changed

+406
-52
lines changed

ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/UserAgent.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ public val UserAgent: ClientPlugin<UserAgentConfig> = createClientPlugin("UserAg
3030
val agent = pluginConfig.agent
3131

3232
onRequest { request, _ ->
33-
LOGGER.trace("Adding User-Agent header: agent for ${request.url}")
34-
request.header(HttpHeaders.UserAgent, agent)
33+
if (!request.headers.contains(HttpHeaders.UserAgent)) {
34+
LOGGER.trace("Adding User-Agent header: agent for ${request.url}")
35+
request.header(HttpHeaders.UserAgent, agent)
36+
}
3537
}
3638
}
3739

ktor-client/ktor-client-java/jvm/src/io/ktor/client/engine/java/JavaHttpConfig.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import java.net.http.*
1515
public class JavaHttpConfig : HttpClientEngineConfig() {
1616

1717
/**
18-
* An HTTP version to use.
18+
* An HTTP version to use. The default is [HttpClient.Version.HTTP_2].
1919
*
2020
* [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.client.engine.java.JavaHttpConfig.protocolVersion)
2121
*/
22-
public var protocolVersion: HttpClient.Version = HttpClient.Version.HTTP_1_1
22+
public var protocolVersion: HttpClient.Version = HttpClient.Version.HTTP_2
2323

2424
internal var config: HttpClient.Builder.() -> Unit = {
2525
followRedirects(HttpClient.Redirect.NEVER)

ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/RequestResponse.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package io.ktor.http.cio
66

77
import io.ktor.http.*
88
import io.ktor.http.cio.internals.*
9+
import io.ktor.utils.io.InternalAPI
910
import io.ktor.utils.io.core.*
1011

1112
/**

ktor-io/api/ktor-io.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,12 @@ public final class io/ktor/utils/io/ConcurrentIOException : java/lang/IllegalSta
226226
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
227227
}
228228

229+
public final class io/ktor/utils/io/ConnectionClosedException : java/io/IOException {
230+
public fun <init> ()V
231+
public fun <init> (Ljava/lang/String;)V
232+
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
233+
}
234+
229235
public final class io/ktor/utils/io/CountedByteReadChannel : io/ktor/utils/io/ByteReadChannel {
230236
public fun <init> (Lio/ktor/utils/io/ByteReadChannel;)V
231237
public fun awaitContent (ILkotlin/coroutines/Continuation;)Ljava/lang/Object;

ktor-io/api/ktor-io.klib.api

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ final class io.ktor.utils.io/ConcurrentIOException : kotlin/IllegalStateExceptio
236236
constructor <init>(kotlin/String, kotlin/Throwable? = ...) // io.ktor.utils.io/ConcurrentIOException.<init>|<init>(kotlin.String;kotlin.Throwable?){}[0]
237237
}
238238

239+
final class io.ktor.utils.io/ConnectionClosedException : kotlinx.io/IOException { // io.ktor.utils.io/ConnectionClosedException|null[0]
240+
constructor <init>(kotlin/String = ...) // io.ktor.utils.io/ConnectionClosedException.<init>|<init>(kotlin.String){}[0]
241+
}
242+
239243
final class io.ktor.utils.io/CountedByteReadChannel : io.ktor.utils.io/ByteReadChannel { // io.ktor.utils.io/CountedByteReadChannel|null[0]
240244
constructor <init>(io.ktor.utils.io/ByteReadChannel) // io.ktor.utils.io/CountedByteReadChannel.<init>|<init>(io.ktor.utils.io.ByteReadChannel){}[0]
241245

ktor-io/common/src/io/ktor/utils/io/Exceptions.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,11 @@ public class ClosedWriteChannelException(cause: Throwable? = null) : ClosedByteC
2828
* [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.utils.io.ClosedReadChannelException)
2929
*/
3030
public class ClosedReadChannelException(cause: Throwable? = null) : ClosedByteChannelException(cause)
31+
32+
/**
33+
* Exception thrown when a network connection is closed or reset by peer.
34+
* This exception is used to signal that the underlying connection was terminated.
35+
*
36+
* [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.utils.io.ConnectionClosedException)
37+
*/
38+
public class ConnectionClosedException(message: String = "Connection was closed") : IOException(message)

ktor-server/ktor-server-cio/common/src/io/ktor/server/cio/CIOApplicationEngine.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ package io.ktor.server.cio
66

77
import io.ktor.events.*
88
import io.ktor.http.*
9+
import io.ktor.http.cio.Request
910
import io.ktor.server.application.*
1011
import io.ktor.server.cio.backend.*
1112
import io.ktor.server.cio.internal.*
1213
import io.ktor.server.engine.*
14+
import io.ktor.server.http.HttpRequestCloseHandlerKey
1315
import io.ktor.server.request.*
1416
import io.ktor.server.response.*
1517
import io.ktor.util.pipeline.*
@@ -169,7 +171,15 @@ public class CIOApplicationEngine(
169171
return transferEncoding != null || (contentLength != null && contentLength > 0)
170172
}
171173

172-
private suspend fun ServerRequestScope.handleRequest(request: io.ktor.http.cio.Request) {
174+
@OptIn(InternalAPI::class)
175+
private fun ServerRequestScope.setCloseHandler(call: CIOApplicationCall) {
176+
onClose = {
177+
val requestCloseHandler = call.attributes.getOrNull(HttpRequestCloseHandlerKey)
178+
requestCloseHandler?.invoke()
179+
}
180+
}
181+
182+
private suspend fun ServerRequestScope.handleRequest(request: Request) {
173183
withContext(userDispatcher) requestContext@{
174184
val call = CIOApplicationCall(
175185
applicationProvider(),
@@ -186,6 +196,7 @@ public class CIOApplicationEngine(
186196

187197
try {
188198
addHandlerForExpectedHeader(output, call)
199+
setCloseHandler(call)
189200
pipeline.execute(call)
190201
} catch (error: Throwable) {
191202
handleFailure(call, error)

ktor-server/ktor-server-cio/common/src/io/ktor/server/cio/backend/ServerPipeline.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ public fun CoroutineScope.startServerConnectionPipeline(
5959

6060
val requestContext = RequestHandlerCoroutine + Dispatchers.Unconfined
6161

62+
var handlerScope: ServerRequestScope? = null
6263
try {
6364
while (true) { // parse requests loop
6465
val request = try {
6566
parseRequest(connection.input) ?: break
66-
} catch (cause: TooLongLineException) {
67+
} catch (_: TooLongLineException) {
6768
respondBadRequest(actorChannel)
6869
break // end pipeline loop
6970
} catch (io: IOException) {
@@ -113,7 +114,7 @@ public fun CoroutineScope.startServerConnectionPipeline(
113114
contentType
114115
)
115116
expectedHttpUpgrade = !expectedHttpBody && expectHttpUpgrade(request.method, upgrade, connectionOptions)
116-
} catch (cause: Throwable) {
117+
} catch (_: Throwable) {
117118
request.release()
118119
response.writePacket(BadRequestPacket.copy())
119120
response.close()
@@ -129,7 +130,7 @@ public fun CoroutineScope.startServerConnectionPipeline(
129130
val upgraded = if (expectedHttpUpgrade) CompletableDeferred<Boolean>() else null
130131

131132
launch(requestContext, start = CoroutineStart.UNDISPATCHED) {
132-
val handlerScope = ServerRequestScope(
133+
handlerScope = ServerRequestScope(
133134
coroutineContext,
134135
requestBody,
135136
response,
@@ -181,10 +182,11 @@ public fun CoroutineScope.startServerConnectionPipeline(
181182

182183
if (isLastHttpRequest(version, connectionOptions)) break
183184
}
184-
} catch (cause: IOException) {
185+
} catch (_: IOException) {
185186
// already handled
186187
coroutineContext.cancel()
187188
} finally {
189+
handlerScope?.onClose?.invoke()
188190
actorChannel.close()
189191
}
190192
}

ktor-server/ktor-server-cio/common/src/io/ktor/server/cio/backend/ServerRequestScope.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,6 @@ public class ServerRequestScope internal constructor(
4242
localAddress,
4343
upgraded
4444
)
45+
46+
internal var onClose: (() -> Unit)? = null
4547
}

ktor-server/ktor-server-cio/jvm/test/io/ktor/tests/server/cio/CIOEngineTestJvm.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,11 @@ class CIOHooksTest : HooksTestSuite<CIOApplicationEngine, CIOApplicationEngine.C
8888
enableSsl = false
8989
}
9090
}
91+
92+
class CIOHttpRequestLifecycleTest :
93+
HttpRequestLifecycleTest<CIOApplicationEngine, CIOApplicationEngine.Configuration>(CIO) {
94+
init {
95+
enableSsl = false
96+
enableHttp2 = false
97+
}
98+
}

0 commit comments

Comments
 (0)