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