OkHttp之Headers请求头

QuibblerQuibbler 2021-03-14 约 10 分钟 3268 次阅读

OkHttp之Headers请求头

前面看了请求Request和请求体RequestBody类实现源码,最后我们来看看请求头Headers。Headers是HTTP请求和相应的头部所包含的信息:MIME、日期、缓存、Cookie等。

1、Headers

OkHttp设计的Headers类结构如下,让我们仔细看看其中的实现。


1.1、核心成员

Headers类内部并没有用两个数组分别维护请求头中的name和value值。而是用一个集合namesAndValues维护两份一一对应的数据:name和values。这样做适合请求头信息直接发送和解析,省去网络请求的时候再对头部信息进行拼接处理。

private val namesAndValues: Array<String>

在集合中Headers信息的存储是:[name1,value1,name2,value2,name3,value3,... ],所以Headers的大小size是数据集合namesAndValues的一半,

/** Returns the number of field values. */
    val size: Int
        get() = namesAndValues.size / 2

1.2、获取Header信息

获取Headers数据,value紧接着name存储。

/** Returns the field at `position`. */
    fun name(index: Int): String = namesAndValues[index * 2]
  
    /** Returns the value at `index`. */
    fun value(index: Int): String = namesAndValues[index * 2 + 1]

1.3、获取name和values

通过names()方法可以获取Headers请求头中所有不重复的name集合:

fun names(): Set<String> {
        val result = TreeSet(String.CASE_INSENSITIVE_ORDER)
        for (i in 0 until size) {
          result.add(name(i))
        }
        return Collections.unmodifiableSet(result)
    }

借助values(name )可以获取values集合。有点奇怪,为什么获取的values会是集合,一个name不只对应一个value吗?为什么可以用一个name获取values的集合呢?后面看到通过Builder添加name-value的方法就可以发现,name是可以重复添加的,这会导致同一个name可能对应多个values。所以会返回重复name的所有value。

/** Returns an immutable list of the header values for `name`. */
  fun values(name: String): List<String> {
    var result: MutableList<String>? = 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 if (result != null) {
      Collections.unmodifiableList(result)
    } else {
      emptyList()
    }
  }

2、Builder

不能直接通过构造方法创建Headers实例,而应该通过Builder构建Headers实例。

2.1、集合成员

内部也是用一个可修改集合namesAndValues保存,最后将用于构造Headers实例。

internal val namesAndValues: MutableList<String> = ArrayList(20)

2.2、添加Header信息

通过向Headers中添加头部信息,了解Headers是如何用一个集合namesAndValues存储K-V的。以add(name,value)为例:

fun add(name: String, value: String) = apply {
      checkName(name)
      checkValue(value, name)
      addLenient(name, value)
    }

先校验name和value的合法性,再调用内部方法addLenient(name,value)完成请求头信息添加。其它add方法也是如此。

/**
     * Add a field with the specified value without any validation. Only appropriate for headers
     * from the remote peer or cache.
     */
    internal fun addLenient(name: String, value: String) = apply {
      namesAndValues.add(name)
      namesAndValues.add(value.trim())
    }

如果Headers中已有name需要更新,应使用set(name,value)方法,否者会添加重复的name到namesAndValues集合中。

/**
     * 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.
     */
    operator fun set(name: String, value: String) = apply {
      checkName(name)
      checkValue(value, name)
      removeAll(name)
      addLenient(name, value)
    }

还有很多其它添加请求头信息的便捷方法,就不一一列出。开发者在开发中应当以源码为准,实时查看源码的实现。

2.3、校验name和values

Headers内部提供两个check静态方法,用于校验Headers请求头中的name和value是否有效,checkName(name)方法实现如下,如果加了什么奇怪的name导致失败,最好来这里看看原因。

private fun checkName(name: String) {
      require(name.isNotEmpty()) { "name is empty" }
      for (i in 0 until name.length) {
        val c = name[i]
        require(c in '\u0021'..'\u007e') {
          format("Unexpected char %#04x at %d in header name: %s", c.toInt(), i, name)
        }
      }
    }

checkValue(value ,name)校验value值实现如下:

private fun checkValue(value: String, name: String) {
      for (i in 0 until value.length) {
        val c = value[i]
        require(c == '\t' || c in '\u0020'..'\u007e') {
          format("Unexpected char %#04x at %d in %s value: %s", c.toInt(), i, name, value)
        }
      }
    }

连着两天把Request、RequestBody、Headers都看完了,对其内部的实现都已经非常清楚。接下来就开始撸OkHttp网络请求流程。

相关推荐

Airbnb开源项目
精选开源

Airbnb开源项目

Airbnb Engineering & Data ScienceAirflow Use Apache Airflow (incubating) to author workflows as directed acyclic graphs (DAGs) of tasks12,263Airpal Web UI for PrestoDB2,502Aerosolve A machine learning

2.3k
WilliamChart:轻量美观的 Android 图表库实战
精选开源

WilliamChart:轻量美观的 Android 图表库实战

WilliamChart:轻量美观的 Android 图表库实战数据可视化是运动、健康、金融、统计类 App 的刚需。MPAndroidChart 功能最全但偏重复杂,WilliamChart 走的则是"轻量 + 美观 + Kotlin 原生"路线——v3 用 Kotlin 完全重写,API 极简,一行 chart.animate(dataSet) 就能把数据画成带动画的折线/柱状/环形图,开箱即

175
sherpa解析:开源语音技术全家桶
精选开源

sherpa解析:开源语音技术全家桶

想在手机、树莓派甚至 RISC-V 开发板上离线跑语音识别,五年前还是个需要啃 Kaldi 源码的硬核课题。k2-fsa 组织的 sherpa 把这件事变成了"下载预训练模型、装个包、跑示例"的三步操作。这个以 Apache-2.0 协议开源的项目自我定位是"新一代 Kaldi 的语音转文本框架",它专注端到端模型的推理部署:transducer 与 CTC 两类架构,C++ 与 Python 双…

44