/
Http2.kt
180 lines (163 loc) · 5.67 KB
/
Http2.kt
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
* Copyright (C) 2013 Square, Inc.
*
* 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.
*/
package okhttp3.internal.http2
import okhttp3.internal.format
import okio.ByteString.Companion.encodeUtf8
object Http2 {
@JvmField
val CONNECTION_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".encodeUtf8()
/** The initial max frame size, applied independently writing to, or reading from the peer. */
const val INITIAL_MAX_FRAME_SIZE = 0x4000 // 16384
const val TYPE_DATA = 0x0
const val TYPE_HEADERS = 0x1
const val TYPE_PRIORITY = 0x2
const val TYPE_RST_STREAM = 0x3
const val TYPE_SETTINGS = 0x4
const val TYPE_PUSH_PROMISE = 0x5
const val TYPE_PING = 0x6
const val TYPE_GOAWAY = 0x7
const val TYPE_WINDOW_UPDATE = 0x8
const val TYPE_CONTINUATION = 0x9
const val FLAG_NONE = 0x0
const val FLAG_ACK = 0x1 // Used for settings and ping.
const val FLAG_END_STREAM = 0x1 // Used for headers and data.
const val FLAG_END_HEADERS = 0x4 // Used for headers and continuation.
const val FLAG_END_PUSH_PROMISE = 0x4
const val FLAG_PADDED = 0x8 // Used for headers and data.
const val FLAG_PRIORITY = 0x20 // Used for headers.
const val FLAG_COMPRESSED = 0x20 // Used for data.
/** Lookup table for valid frame types. */
private val FRAME_NAMES =
arrayOf(
"DATA", "HEADERS", "PRIORITY", "RST_STREAM", "SETTINGS", "PUSH_PROMISE", "PING", "GOAWAY",
"WINDOW_UPDATE", "CONTINUATION",
)
/**
* Lookup table for valid flags for DATA, HEADERS, CONTINUATION. Invalid combinations are
* represented in binary.
*/
private val FLAGS = arrayOfNulls<String>(0x40) // Highest bit flag is 0x20.
private val BINARY =
Array(256) {
format("%8s", Integer.toBinaryString(it)).replace(' ', '0')
}
init {
FLAGS[FLAG_NONE] = ""
FLAGS[FLAG_END_STREAM] = "END_STREAM"
val prefixFlags = intArrayOf(FLAG_END_STREAM)
FLAGS[FLAG_PADDED] = "PADDED"
for (prefixFlag in prefixFlags) {
FLAGS[prefixFlag or FLAG_PADDED] = FLAGS[prefixFlag] + "|PADDED"
}
FLAGS[FLAG_END_HEADERS] = "END_HEADERS" // Same as END_PUSH_PROMISE.
FLAGS[FLAG_PRIORITY] = "PRIORITY" // Same as FLAG_COMPRESSED.
FLAGS[FLAG_END_HEADERS or FLAG_PRIORITY] = "END_HEADERS|PRIORITY" // Only valid on HEADERS.
val frameFlags = intArrayOf(FLAG_END_HEADERS, FLAG_PRIORITY, FLAG_END_HEADERS or FLAG_PRIORITY)
for (frameFlag in frameFlags) {
for (prefixFlag in prefixFlags) {
FLAGS[prefixFlag or frameFlag] = FLAGS[prefixFlag] + '|'.toString() + FLAGS[frameFlag]
FLAGS[prefixFlag or frameFlag or FLAG_PADDED] =
FLAGS[prefixFlag] + '|'.toString() + FLAGS[frameFlag] + "|PADDED"
}
}
for (i in FLAGS.indices) { // Fill in holes with binary representation.
if (FLAGS[i] == null) FLAGS[i] = BINARY[i]
}
}
/**
* Returns a human-readable representation of HTTP/2 frame headers.
*
* The format is:
*
* ```
* direction streamID length type flags
* ```
*
* Where direction is `<<` for inbound and `>>` for outbound.
*
* For example, the following would indicate a HEAD request sent from the client.
* ```
* `<< 0x0000000f 12 HEADERS END_HEADERS|END_STREAM
* ```
*/
fun frameLog(
inbound: Boolean,
streamId: Int,
length: Int,
type: Int,
flags: Int,
): String {
val formattedType = formattedType(type)
val formattedFlags = formatFlags(type, flags)
val direction = if (inbound) "<<" else ">>"
return format(
"%s 0x%08x %5d %-13s %s",
direction,
streamId,
length,
formattedType,
formattedFlags,
)
}
/**
* Returns a human-readable representation of a `WINDOW_UPDATE` frame. This frame includes the
* window size increment instead of flags.
*/
fun frameLogWindowUpdate(
inbound: Boolean,
streamId: Int,
length: Int,
windowSizeIncrement: Long,
): String {
val formattedType = formattedType(TYPE_WINDOW_UPDATE)
val direction = if (inbound) "<<" else ">>"
return format(
"%s 0x%08x %5d %-13s %d",
direction,
streamId,
length,
formattedType,
windowSizeIncrement,
)
}
internal fun formattedType(type: Int): String = if (type < FRAME_NAMES.size) FRAME_NAMES[type] else format("0x%02x", type)
/**
* Looks up valid string representing flags from the table. Invalid combinations are represented
* in binary.
*/
fun formatFlags(
type: Int,
flags: Int,
): String {
if (flags == 0) return ""
when (type) {
// Special case types that have 0 or 1 flag.
TYPE_SETTINGS, TYPE_PING -> return if (flags == FLAG_ACK) "ACK" else BINARY[flags]
TYPE_PRIORITY, TYPE_RST_STREAM, TYPE_GOAWAY, TYPE_WINDOW_UPDATE -> return BINARY[flags]
}
val result = if (flags < FLAGS.size) FLAGS[flags]!! else BINARY[flags]
// Special case types that have overlap flag values.
return when {
type == TYPE_PUSH_PROMISE && flags and FLAG_END_PUSH_PROMISE != 0 -> {
result.replace("HEADERS", "PUSH_PROMISE") // TODO: Avoid allocation.
}
type == TYPE_DATA && flags and FLAG_COMPRESSED != 0 -> {
result.replace("PRIORITY", "COMPRESSED") // TODO: Avoid allocation.
}
else -> result
}
}
}