All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.aws.sdk.kotlin.crt.http.Headers.kt Maven / Gradle / Ivy

There is a newer version: 0.8.9
Show newest version
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

package aws.sdk.kotlin.crt.http

import aws.sdk.kotlin.crt.util.CaseInsensitiveMap

/**
 * A single HTTP header key/value pair
 */
public data class HttpHeader(
    val name: String,
    val value: String,
)

/**
 * An immutable map of HTTP headers
 */
public interface Headers {

    public companion object {
        public fun build(block: HeadersBuilder.() -> Unit): Headers = HeadersBuilder().apply(block).build()
    }

    /**
     * Gets first value from the list of values associated with a [name], or null if the name is not present
     */
    public operator fun get(name: String): String? = getAll(name)?.firstOrNull()

    /**
     * Gets all values associated with the [name], or null if the name is not present
     */
    public fun getAll(name: String): List?

    /**
     * Gets all names from the map
     */
    public fun names(): Set

    /**
     * Gets all entries from the map
     */
    public fun entries(): Set>>

    /**
     * Checks if the given [name] exists in the map
     */
    public operator fun contains(name: String): Boolean

    /**
     * Checks if the given [name] and [value] pair exists in the map
     */
    public fun contains(name: String, value: String): Boolean = getAll(name)?.contains(value) ?: false

    /**
     * Iterates over all entries in this map and calls [body] for each pair
     *
     * Can be optimized in implementations
     */
    public fun forEach(body: (String, List) -> Unit): Unit = entries().forEach { (k, v) -> body(k, v) }

    /**
     * Checks if this map is empty
     */
    public fun isEmpty(): Boolean
}

/**
 * Build an immutable HTTP header map
 */
public class HeadersBuilder {
    private val values: MutableMap> = CaseInsensitiveMap()
    private var built: Boolean = false

    public fun getAll(name: String): List? = values[name]

    public operator fun contains(name: String): Boolean = name in values

    public fun contains(name: String, value: String): Boolean = values[name]?.contains(value) ?: false

    public fun names(): Set = values.keys

    public fun isEmpty(): Boolean = values.isEmpty()

    public fun entries(): Set>> = values.entries

    public operator fun set(name: String, value: String) {
        val list = ensureListForKey(name, 1)
        list.clear()
        list.add(value)
    }

    public operator fun get(name: String): String? = getAll(name)?.firstOrNull()

    public fun append(name: String, value: String) {
        ensureListForKey(name, 1).add(value)
    }

    public fun appendAll(stringValues: Headers) {
        stringValues.forEach { name, values ->
            appendAll(name, values)
        }
    }

    public fun appendMissing(stringValues: Headers) {
        stringValues.forEach { name, values ->
            appendMissing(name, values)
        }
    }

    public fun appendAll(name: String, values: Iterable) {
        ensureListForKey(name, (values as? Collection)?.size ?: 2).let { list ->
            values.forEach { value ->
                list.add(value)
            }
        }
    }

    public fun appendMissing(name: String, values: Iterable) {
        val existing = this.values[name]?.toSet() ?: emptySet()

        appendAll(name, values.filter { it !in existing })
    }

    public fun remove(name: String): MutableList? = values.remove(name)

    public fun removeKeysWithNoEntries() {
        for ((k, _) in values.filter { it.value.isEmpty() }) {
            remove(k)
        }
    }

    public fun remove(name: String, value: String): Boolean = values[name]?.remove(value) ?: false

    public fun clear(): Unit = values.clear()

    public fun build(): Headers {
        require(!built) { "HeadersBuilder can only build a single instance" }
        built = true
        return HeadersImpl(values)
    }

    private fun ensureListForKey(name: String, size: Int): MutableList {
        if (built) {
            throw IllegalStateException("Cannot modify a builder when final structure has already been built")
        }
        return values[name] ?: ArrayList(size).also { values[name] = it }
    }
}

private class HeadersImpl(
    initialValues: Map> = emptyMap(),
) : Headers {
    private val values: Map> =
        CaseInsensitiveMap>().apply { putAll(initialValues) }

    override fun getAll(name: String): List? = values[name]

    override fun names(): Set = values.keys

    override fun entries(): Set>> = values.entries

    override operator fun contains(name: String): Boolean = values.containsKey(name)

    override fun contains(name: String, value: String): Boolean = getAll(name)?.contains(value) ?: false

    override fun isEmpty(): Boolean = values.isEmpty()

    override fun toString(): String = "Headers ${entries()}"
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy