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

main.okhttp3.internal.-HeadersCommon.kt Maven / Gradle / Ivy

The newest version!
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
@file:Suppress("ktlint:standard:filename")

package okhttp3.internal

import okhttp3.Headers

internal fun Headers.commonName(index: Int): String = namesAndValues.getOrNull(index * 2) ?: throw IndexOutOfBoundsException("name[$index]")

internal fun Headers.commonValue(index: Int): String =
  namesAndValues.getOrNull(index * 2 + 1) ?: throw IndexOutOfBoundsException("value[$index]")

internal fun Headers.commonValues(name: String): List {
  var result: MutableList? = null
  for (i in 0 until size) {
    if (name.equals(name(i), ignoreCase = true)) {
      if (result == null) result = ArrayList(2)
      result.add(value(i))
    }
  }
  return result?.toList().orEmpty()
}

internal fun Headers.commonIterator(): Iterator> {
  return Array(size) { name(it) to value(it) }.iterator()
}

internal fun Headers.commonNewBuilder(): Headers.Builder {
  val result = Headers.Builder()
  result.namesAndValues += namesAndValues
  return result
}

internal fun Headers.commonEquals(other: Any?): Boolean {
  return other is Headers && namesAndValues.contentEquals(other.namesAndValues)
}

internal fun Headers.commonHashCode(): Int = namesAndValues.contentHashCode()

internal fun Headers.commonToString(): String {
  return buildString {
    for (i in 0 until size) {
      val name = name(i)
      val value = value(i)
      append(name)
      append(": ")
      append(if (isSensitiveHeader(name)) "██" else value)
      append("\n")
    }
  }
}

internal fun commonHeadersGet(
  namesAndValues: Array,
  name: String,
): String? {
  for (i in namesAndValues.size - 2 downTo 0 step 2) {
    if (name.equals(namesAndValues[i], ignoreCase = true)) {
      return namesAndValues[i + 1]
    }
  }
  return null
}

internal fun Headers.Builder.commonAdd(
  name: String,
  value: String,
) = apply {
  headersCheckName(name)
  headersCheckValue(value, name)
  commonAddLenient(name, value)
}

internal fun Headers.Builder.commonAddAll(headers: Headers) =
  apply {
    for (i in 0 until headers.size) {
      commonAddLenient(headers.name(i), headers.value(i))
    }
  }

internal fun Headers.Builder.commonAddLenient(
  name: String,
  value: String,
) = apply {
  namesAndValues.add(name)
  namesAndValues.add(value.trim())
}

internal fun Headers.Builder.commonRemoveAll(name: String) =
  apply {
    var i = 0
    while (i < namesAndValues.size) {
      if (name.equals(namesAndValues[i], ignoreCase = true)) {
        namesAndValues.removeAt(i) // name
        namesAndValues.removeAt(i) // value
        i -= 2
      }
      i += 2
    }
  }

/**
 * Set a field with the specified value. If the field is not found, it is added. If the field is
 * found, the existing values are replaced.
 */
internal fun Headers.Builder.commonSet(
  name: String,
  value: String,
) = apply {
  headersCheckName(name)
  headersCheckValue(value, name)
  removeAll(name)
  commonAddLenient(name, value)
}

/** Equivalent to `build().get(name)`, but potentially faster. */
internal fun Headers.Builder.commonGet(name: String): String? {
  for (i in namesAndValues.size - 2 downTo 0 step 2) {
    if (name.equals(namesAndValues[i], ignoreCase = true)) {
      return namesAndValues[i + 1]
    }
  }
  return null
}

internal fun Headers.Builder.commonBuild(): Headers = Headers(namesAndValues.toTypedArray())

internal fun headersCheckName(name: String) {
  require(name.isNotEmpty()) { "name is empty" }
  for (i in name.indices) {
    val c = name[i]
    require(c in '\u0021'..'\u007e') {
      "Unexpected char 0x${c.charCode()} at $i in header name: $name"
    }
  }
}

internal fun headersCheckValue(
  value: String,
  name: String,
) {
  for (i in value.indices) {
    val c = value[i]
    require(c == '\t' || c in '\u0020'..'\u007e') {
      "Unexpected char 0x${c.charCode()} at $i in $name value" +
        (if (isSensitiveHeader(name)) "" else ": $value")
    }
  }
}

private fun Char.charCode() =
  code.toString(16).let {
    if (it.length < 2) {
      "0$it"
    } else {
      it
    }
  }

internal fun commonHeadersOf(vararg inputNamesAndValues: String): Headers {
  require(inputNamesAndValues.size % 2 == 0) { "Expected alternating header names and values" }

  // Make a defensive copy and clean it up.
  val namesAndValues: Array = arrayOf(*inputNamesAndValues)
  for (i in namesAndValues.indices) {
    require(namesAndValues[i] != null) { "Headers cannot be null" }
    namesAndValues[i] = inputNamesAndValues[i].trim()
  }

  // Check for malformed headers.
  for (i in namesAndValues.indices step 2) {
    val name = namesAndValues[i]
    val value = namesAndValues[i + 1]
    headersCheckName(name)
    headersCheckValue(value, name)
  }

  return Headers(namesAndValues)
}

internal fun Map.commonToHeaders(): Headers {
  // Make a defensive copy and clean it up.
  val namesAndValues = arrayOfNulls(size * 2)
  var i = 0
  for ((k, v) in this) {
    val name = k.trim()
    val value = v.trim()
    headersCheckName(name)
    headersCheckValue(value, name)
    namesAndValues[i] = name
    namesAndValues[i + 1] = value
    i += 2
  }

  return Headers(namesAndValues as Array)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy