CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-ktor--ktor-http-js

JavaScript/WebAssembly implementation of Ktor's HTTP core library providing HTTP utilities, URL building, request/response handling, and HTTP method definitions for multiplatform Kotlin applications targeting JavaScript environments

Pending
Overview
Eval results
Files

content.mddocs/

Content Framework

Outgoing content types for representing different data sources and formats in HTTP responses, with support for multipart data, versioning, and caching.

Capabilities

OutgoingContent Base Class

Abstract base class for all outgoing HTTP content with common properties and extensibility.

/**
 * Base class for all outgoing HTTP content
 */
abstract class OutgoingContent {
    open val contentType: ContentType?
    open val contentLength: Long?
    open val status: HttpStatusCode?
    open val headers: Headers?
    
    /**
     * Get content property by key
     * @param key Property key
     * @return Property value or null
     */
    fun <T : Any> getProperty(key: AttributeKey<T>): T?
    
    /**
     * Set content property
     * @param key Property key
     * @param value Property value
     */
    fun <T : Any> setProperty(key: AttributeKey<T>, value: T?)
    
    /**
     * Get trailer headers (sent after content)
     * @return Trailer headers or null
     */
    open fun trailers(): Headers?
}

/**
 * Check if content is empty
 * @return true if content has no body
 */
fun OutgoingContent.isEmpty(): Boolean

ByteArrayContent

Content type for byte array data.

/**
 * Content from byte array
 */
abstract class OutgoingContent.ByteArrayContent : OutgoingContent() {
    /**
     * Get content as byte array
     * @return Content bytes
     */
    abstract fun bytes(): ByteArray
}

/**
 * Concrete implementation for byte array content
 */
class ByteArrayContent(
    private val bytes: ByteArray,
    override val contentType: ContentType?,
    override val status: HttpStatusCode? = null
) : OutgoingContent.ByteArrayContent() {
    override fun bytes(): ByteArray = bytes
    override val contentLength: Long? = bytes.size.toLong()
}

TextContent

Content type for text data with automatic encoding.

/**
 * Content from text string
 */
class TextContent(
    val text: String,
    override val contentType: ContentType,
    override val status: HttpStatusCode? = null
) : OutgoingContent.ByteArrayContent() {
    
    override fun bytes(): ByteArray
    override val contentLength: Long?
}

ReadChannelContent

Content type for streaming data from ByteReadChannel.

/**
 * Content from ByteReadChannel
 */
abstract class OutgoingContent.ReadChannelContent : OutgoingContent() {
    /**
     * Get read channel for content
     * @return ByteReadChannel with content
     */
    abstract fun readFrom(): ByteReadChannel
    
    /**
     * Get read channel for content range
     * @param range Byte range to read
     * @return ByteReadChannel with range content
     */
    open fun readFrom(range: LongRange): ByteReadChannel = readFrom()
}

WriteChannelContent

Content type for data written to ByteWriteChannel.

/**
 * Content written to ByteWriteChannel
 */
abstract class OutgoingContent.WriteChannelContent : OutgoingContent() {
    /**
     * Write content to channel
     * @param channel Channel to write to
     */
    abstract suspend fun writeTo(channel: ByteWriteChannel)
}

/**
 * Content written via lambda function
 */
class ChannelWriterContent(
    private val body: suspend ByteWriteChannel.() -> Unit,
    override val contentType: ContentType?,
    override val status: HttpStatusCode? = null,
    override val contentLength: Long? = null
) : OutgoingContent.WriteChannelContent() {
    
    override suspend fun writeTo(channel: ByteWriteChannel) {
        channel.body()
    }
}

Additional Content Types

Specialized content types for different output methods.

/**
 * Content written to OutputStream
 */
class OutputStreamContent(
    private val body: suspend OutputStream.() -> Unit,
    override val contentType: ContentType?,
    override val status: HttpStatusCode? = null,
    override val contentLength: Long? = null
) : OutgoingContent.WriteChannelContent()

/**
 * Content written to Writer
 */
class WriterContent(
    private val body: suspend Writer.() -> Unit,
    override val contentType: ContentType?,
    override val status: HttpStatusCode? = null,
    override val contentLength: Long? = null
) : OutgoingContent.WriteChannelContent()

/**
 * Content from URI/URL
 */
class URIFileContent(
    val uri: URI,
    override val contentType: ContentType?,
    override val contentLength: Long? = null
) : OutgoingContent.ReadChannelContent() {
    
    constructor(url: URL, contentType: ContentType?)
    
    override fun readFrom(): ByteReadChannel
}

NoContent and Special Types

Special content types for empty responses and protocol upgrades.

/**
 * Empty content (no body)
 */
