OkHttp-源码分析(三)

652 阅读4分钟

前言

OkHttp系列其他篇章

开篇

前面分别介绍了一些OkHttp的执行流程和OkHttp的前四个拦截器,还剩下最后一个拦截器,下面来看看CallServerInterceptor连接拦截器都做了些什么

CallServerInterceptor

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
  val realChain = chain as RealInterceptorChain
  val exchange = realChain.exchange!!
  val request = realChain.request
  val requestBody = request.body
  val sentRequestMillis = System.currentTimeMillis()

  exchange.writeRequestHeaders(request)

  var invokeStartEvent = true
  var responseBuilder: Response.Builder? = null
  if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
    if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
      exchange.flushRequest()
      responseBuilder = exchange.readResponseHeaders(expectContinue = true)
      exchange.responseHeadersStart()
      invokeStartEvent = false
    }
    if (responseBuilder == null) {
      if (requestBody.isDuplex()) {
        exchange.flushRequest()
        val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
        requestBody.writeTo(bufferedRequestBody)
      } else {
        val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
        requestBody.writeTo(bufferedRequestBody)
        bufferedRequestBody.close()
      }
    } else {
      exchange.noRequestBody()
      if (!exchange.connection.isMultiplexed) {
        exchange.noNewExchangesOnConnection()
      }
    }
  } else {
    exchange.noRequestBody()
  }

  if (requestBody == null || !requestBody.isDuplex()) {
    exchange.finishRequest()
  }
  if (responseBuilder == null) {
    responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
    if (invokeStartEvent) {
      exchange.responseHeadersStart()
      invokeStartEvent = false
    }
  }
  var response = responseBuilder
      .request(request)
      .handshake(exchange.connection.handshake())
      .sentRequestAtMillis(sentRequestMillis)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build()
  var code = response.code
  if (code == 100) {
    responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
    if (invokeStartEvent) {
      exchange.responseHeadersStart()
    }
    response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    code = response.code
  }

  exchange.responseHeadersEnd(response)

  response = if (forWebSocket && code == 101) {
    response.newBuilder()
        .body(EMPTY_RESPONSE)
        .build()
  } else {
    response.newBuilder()
        .body(exchange.openResponseBody(response))
        .build()
  }
  if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
      "close".equals(response.header("Connection"), ignoreCase = true)) {
    exchange.noNewExchangesOnConnection()
  }
  if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
    throw ProtocolException(
        "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
  }
  return response
}
  • exchange.writeRequestHeaders(request)写入请求头信息
  • 判断请求方式是不是GETHEAD,如果不是则需要传入请求体,接着判断请求头中的Expect值是否为100-continue,如果是的话,则会读取响应体的头部信息,如果读出的Response.Builder为空,接着判断 requestBody.isDuplex(),如果为true的话,则刷新缓冲区,通过exchange.createRequestBody(request, true).buffer()创建bufferedRequestBody,往请求的requestBody写入数据,如果为false的话,,通过exchange.createRequestBody(request, true).buffer()创建bufferedRequestBody,写入输出流中发送数据。
  • 如果请求方式是GETHEAD的话,则没有请求体。
  • 如果requestBody为空,也就是没有请求体,或者requestBody.isDuplex()为false的话,则结束请求。
  • 如果responseBuilder为空的话,则调用exchange.readResponseHeaders方法
  • 获取响应体
  • 判断响应体的code是否为100,如果响应体为100 则是说后面还有数据需要传输,则会重新调用 exchange.readResponseHeaders方法,再次生成响应体
  • 判断code是否为101 并且 为websocekt请求,如果是的话,则生成一个空的response,否则就会通过exchange.openResponseBody(response)读取response中的数据生成一个响应体。
  • 最后判断code是否为204、205并且响应体的body为空的话则抛出异常,否则正常返回。

总的来看其中有这几个重要的方法:

  1. 写入请求头Exchange.writeRequestHeaders
  2. 创建请求体Exchange.createRequestBody
  3. 写入请求体 requestBody.writeTo
  4. 读取响应头Exchange.readResponseHeaders
  5. 读取响应体Exchange.openResponseBody 可以看到ConnectInterceptor拦截器是通过Exchange类完成请求与响应的读写操作,而Exchange通过ExchangeCodec的实现类完成世纪的I/O操作,ExchangeCodec有两个主要的实现类(Http1ExchangeCodecHttp2ExchangeCodec),下面就从这两个不同的协议进行分析

下面分别来看看他们都干了些什么

Http1ExchangeCodec
Exchange.writeRequestHeaders

这个方法从名字就可以看出主要是写入请求头的作用,具体看看是如何写入的

@Throws(IOException::class)
fun writeRequestHeaders(request: Request) {
  try {
    eventListener.requestHeadersStart(call)
    codec.writeRequestHeaders(request)
    eventListener.requestHeadersEnd(call, request)
  } catch (e: IOException) {
    eventListener.requestFailed(call, e)
    trackFailure(e)
    throw e
  }
}
override fun writeRequestHeaders(request: Request) {
  val requestLine = RequestLine.get(request, connection.route().proxy.type())
  writeRequest(request.headers, requestLine)
}
fun writeRequest(headers: Headers, requestLine: String) {
  check(state == STATE_IDLE) { "state: $state" }
  sink.writeUtf8(requestLine).writeUtf8("\r\n")
  for (i in 0 until headers.size) {
    sink.writeUtf8(headers.name(i))
        .writeUtf8(": ")
        .writeUtf8(headers.value(i))
        .writeUtf8("\r\n")
  }
  sink.writeUtf8("\r\n")
  state = STATE_OPEN_REQUEST_BODY
}

通过上面我们看一看出:

  • 首先通过RequestLine.get方法构建一个http请求行,然后进行写入请求行,
  • 循环遍历,取出请求头的key、value以utf-8的格式进行拼接写入
Exchange.createRequestBody
@Throws(IOException::class)
fun createRequestBody(request: Request, duplex: Boolean): Sink {
  this.isDuplex = duplex
  val contentLength = request.body!!.contentLength()
  eventListener.requestBodyStart(call)
  val rawRequestBody = codec.createRequestBody(request, contentLength)
  return RequestBodySink(rawRequestBody, contentLength)
}
  • 首先获取request.body的长度,然后再调用Http1ExchangeCodec.createRequestBody方法,将createRequestBody返回的sink对象用RequestBodySink对象包裹。
override fun createRequestBody(request: Request, contentLength: Long): Sink {
  return when {
    request.body != null && request.body.isDuplex() -> throw ProtocolException(
        "Duplex connections are not supported for HTTP/1")
    request.isChunked -> newChunkedSink() 
    contentLength != -1L -> newKnownLengthSink() 
    else -> 
      throw IllegalStateException(
          "Cannot stream a request body without chunked encoding or a known content length!")
  }
}
  • 这里的过程就返回了一个 ChunkedSink对象,下面看一个分析一下这个对象

private inner class ChunkedSink : Sink {
  private val timeout = ForwardingTimeout(sink.timeout())
  private var closed: Boolean = false

  override fun timeout(): Timeout = timeout

  override fun write(source: Buffer, byteCount: Long) {
    check(!closed) { "closed" }
    if (byteCount == 0L) return

    sink.writeHexadecimalUnsignedLong(byteCount)
    sink.writeUtf8("\r\n")
    sink.write(source, byteCount)
    sink.writeUtf8("\r\n")
  }

  @Synchronized
  override fun flush() {
    if (closed) return 
    sink.flush()
  }

  @Synchronized
  override fun close() {
    if (closed) return
    closed = true
    sink.writeUtf8("0\r\n\r\n")
    detachTimeout(timeout)
    state = STATE_READ_RESPONSE_HEADERS
  }
}

这个类的功能 主要是处理I/O流,之后的i/o操作都会走这个类,可以看到写入的格式为\r\n+内容+\r\n。

在执行完createRequestBody之后返回一个Sink对象,通过该Sink对象生成RequestBodySink对象进行返回。下面就来看看RequestBodySink这个类做了些什么

private inner class RequestBodySink(
  delegate: Sink,
  private val contentLength: Long
) : ForwardingSink(delegate) {
  private var completed = false
  private var bytesReceived = 0L
  private var closed = false

  @Throws(IOException::class)
  override fun write(source: Buffer, byteCount: Long) {
    check(!closed) { "closed" }
    if (contentLength != -1L && bytesReceived + byteCount > contentLength) {
      throw ProtocolException(
          "expected $contentLength bytes but received ${bytesReceived + byteCount}")
    }
    try {
      super.write(source, byteCount)
      this.bytesReceived += byteCount
    } catch (e: IOException) {
      throw complete(e)
    }
  }

  @Throws(IOException::class)
  override fun flush() {
    try {
      super.flush()
    } catch (e: IOException) {
      throw complete(e)
    }
  }

  @Throws(IOException::class)
  override fun close() {
    if (closed) return
    closed = true
    if (contentLength != -1L && bytesReceived != contentLength) {
      throw ProtocolException("unexpected end of stream")
    }
    try {
      super.close()
      complete(null)
    } catch (e: IOException) {
      throw complete(e)
    }
  }

  private fun <E : IOException?> complete(e: E): E {
    if (completed) return e
    completed = true
    return bodyComplete(bytesReceived, responseDone = false, requestDone = true, e = e)
  }
}

在这个类中可以看到,传入的sink对象,又通过构造方法传给了该类的父类,这个类所有I/O操作也全部是调用父类处方法,而父类又是交由传入的sink对象处理的,这里该类只是记录了一些关键信息。

requestBody.writeTo

这里的requestBody是一个抽象类,具体的实现都在他的实现类(FormBodyMultipartBody)中。下面先来看看requestBody的主要方法,以及主要功能


abstract class RequestBody {

 abstract fun contentType(): MediaType?

  open fun contentLength(): Long = -1L

  @Throws(IOException::class)
  abstract fun writeTo(sink: BufferedSink)

  open fun isDuplex(): Boolean = false

  open fun isOneShot(): Boolean = false
  }
  • contentType 代表当前请求体的格式 如text/html
  • contentLength代表请求体的长度
  • writeTo 往请求体写入操作
  • isDuplex 代表当前的请求体中的写入读取全双工流是否可以常驻
  • isOneShot 代表当前请求体是否只能使用一次,如果是遇到408,401,407等情况可以重复请求。此时需要这个标志位判断 其中主要的方法还是writeTo

FormBody

@Throws(IOException::class)
override fun writeTo(sink: BufferedSink) {
  writeOrCountBytes(sink, false)
}
private fun writeOrCountBytes(sink: BufferedSink?, countBytes: Boolean): Long {
  var byteCount = 0L
  val buffer: Buffer = if (countBytes) Buffer() else sink!!.buffer

  for (i in 0 until encodedNames.size) {
    if (i > 0) buffer.writeByte('&'.toInt())
    buffer.writeUtf8(encodedNames[i])
    buffer.writeByte('='.toInt())
    buffer.writeUtf8(encodedValues[i])
  }

  if (countBytes) {
    byteCount = buffer.size
    buffer.clear()
  }

  return byteCount
}

这里可以看到提交表单的格式为${encodedNames[i]}=${encodedValues[i]}键值对的方式,注意FormBody只能传递键值对,不可以传递文件

MultipartBody

