CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/gradle-io-ktor--ktor-client-core-jvm

Multiplatform asynchronous HTTP client core library for JVM that provides request/response handling, plugin architecture, and extensible HTTP communication capabilities.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

content-handling.mddocs/

Content Handling

Content serialization, form data, file uploads, and multipart support with type-safe handling. The content handling system provides comprehensive support for various content types, including forms, files, JSON, and custom serialization formats.

Capabilities

Form Data Content

Support for URL-encoded and multipart form data with builder DSL.

/**
 * Form URL-encoded content (application/x-www-form-urlencoded)
 */
class FormDataContent(
    val formData: Parameters
) : OutgoingContent.ByteArrayContent() {
    override val contentLength: Long
    override val contentType: ContentType
    override fun bytes(): ByteArray
}

/**
 * Multipart form data content (multipart/form-data)
 */
class MultiPartFormDataContent(
    parts: List<PartData>,
    val boundary: String = generateBoundary(),
    override val contentType: ContentType = ContentType.MultiPart.FormData.withParameter("boundary", boundary)
) : OutgoingContent.WriteChannelContent() {
    override var contentLength: Long?
    
    override suspend fun writeTo(channel: ByteWriteChannel)
}

/**
 * Generate a unique boundary for multipart content
 */
fun generateBoundary(): String

Form Submission Functions

Convenience functions for submitting forms with various content types.

/**
 * Submit form data using URL encoding or query parameters
 */
suspend fun HttpClient.submitForm(
    url: String,
    formParameters: Parameters,
    encodeInQuery: Boolean = false,
    block: HttpRequestBuilder.() -> Unit = {}
): HttpResponse

suspend fun HttpClient.submitForm(
    url: Url,
    formParameters: Parameters, 
    encodeInQuery: Boolean = false,
    block: HttpRequestBuilder.() -> Unit = {}
): HttpResponse

/**
 * Submit multipart form data with binary content
 */
suspend fun HttpClient.submitFormWithBinaryData(
    url: String,
    formData: List<PartData>,
    block: HttpRequestBuilder.() -> Unit = {}
): HttpResponse

suspend fun HttpClient.submitFormWithBinaryData(
    url: Url,
    formData: List<PartData>,
    block: HttpRequestBuilder.() -> Unit = {}
): HttpResponse

/**
 * Prepare form submission statements
 */
fun HttpClient.prepareForm(
    url: String,
    formParameters: Parameters,
    encodeInQuery: Boolean = false,
    block: HttpRequestBuilder.() -> Unit = {}
): HttpStatement

fun HttpClient.prepareFormWithBinaryData(
    url: String,
    formData: List<PartData>,
    block: HttpRequestBuilder.() -> Unit = {}
): HttpStatement

Form Building DSL

DSL for building form data with type-safe parameter handling.

/**
 * Build multipart form data using DSL
 */
fun formData(block: FormBuilder.() -> Unit): List<PartData>

/**
 * Builder for constructing multipart form data
 */
class FormBuilder {
    /**
     * Append a text part
     */
    fun append(key: String, value: String, headers: Headers = Headers.Empty)
    
    /**
     * Append a number part
     */
    fun append(key: String, value: Number, headers: Headers = Headers.Empty)
    
    /**
     * Append a byte array part
     */
    fun append(key: String, value: ByteArray, headers: Headers = Headers.Empty)
    
    /**
     * Append a channel content part
     */
    fun append(key: String, value: ByteReadChannel, headers: Headers = Headers.Empty)
    
    /**
     * Append a file part (JVM-specific)
     */
    fun append(key: String, file: File, headers: Headers = Headers.Empty)
    
    /**
     * Append content with custom headers and filename
     */
    fun append(
        key: String,
        value: ByteArray,
        headers: Headers = Headers.Empty,
        filename: String? = null
    )
    
    /**
     * Append streaming content
     */
    fun append(
        key: String,
        provider: ChannelProvider,
        headers: Headers = Headers.Empty,
        filename: String? = null
    )
}

/**
 * Channel provider for streaming content
 */
fun interface ChannelProvider {
    fun invoke(): ByteReadChannel
}

File Content Support (JVM)

JVM-specific file content handling for uploads and serving.

/**
 * JVM file content for uploading files
 */
class LocalFileContent(
    val file: File,
    override val contentType: ContentType = ContentType.defaultForFile(file)
) : OutgoingContent.ReadChannelContent() {
    /** File size in bytes */
    override val contentLength: Long = file.length()
    
    /**
     * Read the entire file content
     */
    override fun readFrom(): ByteReadChannel
    
    /**
     * Read a range of the file content
     */
    override fun readFrom(range: LongRange): ByteReadChannel
}

/**
 * Create LocalFileContent from base directory and relative path
 */
fun LocalFileContent(
    baseDir: File,
    relativePath: String,
    contentType: ContentType = ContentType.defaultForFilePath(relativePath)
): LocalFileContent

/**
 * Get default content type for file extension
 */
fun ContentType.Companion.defaultForFile(file: File): ContentType
fun ContentType.Companion.defaultForFilePath(path: String): ContentType

