Github: okhttp 分析版本:930d4d0

Bridges from application code to network code

intercept(chain: Interceptor.Chain)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val userRequest = chain.request()
val requestBuilder = userRequest.newBuilder()

val body = userRequest.body
// 对请求头的补充
if (body != null) {
val contentType = body.contentType()
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString())
}

val contentLength = body.contentLength()
if (contentLength != -1L) {
requestBuilder.header("Content-Length", contentLength.toString())
requestBuilder.removeHeader("Transfer-Encoding")
} else {
requestBuilder.header("Transfer-Encoding", "chunked")
requestBuilder.removeHeader("Content-Length")
}
}

if (userRequest.header("Host") == null) {
requestBuilder.header("Host", userRequest.url.toHostHeader())
}

// 默认保持连接 Keep-Alive
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive")
}

// 默认 GZIP 压缩
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
var transparentGzip = false
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true
requestBuilder.header("Accept-Encoding", "gzip")
}

// cookies 信息
val cookies = cookieJar.loadForRequest(userRequest.url)
if (cookies.isNotEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies))
}

if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}

// 丢给下一个拦截器发送网络请求
val networkResponse = chain.proceed(requestBuilder.build())

// 接受服务器返回的 Cookies
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers())

val responseBuilder = networkResponse.newBuilder()
.request(userRequest)

if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()) {
// 当服务器返回的数据是 GZIP 压缩的,那么客户端就进行解压操作
val responseBody = networkResponse.body()
if (responseBody != null) {
val gzipSource = GzipSource(responseBody.source())
val strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build()
responseBuilder.headers(strippedHeaders)
val contentType = networkResponse.header("Content-Type")
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}

// 构建 Response
return responseBuilder.build()
}

/** Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`. */
private fun cookieHeader(cookies: List<Cookie>): String = buildString {
cookies.forEachIndexed { index, cookie ->
if (index > 0) append("; ")
append(cookie.name).append('=').append(cookie.value)
}
}
}
  • 将用户构建的一个 Request 请求转化为能够进行网络访问的请求
  • 将 Request 丢给下一个拦截器去进行网络请求
  • 将网络请求回来的响应 Response 转化为用户可用的 Response

Request 转换

因为用户在构建一个 Request 对象的时侯往往只会简单设置 url, RequestBody 等几个属性,但是真正要发送一个请求实际上没这么简单,在原来的基础上添加了很多请求头

  • Content-Type: 网络请求类型
  • Content-Length: 请求体内容的长度,与 Transfer-Encoding 互斥
  • Transfer-Encoding: 值为 chunked 表示请求体的内容大小是未知的,与 Content-Length 互斥
  • Host: 请求的 url 的主机
  • Connection: 默认就是 Keep-Alive,就是一个 TCP 连接之后不会关闭,保持连接状态
  • Accept-Encoding: 默认是 gzip,告诉服务器客户端支持 gzip 编码的响应
  • Cookie: 当请求设置了 Cookie 那么就是添加 Cookie 这个请求头
  • User-Agent: 根据 OKHTTP 的版本来返回

Response 转换

  • cookie 处理

    1
    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers())
  • gzip 处理

    1
    GzipSource(responseBody.source()).buffer()