@Throws(IOException::class)
override fun writeTo(sink: BufferedSink) {
  writeOrCountBytes(sink, false)
}
private fun writeOrCountBytes(
  sink: BufferedSink?,
  countBytes: Boolean
): Long {
  var sink = sink
  var byteCount = 0L

  var byteCountBuffer: Buffer? = null
  if (countBytes) {
    byteCountBuffer = Buffer()
    sink = byteCountBuffer
  }

  for (p in 0 until parts.size) {
    val part = parts[p]
    val headers = part.headers
    val body = part.body

    sink!!.write(DASHDASH)
    sink.write(boundaryByteString)
    sink.write(CRLF)

    if (headers != null) {
      for (h in 0 until headers.size) {
        sink.writeUtf8(headers.name(h))
            .write(COLONSPACE)
            .writeUtf8(headers.value(h))
            .write(CRLF)
      }
    }

    val contentType = body.contentType()
    if (contentType != null) {
      sink.writeUtf8("Content-Type: ")
          .writeUtf8(contentType.toString())
          .write(CRLF)
    }

    val contentLength = body.contentLength()
    if (contentLength != -1L) {
      sink.writeUtf8("Content-Length: ")
          .writeDecimalLong(contentLength)
          .write(CRLF)
    } else if (countBytes) {
    
      byteCountBuffer!!.clear()
      return -1L
    }

    sink.write(CRLF)

    if (countBytes) {
      byteCount += contentLength
    } else {
      body.writeTo(sink)
    }

    sink.write(CRLF)
  }

  sink!!.write(DASHDASH)
  sink.write(boundaryByteString)
  sink.write(DASHDASH)
  sink.write(CRLF)

  if (countBytes) {
    byteCount += byteCountBuffer!!.size
    byteCountBuffer.clear()
  }

  return byteCount
}

可以看出MultipartBody提交表单的格式是下面这样的

-- ${UUID.randomUUID()} \r\n
${headers.name(h)} : ${headers.value(h)} \r\n
Content-Type: ${contentType.toString()} \r\n
Content-Length: ${contentLength}\r\n
-- ${UUID.randomUUID()} -- \r\n

MultipartBody不仅可以提交键值对,还可以提交文件

Exchange.readResponseHeaders
@Throws(IOException::class)
fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
  try {
    val result = codec.readResponseHeaders(expectContinue)
    result?.initExchange(this)
    return result
  } catch (e: IOException) {
    eventListener.responseFailed(call, e)
    trackFailure(e)
    throw e
  }
}
  • 这里直接调用Http1ExchangeCodec.readResponseHeaders,所拥有的操作都在Http1ExchangeCodec中。
override fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
  check(state == STATE_OPEN_REQUEST_BODY || state == STATE_READ_RESPONSE_HEADERS) {
    "state: $state"
  }

  try {
    val statusLine = StatusLine.parse(headersReader.readLine())

    val responseBuilder = Response.Builder()
        .protocol(statusLine.protocol)
        .code(statusLine.code)
        .message(statusLine.message)
        .headers(headersReader.readHeaders())

    return when {
      expectContinue && statusLine.code == HTTP_CONTINUE -> {
        null
      }
      statusLine.code == HTTP_CONTINUE -> {
        state = STATE_READ_RESPONSE_HEADERS
        responseBuilder
      }
      else -> {
        state = STATE_OPEN_RESPONSE_BODY
        responseBuilder
      }
    }
  } catch (e: EOFException) {
    val address = connection.route().address.url.redact()
    throw IOException("unexpected end of stream on $address", e)
  }
}
  • headersReader包裹着在RealConnection中获取的socket输出流,通过headersReader.readLine()读出状态行信息。
  • 构建response对象,将statusLine.code、statusLine.message传入
  • 通过headersReader.readHeaders()读出请求头信息 -如果code是101 则记录当前状态是STATE_READ_RESPONSE_HEADERS,否则就是STATE_OPEN_RESPONSE_BODY。并返回Response.Builder。