Part Data Types

Types representing different parts of multipart content.

/**
 * Base interface for multipart form data parts
 */
sealed class PartData {
    abstract val name: String
    abstract val headers: Headers
    
    abstract fun dispose()
}

/**
 * Text part data
 */
class PartData.FormItem(
    override val name: String,
    val value: String,
    override val headers: Headers = Headers.Empty
) : PartData()

/**
 * Binary part data
 */
class PartData.BinaryItem(
    override val name: String,
    val provider: ChannelProvider,
    override val headers: Headers = Headers.Empty,
    val filename: String? = null
) : PartData()

/**
 * File part data
 */
class PartData.FileItem(
    override val name: String,
    val originalFileName: String?,
    val provider: ChannelProvider,
    override val headers: Headers = Headers.Empty
) : PartData()

/**
 * Binary channel item
 */
class PartData.BinaryChannelItem(
    override val name: String,
    val provider: ChannelProvider,
    override val headers: Headers = Headers.Empty
) : PartData()

Observable Content

Content wrapper for monitoring upload/download progress.

/**
 * Content wrapper that observes transfer progress
 */
class ObservableContent<T : OutgoingContent>(
    private val delegate: T,
    private val listener: (Long) -> Unit
) : OutgoingContent by delegate {
    
    override suspend fun writeTo(channel: ByteWriteChannel) {
        val observableChannel = channel.observable(listener)
        delegate.writeTo(observableChannel)
    }
}

/**
 * Create observable content with progress callback
 */
fun <T : OutgoingContent> T.observable(
    listener: (bytesWritten: Long) -> Unit
): ObservableContent<T>

Content Types and Serialization

Content type definitions and serialization support.

/**
 * Set content type for request
 */
fun HttpRequestBuilder.contentType(contentType: ContentType)

/**
 * Set request body with automatic content type detection
 */
fun HttpRequestBuilder.setBody(body: Any)

/**
 * Set request body with explicit type information
 */
fun HttpRequestBuilder.setBody(body: Any, typeInfo: TypeInfo)

/**
 * Common content types
 */
object ContentType {
    object Application {
        val Json: ContentType // application/json
        val Xml: ContentType // application/xml
        val FormUrlEncoded: ContentType // application/x-www-form-urlencoded
        val OctetStream: ContentType // application/octet-stream
        val Pdf: ContentType // application/pdf
        val Zip: ContentType // application/zip
        val ProtoBuf: ContentType // application/x-protobuf
    }
    
    object Text {
        val Plain: ContentType // text/plain
        val Html: ContentType // text/html
        val CSS: ContentType // text/css
        val JavaScript: ContentType // text/javascript
        val CSV: ContentType // text/csv
        val EventStream: ContentType // text/event-stream
    }
    
    object Image {
        val PNG: ContentType // image/png
        val JPEG: ContentType // image/jpeg
        val GIF: ContentType // image/gif
        val SVG: ContentType // image/svg+xml
        val WebP: ContentType // image/webp
    }
    
    object MultiPart {
        val FormData: ContentType // multipart/form-data
        val Mixed: ContentType // multipart/mixed
        val Alternative: ContentType // multipart/alternative
        val Related: ContentType // multipart/related
    }
    
    object Video {
        val MP4: ContentType // video/mp4
        val MPEG: ContentType // video/mpeg
        val QuickTime: ContentType // video/quicktime
    }
    
    object Audio {
        val MP3: ContentType // audio/mpeg
        val MP4: ContentType // audio/mp4
        val OGG: ContentType // audio/ogg
        val WAV: ContentType // audio/wav
    }
}

Request Body Types

Different types of outgoing content for HTTP requests.

/**
 * Base class for all outgoing content
 */
abstract class OutgoingContent {
    abstract val contentType: ContentType?
    abstract val contentLength: Long?
    
    /** Text content with string body */
    abstract class TextContent(
        private val text: String,
        override val contentType: ContentType
    ) : OutgoingContent {
        override val contentLength: Long = text.toByteArray().size.toLong()
    }
    
    /** Binary content with byte array body */
    abstract class ByteArrayContent : OutgoingContent {
        abstract fun bytes(): ByteArray
        override val contentLength: Long? get() = bytes().size.toLong()
    }
    
    /** Content that can be read from a channel */
    abstract class ReadChannelContent : OutgoingContent {
        abstract fun readFrom(): ByteReadChannel
        open fun readFrom(range: LongRange): ByteReadChannel = readFrom()
    }
    
    /** Content that writes to a channel */
    abstract class WriteChannelContent(
        override val contentType: ContentType? = null,
        override val contentLength: Long? = null
    ) : OutgoingContent {
        abstract suspend fun writeTo(channel: ByteWriteChannel)
    }
    
    /** No content (for methods like DELETE, GET) */
    object NoContent : OutgoingContent {
        override val contentLength: Long? = 0L
        override val contentType: ContentType? = null
    }
}

Usage Examples:

import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.http.*
import io.ktor.utils.io.*
import java.io.File

val client = HttpClient()

