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