abstract class OutgoingContent.NoContent : OutgoingContent()

/**
 * Protocol upgrade content (e.g., WebSocket)
 */
abstract class OutgoingContent.ProtocolUpgrade : OutgoingContent() {
    override val status: HttpStatusCode? = HttpStatusCode.SwitchingProtocols
    
    /**
     * Handle protocol upgrade
     * @param input Input channel
     * @param output Output channel
     * @param engineContext Engine coroutine context
     * @param userContext User coroutine context
     */
    abstract suspend fun upgrade(
        input: ByteReadChannel,
        output: ByteWriteChannel,
        engineContext: CoroutineContext,
        userContext: CoroutineContext
    )
}

/**
 * Null body marker
 */
object NullBody

Content Wrapper

Base class for wrapping and modifying existing content.

/**
 * Content wrapper for modifying existing content
 */
abstract class OutgoingContent.ContentWrapper(
    private val delegate: OutgoingContent
) : OutgoingContent() {
    
    /**
     * Create copy with different delegate
     * @param delegate New delegate content
     * @return New ContentWrapper instance
     */
    abstract fun copy(delegate: OutgoingContent): OutgoingContent.ContentWrapper
    
    /**
     * Get wrapped content
     * @return Delegate content
     */
    fun delegate(): OutgoingContent = delegate
    
    // Default implementations delegate to wrapped content
    override val contentType: ContentType? get() = delegate.contentType
    override val contentLength: Long? get() = delegate.contentLength
    override val status: HttpStatusCode? get() = delegate.status
    override val headers: Headers? get() = delegate.headers
    override fun <T : Any> getProperty(key: AttributeKey<T>): T? = delegate.getProperty(key)
    override fun <T : Any> setProperty(key: AttributeKey<T>, value: T?) = delegate.setProperty(key, value)
}

Multipart Data

Interface and types for handling multipart content.

/**
 * Interface for multipart data reading  
 */
interface MultiPartData {
    /**
     * Read next part from multipart data
     * @return Next PartData or null if no more parts
     */
    suspend fun readPart(): PartData?
    
    companion object {
        val Empty: MultiPartData
    }
}

/**
 * Base class for multipart parts
 */
abstract class PartData(
    private val dispose: () -> Unit,
    val headers: Headers
) {
    val contentDisposition: ContentDisposition?
    val contentType: ContentType?
    val name: String?
    
    /**
     * Dispose part resources
     */
    fun dispose() = dispose.invoke()
}

/**
 * Form field part
 */
class PartData.FormItem(
    val value: String,
    dispose: () -> Unit,
    headers: Headers
) : PartData(dispose, headers)

/**
 * File upload part
 */
class PartData.FileItem(
    val provider: () -> InputStream,
    dispose: () -> Unit,
    headers: Headers
) : PartData(dispose, headers) {
    val originalFileName: String?
    val streamProvider: () -> InputStream  // JVM-specific
}

/**
 * Binary data part
 */
class PartData.BinaryItem(
    val provider: () -> ByteArray,
    dispose: () -> Unit,
    headers: Headers
) : PartData(dispose, headers)

/**
 * Binary channel part
 */
class PartData.BinaryChannelItem(
    val provider: () -> ByteReadChannel,
    dispose: () -> Unit,
    headers: Headers
) : PartData(dispose, headers)

Multipart Utilities

Utility functions for working with multipart data.

/**
 * Convert multipart data to Flow
 * @return Flow of PartData
 */
fun MultiPartData.asFlow(): Flow<PartData>

/**
 * Read all parts into list
 * @return List of all PartData
 */
suspend fun MultiPartData.readAllParts(): List<PartData>

/**
 * Process each part with action
 * @param action Suspend function to process each part
 */
suspend fun MultiPartData.forEachPart(action: suspend (PartData) -> Unit)

Content Versioning

System for content versioning with ETags and Last-Modified headers.

/**
 * Base interface for content versioning
 */
interface Version {
    /**
     * Check version against request headers
     * @param headers Request headers
     * @return Version check result
     */
    fun check(headers: Headers): VersionCheckResult
    
    /**
     * Append version headers to response
     * @param builder Headers builder
     */
    fun appendHeadersTo(builder: HeadersBuilder)
}

/**
 * ETag-based versioning
 */
