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

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

The newest version!
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed 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 java.io.IOException
import java.io.InterruptedIOException
import java.net.InetSocketAddress
import java.net.ServerSocket
import java.net.Socket
import java.net.SocketTimeoutException
import java.nio.charset.Charset
import java.util.Collections
import java.util.Locale
import java.util.TimeZone
import java.util.concurrent.ThreadFactory
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import kotlin.text.Charsets.UTF_16BE
import kotlin.text.Charsets.UTF_16LE
import kotlin.text.Charsets.UTF_32BE
import kotlin.text.Charsets.UTF_32LE
import kotlin.text.Charsets.UTF_8
import kotlin.time.Duration
import okhttp3.EventListener
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.defaultPort
import okhttp3.OkHttpClient
import okhttp3.RequestBody
import okhttp3.Response
import okhttp3.ResponseBody
import okhttp3.internal.http2.Header
import okio.Buffer
import okio.BufferedSource
import okio.Source

@JvmField
internal val EMPTY_HEADERS: Headers = commonEmptyHeaders

@JvmField
internal val EMPTY_REQUEST: RequestBody = commonEmptyRequestBody

@JvmField
internal val EMPTY_RESPONSE: ResponseBody = commonEmptyResponse

/** GMT and UTC are equivalent for our purposes. */
@JvmField
internal val UTC: TimeZone = TimeZone.getTimeZone("GMT")!!

internal fun threadFactory(
  name: String,
  daemon: Boolean,
): ThreadFactory =
  ThreadFactory { runnable ->
    Thread(runnable, name).apply {
      isDaemon = daemon
    }
  }

internal fun HttpUrl.toHostHeader(includeDefaultPort: Boolean = false): String {
  val host =
    if (":" in host) {
      "[$host]"
    } else {
      host
    }
  return if (includeDefaultPort || port != defaultPort(scheme)) {
    "$host:$port"
  } else {
    host
  }
}

/** Returns a [Locale.US] formatted [String]. */
internal fun format(
  format: String,
  vararg args: Any,
): String {
  return String.format(Locale.US, format, *args)
}

@Throws(IOException::class)
internal fun BufferedSource.readBomAsCharset(default: Charset): Charset {
  return when (select(UNICODE_BOMS)) {
    0 -> UTF_8
    1 -> UTF_16BE
    2 -> UTF_16LE
    3 -> UTF_32BE
    4 -> UTF_32LE
    -1 -> default
    else -> throw AssertionError()
  }
}

internal fun checkDuration(
  name: String,
  duration: Long,
  unit: TimeUnit,
): Int {
  check(duration >= 0L) { "$name < 0" }
  val millis = unit.toMillis(duration)
  require(millis <= Integer.MAX_VALUE) { "$name too large" }
  require(millis != 0L || duration <= 0L) { "$name too small" }
  return millis.toInt()
}

internal fun checkDuration(
  name: String,
  duration: Duration,
): Int {
  check(!duration.isNegative()) { "$name < 0" }
  val millis = duration.inWholeMilliseconds
  require(millis <= Integer.MAX_VALUE) { "$name too large" }
  require(millis != 0L || !duration.isPositive()) { "$name too small" }
  return millis.toInt()
}

