Skip to content
Draft
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ebce600
KTOR-9028 Add docs for the OAuth fallback function (#722)
vnikolova Dec 2, 2025
6ce1e92
KTOR-8952 Documentation for multiple header parsing (#727)
vnikolova Dec 5, 2025
bdeb6f4
fix: KTOR-8880
nomisRev Dec 8, 2025
522c4a5
Merge pull request #731 from ktorio/nomisrev/ktor-8880
nomisRev Dec 9, 2025
d30f9dc
Merge remote-tracking branch 'origin/main' into 3.4.0
vnikolova Dec 10, 2025
e748739
KTOR-8949, KTOR-8950, KTOR-8948 Docs for client auth cache control (#…
vnikolova Dec 10, 2025
9663db3
KTOR-9068 Documentation for OkHttp duplex streaming config property (…
vnikolova Dec 10, 2025
6f51031
KTOR-9149 Add documentation for SSL trust store settings in a config …
vnikolova Dec 10, 2025
5ef7f88
KTOR-9150 Documentation for Partial HTML response (#736)
vnikolova Dec 15, 2025
a8533c0
KTOR-9185 Http Request Lifecycle (#733)
nomisRev Dec 15, 2025
2d8ea58
fix typo
vnikolova Dec 15, 2025
c58e767
KTOR-8900 Documentation for Apache5 connection manager config (#739)
vnikolova Dec 16, 2025
3ae05c1
KTOR-8902 Add documentation for replacing plugin configuration (#740)
vnikolova Dec 16, 2025
4c69ee1
KTOR-9175 Add API Key auth documentation (#737)
nathanfallet Dec 16, 2025
61bd4fa
KTOR-8959 Add docs for the `respondResource` method (#738)
Stexxe Dec 18, 2025
e69cc58
add a what's new entry on API key auth and fix dependencies
vnikolova Dec 18, 2025
fb73e9f
KTOR-9160 Add a what's new entry for native engines dispatcher config…
vnikolova Dec 19, 2025
1a80da6
KTOR-9161 Add a what's new entry for running .execute on the engine d…
vnikolova Dec 19, 2025
87bcf75
resolve comments from tw review
vnikolova Dec 19, 2025
1bea16b
update ktor version in docs only
vnikolova Dec 19, 2025
b598d6f
remove code comment
vnikolova Jan 2, 2026
7fcd193
KTOR-9216 Documentation for ByteReadChannel.readTo util (#748)
vnikolova Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cfg/glossary.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@
<term name="query_string">
A part of a URL that assigns values to specified parameters and starts with the ? character.
</term>
<term name="classpath">
A list of locations where the JVM looks for user classes and resources.
</term>
</terms>
2 changes: 1 addition & 1 deletion codeSnippets/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ kotlin.native.binary.memoryModel = experimental
org.gradle.configureondemand = false
# versions
kotlin_version = 2.2.20
ktor_version = 3.3.3
ktor_version = 3.4.0-eap-1477
kotlinx_coroutines_version = 1.10.1
kotlinx_serialization_version = 1.8.0
kotlin_css_version = 1.0.0-pre.721
Expand Down
1 change: 1 addition & 0 deletions codeSnippets/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ module("snippets", "tutorial-server-restful-api")
module("snippets", "tutorial-server-websockets")
module("snippets", "tutorial-server-docker-compose")
module("snippets", "htmx-integration")
module("snippets", "server-http-request-lifecycle")

if(!System.getProperty("os.name").startsWith("Windows")) {
module("snippets", "embedded-server-native")
Expand Down
10 changes: 10 additions & 0 deletions codeSnippets/snippets/_misc_client/Apache5Create.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,21 @@ val client = HttpClient(Apache5) {
socketTimeout = 10_000
connectTimeout = 10_000
connectionRequestTimeout = 20_000

// Configure the Apache5 ConnectionManager
configureConnectionManager {
setMaxConnPerRoute(1_000)
setMaxConnTotal(2_000)
}

// Customize the underlying Apache client for other settings
customizeClient {
// this: HttpAsyncClientBuilder
setProxy(HttpHost("127.0.0.1", 8080))
// ...
}

// Customize per-request settings
customizeRequest {
// this: RequestConfig.Builder
}
Expand Down
8 changes: 8 additions & 0 deletions codeSnippets/snippets/_misc_client/InstallOrReplacePlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import io.ktor.client.*
import io.ktor.client.engine.cio.*

val client = HttpClient(CIO) {
installOrReplace(ContentNegotiation) {
// ...
}
}
1 change: 1 addition & 0 deletions codeSnippets/snippets/_misc_client/OkHttpConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ val client = HttpClient(OkHttp) {
addNetworkInterceptor(interceptor)

preconfigured = okHttpClientInstance
duplexStreamingEnabled = true // Only available for HTTP/2 connections
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ fun Application.main(httpClient: HttpClient = applicationHttpClient) {
}
)
}
fallback = { cause ->
if (cause is OAuth2RedirectError) {
respondRedirect("/login-after-fallback")
} else {
respond(HttpStatusCode.Forbidden, cause.message)
}
}
client = httpClient
}
}
Expand Down Expand Up @@ -101,6 +108,9 @@ fun Application.main(httpClient: HttpClient = applicationHttpClient) {
call.respondText("Hello, ${userInfo.name}!")
}
}
get("/login-after-fallback") {
call.respondText("Redirected after fallback")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,12 @@ fun Application.module() {
}
}
}
get("/fragment") {
call.respondHtmlFragment(HttpStatusCode.Created) {
div("fragment") {
span { +"Created!" }
}
}
}
}
}
14 changes: 14 additions & 0 deletions codeSnippets/snippets/server-http-request-lifecycle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# HTTP request lifecycle

A sample Ktor project showing how to cancel request processing as soon as the client disconnects, using the
`HttpRequestLifecycle` plugin.

> This sample is a part of the [`codeSnippets`](../../README.md) Gradle project.

## Running

To run the sample, execute the following command in the repository's root directory:

```bash
./gradlew :server-http-request-lifecycle:run
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
val ktor_version: String by project
val kotlin_version: String by project
val logback_version: String by project

plugins {
application
kotlin("jvm")
kotlin("plugin.serialization").version("2.2.20")
}

application {
mainClass.set("io.ktor.server.netty.EngineMain")
}

repositories {
mavenCentral()
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/ktor/eap") }
}

dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
implementation("io.ktor:ktor-server-core:$ktor_version")
implementation("io.ktor:ktor-server-netty:$ktor_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
testImplementation("io.ktor:ktor-server-test-host-jvm:$ktor_version")
testImplementation("io.ktor:ktor-server-netty")
testImplementation("io.ktor:ktor-client-cio")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test")
testImplementation(kotlin("test"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.example

/*
Important: The contents of this file are referenced by line numbers in `server-http-request-lifecycle.md`.
If you add, remove, or modify any lines, ensure you update the corresponding
line numbers in the code-block element of the referenced file.
*/

import io.ktor.server.application.*
import io.ktor.server.http.HttpRequestLifecycle
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.utils.io.CancellationException
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

fun Application.module() {
install(HttpRequestLifecycle) {
cancelCallOnClose = true
}

routing {
get("/long-process") {
try {
while (isActive) {
delay(10_000)
log.info("Very important work.")
}
call.respond("Completed")
} catch (e: CancellationException) {
log.info("Cleaning up resources.")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ktor {
deployment {
port = 8080
}
application {
modules = [ com.example.ApplicationKt.module ]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example

import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.request.*
import kotlinx.coroutines.*
import kotlinx.coroutines.test.runTest
import kotlin.test.Test


class ApplicationTest {

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testClientDisconnectionCancelsRequest() = runTest {
val server = embeddedServer(Netty, port = 8080) {
module()
}.start()

val client = HttpClient(CIO)

val job = launch {
client.get("http://localhost:8080/long")
}

delay(300)
job.cancelAndJoin() // Simulate client disconnect

server.stop()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ ktor:
keyStore: keystore.jks
keyAlias: sampleAlias
keyStorePassword: foobar
privateKeyPassword: foobar
privateKeyPassword: foobar
trustStore: truststore.jks
trustStorePassword: foobar
enabledProtocols: ["TLSv1.2", "TLSv1.3"]
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ ktor {
keyAlias = sampleAlias
keyStorePassword = foobar
privateKeyPassword = foobar
trustStore = truststore.jks
trustStorePassword = foobar
enabledProtocols = ["TLSv1.2", "TLSv1.3"]
}
}
}
2 changes: 1 addition & 1 deletion help-versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"isCurrent": false
},
{
"version": "3.3.3",
"version": "3.4.0",
"url": "/docs/",
"isCurrent": true
}
Expand Down
5 changes: 5 additions & 0 deletions ktor.tree
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
accepts-web-file-names="request-validation.html"/>
<toc-element topic="server-rate-limit.md"
accepts-web-file-names="rate-limit.html"/>
<toc-element topic="server-http-request-lifecycle.md"/>
<toc-element topic="server-double-receive.md"
accepts-web-file-names="double-receive.html"/>
</toc-element>
Expand Down Expand Up @@ -145,6 +146,9 @@
<toc-element topic="server-bearer-auth.md"
toc-title="Bearer authentication"
accepts-web-file-names="bearer.html"/>
<toc-element topic="server-api-key-auth.md"
toc-title="API Key authentication"
accepts-web-file-names="api-key.html"/>
<toc-element topic="server-form-based-auth.md"
toc-title="Form-based authentication"
accepts-web-file-names="form.html"/>
Expand Down Expand Up @@ -392,6 +396,7 @@

<toc-element toc-title="Releases">
<toc-element topic="releases.md"/>
<toc-element topic="whats-new-340.md"/>
<toc-element topic="whats-new-330.md"/>
<toc-element topic="whats-new-320.md"/>
<toc-element topic="migration-to-20x.md"
Expand Down
2 changes: 1 addition & 1 deletion project.ihp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<categories src="c.list"/>
<instance src="ktor.tree"
web-path="/docs/"
version="3.3.3"
version="3.4.0"
id="docs"
keymaps-mode="none"/>

Expand Down
Loading