Exchange.openResponseBody
@Throws(IOException::class)
fun openResponseBody(response: Response): ResponseBody {
  try {
    val contentType = response.header("Content-Type")
    val contentLength = codec.reportedContentLength(response)
    val rawSource = codec.openResponseBodySource(response)
    val source = ResponseBodySource(rawSource, contentLength)
    return RealResponseBody(contentType, contentLength, source.buffer())
  } catch (e: IOException) {
    eventListener.responseFailed(call, e)
    trackFailure(e)
    throw e
  }
}
  • 从Response 中读取应答头部的Content-Type
  • 从Response 中读取应答头部的Content-Length
  • openResponseBodySource 生成一个ChunkedSource 对象,这个对象调用read方法读取时候,将会根据流读取socket输入流中的内容,知道长度为消费完毕。
  • 生成一个ResponseBodySource 对象,持有ChunkedSource读取流以及contentLength。生成RealResponseBody 对象持有ResponseBodySource对象。返回RealResponseBody。
Http2ExchangeCodec
exchange.writeRequestHeaders(request)
@Throws(IOException::class)
fun writeRequestHeaders(request: Request) {
  try {
    eventListener.requestHeadersStart(call)
    codec.writeRequestHeaders(request)
    eventListener.requestHeadersEnd(call, request)
  } catch (e: IOException) {
    eventListener.requestFailed(call, e)
    trackFailure(e)
    throw e
  }
}
override fun writeRequestHeaders(request: Request) {
  if (stream != null) return

  val hasRequestBody = request.body != null
  val requestHeaders = http2HeadersList(request)
  stream = http2Connection.newStream(requestHeaders, hasRequestBody)
  if (canceled) {
    stream!!.closeLater(ErrorCode.CANCEL)
    throw IOException("Canceled")
  }
  stream!!.readTimeout().timeout(chain.readTimeoutMillis.toLong(), TimeUnit.MILLISECONDS)
  stream!!.writeTimeout().timeout(chain.writeTimeoutMillis.toLong(), TimeUnit.MILLISECONDS)
}

在http2中的做法和http1有些不一样:

  • 通过http2HeadersList方法从请求体中获取请求头信息,在这个方法中添加几个公共的请求头信息(TARGET_METHODTARGET_PATHTARGET_AUTHORITYTARGET_SCHEME).再将请求头重key全部转化为小写
  • http2Connection.newStream 生成全新的Http2Stream,在该方法中进行对请求头的拼接写入。
@Throws(IOException::class)
private fun newStream(
  associatedStreamId: Int,
  requestHeaders: List<Header>,
  out: Boolean
): Http2Stream {
  val outFinished = !out
  val inFinished = false
  val flushHeaders: Boolean
  val stream: Http2Stream
  val streamId: Int

  synchronized(writer) {
    synchronized(this) {
      if (nextStreamId > Int.MAX_VALUE / 2) {
        shutdown(REFUSED_STREAM)
      }
      if (isShutdown) {
        throw ConnectionShutdownException()
      }
      streamId = nextStreamId
      nextStreamId += 2
      stream = Http2Stream(streamId, this, outFinished, inFinished, null)
      flushHeaders = !out ||
          writeBytesTotal >= writeBytesMaximum ||
          stream.writeBytesTotal >= stream.writeBytesMaximum
      if (stream.isOpen) {
        streams[streamId] = stream
      }
    }
    if (associatedStreamId == 0) {
      writer.headers(outFinished, streamId, requestHeaders)
    } else {
      require(!client) { "client streams shouldn't have associated stream IDs" }
      writer.pushPromise(associatedStreamId, streamId, requestHeaders)
    }
  }

  if (flushHeaders) {
    writer.flush()
  }

  return stream
}
  • 如果nextStreamId > Int.MAX_VALUE / 2,调用shutdown关闭上一次读去过的头部信息流
  • nextStreamId不断的加2成为新的streamId,并创建Http2Stream对象将streamid赋值给Http2Stream,并将Http2Stream保存在streams集合中
  • 调用Http2Writer对象的headers方法写入头部信息
