1 /*
2  * Copyright (C) 2013 Square, Inc.
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 com.squareup.okhttp.internal.spdy;
17 
18 import com.squareup.okhttp.Protocol;
19 import java.io.IOException;
20 import java.util.List;
21 import java.util.logging.Logger;
22 import okio.Buffer;
23 import okio.BufferedSink;
24 import okio.BufferedSource;
25 import okio.ByteString;
26 import okio.Source;
27 import okio.Timeout;
28 
29 import static com.squareup.okhttp.internal.spdy.Http2.FrameLogger.formatHeader;
30 import static java.lang.String.format;
31 import static java.util.logging.Level.FINE;
32 import static okio.ByteString.EMPTY;
33 
34 /**
35  * Read and write HTTP/2 frames.
36  * <p>
37  * This implementation assumes we do not send an increased
38  * {@link Settings#getMaxFrameSize frame size setting} to the peer. Hence, we
39  * expect all frames to have a max length of {@link #INITIAL_MAX_FRAME_SIZE}.
40  * <p>http://tools.ietf.org/html/draft-ietf-httpbis-http2-17
41  */
42 public final class Http2 implements Variant {
43   private static final Logger logger = Logger.getLogger(FrameLogger.class.getName());
44 
getProtocol()45   @Override public Protocol getProtocol() {
46     return Protocol.HTTP_2;
47   }
48 
49   private static final ByteString CONNECTION_PREFACE
50       = ByteString.encodeUtf8("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
51 
52   /** The initial max frame size, applied independently writing to, or reading from the peer. */
53   static final int INITIAL_MAX_FRAME_SIZE = 0x4000; // 16384
54 
55   static final byte TYPE_DATA = 0x0;
56   static final byte TYPE_HEADERS = 0x1;
57   static final byte TYPE_PRIORITY = 0x2;
58   static final byte TYPE_RST_STREAM = 0x3;
59   static final byte TYPE_SETTINGS = 0x4;
60   static final byte TYPE_PUSH_PROMISE = 0x5;
61   static final byte TYPE_PING = 0x6;
62   static final byte TYPE_GOAWAY = 0x7;
63   static final byte TYPE_WINDOW_UPDATE = 0x8;
64   static final byte TYPE_CONTINUATION = 0x9;
65 
66   static final byte FLAG_NONE = 0x0;
67   static final byte FLAG_ACK = 0x1; // Used for settings and ping.
68   static final byte FLAG_END_STREAM = 0x1; // Used for headers and data.
69   static final byte FLAG_END_HEADERS = 0x4; // Used for headers and continuation.
70   static final byte FLAG_END_PUSH_PROMISE = 0x4;
71   static final byte FLAG_PADDED = 0x8; // Used for headers and data.
72   static final byte FLAG_PRIORITY = 0x20; // Used for headers.
73   static final byte FLAG_COMPRESSED = 0x20; // Used for data.
74 
75   /**
76    * Creates a frame reader with max header table size of 4096 and data frame
77    * compression disabled.
78    */
newReader(BufferedSource source, boolean client)79   @Override public FrameReader newReader(BufferedSource source, boolean client) {
80     return new Reader(source, 4096, client);
81   }
82 
newWriter(BufferedSink sink, boolean client)83   @Override public FrameWriter newWriter(BufferedSink sink, boolean client) {
84     return new Writer(sink, client);
85   }
86 
87   static final class Reader implements FrameReader {
88     private final BufferedSource source;
89     private final ContinuationSource continuation;
90     private final boolean client;
91 
92     // Visible for testing.
93     final Hpack.Reader hpackReader;
94 
Reader(BufferedSource source, int headerTableSize, boolean client)95     Reader(BufferedSource source, int headerTableSize, boolean client) {
96       this.source = source;
97       this.client = client;
98       this.continuation = new ContinuationSource(this.source);
99       this.hpackReader = new Hpack.Reader(headerTableSize, continuation);
100     }
101 
readConnectionPreface()102     @Override public void readConnectionPreface() throws IOException {
103       if (client) return; // Nothing to read; servers doesn't send a connection preface!
104       ByteString connectionPreface = source.readByteString(CONNECTION_PREFACE.size());
105       if (logger.isLoggable(FINE)) logger.fine(format("<< CONNECTION %s", connectionPreface.hex()));
106       if (!CONNECTION_PREFACE.equals(connectionPreface)) {
107         throw ioException("Expected a connection header but was %s", connectionPreface.utf8());
108       }
109     }
110 
nextFrame(Handler handler)111     @Override public boolean nextFrame(Handler handler) throws IOException {
112       try {
113         source.require(9); // Frame header size
114       } catch (IOException e) {
115         return false; // This might be a normal socket close.
116       }
117 
118       /*  0                   1                   2                   3
119        *  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
120        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
121        * |                 Length (24)                   |
122        * +---------------+---------------+---------------+
123        * |   Type (8)    |   Flags (8)   |
124        * +-+-+-----------+---------------+-------------------------------+
125        * |R|                 Stream Identifier (31)                      |
126        * +=+=============================================================+
127        * |                   Frame Payload (0...)                      ...
128        * +---------------------------------------------------------------+
129        */
130       int length = readMedium(source);
131       if (length < 0 || length > INITIAL_MAX_FRAME_SIZE) {
132         throw ioException("FRAME_SIZE_ERROR: %s", length);
133       }
134       byte type = (byte) (source.readByte() & 0xff);
135       byte flags = (byte) (source.readByte() & 0xff);
136       int streamId = (source.readInt() & 0x7fffffff); // Ignore reserved bit.
137       if (logger.isLoggable(FINE)) logger.fine(formatHeader(true, streamId, length, type, flags));
138 
139       switch (type) {
140         case TYPE_DATA:
141           readData(handler, length, flags, streamId);
142           break;
143 
144         case TYPE_HEADERS:
145           readHeaders(handler, length, flags, streamId);
146           break;
147 
148         case TYPE_PRIORITY:
149           readPriority(handler, length, flags, streamId);
150           break;
151 
152         case TYPE_RST_STREAM:
153           readRstStream(handler, length, flags, streamId);
154           break;
155 
156         case TYPE_SETTINGS:
157           readSettings(handler, length, flags, streamId);
158           break;
159 
160         case TYPE_PUSH_PROMISE:
161           readPushPromise(handler, length, flags, streamId);
162           break;
163 
164         case TYPE_PING:
165           readPing(handler, length, flags, streamId);
166           break;
167 
168         case TYPE_GOAWAY:
169           readGoAway(handler, length, flags, streamId);
170           break;
171 
172         case TYPE_WINDOW_UPDATE:
173           readWindowUpdate(handler, length, flags, streamId);
174           break;
175 
176         default:
177           // Implementations MUST discard frames that have unknown or unsupported types.
178           source.skip(length);
179       }
180       return true;
181     }
182 
readHeaders(Handler handler, int length, byte flags, int streamId)183     private void readHeaders(Handler handler, int length, byte flags, int streamId)
184         throws IOException {
185       if (streamId == 0) throw ioException("PROTOCOL_ERROR: TYPE_HEADERS streamId == 0");
186 
187       boolean endStream = (flags & FLAG_END_STREAM) != 0;
188 
189       short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0;
190 
191       if ((flags & FLAG_PRIORITY) != 0) {
192         readPriority(handler, streamId);
193         length -= 5; // account for above read.
194       }
195 
196       length = lengthWithoutPadding(length, flags, padding);
197 
198       List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId);
199 
200       handler.headers(false, endStream, streamId, -1, headerBlock, HeadersMode.HTTP_20_HEADERS);
201     }
202 
readHeaderBlock(int length, short padding, byte flags, int streamId)203     private List<Header> readHeaderBlock(int length, short padding, byte flags, int streamId)
204         throws IOException {
205       continuation.length = continuation.left = length;
206       continuation.padding = padding;
207       continuation.flags = flags;
208       continuation.streamId = streamId;
209 
210       // TODO: Concat multi-value headers with 0x0, except COOKIE, which uses 0x3B, 0x20.
211       // http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8.1.2.5
212       hpackReader.readHeaders();
213       return hpackReader.getAndResetHeaderList();
214     }
215 
readData(Handler handler, int length, byte flags, int streamId)216     private void readData(Handler handler, int length, byte flags, int streamId)
217         throws IOException {
218       // TODO: checkState open or half-closed (local) or raise STREAM_CLOSED
219       boolean inFinished = (flags & FLAG_END_STREAM) != 0;
220       boolean gzipped = (flags & FLAG_COMPRESSED) != 0;
221       if (gzipped) {
222         throw ioException("PROTOCOL_ERROR: FLAG_COMPRESSED without SETTINGS_COMPRESS_DATA");
223       }
224 
225       short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0;
226       length = lengthWithoutPadding(length, flags, padding);
227 
228       handler.data(inFinished, streamId, source, length);
229       source.skip(padding);
230     }
231 
readPriority(Handler handler, int length, byte flags, int streamId)232     private void readPriority(Handler handler, int length, byte flags, int streamId)
233         throws IOException {
234       if (length != 5) throw ioException("TYPE_PRIORITY length: %d != 5", length);
235       if (streamId == 0) throw ioException("TYPE_PRIORITY streamId == 0");
236       readPriority(handler, streamId);
237     }
238 
readPriority(Handler handler, int streamId)239     private void readPriority(Handler handler, int streamId) throws IOException {
240       int w1 = source.readInt();
241       boolean exclusive = (w1 & 0x80000000) != 0;
242       int streamDependency = (w1 & 0x7fffffff);
243       int weight = (source.readByte() & 0xff) + 1;
244       handler.priority(streamId, streamDependency, weight, exclusive);
245     }
246 
readRstStream(Handler handler, int length, byte flags, int streamId)247     private void readRstStream(Handler handler, int length, byte flags, int streamId)
248         throws IOException {
249       if (length != 4) throw ioException("TYPE_RST_STREAM length: %d != 4", length);
250       if (streamId == 0) throw ioException("TYPE_RST_STREAM streamId == 0");
251       int errorCodeInt = source.readInt();
252       ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
253       if (errorCode == null) {
254         throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt);
255       }
256       handler.rstStream(streamId, errorCode);
257     }
258 
readSettings(Handler handler, int length, byte flags, int streamId)259     private void readSettings(Handler handler, int length, byte flags, int streamId)
260         throws IOException {
261       if (streamId != 0) throw ioException("TYPE_SETTINGS streamId != 0");
262       if ((flags & FLAG_ACK) != 0) {
263         if (length != 0) throw ioException("FRAME_SIZE_ERROR ack frame should be empty!");
264         handler.ackSettings();
265         return;
266       }
267 
268       if (length % 6 != 0) throw ioException("TYPE_SETTINGS length %% 6 != 0: %s", length);
269       Settings settings = new Settings();
270       for (int i = 0; i < length; i += 6) {
271         short id = source.readShort();
272         int value = source.readInt();
273 
274         switch (id) {
275           case 1: // SETTINGS_HEADER_TABLE_SIZE
276             break;
277           case 2: // SETTINGS_ENABLE_PUSH
278             if (value != 0 && value != 1) {
279               throw ioException("PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1");
280             }
281             break;
282           case 3: // SETTINGS_MAX_CONCURRENT_STREAMS
283             id = 4; // Renumbered in draft 10.
284             break;
285           case 4: // SETTINGS_INITIAL_WINDOW_SIZE
286             id = 7; // Renumbered in draft 10.
287             if (value < 0) {
288               throw ioException("PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1");
289             }
290             break;
291           case 5: // SETTINGS_MAX_FRAME_SIZE
292             if (value < INITIAL_MAX_FRAME_SIZE || value > 16777215) {
293               throw ioException("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: %s", value);
294             }
295             break;
296           case 6: // SETTINGS_MAX_HEADER_LIST_SIZE
297             break; // Advisory only, so ignored.
298           default:
299             throw ioException("PROTOCOL_ERROR invalid settings id: %s", id);
300         }
301         settings.set(id, 0, value);
302       }
303       handler.settings(false, settings);
304       if (settings.getHeaderTableSize() >= 0) {
305         hpackReader.headerTableSizeSetting(settings.getHeaderTableSize());
306       }
307     }
308 
readPushPromise(Handler handler, int length, byte flags, int streamId)309     private void readPushPromise(Handler handler, int length, byte flags, int streamId)
310         throws IOException {
311       if (streamId == 0) {
312         throw ioException("PROTOCOL_ERROR: TYPE_PUSH_PROMISE streamId == 0");
313       }
314       short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0;
315       int promisedStreamId = source.readInt() & 0x7fffffff;
316       length -= 4; // account for above read.
317       length = lengthWithoutPadding(length, flags, padding);
318       List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId);
319       handler.pushPromise(streamId, promisedStreamId, headerBlock);
320     }
321 
readPing(Handler handler, int length, byte flags, int streamId)322     private void readPing(Handler handler, int length, byte flags, int streamId)
323         throws IOException {
324       if (length != 8) throw ioException("TYPE_PING length != 8: %s", length);
325       if (streamId != 0) throw ioException("TYPE_PING streamId != 0");
326       int payload1 = source.readInt();
327       int payload2 = source.readInt();
328       boolean ack = (flags & FLAG_ACK) != 0;
329       handler.ping(ack, payload1, payload2);
330     }
331 
readGoAway(Handler handler, int length, byte flags, int streamId)332     private void readGoAway(Handler handler, int length, byte flags, int streamId)
333         throws IOException {
334       if (length < 8) throw ioException("TYPE_GOAWAY length < 8: %s", length);
335       if (streamId != 0) throw ioException("TYPE_GOAWAY streamId != 0");
336       int lastStreamId = source.readInt();
337       int errorCodeInt = source.readInt();
338       int opaqueDataLength = length - 8;
339       ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
340       if (errorCode == null) {
341         throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt);
342       }
343       ByteString debugData = EMPTY;
344       if (opaqueDataLength > 0) { // Must read debug data in order to not corrupt the connection.
345         debugData = source.readByteString(opaqueDataLength);
346       }
347       handler.goAway(lastStreamId, errorCode, debugData);
348     }
349 
readWindowUpdate(Handler handler, int length, byte flags, int streamId)350     private void readWindowUpdate(Handler handler, int length, byte flags, int streamId)
351         throws IOException {
352       if (length != 4) throw ioException("TYPE_WINDOW_UPDATE length !=4: %s", length);
353       long increment = (source.readInt() & 0x7fffffffL);
354       if (increment == 0) throw ioException("windowSizeIncrement was 0", increment);
355       handler.windowUpdate(streamId, increment);
356     }
357 
close()358     @Override public void close() throws IOException {
359       source.close();
360     }
361   }
362 
363   static final class Writer implements FrameWriter {
364     private final BufferedSink sink;
365     private final boolean client;
366     private final Buffer hpackBuffer;
367     private final Hpack.Writer hpackWriter;
368     private int maxFrameSize;
369     private boolean closed;
370 
Writer(BufferedSink sink, boolean client)371     Writer(BufferedSink sink, boolean client) {
372       this.sink = sink;
373       this.client = client;
374       this.hpackBuffer = new Buffer();
375       this.hpackWriter = new Hpack.Writer(hpackBuffer);
376       this.maxFrameSize = INITIAL_MAX_FRAME_SIZE;
377     }
378 
flush()379     @Override public synchronized void flush() throws IOException {
380       if (closed) throw new IOException("closed");
381       sink.flush();
382     }
383 
ackSettings(Settings peerSettings)384     @Override public synchronized void ackSettings(Settings peerSettings) throws IOException {
385       if (closed) throw new IOException("closed");
386       this.maxFrameSize = peerSettings.getMaxFrameSize(maxFrameSize);
387       int length = 0;
388       byte type = TYPE_SETTINGS;
389       byte flags = FLAG_ACK;
390       int streamId = 0;
391       frameHeader(streamId, length, type, flags);
392       sink.flush();
393     }
394 
connectionPreface()395     @Override public synchronized void connectionPreface() throws IOException {
396       if (closed) throw new IOException("closed");
397       if (!client) return; // Nothing to write; servers don't send connection headers!
398       if (logger.isLoggable(FINE)) {
399         logger.fine(format(">> CONNECTION %s", CONNECTION_PREFACE.hex()));
400       }
401       sink.write(CONNECTION_PREFACE.toByteArray());
402       sink.flush();
403     }
404 
synStream(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, List<Header> headerBlock)405     @Override public synchronized void synStream(boolean outFinished, boolean inFinished,
406         int streamId, int associatedStreamId, List<Header> headerBlock)
407         throws IOException {
408       if (inFinished) throw new UnsupportedOperationException();
409       if (closed) throw new IOException("closed");
410       headers(outFinished, streamId, headerBlock);
411     }
412 
synReply(boolean outFinished, int streamId, List<Header> headerBlock)413     @Override public synchronized void synReply(boolean outFinished, int streamId,
414         List<Header> headerBlock) throws IOException {
415       if (closed) throw new IOException("closed");
416       headers(outFinished, streamId, headerBlock);
417     }
418 
headers(int streamId, List<Header> headerBlock)419     @Override public synchronized void headers(int streamId, List<Header> headerBlock)
420         throws IOException {
421       if (closed) throw new IOException("closed");
422       headers(false, streamId, headerBlock);
423     }
424 
pushPromise(int streamId, int promisedStreamId, List<Header> requestHeaders)425     @Override public synchronized void pushPromise(int streamId, int promisedStreamId,
426         List<Header> requestHeaders) throws IOException {
427       if (closed) throw new IOException("closed");
428       if (hpackBuffer.size() != 0) throw new IllegalStateException();
429       hpackWriter.writeHeaders(requestHeaders);
430 
431       long byteCount = hpackBuffer.size();
432       int length = (int) Math.min(maxFrameSize - 4, byteCount);
433       byte type = TYPE_PUSH_PROMISE;
434       byte flags = byteCount == length ? FLAG_END_HEADERS : 0;
435       frameHeader(streamId, length + 4, type, flags);
436       sink.writeInt(promisedStreamId & 0x7fffffff);
437       sink.write(hpackBuffer, length);
438 
439       if (byteCount > length) writeContinuationFrames(streamId, byteCount - length);
440     }
441 
headers(boolean outFinished, int streamId, List<Header> headerBlock)442     void headers(boolean outFinished, int streamId, List<Header> headerBlock) throws IOException {
443       if (closed) throw new IOException("closed");
444       if (hpackBuffer.size() != 0) throw new IllegalStateException();
445       hpackWriter.writeHeaders(headerBlock);
446 
447       long byteCount = hpackBuffer.size();
448       int length = (int) Math.min(maxFrameSize, byteCount);
449       byte type = TYPE_HEADERS;
450       byte flags = byteCount == length ? FLAG_END_HEADERS : 0;
451       if (outFinished) flags |= FLAG_END_STREAM;
452       frameHeader(streamId, length, type, flags);
453       sink.write(hpackBuffer, length);
454 
455       if (byteCount > length) writeContinuationFrames(streamId, byteCount - length);
456     }
457 
writeContinuationFrames(int streamId, long byteCount)458     private void writeContinuationFrames(int streamId, long byteCount) throws IOException {
459       while (byteCount > 0) {
460         int length = (int) Math.min(maxFrameSize, byteCount);
461         byteCount -= length;
462         frameHeader(streamId, length, TYPE_CONTINUATION, byteCount == 0 ? FLAG_END_HEADERS : 0);
463         sink.write(hpackBuffer, length);
464       }
465     }
466 
rstStream(int streamId, ErrorCode errorCode)467     @Override public synchronized void rstStream(int streamId, ErrorCode errorCode)
468         throws IOException {
469       if (closed) throw new IOException("closed");
470       if (errorCode.spdyRstCode == -1) throw new IllegalArgumentException();
471 
472       int length = 4;
473       byte type = TYPE_RST_STREAM;
474       byte flags = FLAG_NONE;
475       frameHeader(streamId, length, type, flags);
476       sink.writeInt(errorCode.httpCode);
477       sink.flush();
478     }
479 
maxDataLength()480     @Override public int maxDataLength() {
481       return maxFrameSize;
482     }
483 
data(boolean outFinished, int streamId, Buffer source, int byteCount)484     @Override public synchronized void data(boolean outFinished, int streamId, Buffer source,
485         int byteCount) throws IOException {
486       if (closed) throw new IOException("closed");
487       byte flags = FLAG_NONE;
488       if (outFinished) flags |= FLAG_END_STREAM;
489       dataFrame(streamId, flags, source, byteCount);
490     }
491 
dataFrame(int streamId, byte flags, Buffer buffer, int byteCount)492     void dataFrame(int streamId, byte flags, Buffer buffer, int byteCount) throws IOException {
493       byte type = TYPE_DATA;
494       frameHeader(streamId, byteCount, type, flags);
495       if (byteCount > 0) {
496         sink.write(buffer, byteCount);
497       }
498     }
499 
settings(Settings settings)500     @Override public synchronized void settings(Settings settings) throws IOException {
501       if (closed) throw new IOException("closed");
502       int length = settings.size() * 6;
503       byte type = TYPE_SETTINGS;
504       byte flags = FLAG_NONE;
505       int streamId = 0;
506       frameHeader(streamId, length, type, flags);
507       for (int i = 0; i < Settings.COUNT; i++) {
508         if (!settings.isSet(i)) continue;
509         int id = i;
510         if (id == 4) id = 3; // SETTINGS_MAX_CONCURRENT_STREAMS renumbered.
511         else if (id == 7) id = 4; // SETTINGS_INITIAL_WINDOW_SIZE renumbered.
512         sink.writeShort(id);
513         sink.writeInt(settings.get(i));
514       }
515       sink.flush();
516     }
517 
ping(boolean ack, int payload1, int payload2)518     @Override public synchronized void ping(boolean ack, int payload1, int payload2)
519         throws IOException {
520       if (closed) throw new IOException("closed");
521       int length = 8;
522       byte type = TYPE_PING;
523       byte flags = ack ? FLAG_ACK : FLAG_NONE;
524       int streamId = 0;
525       frameHeader(streamId, length, type, flags);
526       sink.writeInt(payload1);
527       sink.writeInt(payload2);
528       sink.flush();
529     }
530 
goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData)531     @Override public synchronized void goAway(int lastGoodStreamId, ErrorCode errorCode,
532         byte[] debugData) throws IOException {
533       if (closed) throw new IOException("closed");
534       if (errorCode.httpCode == -1) throw illegalArgument("errorCode.httpCode == -1");
535       int length = 8 + debugData.length;
536       byte type = TYPE_GOAWAY;
537       byte flags = FLAG_NONE;
538       int streamId = 0;
539       frameHeader(streamId, length, type, flags);
540       sink.writeInt(lastGoodStreamId);
541       sink.writeInt(errorCode.httpCode);
542       if (debugData.length > 0) {
543         sink.write(debugData);
544       }
545       sink.flush();
546     }
547 
windowUpdate(int streamId, long windowSizeIncrement)548     @Override public synchronized void windowUpdate(int streamId, long windowSizeIncrement)
549         throws IOException {
550       if (closed) throw new IOException("closed");
551       if (windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL) {
552         throw illegalArgument("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: %s",
553             windowSizeIncrement);
554       }
555       int length = 4;
556       byte type = TYPE_WINDOW_UPDATE;
557       byte flags = FLAG_NONE;
558       frameHeader(streamId, length, type, flags);
559       sink.writeInt((int) windowSizeIncrement);
560       sink.flush();
561     }
562 
close()563     @Override public synchronized void close() throws IOException {
564       closed = true;
565       sink.close();
566     }
567 
frameHeader(int streamId, int length, byte type, byte flags)568     void frameHeader(int streamId, int length, byte type, byte flags) throws IOException {
569       if (logger.isLoggable(FINE)) logger.fine(formatHeader(false, streamId, length, type, flags));
570       if (length > maxFrameSize) {
571         throw illegalArgument("FRAME_SIZE_ERROR length > %d: %d", maxFrameSize, length);
572       }
573       if ((streamId & 0x80000000) != 0) throw illegalArgument("reserved bit set: %s", streamId);
574       writeMedium(sink, length);
575       sink.writeByte(type & 0xff);
576       sink.writeByte(flags & 0xff);
577       sink.writeInt(streamId & 0x7fffffff);
578     }
579   }
580 
illegalArgument(String message, Object... args)581   private static IllegalArgumentException illegalArgument(String message, Object... args) {
582     throw new IllegalArgumentException(format(message, args));
583   }
584 
ioException(String message, Object... args)585   private static IOException ioException(String message, Object... args) throws IOException {
586     throw new IOException(format(message, args));
587   }
588 
589   /**
590    * Decompression of the header block occurs above the framing layer. This
591    * class lazily reads continuation frames as they are needed by {@link
592    * Hpack.Reader#readHeaders()}.
593    */
594   static final class ContinuationSource implements Source {
595     private final BufferedSource source;
596 
597     int length;
598     byte flags;
599     int streamId;
600 
601     int left;
602     short padding;
603 
ContinuationSource(BufferedSource source)604     public ContinuationSource(BufferedSource source) {
605       this.source = source;
606     }
607 
read(Buffer sink, long byteCount)608     @Override public long read(Buffer sink, long byteCount) throws IOException {
609       while (left == 0) {
610         source.skip(padding);
611         padding = 0;
612         if ((flags & FLAG_END_HEADERS) != 0) return -1;
613         readContinuationHeader();
614         // TODO: test case for empty continuation header?
615       }
616 
617       long read = source.read(sink, Math.min(byteCount, left));
618       if (read == -1) return -1;
619       left -= read;
620       return read;
621     }
622 
timeout()623     @Override public Timeout timeout() {
624       return source.timeout();
625     }
626 
close()627     @Override public void close() throws IOException {
628     }
629 
readContinuationHeader()630     private void readContinuationHeader() throws IOException {
631       int previousStreamId = streamId;
632 
633       length = left = readMedium(source);
634       byte type = (byte) (source.readByte() & 0xff);
635       flags = (byte) (source.readByte() & 0xff);
636       if (logger.isLoggable(FINE)) logger.fine(formatHeader(true, streamId, length, type, flags));
637       streamId = (source.readInt() & 0x7fffffff);
638       if (type != TYPE_CONTINUATION) throw ioException("%s != TYPE_CONTINUATION", type);
639       if (streamId != previousStreamId) throw ioException("TYPE_CONTINUATION streamId changed");
640     }
641   }
642 
lengthWithoutPadding(int length, byte flags, short padding)643   private static int lengthWithoutPadding(int length, byte flags, short padding)
644       throws IOException {
645     if ((flags & FLAG_PADDED) != 0) length--; // Account for reading the padding length.
646     if (padding > length) {
647       throw ioException("PROTOCOL_ERROR padding %s > remaining length %s", padding, length);
648     }
649     return (short) (length - padding);
650   }
651 
652   /**
653    * Logs a human-readable representation of HTTP/2 frame headers.
654    *
655    * <p>The format is:
656    *
657    * <pre>
658    *   direction streamID length type flags
659    * </pre>
660    * Where direction is {@code <<} for inbound and {@code >>} for outbound.
661    *
662    * <p> For example, the following would indicate a HEAD request sent from
663    * the client.
664    * <pre>
665    * {@code
666    *   << 0x0000000f    12 HEADERS       END_HEADERS|END_STREAM
667    * }
668    * </pre>
669    */
670   static final class FrameLogger {
671 
formatHeader(boolean inbound, int streamId, int length, byte type, byte flags)672     static String formatHeader(boolean inbound, int streamId, int length, byte type, byte flags) {
673       String formattedType = type < TYPES.length ? TYPES[type] : format("0x%02x", type);
674       String formattedFlags = formatFlags(type, flags);
675       return format("%s 0x%08x %5d %-13s %s", inbound ? "<<" : ">>", streamId, length,
676           formattedType, formattedFlags);
677     }
678 
679     /**
680      * Looks up valid string representing flags from the table. Invalid
681      * combinations are represented in binary.
682      */
683     // Visible for testing.
684     static String formatFlags(byte type, byte flags) {
685       if (flags == 0) return "";
686       switch (type) { // Special case types that have 0 or 1 flag.
687         case TYPE_SETTINGS:
688         case TYPE_PING:
689           return flags == FLAG_ACK ? "ACK" : BINARY[flags];
690         case TYPE_PRIORITY:
691         case TYPE_RST_STREAM:
692         case TYPE_GOAWAY:
693         case TYPE_WINDOW_UPDATE:
694           return BINARY[flags];
695       }
696       String result = flags < FLAGS.length ? FLAGS[flags] : BINARY[flags];
697       // Special case types that have overlap flag values.
698       if (type == TYPE_PUSH_PROMISE && (flags & FLAG_END_PUSH_PROMISE) != 0) {
699         return result.replace("HEADERS", "PUSH_PROMISE"); // TODO: Avoid allocation.
700       } else if (type == TYPE_DATA && (flags & FLAG_COMPRESSED) != 0) {
701         return result.replace("PRIORITY", "COMPRESSED"); // TODO: Avoid allocation.
702       }
703       return result;
704     }
705 
706     /** Lookup table for valid frame types. */
707     private static final String[] TYPES = new String[] {
708         "DATA",
709         "HEADERS",
710         "PRIORITY",
711         "RST_STREAM",
712         "SETTINGS",
713         "PUSH_PROMISE",
714         "PING",
715         "GOAWAY",
716         "WINDOW_UPDATE",
717         "CONTINUATION"
718     };
719 
720     /**
721      * Lookup table for valid flags for DATA, HEADERS, CONTINUATION. Invalid
722      * combinations are represented in binary.
723      */
724     private static final String[] FLAGS = new String[0x40]; // Highest bit flag is 0x20.
725     private static final String[] BINARY = new String[256];
726 
727     static {
728       for (int i = 0; i < BINARY.length; i++) {
729         BINARY[i] = format("%8s", Integer.toBinaryString(i)).replace(' ', '0');
730       }
731 
732       FLAGS[FLAG_NONE] = "";
733       FLAGS[FLAG_END_STREAM] = "END_STREAM";
734 
735       int[] prefixFlags = new int[] {FLAG_END_STREAM};
736 
737       FLAGS[FLAG_PADDED] = "PADDED";
738       for (int prefixFlag : prefixFlags) {
739          FLAGS[prefixFlag | FLAG_PADDED] = FLAGS[prefixFlag] + "|PADDED";
740       }
741 
742       FLAGS[FLAG_END_HEADERS] = "END_HEADERS"; // Same as END_PUSH_PROMISE.
743       FLAGS[FLAG_PRIORITY] = "PRIORITY"; // Same as FLAG_COMPRESSED.
744       FLAGS[FLAG_END_HEADERS | FLAG_PRIORITY] = "END_HEADERS|PRIORITY"; // Only valid on HEADERS.
745       int[] frameFlags =
746           new int[] {FLAG_END_HEADERS, FLAG_PRIORITY, FLAG_END_HEADERS | FLAG_PRIORITY};
747 
748       for (int frameFlag : frameFlags) {
749         for (int prefixFlag : prefixFlags) {
750           FLAGS[prefixFlag | frameFlag] = FLAGS[prefixFlag] + '|' + FLAGS[frameFlag];
751           FLAGS[prefixFlag | frameFlag | FLAG_PADDED] =
752               FLAGS[prefixFlag] + '|' + FLAGS[frameFlag] + "|PADDED";
753         }
754       }
755 
756       for (int i = 0; i < FLAGS.length; i++) { // Fill in holes with binary representation.
757         if (FLAGS[i] == null) FLAGS[i] = BINARY[i];
758       }
759     }
760   }
761 
762   private static int readMedium(BufferedSource source) throws IOException {
763     return (source.readByte() & 0xff) << 16
764         |  (source.readByte() & 0xff) <<  8
765         |  (source.readByte() & 0xff);
766   }
767 
768   private static void writeMedium(BufferedSink sink, int i) throws IOException {
769     sink.writeByte((i >>> 16) & 0xff);
770     sink.writeByte((i >>>  8) & 0xff);
771     sink.writeByte(i          & 0xff);
772   }
773 }
774