class EntityTagVersion(
    val etag: String,
    val weak: Boolean = false
) : Version {
    
    /**
     * Check if this ETag matches another
     * @param other Other ETag version
     * @return true if matches
     */
    fun match(other: EntityTagVersion): Boolean
    
    /**
     * Check if this ETag matches any in list
     * @param etags List of ETag versions
     * @return Version check result
     */
    fun match(etags: List<EntityTagVersion>): VersionCheckResult
    
    /**
     * Check if this ETag matches none in list
     * @param etags List of ETag versions  
     * @return Version check result
     */
    fun noneMatch(etags: List<EntityTagVersion>): VersionCheckResult
    
    companion object {
        val STAR: EntityTagVersion
        
        /**
         * Parse ETag header value
         * @param value ETag header value
         * @return List of EntityTagVersion instances
         */
        fun parse(value: String): List<EntityTagVersion>
        
        /**
         * Parse single ETag value
         * @param value Single ETag value
         * @return EntityTagVersion instance
         */
        fun parseSingle(value: String): EntityTagVersion
    }
}

/**
 * Last-Modified based versioning
 */
class LastModifiedVersion(
    val lastModified: GMTDate
) : Version {
    
    /**
     * Check if modified since dates
     * @param dates List of dates to check
     * @return true if modified
     */
    fun ifModifiedSince(dates: List<GMTDate>): Boolean
    
    /**
     * Check if unmodified since dates
     * @param dates List of dates to check
     * @return true if unmodified
     */
    fun ifUnmodifiedSince(dates: List<GMTDate>): Boolean
}

/**
 * Version check result
 */
enum class VersionCheckResult(val statusCode: HttpStatusCode) {
    OK(HttpStatusCode.OK),
    NOT_MODIFIED(HttpStatusCode.NotModified),
    PRECONDITION_FAILED(HttpStatusCode.PreconditionFailed)
}

/**
 * Create EntityTagVersion from string
 * @param etag ETag value
 * @return EntityTagVersion instance
 */
fun EntityTagVersion(etag: String): EntityTagVersion

/**
 * Create LastModifiedVersion from Date
 * @param date Last modified date
 * @return LastModifiedVersion instance
 */
fun LastModifiedVersion(date: Date): LastModifiedVersion

Caching Options

System for content caching configuration.

/**
 * Content caching configuration
 */
data class CachingOptions(
    val cacheControl: CacheControl? = null,
    val expires: GMTDate? = null
)

/**
 * Get caching options from content
 * @return CachingOptions or null
 */
fun OutgoingContent.getCaching(): CachingOptions?

/**
 * Set caching options on content
 * @param options Caching options to set
 */
fun OutgoingContent.setCaching(options: CachingOptions)

/**
 * Get versions list from content
 * @return List of Version instances
 */
fun OutgoingContent.getVersions(): List<Version>

/**
 * Set versions on content
 * @param versions List of Version instances
 */
fun OutgoingContent.setVersions(versions: List<Version>)

Content Compression

Utilities for content compression.

/**
 * Create compressed version of content
 * @param encoder Content encoder to use
 * @param coroutineContext Coroutine context for compression
 * @return Compressed OutgoingContent
 */
fun OutgoingContent.compressed(
    encoder: ContentEncoder,
    coroutineContext: CoroutineContext = EmptyCoroutineContext
): OutgoingContent

Usage Examples:

import io.ktor.http.content.*
import io.ktor.http.*

// Create different content types
val textContent = TextContent("Hello World", ContentType.Text.Plain)
val jsonContent = TextContent("""{"message": "hello"}""", ContentType.Application.Json)
val binaryContent = ByteArrayContent(byteArrayOf(1, 2, 3), ContentType.Application.OctetStream)

// Create streaming content
val channelContent = ChannelWriterContent(
    body = { writeStringUtf8("Hello from channel") },
    contentType = ContentType.Text.Plain
)

// Work with multipart data
suspend fun processMultipart(multipart: MultiPartData) {
    multipart.forEachPart { part ->
        when (part) {
            is PartData.FormItem -> println("Form field: ${part.name} = ${part.value}")
            is PartData.FileItem -> println("File: ${part.originalFileName}")
            is PartData.BinaryItem -> println("Binary data: ${part.provider().size} bytes")
        }
        part.dispose()
    }
}

// Content versioning
val etag = EntityTagVersion("abc123")
val lastModified = LastModifiedVersion(GMTDate())

textContent.setVersions(listOf(etag, lastModified))

// Caching
val caching = CachingOptions(
    cacheControl = CacheControl.MaxAge(3600),
    expires = GMTDate(System.currentTimeMillis() + 3600000)
)
textContent.setCaching(caching)

// Check content properties
val isEmpty = textContent.isEmpty() // false
val versions = textContent.getVersions()
val cachingOptions = textContent.getCaching()

Install with Tessl CLI

npx tessl i tessl/maven-io-ktor--ktor-http-js

docs

authentication.md

content-types.md

content.md

cookies.md

headers.md

http-core.md

index.md

urls.md

tile.json