@Synchronized @Throws(IOException::class)
fun headers(
  outFinished: Boolean,
  streamId: Int,
  headerBlock: List<Header>
) {
  if (closed) throw IOException("closed")
  hpackWriter.writeHeaders(headerBlock)

  val byteCount = hpackBuffer.size
  val length = minOf(maxFrameSize.toLong(), byteCount)
  var flags = if (byteCount == length) FLAG_END_HEADERS else 0
  if (outFinished) flags = flags or FLAG_END_STREAM
  frameHeader(
      streamId = streamId,
      length = length.toInt(),
      type = TYPE_HEADERS,
      flags = flags
  )
  sink.write(hpackBuffer, length)

  if (byteCount > length) writeContinuationFrames(streamId, byteCount - length)
}
  • hpackWriter.writeHeaders(headerBlock)把所有的请求头信息都会写入到一个 hpackBuffer的缓冲区,
  • 调用frameHeader方法构造头部信息进行写入
  • sink.write(hpackBuffer, length)写入缓冲区的请求头
Exchange.createRequestBody
@Throws(IOException::class)
fun createRequestBody(request: Request, duplex: Boolean): Sink {
  this.isDuplex = duplex
  val contentLength = request.body!!.contentLength()
  eventListener.requestBodyStart(call)
  val rawRequestBody = codec.createRequestBody(request, contentLength)
  return RequestBodySink(rawRequestBody, contentLength)
}
override fun createRequestBody(request: Request, contentLength: Long): Sink {
  return stream!!.getSink()
}

这里就是获取到了Http2Stream的sink对象,这个sink对象其实就是FramingSink

internal val sink = FramingSink(
    finished = outFinished
)

那么这个sink对象就同上面Http1中的一样,由RequestBodySink包裹, 主要是处理I/O流,之后的i/o操作都会走这个类。

FramingSink write
override fun write(source: Buffer, byteCount: Long) {
  this@Http2Stream.assertThreadDoesntHoldLock()

  sendBuffer.write(source, byteCount)
  while (sendBuffer.size >= EMIT_BUFFER_SIZE) {
    emitFrame(false)
  }
}
  • 向sendBuffer缓冲区写入数据
  • 如果数据量大小 大于 EMIT_BUFFER_SIZE调用emitFrame方法
@Throws(IOException::class)
private fun emitFrame(outFinishedOnLastFrame: Boolean) {
  val toWrite: Long
  val outFinished: Boolean
  synchronized(this@Http2Stream) {
    writeTimeout.enter()
    try {
      while (writeBytesTotal >= writeBytesMaximum &&
          !finished &&
          !closed &&
          errorCode == null) {
        waitForIo() 
      }
    } finally {
      writeTimeout.exitAndThrowIfTimedOut()
    }

    checkOutNotClosed() 
    toWrite = minOf(writeBytesMaximum - writeBytesTotal, sendBuffer.size)
    writeBytesTotal += toWrite
    outFinished = outFinishedOnLastFrame && toWrite == sendBuffer.size && errorCode == null
  }

  writeTimeout.enter()
  try {
    connection.writeData(id, outFinished, sendBuffer, toWrite)
  } finally {
    writeTimeout.exitAndThrowIfTimedOut()
  }
}
  • 如果在这个临时写入缓冲区中,已经大于writeBytesMaximum写入最大的数据荷载极限,那么就会阻塞该写入流程
  • 调用 connection.writeData方法向socket中写入数据
@Throws(IOException::class)
fun writeData(
  streamId: Int,
  outFinished: Boolean,
  buffer: Buffer?,
  byteCount: Long
) {
  if (byteCount == 0L) {
    writer.data(outFinished, streamId, buffer, 0)
    return
  }

  var byteCount = byteCount
  while (byteCount > 0L) {
    var toWrite: Int
    synchronized(this@Http2Connection) {
      try {
        while (writeBytesTotal >= writeBytesMaximum) {
          if (!streams.containsKey(streamId)) {
            throw IOException("stream closed")
          }
          this@Http2Connection.wait() 
        }
      } catch (e: InterruptedException) {
        Thread.currentThread().interrupt() 
        throw InterruptedIOException()
      }

      toWrite = minOf(byteCount, writeBytesMaximum - writeBytesTotal).toInt()
      toWrite = minOf(toWrite, writer.maxDataLength())
      writeBytesTotal += toWrite.toLong()
    }

    byteCount -= toWrite.toLong()
    writer.data(outFinished && byteCount == 0L, streamId, buffer, toWrite)
  }
}
  • 判断写入的数据是否大于最大数 writeBytesTotal >= writeBytesMaximum 如果大于则调用wait方法阻塞
  • 调用writer.data 写入数据
@Synchronized @Throws(IOException::class)
fun data(outFinished: Boolean, streamId: Int, source: Buffer?, byteCount: Int) {
  if (closed) throw IOException("closed")
  var flags = FLAG_NONE
  if (outFinished) flags = flags or FLAG_END_STREAM
  dataFrame(streamId, flags, source, byteCount)
}
@Throws(IOException::class)
fun dataFrame(streamId: Int, flags: Int, buffer: Buffer?, byteCount: Int) {
  frameHeader(
      streamId = streamId,
      length = byteCount,
      type = TYPE_DATA,
      flags = flags
  )
  if (byteCount > 0) {
    sink.write(buffer!!, byteCount.toLong())
  }
}
  • 调用frameHeader方法构造头部信息进行写入
  • 调用 sink.write 方法写入数据
Exchange.readResponseHeaders
override fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
  val headers = stream!!.takeHeaders()
  val responseBuilder = readHttp2HeadersList(headers, protocol)
  return if (expectContinue && responseBuilder.code == HTTP_CONTINUE) {
    null
  } else {
    responseBuilder
  }
}
@Synchronized @Throws(IOException::class)
fun takeHeaders(): Headers {
  readTimeout.enter()
  try {
    while (headersQueue.isEmpty() && errorCode == null) {
      waitForIo()
    }
  } finally {
    readTimeout.exitAndThrowIfTimedOut()
  }
  if (headersQueue.isNotEmpty()) {
    return headersQueue.removeFirst()
  }
  throw errorException ?: StreamResetException(errorCode!!)
}
  • 如果判断headersQueue.isEmpty()和errorCode == null是否为true,如果为true则阻塞当前线程,
  • 如果headersQueue.isNotEmpty()则从headersQueue中获取到存入流的头部结果,进行返回
fun readHttp2HeadersList(headerBlock: Headers, protocol: Protocol): Response.Builder {
  var statusLine: StatusLine? = null
  val headersBuilder = Headers.Builder()
  for (i in 0 until headerBlock.size) {
    val name = headerBlock.name(i)
    val value = headerBlock.value(i)
    if (name == RESPONSE_STATUS_UTF8) {
      statusLine = StatusLine.parse("HTTP/1.1 $value")
    } else if (name !in HTTP_2_SKIPPED_RESPONSE_HEADERS) {
      headersBuilder.addLenient(name, value)
    }
  }
  if (statusLine == null) throw ProtocolException("Expected ':status' header not present")

  return Response.Builder()
      .protocol(protocol)
      .code(statusLine.code)
      .message(statusLine.message)
      .headers(headersBuilder.build())
}
  • 通过循环遍历获取到所有头部信息,创建状态行,将头部信息存入namesAndValues集合中
  • 如果状态行对象为空,则抛出异常
  • 通过headersBuilder.build构建Headers对象,将namesAndValues传入Headers对象当中
  • 构建response对象,将statusLine.code、statusLine.message、Headers对象传入,进行返回

总结

CallServerIntercepter的拦截逻辑很简单,总的来说就是将请求头,请求体写入Socket,然后读取Socket的响应头和响应体。而具体的IO操作,OkHttp是采用的okio,这是个优秀的IO库,具体的逻辑这里就不深挖了。具体的流程如下:

  1. 写入请求头。
  2. 如果请求头里有"100-continue", 代表先将请求头发送给服务器,看服务器的响应决定是否进行下一步请求体的发送。
  3. 写入请求体,并发送请求。
  4. 读取响应体,并构建一个Resonse
  5. 如果响应码为100,需要再请求一次。
  6. 读取详细的响应体。
  7. 如果响应头有“close”,那么关闭这条连接。
  8. 返回响应。