// Submit URL-encoded form
val formResponse = client.submitForm(
    url = "https://api.example.com/login",
    formParameters = Parameters.build {
        append("username", "john_doe")
        append("password", "secret123")
        append("remember", "true")
    }
)

// Submit form as query parameters
val queryResponse = client.submitForm(
    url = "https://api.example.com/search",
    formParameters = Parameters.build {
        append("q", "kotlin ktor")
        append("limit", "10")
    },
    encodeInQuery = true
)

// Submit multipart form with binary data
val multipartResponse = client.submitFormWithBinaryData(
    url = "https://api.example.com/upload",
    formData = formData {
        append("title", "My Document")
        append("description", "Important file upload")
        append("file", File("document.pdf").readBytes(), Headers.build {
            append(HttpHeaders.ContentDisposition, "filename=\"document.pdf\"")
        })
        append("metadata", """{"category": "documents"}""")
    }
)

// Manual form data construction
val manualFormData = formData {
    // Text fields
    append("name", "John Doe")
    append("age", 30)
    append("active", true)
    
    // File upload (JVM)
    val imageFile = File("profile.jpg")
    append("avatar", imageFile, Headers.build {
        append(HttpHeaders.ContentType, "image/jpeg")
    })
    
    // Binary data
    val documentBytes = "Document content".toByteArray()
    append("document", documentBytes, Headers.build {
        append(HttpHeaders.ContentDisposition, "filename=\"document.txt\"")
    }, filename = "document.txt")
    
    // Streaming content
    append("stream", {
        // Return a ByteReadChannel
        ByteReadChannel("Streaming data content")
    }, filename = "stream.txt")
}

val manualResponse = client.post("https://api.example.com/complex-upload") {
    setBody(MultiPartFormDataContent(manualFormData))
}

// File upload with progress monitoring
val largeFile = File("large-video.mp4")
val fileContent = LocalFileContent(largeFile).observable { bytesWritten ->
    val progress = (bytesWritten.toDouble() / largeFile.length() * 100).toInt()
    println("Upload progress: $progress%")
}

val uploadResponse = client.post("https://api.example.com/video-upload") {
    setBody(fileContent)
}

// JSON content handling
val jsonResponse = client.post("https://api.example.com/users") {
    contentType(ContentType.Application.Json)
    setBody("""
        {
            "name": "Jane Smith",
            "email": "jane@example.com",
            "profile": {
                "age": 28,
                "location": "New York"
            }
        }
    """.trimIndent())
}

// XML content handling
val xmlResponse = client.post("https://api.example.com/xml-endpoint") {
    contentType(ContentType.Application.Xml)
    setBody("""
        <?xml version="1.0" encoding="UTF-8"?>
        <user>
            <name>Bob Johnson</name>
            <email>bob@example.com</email>
        </user>
    """.trimIndent())
}

// Custom content with headers
val customResponse = client.post("https://api.example.com/custom") {
    headers {
        append("X-Custom-Header", "custom-value")
        append("Authorization", "Bearer token123")
    }
    contentType(ContentType.Application.OctetStream)
    setBody("Custom binary content".toByteArray())
}

// Streaming request content
val streamingResponse = client.post("https://api.example.com/stream") {
    setBody(object : OutgoingContent.WriteChannelContent() {
        override val contentType = ContentType.Text.Plain
        
        override suspend fun writeTo(channel: ByteWriteChannel) {
            repeat(1000) { i ->
                channel.writeStringUtf8("Line $i\n")
                if (i % 100 == 0) {
                    channel.flush()
                    kotlinx.coroutines.delay(10) // Simulate streaming delay
                }
            }
        }
    })
}

// Form data with content length
val contentLengthForm = FormDataContent(Parameters.build {
    append("field1", "value1")
    append("field2", "value2")
})

val contentLengthResponse = client.post("https://api.example.com/sized-form") {
    setBody(contentLengthForm)
    headers {
        append(HttpHeaders.ContentLength, contentLengthForm.contentLength.toString())
    }
}

client.close()

Parameters Handling

Parameter building and manipulation for forms and URLs.

/**
 * Immutable parameters collection
 */
interface Parameters : StringValues {
    companion object {
        val Empty: Parameters
        
        /** Build parameters using DSL */
        fun build(builder: ParametersBuilder.() -> Unit): Parameters
    }
}

/**
 * Mutable parameters builder
 */
class ParametersBuilder : StringValuesBuilder() {
    /** Build immutable Parameters */
    fun build(): Parameters
}

/**
 * Common string values interface
 */
interface StringValues {
    /** Get all value names */
    fun names(): Set<String>
    
    /** Get all values for a name */
    fun getAll(name: String): List<String>?
    
    /** Get first value for a name */
    operator fun get(name: String): String?
    
    /** Check if name exists */
    fun contains(name: String): Boolean
    
    /** Check if name contains specific value */
    fun contains(name: String, value: String): Boolean
    
    /** Iterate over all entries */
    fun forEach(action: (String, List<String>) -> Unit)
}

docs

content-handling.md

core-client.md

engine-configuration.md

index.md

plugin-system.md

request-building.md

response-processing.md

server-sent-events.md

websocket-support.md

tile.json