internal fun List
.toHeaders(): Headers { val builder = Headers.Builder() for ((name, value) in this) { builder.addLenient(name.utf8(), value.utf8()) } return builder.build() } internal fun Headers.toHeaderList(): List
= (0 until size).map { Header(name(it), value(it)) } /** Returns true if an HTTP request for this URL and [other] can reuse a connection. */ internal fun HttpUrl.canReuseConnectionFor(other: HttpUrl): Boolean = host == other.host && port == other.port && scheme == other.scheme internal fun EventListener.asFactory() = EventListener.Factory { this } /** * Reads until this is exhausted or the deadline has been reached. This is careful to not extend the * deadline if one exists already. */ @Throws(IOException::class) internal fun Source.skipAll( duration: Int, timeUnit: TimeUnit, ): Boolean { val nowNs = System.nanoTime() val originalDurationNs = if (timeout().hasDeadline()) { timeout().deadlineNanoTime() - nowNs } else { Long.MAX_VALUE } timeout().deadlineNanoTime(nowNs + minOf(originalDurationNs, timeUnit.toNanos(duration.toLong()))) return try { val skipBuffer = Buffer() while (read(skipBuffer, 8192) != -1L) { skipBuffer.clear() } true // Success! The source has been exhausted. } catch (_: InterruptedIOException) { false // We ran out of time before exhausting the source. } finally { if (originalDurationNs == Long.MAX_VALUE) { timeout().clearDeadline() } else { timeout().deadlineNanoTime(nowNs + originalDurationNs) } } } /** * Attempts to exhaust this, returning true if successful. This is useful when reading a complete * source is helpful, such as when doing so completes a cache body or frees a socket connection for * reuse. */ internal fun Source.discard( timeout: Int, timeUnit: TimeUnit, ): Boolean = try { this.skipAll(timeout, timeUnit) } catch (_: IOException) { false } internal fun Socket.peerName(): String { val address = remoteSocketAddress return if (address is InetSocketAddress) address.hostName else address.toString() } /** * Returns true if new reads and writes should be attempted on this. * * Unfortunately Java's networking APIs don't offer a good health check, so we go on our own by * attempting to read with a short timeout. If the fails immediately we know the socket is * unhealthy. * * @param source the source used to read bytes from the socket. */ internal fun Socket.isHealthy(source: BufferedSource): Boolean { return try { val readTimeout = soTimeout try { soTimeout = 1 !source.exhausted() } finally { soTimeout = readTimeout } } catch (_: SocketTimeoutException) { true // Read timed out; socket is good. } catch (_: IOException) { false // Couldn't read; socket is closed. } } internal inline fun threadName( name: String, block: () -> Unit, ) { val currentThread = Thread.currentThread() val oldName = currentThread.name currentThread.name = name try { block() } finally { currentThread.name = oldName } } /** Returns the Content-Length as reported by the response headers. */ internal fun Response.headersContentLength(): Long { return headers["Content-Length"]?.toLongOrDefault(-1L) ?: -1L } /** Returns an immutable copy of this. */ internal fun List.toImmutableList(): List { return Collections.unmodifiableList(toMutableList()) } /** Returns an immutable list containing [elements]. */ @SafeVarargs internal fun immutableListOf(vararg elements: T): List { return Collections.unmodifiableList(listOf(*elements.clone())) } /** Closes this, ignoring any checked exceptions. */ internal fun Socket.closeQuietly() { try { close() } catch (e: AssertionError) { throw e } catch (rethrown: RuntimeException) { if (rethrown.message == "bio == null") { // Conscrypt in Android 10 and 11 may throw closing an SSLSocket. This is safe to ignore. // https://issuetracker.google.com/issues/177450597 return } throw rethrown } catch (_: Exception) { } } /** Closes this, ignoring any checked exceptions. */ internal fun ServerSocket.closeQuietly() { try { close() } catch (rethrown: RuntimeException) { throw rethrown } catch (_: Exception) { } } internal fun Long.toHexString(): String = java.lang.Long.toHexString(this) internal fun Int.toHexString(): String = Integer.toHexString(this) @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE") internal inline fun Any.wait() = (this as Object).wait() @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE") internal inline fun Any.notify() = (this as Object).notify() @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE") internal inline fun Any.notifyAll() = (this as Object).notifyAll() internal fun readFieldOrNull( instance: Any, fieldType: Class, fieldName: String, ): T? { var c: Class<*> = instance.javaClass while (c != Any::class.java) { try { val field = c.getDeclaredField(fieldName) field.isAccessible = true val value = field.get(instance) return if (!fieldType.isInstance(value)) null else fieldType.cast(value) } catch (_: NoSuchFieldException) { } c = c.superclass } // Didn't find the field we wanted. As a last gasp attempt, // try to find the value on a delegate. if (fieldName != "delegate") { val delegate = readFieldOrNull(instance, Any::class.java, "delegate") if (delegate != null) return readFieldOrNull(delegate, fieldType, fieldName) } return null } @JvmField internal val assertionsEnabled: Boolean = OkHttpClient::class.java.desiredAssertionStatus() /** * Returns the string "OkHttp" unless the library has been shaded for inclusion in another library, * or obfuscated with tools like R8 or ProGuard. In such cases it'll return a longer string like * "com.example.shaded.okhttp3.OkHttp". In large applications it's possible to have multiple OkHttp * instances; this makes it clear which is which. */ @JvmField internal val okHttpName: String = OkHttpClient::class.java.name.removePrefix("okhttp3.").removeSuffix("Client") @Suppress("NOTHING_TO_INLINE") internal inline fun ReentrantLock.assertHeld() { if (assertionsEnabled && !this.isHeldByCurrentThread) { throw AssertionError("Thread ${Thread.currentThread().name} MUST hold lock on $this") } } @Suppress("NOTHING_TO_INLINE") internal inline fun Any.assertThreadHoldsLock() { if (assertionsEnabled && !Thread.holdsLock(this)) { throw AssertionError("Thread ${Thread.currentThread().name} MUST hold lock on $this") } } @Suppress("NOTHING_TO_INLINE") internal inline fun ReentrantLock.assertNotHeld() { if (assertionsEnabled && this.isHeldByCurrentThread) { throw AssertionError("Thread ${Thread.currentThread().name} MUST NOT hold lock on $this") } } @Suppress("NOTHING_TO_INLINE") internal inline fun Any.assertThreadDoesntHoldLock() { if (assertionsEnabled && Thread.holdsLock(this)) { throw AssertionError("Thread ${Thread.currentThread().name} MUST NOT hold lock on $this") } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy