1
16 package okhttp3.internal.http2;
17
18 import java.io.Closeable;
19 import java.io.EOFException;
20 import java.io.IOException;
21 import java.util.List;
22 import java.util.logging.Logger;
23 import okio.Buffer;
24 import okio.BufferedSource;
25 import okio.ByteString;
26 import okio.Source;
27 import okio.Timeout;
28
29 import static java.util.logging.Level.FINE;
30 import static okhttp3.internal.Util.format;
31 import static okhttp3.internal.http2.Http2.CONNECTION_PREFACE;
32 import static okhttp3.internal.http2.Http2.FLAG_ACK;
33 import static okhttp3.internal.http2.Http2.FLAG_COMPRESSED;
34 import static okhttp3.internal.http2.Http2.FLAG_END_HEADERS;
35 import static okhttp3.internal.http2.Http2.FLAG_END_STREAM;
36 import static okhttp3.internal.http2.Http2.FLAG_PADDED;
37 import static okhttp3.internal.http2.Http2.FLAG_PRIORITY;
38 import static okhttp3.internal.http2.Http2.INITIAL_MAX_FRAME_SIZE;
39 import static okhttp3.internal.http2.Http2.TYPE_CONTINUATION;
40 import static okhttp3.internal.http2.Http2.TYPE_DATA;
41 import static okhttp3.internal.http2.Http2.TYPE_GOAWAY;
42 import static okhttp3.internal.http2.Http2.TYPE_HEADERS;
43 import static okhttp3.internal.http2.Http2.TYPE_PING;
44 import static okhttp3.internal.http2.Http2.TYPE_PRIORITY;
45 import static okhttp3.internal.http2.Http2.TYPE_PUSH_PROMISE;
46 import static okhttp3.internal.http2.Http2.TYPE_RST_STREAM;
47 import static okhttp3.internal.http2.Http2.TYPE_SETTINGS;
48 import static okhttp3.internal.http2.Http2.TYPE_WINDOW_UPDATE;
49 import static okhttp3.internal.http2.Http2.frameLog;
50 import static okhttp3.internal.http2.Http2.ioException;
51 import static okio.ByteString.EMPTY;
52
53
60 final class Http2Reader implements Closeable {
61 static final Logger logger = Logger.getLogger(Http2.class.getName());
62
63 private final BufferedSource source;
64 private final ContinuationSource continuation;
65 private final boolean client;
66
67
68 final Hpack.Reader hpackReader;
69
70
71 Http2Reader(BufferedSource source, boolean client) {
72 this.source = source;
73 this.client = client;
74 this.continuation = new ContinuationSource(this.source);
75 this.hpackReader = new Hpack.Reader(4096, continuation);
76 }
77
78 public void readConnectionPreface(Handler handler) throws IOException {
79 if (client) {
80
81 if (!nextFrame(true, handler)) {
82 throw ioException("Required SETTINGS preface not received");
83 }
84 } else {
85
86 ByteString connectionPreface = source.readByteString(CONNECTION_PREFACE.size());
87 if (logger.isLoggable(FINE)) logger.fine(format("<< CONNECTION %s", connectionPreface.hex()));
88 if (!CONNECTION_PREFACE.equals(connectionPreface)) {
89 throw ioException("Expected a connection header but was %s", connectionPreface.utf8());
90 }
91 }
92 }
93
94 public boolean nextFrame(boolean requireSettings, Handler handler) throws IOException {
95 try {
96 source.require(9);
97 } catch (EOFException e) {
98 return false;
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112 int length = readMedium(source);
113 if (length < 0 || length > INITIAL_MAX_FRAME_SIZE) {
114 throw ioException("FRAME_SIZE_ERROR: %s", length);
115 }
116 byte type = (byte) (source.readByte() & 0xff);
117 if (requireSettings && type != TYPE_SETTINGS) {
118 throw ioException("Expected a SETTINGS frame but was %s", type);
119 }
120 byte flags = (byte) (source.readByte() & 0xff);
121 int streamId = (source.readInt() & 0x7fffffff);
122 if (logger.isLoggable(FINE)) logger.fine(frameLog(true, streamId, length, type, flags));
123
124 switch (type) {
125 case TYPE_DATA:
126 readData(handler, length, flags, streamId);
127 break;
128
129 case TYPE_HEADERS:
130 readHeaders(handler, length, flags, streamId);
131 break;
132
133 case TYPE_PRIORITY:
134 readPriority(handler, length, flags, streamId);
135 break;
136
137 case TYPE_RST_STREAM:
138 readRstStream(handler, length, flags, streamId);
139 break;
140
141 case TYPE_SETTINGS:
142 readSettings(handler, length, flags, streamId);
143 break;
144
145 case TYPE_PUSH_PROMISE:
146 readPushPromise(handler, length, flags, streamId);
147 break;
148
149 case TYPE_PING:
150 readPing(handler, length, flags, streamId);
151 break;
152
153 case TYPE_GOAWAY:
154 readGoAway(handler, length, flags, streamId);
155 break;
156
157 case TYPE_WINDOW_UPDATE:
158 readWindowUpdate(handler, length, flags, streamId);
159 break;
160
161 default:
162
163 source.skip(length);
164 }
165 return true;
166 }
167
168 private void readHeaders(Handler handler, int length, byte flags, int streamId)
169 throws IOException {
170 if (streamId == 0) throw ioException("PROTOCOL_ERROR: TYPE_HEADERS streamId == 0");
171
172 boolean endStream = (flags & FLAG_END_STREAM) != 0;
173
174 short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0;
175
176 if ((flags & FLAG_PRIORITY) != 0) {
177 readPriority(handler, streamId);
178 length -= 5;
179 }
180
181 length = lengthWithoutPadding(length, flags, padding);
182
183 List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId);
184
185 handler.headers(endStream, streamId, -1, headerBlock);
186 }
187
188 private List<Header> readHeaderBlock(int length, short padding, byte flags, int streamId)
189 throws IOException {
190 continuation.length = continuation.left = length;
191 continuation.padding = padding;
192 continuation.flags = flags;
193 continuation.streamId = streamId;
194
195
196
197 hpackReader.readHeaders();
198 return hpackReader.getAndResetHeaderList();
199 }
200
201 private void readData(Handler handler, int length, byte flags, int streamId)
202 throws IOException {
203 if (streamId == 0) throw ioException("PROTOCOL_ERROR: TYPE_DATA streamId == 0");
204
205
206 boolean inFinished = (flags & FLAG_END_STREAM) != 0;
207 boolean gzipped = (flags & FLAG_COMPRESSED) != 0;
208 if (gzipped) {
209 throw ioException("PROTOCOL_ERROR: FLAG_COMPRESSED without SETTINGS_COMPRESS_DATA");
210 }
211
212 short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0;
213 length = lengthWithoutPadding(length, flags, padding);
214
215 handler.data(inFinished, streamId, source, length);
216 source.skip(padding);
217 }
218
219 private void readPriority(Handler handler, int length, byte flags, int streamId)
220 throws IOException {
221 if (length != 5) throw ioException("TYPE_PRIORITY length: %d != 5", length);
222 if (streamId == 0) throw ioException("TYPE_PRIORITY streamId == 0");
223 readPriority(handler, streamId);
224 }
225
226 private void readPriority(Handler handler, int streamId) throws IOException {
227 int w1 = source.readInt();
228 boolean exclusive = (w1 & 0x80000000) != 0;
229 int streamDependency = (w1 & 0x7fffffff);
230 int weight = (source.readByte() & 0xff) + 1;
231 handler.priority(streamId, streamDependency, weight, exclusive);
232 }
233
234 private void readRstStream(Handler handler, int length, byte flags, int streamId)
235 throws IOException {
236 if (length != 4) throw ioException("TYPE_RST_STREAM length: %d != 4", length);
237 if (streamId == 0) throw ioException("TYPE_RST_STREAM streamId == 0");
238 int errorCodeInt = source.readInt();
239 ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
240 if (errorCode == null) {
241 throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt);
242 }
243 handler.rstStream(streamId, errorCode);
244 }
245
246 private void readSettings(Handler handler, int length, byte flags, int streamId)
247 throws IOException {
248 if (streamId != 0) throw ioException("TYPE_SETTINGS streamId != 0");
249 if ((flags & FLAG_ACK) != 0) {
250 if (length != 0) throw ioException("FRAME_SIZE_ERROR ack frame should be empty!");
251 handler.ackSettings();
252 return;
253 }
254
255 if (length % 6 != 0) throw ioException("TYPE_SETTINGS length %% 6 != 0: %s", length);
256 Settings settings = new Settings();
257 for (int i = 0; i < length; i += 6) {
258 int id = source.readShort() & 0xFFFF;
259 int value = source.readInt();
260
261 switch (id) {
262 case 1:
263 break;
264 case 2:
265 if (value != 0 && value != 1) {
266 throw ioException("PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1");
267 }
268 break;
269 case 3:
270 id = 4;
271 break;
272 case 4:
273 id = 7;
274 if (value < 0) {
275 throw ioException("PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1");
276 }
277 break;
278 case 5:
279 if (value < INITIAL_MAX_FRAME_SIZE || value > 16777215) {
280 throw ioException("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: %s", value);
281 }
282 break;
283 case 6:
284 break;
285 default:
286 break;
287 }
288 settings.set(id, value);
289 }
290 handler.settings(false, settings);
291 }
292
293 private void readPushPromise(Handler handler, int length, byte flags, int streamId)
294 throws IOException {
295 if (streamId == 0) {
296 throw ioException("PROTOCOL_ERROR: TYPE_PUSH_PROMISE streamId == 0");
297 }
298 short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0;
299 int promisedStreamId = source.readInt() & 0x7fffffff;
300 length -= 4;
301 length = lengthWithoutPadding(length, flags, padding);
302 List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId);
303 handler.pushPromise(streamId, promisedStreamId, headerBlock);
304 }
305
306 private void readPing(Handler handler, int length, byte flags, int streamId)
307 throws IOException {
308 if (length != 8) throw ioException("TYPE_PING length != 8: %s", length);
309 if (streamId != 0) throw ioException("TYPE_PING streamId != 0");
310 int payload1 = source.readInt();
311 int payload2 = source.readInt();
312 boolean ack = (flags & FLAG_ACK) != 0;
313 handler.ping(ack, payload1, payload2);
314 }
315
316 private void readGoAway(Handler handler, int length, byte flags, int streamId)
317 throws IOException {
318 if (length < 8) throw ioException("TYPE_GOAWAY length < 8: %s", length);
319 if (streamId != 0) throw ioException("TYPE_GOAWAY streamId != 0");
320 int lastStreamId = source.readInt();
321 int errorCodeInt = source.readInt();
322 int opaqueDataLength = length - 8;
323 ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
324 if (errorCode == null) {
325 throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt);
326 }
327 ByteString debugData = EMPTY;
328 if (opaqueDataLength > 0) {
329 debugData = source.readByteString(opaqueDataLength);
330 }
331 handler.goAway(lastStreamId, errorCode, debugData);
332 }
333
334 private void readWindowUpdate(Handler handler, int length, byte flags, int streamId)
335 throws IOException {
336 if (length != 4) throw ioException("TYPE_WINDOW_UPDATE length !=4: %s", length);
337 long increment = (source.readInt() & 0x7fffffffL);
338 if (increment == 0) throw ioException("windowSizeIncrement was 0", increment);
339 handler.windowUpdate(streamId, increment);
340 }
341
342 @Override public void close() throws IOException {
343 source.close();
344 }
345
346
350 static final class ContinuationSource implements Source {
351 private final BufferedSource source;
352
353 int length;
354 byte flags;
355 int streamId;
356
357 int left;
358 short padding;
359
360 ContinuationSource(BufferedSource source) {
361 this.source = source;
362 }
363
364 @Override public long read(Buffer sink, long byteCount) throws IOException {
365 while (left == 0) {
366 source.skip(padding);
367 padding = 0;
368 if ((flags & FLAG_END_HEADERS) != 0) return -1;
369 readContinuationHeader();
370
371 }
372
373 long read = source.read(sink, Math.min(byteCount, left));
374 if (read == -1) return -1;
375 left -= read;
376 return read;
377 }
378
379 @Override public Timeout timeout() {
380 return source.timeout();
381 }
382
383 @Override public void close() throws IOException {
384 }
385
386 private void readContinuationHeader() throws IOException {
387 int previousStreamId = streamId;
388
389 length = left = readMedium(source);
390 byte type = (byte) (source.readByte() & 0xff);
391 flags = (byte) (source.readByte() & 0xff);
392 if (logger.isLoggable(FINE)) logger.fine(frameLog(true, streamId, length, type, flags));
393 streamId = (source.readInt() & 0x7fffffff);
394 if (type != TYPE_CONTINUATION) throw ioException("%s != TYPE_CONTINUATION", type);
395 if (streamId != previousStreamId) throw ioException("TYPE_CONTINUATION streamId changed");
396 }
397 }
398
399 static int readMedium(BufferedSource source) throws IOException {
400 return (source.readByte() & 0xff) << 16
401 | (source.readByte() & 0xff) << 8
402 | (source.readByte() & 0xff);
403 }
404
405 static int lengthWithoutPadding(int length, byte flags, short padding)
406 throws IOException {
407 if ((flags & FLAG_PADDED) != 0) length--;
408 if (padding > length) {
409 throw ioException("PROTOCOL_ERROR padding %s > remaining length %s", padding, length);
410 }
411 return (short) (length - padding);
412 }
413
414 interface Handler {
415 void data(boolean inFinished, int streamId, BufferedSource source, int length)
416 throws IOException;
417
418
426 void headers(boolean inFinished, int streamId, int associatedStreamId,
427 List<Header> headerBlock);
428
429 void rstStream(int streamId, ErrorCode errorCode);
430
431 void settings(boolean clearPrevious, Settings settings);
432
433
434 void ackSettings();
435
436
441 void ping(boolean ack, int payload1, int payload2);
442
443
453 void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData);
454
455
459 void windowUpdate(int streamId, long windowSizeIncrement);
460
461
470 void priority(int streamId, int streamDependency, int weight, boolean exclusive);
471
472
483 void pushPromise(int streamId, int promisedStreamId, List<Header> requestHeaders)
484 throws IOException;
485
486
503 void alternateService(int streamId, String origin, ByteString protocol, String host, int port,
504 long maxAge);
505 }
506 }
507