1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

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 /**
54  * Reads HTTP/2 transport frames.
55  *
56  * <p>This implementation assumes we do not send an increased {@link Settings#getMaxFrameSize frame
57  * size setting} to the peer. Hence, we expect all frames to have a max length of {@link
58  * Http2#INITIAL_MAX_FRAME_SIZE}.
59  */

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   // Visible for testing.
68   final Hpack.Reader hpackReader;
69
70   /** Creates a frame reader with max header table size of 4096. */
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       // The client reads the initial SETTINGS frame.
81       if (!nextFrame(true, handler)) {
82         throw ioException("Required SETTINGS preface not received");
83       }
84     } else {
85       // The server reads the CONNECTION_PREFACE byte string.
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); // Frame header size
97     } catch (EOFException e) {
98       return false// This might be a normal socket close.
99     }
100
101     //  0                   1                   2                   3
102     //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
103     // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104     // |                 Length (24)                   |
105     // +---------------+---------------+---------------+
106     // |   Type (8)    |   Flags (8)   |
107     // +-+-+-----------+---------------+-------------------------------+
108     // |R|                 Stream Identifier (31)                      |
109     // +=+=============================================================+
110     // |                   Frame Payload (0...)                      ...
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); // Ignore reserved bit.
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         // Implementations MUST discard frames that have unknown or unsupported types.
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; // account for above read.
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     // TODO: Concat multi-value headers with 0x0, except COOKIE, which uses 0x3B, 0x20.
196     // http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8.1.2.5
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     // TODO: checkState open or half-closed (local) or raise STREAM_CLOSED
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: // SETTINGS_HEADER_TABLE_SIZE
263           break;
264         case 2: // SETTINGS_ENABLE_PUSH
265           if (value != 0 && value != 1) {
266             throw ioException("PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1");
267           }
268           break;
269         case 3: // SETTINGS_MAX_CONCURRENT_STREAMS
270           id = 4; // Renumbered in draft 10.
271           break;
272         case 4: // SETTINGS_INITIAL_WINDOW_SIZE
273           id = 7; // Renumbered in draft 10.
274           if (value < 0) {
275             throw ioException("PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1");
276           }
277           break;
278         case 5: // SETTINGS_MAX_FRAME_SIZE
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: // SETTINGS_MAX_HEADER_LIST_SIZE
284           break// Advisory only, so ignored.
285         default:
286           break// Must ignore setting with unknown id.
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; // account for above read.
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) { // Must read debug data in order to not corrupt the connection.
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   /**
347    * Decompression of the header block occurs above the framing layer. This class lazily reads
348    * continuation frames as they are needed by {@link Hpack.Reader#readHeaders()}.
349    */

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         // TODO: test case for empty continuation header?
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--; // Account for reading the padding 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     /**
419      * Create or update incoming headers, creating the corresponding streams if necessary. Frames
420      * that trigger this are HEADERS and PUSH_PROMISE.
421      *
422      * @param inFinished true if the sender will not send further frames.
423      * @param streamId the stream owning these headers.
424      * @param associatedStreamId the stream that triggered the sender to create this stream.
425      */

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     /** HTTP/2 only. */
434     void ackSettings();
435
436     /**
437      * Read a connection-level ping from the peer. {@code ack} indicates this is a reply. The data
438      * in {@code payload1} and {@code payload2} opaque binary, and there are no rules on the
439      * content.
440      */

441     void ping(boolean ack, int payload1, int payload2);
442
443     /**
444      * The peer tells us to stop creating streams.  It is safe to replay streams with {@code ID >
445      * lastGoodStreamId} on a new connection.  In- flight streams with {@code ID <=
446      * lastGoodStreamId} can only be replayed on a new connection if they are idempotent.
447      *
448      * @param lastGoodStreamId the last stream ID the peer processed before sending this message. If
449      * {@code lastGoodStreamId} is zero, the peer processed no frames.
450      * @param errorCode reason for closing the connection.
451      * @param debugData only valid for HTTP/2; opaque debug data to send.
452      */

453     void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData);
454
455     /**
456      * Notifies that an additional {@code windowSizeIncrement} bytes can be sent on {@code
457      * streamId}, or the connection if {@code streamId} is zero.
458      */

459     void windowUpdate(int streamId, long windowSizeIncrement);
460
461     /**
462      * Called when reading a headers or priority frame. This may be used to change the stream's
463      * weight from the default (16) to a new value.
464      *
465      * @param streamId stream which has a priority change.
466      * @param streamDependency the stream ID this stream is dependent on.
467      * @param weight relative proportion of priority in [1..256].
468      * @param exclusive inserts this stream ID as the sole child of {@code streamDependency}.
469      */

470     void priority(int streamId, int streamDependency, int weight, boolean exclusive);
471
472     /**
473      * HTTP/2 only. Receive a push promise header block. <p> A push promise contains all the headers
474      * that pertain to a server-initiated request, and a {@code promisedStreamId} to which response
475      * frames will be delivered. Push promise frames are sent as a part of the response to {@code
476      * streamId}.
477      *
478      * @param streamId client-initiated stream ID.  Must be an odd number.
479      * @param promisedStreamId server-initiated stream ID.  Must be an even number.
480      * @param requestHeaders minimally includes {@code :method}, {@code :scheme}, {@code
481      * :authority}, and (@code :path}.
482      */

483     void pushPromise(int streamId, int promisedStreamId, List<Header> requestHeaders)
484         throws IOException;
485
486     /**
487      * HTTP/2 only. Expresses that resources for the connection or a client- initiated stream are
488      * available from a different network location or protocol configuration.
489      *
490      * <p>See <a href="http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-01">alt-svc</a>
491      *
492      * @param streamId when a client-initiated stream ID (odd number), the origin of this alternate
493      * service is the origin of the stream. When zero, the origin is specified in the {@code origin}
494      * parameter.
495      * @param origin when present, the <a href="http://tools.ietf.org/html/rfc6454">origin</a> is
496      * typically represented as a combination of scheme, host and port. When empty, the origin is
497      * that of the {@code streamId}.
498      * @param protocol an ALPN protocol, such as {@code h2}.
499      * @param host an IP address or hostname.
500      * @param port the IP port associated with the service.
501      * @param maxAge time in seconds that this alternative is considered fresh.
502      */

503     void alternateService(int streamId, String origin, ByteString protocol, String host, int port,
504         long maxAge);
505   }
506 }
507