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 */
1671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerpackage com.squareup.okhttp.internal.framed;
17e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
18e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.internal.Util;
19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.IOException;
20e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Arrays;
21e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.List;
22e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer;
23e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.BufferedSink;
24e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.BufferedSource;
25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.ByteString;
26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.GzipSink;
27e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Okio;
28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.Test;
29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.TestUtil.headerEntries;
3171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Http2.FLAG_COMPRESSED;
3271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Http2.FLAG_END_HEADERS;
3371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Http2.FLAG_END_STREAM;
3471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Http2.FLAG_NONE;
3571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Http2.FLAG_PADDED;
3671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Http2.FLAG_PRIORITY;
37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertEquals;
38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertFalse;
39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertTrue;
40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.fail;
41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
42a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerpublic class Http2Test {
43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  final Buffer frame = new Buffer();
44a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  final FrameReader fr = new Http2.Reader(frame, 4096, false);
45e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  final int expectedStreamId = 15;
46e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
47e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void unknownFrameTypeSkipped() throws IOException {
48e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 4); // has a 4-byte field
49e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(99); // type 99
50a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
51e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId);
52e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(111111111); // custom data
53e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
54e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler()); // Should not callback.
55e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
56e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void onlyOneLiteralHeadersFrame() throws IOException {
58e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final List<Header> sentHeaders = headerEntries("name", "value");
59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
60e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer headerBytes = literalHeaders(sentHeaders);
61e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, (int) headerBytes.size());
62a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_HEADERS);
63e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_END_HEADERS | FLAG_END_STREAM);
64e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeAll(headerBytes);
66e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
67e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(frame, sendHeaderFrames(true, sentHeaders)); // Check writer sends the same bytes.
68e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
69e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
70e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override
71e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      public void headers(boolean outFinished, boolean inFinished, int streamId,
72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          int associatedStreamId, List<Header> headerBlock, HeadersMode headersMode) {
73e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(outFinished);
74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertTrue(inFinished);
75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(-1, associatedStreamId);
77e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(sentHeaders, headerBlock);
78e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode);
79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
80e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void headersWithPriority() throws IOException {
84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final List<Header> sentHeaders = headerEntries("name", "value");
85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer headerBytes = literalHeaders(sentHeaders);
87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, (int) (headerBytes.size() + 5));
88a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_HEADERS);
89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_END_HEADERS | FLAG_PRIORITY);
90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
91e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // Independent stream.
92e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(255); // Heaviest weight, zero-indexed.
93e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeAll(headerBytes);
94e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
96e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void priority(int streamId, int streamDependency, int weight,
97e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          boolean exclusive) {
98e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(0, streamDependency);
99e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(256, weight);
100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(exclusive);
101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void headers(boolean outFinished, boolean inFinished, int streamId,
104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          int associatedStreamId, List<Header> nameValueBlock,
105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          HeadersMode headersMode) {
106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(outFinished);
107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(inFinished);
108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(-1, associatedStreamId);
110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(sentHeaders, nameValueBlock);
111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode);
112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Headers are compressed, then framed. */
117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void headersFrameThenContinuation() throws IOException {
118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final List<Header> sentHeaders = largeHeaders();
119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer headerBlock = literalHeaders(sentHeaders);
121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Write the first headers frame.
123a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    writeMedium(frame, Http2.INITIAL_MAX_FRAME_SIZE);
124a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_HEADERS);
125a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
127a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.write(headerBlock, Http2.INITIAL_MAX_FRAME_SIZE);
128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Write the continuation frame, specifying no more frames are expected.
130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, (int) headerBlock.size());
131a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_CONTINUATION);
132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_END_HEADERS);
133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeAll(headerBlock);
135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(frame, sendHeaderFrames(false, sentHeaders)); // Check writer sends the same bytes.
137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
138e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Reading the above frames should result in a concatenated headerBlock.
139e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void headers(boolean outFinished, boolean inFinished, int streamId,
141e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          int associatedStreamId, List<Header> headerBlock, HeadersMode headersMode) {
142e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(outFinished);
143e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(inFinished);
144e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(-1, associatedStreamId);
146e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(sentHeaders, headerBlock);
147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode);
148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
150e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void pushPromise() throws IOException {
153e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final int expectedPromisedStreamId = 11;
154e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
155e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final List<Header> pushPromise = Arrays.asList(
156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        new Header(Header.TARGET_METHOD, "GET"),
157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        new Header(Header.TARGET_SCHEME, "https"),
158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        new Header(Header.TARGET_AUTHORITY, "squareup.com"),
159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        new Header(Header.TARGET_PATH, "/")
160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    );
161e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Write the push promise frame, specifying the associated stream ID.
163e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer headerBytes = literalHeaders(pushPromise);
164e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, (int) (headerBytes.size() + 4));
165a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_PUSH_PROMISE);
166a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_END_PUSH_PROMISE);
167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedPromisedStreamId & 0x7fffffff);
169e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeAll(headerBytes);
170e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(frame, sendPushPromiseFrames(expectedPromisedStreamId, pushPromise));
172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override
175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      public void pushPromise(int streamId, int promisedStreamId, List<Header> headerBlock) {
176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedPromisedStreamId, promisedStreamId);
178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(pushPromise, headerBlock);
179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Headers are compressed, then framed. */
184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void pushPromiseThenContinuation() throws IOException {
185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final int expectedPromisedStreamId = 11;
186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final List<Header> pushPromise = largeHeaders();
187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Decoding the first header will cross frame boundaries.
189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer headerBlock = literalHeaders(pushPromise);
190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Write the first headers frame.
192a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    writeMedium(frame, Http2.INITIAL_MAX_FRAME_SIZE);
193a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_PUSH_PROMISE);
194a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedPromisedStreamId & 0x7fffffff);
197a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.write(headerBlock, Http2.INITIAL_MAX_FRAME_SIZE - 4);
198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
199e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Write the continuation frame, specifying no more frames are expected.
200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, (int) headerBlock.size());
201a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_CONTINUATION);
202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_END_HEADERS);
203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeAll(headerBlock);
205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(frame, sendPushPromiseFrames(expectedPromisedStreamId, pushPromise));
207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Reading the above frames should result in a concatenated headerBlock.
209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override
211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      public void pushPromise(int streamId, int promisedStreamId, List<Header> headerBlock) {
212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedPromisedStreamId, promisedStreamId);
214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(pushPromise, headerBlock);
215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readRstStreamFrame() throws IOException {
220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 4);
221a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_RST_STREAM);
222a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(ErrorCode.COMPRESSION_ERROR.httpCode);
225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void rstStream(int streamId, ErrorCode errorCode) {
228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(ErrorCode.COMPRESSION_ERROR, errorCode);
230e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
231e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readSettingsFrame() throws IOException {
235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final int reducedTableSizeBytes = 16;
236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 12); // 2 settings * 6 bytes (2 for the code and 4 for the value).
238a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_SETTINGS);
239a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // Settings are always on the connection stream 0.
241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeShort(1); // SETTINGS_HEADER_TABLE_SIZE
242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(reducedTableSizeBytes);
243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeShort(2); // SETTINGS_ENABLE_PUSH
244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0);
245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void settings(boolean clearPrevious, Settings settings) {
248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(clearPrevious); // No clearPrevious in HTTP/2.
249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(reducedTableSizeBytes, settings.getHeaderTableSize());
250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(false, settings.getEnablePush(true));
251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readSettingsFrameInvalidPushValue() throws IOException {
256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 6); // 2 for the code and 4 for the value
257a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_SETTINGS);
258a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // Settings are always on the connection stream 0.
260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeShort(2);
261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(2);
262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fr.nextFrame(new BaseTestHandler());
265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException e) {
267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1", e.getMessage());
268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
271e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readSettingsFrameInvalidSettingId() throws IOException {
272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 6); // 2 for the code and 4 for the value
273a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_SETTINGS);
274a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // Settings are always on the connection stream 0.
276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeShort(7); // old number for SETTINGS_INITIAL_WINDOW_SIZE
277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(1);
278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fr.nextFrame(new BaseTestHandler());
281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException e) {
283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("PROTOCOL_ERROR invalid settings id: 7", e.getMessage());
284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
285e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
286e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
287e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readSettingsFrameNegativeWindowSize() throws IOException {
288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 6); // 2 for the code and 4 for the value
289a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_SETTINGS);
290a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
291e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // Settings are always on the connection stream 0.
292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeShort(4); // SETTINGS_INITIAL_WINDOW_SIZE
293e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(Integer.MIN_VALUE);
294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
295e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
296e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fr.nextFrame(new BaseTestHandler());
297e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException e) {
299e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1", e.getMessage());
300e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
301e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
302e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
303e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readSettingsFrameNegativeFrameLength() throws IOException {
304e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 6); // 2 for the code and 4 for the value
305a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_SETTINGS);
306a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
307e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // Settings are always on the connection stream 0.
308e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeShort(5); // SETTINGS_MAX_FRAME_SIZE
309e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(Integer.MIN_VALUE);
310e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
311e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
312e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fr.nextFrame(new BaseTestHandler());
313e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
314e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException e) {
315e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: -2147483648", e.getMessage());
316e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
317e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
318e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
319e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readSettingsFrameTooShortFrameLength() throws IOException {
320e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 6); // 2 for the code and 4 for the value
321a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_SETTINGS);
322a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
323e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // Settings are always on the connection stream 0.
324e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeShort(5); // SETTINGS_MAX_FRAME_SIZE
325e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt((int) Math.pow(2, 14) - 1);
326e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
327e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
328e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fr.nextFrame(new BaseTestHandler());
329e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
330e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException e) {
331e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: 16383", e.getMessage());
332e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
333e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
334e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
335e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readSettingsFrameTooLongFrameLength() throws IOException {
336e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 6); // 2 for the code and 4 for the value
337a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_SETTINGS);
338a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
339e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // Settings are always on the connection stream 0.
340e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeShort(5); // SETTINGS_MAX_FRAME_SIZE
341e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt((int) Math.pow(2, 24));
342e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
343e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
344e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fr.nextFrame(new BaseTestHandler());
345e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
346e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException e) {
347e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: 16777216", e.getMessage());
348e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
349e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
350e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
351e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void pingRoundTrip() throws IOException {
352e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final int expectedPayload1 = 7;
353e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final int expectedPayload2 = 8;
354e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
355e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 8); // length
356a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_PING);
357a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_ACK);
358e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // connection-level
359e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedPayload1);
360e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedPayload2);
361e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
362e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Check writer sends the same bytes.
363e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(frame, sendPingFrame(true, expectedPayload1, expectedPayload2));
364e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
365e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
366e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void ping(boolean ack, int payload1, int payload2) {
367e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertTrue(ack);
368e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedPayload1, payload1);
369e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedPayload2, payload2);
370e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
371e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
372e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
373e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
374e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxLengthDataFrame() throws IOException {
375a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    final byte[] expectedData = new byte[Http2.INITIAL_MAX_FRAME_SIZE];
376e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Arrays.fill(expectedData, (byte) 2);
377e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
378e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, expectedData.length);
379a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_DATA);
380a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
381e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
382e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.write(expectedData);
383e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
384e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Check writer sends the same bytes.
385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(frame, sendDataFrame(new Buffer().write(expectedData)));
386e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
387e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
388e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void data(boolean inFinished, int streamId, BufferedSource source,
389e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          int length) throws IOException {
390e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(inFinished);
391e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
392a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        assertEquals(Http2.INITIAL_MAX_FRAME_SIZE, length);
393e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        ByteString data = source.readByteString(length);
394e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        for (byte b : data.toByteArray()) {
395e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          assertEquals(2, b);
396e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
397e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
398e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
399e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
400e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
401e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** We do not send SETTINGS_COMPRESS_DATA = 1, nor want to. Let's make sure we error. */
402e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void compressedDataFrameWhenSettingDisabled() throws IOException {
403a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    byte[] expectedData = new byte[Http2.INITIAL_MAX_FRAME_SIZE];
404e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Arrays.fill(expectedData, (byte) 2);
405e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer zipped = gzip(expectedData);
406e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    int zippedSize = (int) zipped.size();
407e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
408e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, zippedSize);
409a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_DATA);
410e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_COMPRESSED);
411e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
412e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    zipped.readAll(frame);
413e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
414e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
415e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fr.nextFrame(new BaseTestHandler());
416e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
417e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException e) {
418e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("PROTOCOL_ERROR: FLAG_COMPRESSED without SETTINGS_COMPRESS_DATA",
419e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          e.getMessage());
420e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
421e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
422e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
423e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readPaddedDataFrame() throws IOException {
424e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    int dataLength = 1123;
425e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    byte[] expectedData = new byte[dataLength];
426e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Arrays.fill(expectedData, (byte) 2);
427e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
428e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    int paddingLength = 254;
429e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    byte[] padding = new byte[paddingLength];
430e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Arrays.fill(padding, (byte) 0);
431e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
432e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, dataLength + paddingLength + 1);
433a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_DATA);
434e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_PADDED);
435e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
436e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(paddingLength);
437e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.write(expectedData);
438e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.write(padding);
439e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
440e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(assertData());
441e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(frame.exhausted()); // Padding was skipped.
442e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
443e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
444e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readPaddedDataFrameZeroPadding() throws IOException {
445e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    int dataLength = 1123;
446e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    byte[] expectedData = new byte[dataLength];
447e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Arrays.fill(expectedData, (byte) 2);
448e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
449e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, dataLength + 1);
450a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_DATA);
451e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_PADDED);
452e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
453e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(0);
454e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.write(expectedData);
455e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
456e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(assertData());
457e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
458e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
459e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readPaddedHeadersFrame() throws IOException {
460e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    int paddingLength = 254;
461e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    byte[] padding = new byte[paddingLength];
462e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Arrays.fill(padding, (byte) 0);
463e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer headerBlock = literalHeaders(headerEntries("foo", "barrr", "baz", "qux"));
465e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, (int) headerBlock.size() + paddingLength + 1);
466a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_HEADERS);
467e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_END_HEADERS | FLAG_PADDED);
468e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
469e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(paddingLength);
470e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeAll(headerBlock);
471e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.write(padding);
472e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
473e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(assertHeaderBlock());
474e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(frame.exhausted()); // Padding was skipped.
475e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
476e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
477e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readPaddedHeadersFrameZeroPadding() throws IOException {
478e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer headerBlock = literalHeaders(headerEntries("foo", "barrr", "baz", "qux"));
479e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, (int) headerBlock.size() + 1);
480a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_HEADERS);
481e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_END_HEADERS | FLAG_PADDED);
482e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
483e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(0);
484e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeAll(headerBlock);
485e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
486e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(assertHeaderBlock());
487e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
488e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
489e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Headers are compressed, then framed. */
490e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readPaddedHeadersFrameThenContinuation() throws IOException {
491e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    int paddingLength = 254;
492e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    byte[] padding = new byte[paddingLength];
493e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Arrays.fill(padding, (byte) 0);
494e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
495e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Decoding the first header will cross frame boundaries.
496e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer headerBlock = literalHeaders(headerEntries("foo", "barrr", "baz", "qux"));
497e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
498e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Write the first headers frame.
499e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, (int) (headerBlock.size() / 2) + paddingLength + 1);
500a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_HEADERS);
501e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_PADDED);
502e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
503e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(paddingLength);
504e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.write(headerBlock, headerBlock.size() / 2);
505e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.write(padding);
506e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
507e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Write the continuation frame, specifying no more frames are expected.
508e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, (int) headerBlock.size());
509a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_CONTINUATION);
510e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeByte(FLAG_END_HEADERS);
511e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId & 0x7fffffff);
512e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeAll(headerBlock);
513e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
514e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(assertHeaderBlock());
515e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(frame.exhausted());
516e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
517e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
518e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void tooLargeDataFrame() throws IOException {
519e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
520e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      sendDataFrame(new Buffer().write(new byte[0x1000000]));
521e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
522e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException e) {
523e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("FRAME_SIZE_ERROR length > 16384: 16777216", e.getMessage());
524e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
525e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
526e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
527e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void windowUpdateRoundTrip() throws IOException {
528e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final long expectedWindowSizeIncrement = 0x7fffffff;
529e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
530e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 4); // length
531a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_WINDOW_UPDATE);
532a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
533e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId);
534e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt((int) expectedWindowSizeIncrement);
535e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
536e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Check writer sends the same bytes.
537e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(frame, windowUpdate(expectedWindowSizeIncrement));
538e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
539e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
540e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void windowUpdate(int streamId, long windowSizeIncrement) {
541e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
542e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedWindowSizeIncrement, windowSizeIncrement);
543e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
544e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
545e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
546e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
547e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void badWindowSizeIncrement() throws IOException {
548e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
549e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      windowUpdate(0);
550e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
551e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException e) {
552e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: 0",
553e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          e.getMessage());
554e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
555e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
556e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      windowUpdate(0x80000000L);
557e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
558e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException e) {
559e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: 2147483648",
560e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          e.getMessage());
561e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
562e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
563e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
564e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void goAwayWithoutDebugDataRoundTrip() throws IOException {
565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
566e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
567e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 8); // Without debug data there's only 2 32-bit fields.
568a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_GOAWAY);
569a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
570e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // connection-scope
571e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedStreamId); // last good stream.
572e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedError.httpCode);
573e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
574e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Check writer sends the same bytes.
575e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(frame, sendGoAway(expectedStreamId, expectedError, Util.EMPTY_BYTE_ARRAY));
576e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
577e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
578e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void goAway(
579e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
580e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, lastGoodStreamId);
581e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedError, errorCode);
582e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(0, debugData.size());
583e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
584e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
585e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
586e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
587e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void goAwayWithDebugDataRoundTrip() throws IOException {
588e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
589e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final ByteString expectedData = ByteString.encodeUtf8("abcdefgh");
590e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
591e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Compose the expected GOAWAY frame without debug data.
592e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeMedium(frame, 8 + expectedData.size());
593a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.TYPE_GOAWAY);
594a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    frame.writeByte(Http2.FLAG_NONE);
595e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // connection-scope
596e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(0); // never read any stream!
597e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.writeInt(expectedError.httpCode);
598e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    frame.write(expectedData.toByteArray());
599e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
600e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Check writer sends the same bytes.
601e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(frame, sendGoAway(0, expectedError, expectedData.toByteArray()));
602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
603e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    fr.nextFrame(new BaseTestHandler() {
604e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void goAway(
605e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
606e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(0, lastGoodStreamId);
607e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedError, errorCode);
608e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedData, debugData);
609e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
610e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
611e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
612e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
613e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void frameSizeError() throws IOException {
614a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Http2.Writer writer = new Http2.Writer(new Buffer(), true);
615e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
616e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
617a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      writer.frameHeader(0, 16777216, Http2.TYPE_DATA, FLAG_NONE);
618e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
619e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException e) {
620e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      // TODO: real max is based on settings between 16384 and 16777215
621e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("FRAME_SIZE_ERROR length > 16384: 16777216", e.getMessage());
622e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
623e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
624e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
625e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void ackSettingsAppliesMaxFrameSize() throws IOException {
626e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    int newMaxFrameSize = 16777215;
627e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
628a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Http2.Writer writer = new Http2.Writer(new Buffer(), true);
629e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
630e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writer.ackSettings(new Settings().set(Settings.MAX_FRAME_SIZE, 0, newMaxFrameSize));
631e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
632e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(newMaxFrameSize, writer.maxDataLength());
633a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    writer.frameHeader(0, newMaxFrameSize, Http2.TYPE_DATA, FLAG_NONE);
634e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
635e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
636e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void streamIdHasReservedBit() throws IOException {
637a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Http2.Writer writer = new Http2.Writer(new Buffer(), true);
638e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
639e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
640e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      int streamId = 3;
641e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      streamId |= 1L << 31; // set reserved bit
642a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      writer.frameHeader(streamId, Http2.INITIAL_MAX_FRAME_SIZE, Http2.TYPE_DATA, FLAG_NONE);
643e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
644e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException e) {
645e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("reserved bit set: -2147483645", e.getMessage());
646e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
647e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
648e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
649e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Buffer literalHeaders(List<Header> sentHeaders) throws IOException {
650e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer out = new Buffer();
651a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    new Hpack.Writer(out).writeHeaders(sentHeaders);
652e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return out;
653e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
654e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
655e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Buffer sendHeaderFrames(boolean outFinished, List<Header> headers) throws IOException {
656e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer out = new Buffer();
657a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    new Http2.Writer(out, true).headers(outFinished, expectedStreamId, headers);
658e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return out;
659e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
660e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
661e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Buffer sendPushPromiseFrames(int streamId, List<Header> headers) throws IOException {
662e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer out = new Buffer();
663a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    new Http2.Writer(out, true).pushPromise(expectedStreamId, streamId, headers);
664e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return out;
665e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
666e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
667e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Buffer sendPingFrame(boolean ack, int payload1, int payload2) throws IOException {
668e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer out = new Buffer();
669a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    new Http2.Writer(out, true).ping(ack, payload1, payload2);
670e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return out;
671e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
672e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
673e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Buffer sendGoAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData)
674e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      throws IOException {
675e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer out = new Buffer();
676a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    new Http2.Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData);
677e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return out;
678e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
679e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
680e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Buffer sendDataFrame(Buffer data) throws IOException {
681e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer out = new Buffer();
682a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    new Http2.Writer(out, true).dataFrame(expectedStreamId, FLAG_NONE, data,
683e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        (int) data.size());
684e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return out;
685e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
686e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
687e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Buffer windowUpdate(long windowSizeIncrement) throws IOException {
688e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer out = new Buffer();
689a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    new Http2.Writer(out, true).windowUpdate(expectedStreamId, windowSizeIncrement);
690e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return out;
691e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
692e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
693e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private FrameReader.Handler assertHeaderBlock() {
694e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return new BaseTestHandler() {
695e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void headers(boolean outFinished, boolean inFinished, int streamId,
696e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          int associatedStreamId, List<Header> headerBlock, HeadersMode headersMode) {
697e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(outFinished);
698e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(inFinished);
699e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
700e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(-1, associatedStreamId);
701e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(headerEntries("foo", "barrr", "baz", "qux"), headerBlock);
702e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode);
703e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
704e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    };
705e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
706e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
707e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private FrameReader.Handler assertData() {
708e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return new BaseTestHandler() {
709e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void data(boolean inFinished, int streamId, BufferedSource source,
710e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          int length) throws IOException {
711e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertFalse(inFinished);
712e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(expectedStreamId, streamId);
713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals(1123, length);
714e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        ByteString data = source.readByteString(length);
715e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        for (byte b : data.toByteArray()) {
716e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          assertEquals(2, b);
717e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
718e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
719e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    };
720e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
722e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private static Buffer gzip(byte[] data) throws IOException {
723e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer buffer = new Buffer();
724e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Okio.buffer(new GzipSink(buffer)).write(data).close();
725e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return buffer;
726e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
727e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
728e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Create a sufficiently large header set to overflow Http20Draft12.INITIAL_MAX_FRAME_SIZE bytes. */
729e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private static List<Header> largeHeaders() {
730e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String[] nameValues = new String[32];
731e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    char[] chars = new char[512];
732e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (int i = 0; i < nameValues.length;) {
733e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      Arrays.fill(chars, (char) i);
734e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      nameValues[i++] = nameValues[i++] = String.valueOf(chars);
735e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
736e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return headerEntries(nameValues);
737e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
738e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
739e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private static void writeMedium(BufferedSink sink, int i) throws IOException {
740e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.writeByte((i >>> 16) & 0xff);
741e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.writeByte((i >>>  8) & 0xff);
742e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.writeByte( i         & 0xff);
743e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
744e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller}
745