/* * Copyright (C) 2013 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.okhttp.internal.framed; import com.squareup.okhttp.internal.Util; import java.io.IOException; import java.util.Arrays; import java.util.List; import okio.Buffer; import okio.BufferedSink; import okio.BufferedSource; import okio.ByteString; import okio.GzipSink; import okio.Okio; import org.junit.Test; import static com.squareup.okhttp.TestUtil.headerEntries; import static com.squareup.okhttp.internal.framed.Http2.FLAG_COMPRESSED; import static com.squareup.okhttp.internal.framed.Http2.FLAG_END_HEADERS; import static com.squareup.okhttp.internal.framed.Http2.FLAG_END_STREAM; import static com.squareup.okhttp.internal.framed.Http2.FLAG_NONE; import static com.squareup.okhttp.internal.framed.Http2.FLAG_PADDED; import static com.squareup.okhttp.internal.framed.Http2.FLAG_PRIORITY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class Http2Test { final Buffer frame = new Buffer(); final FrameReader fr = new Http2.Reader(frame, 4096, false); final int expectedStreamId = 15; @Test public void unknownFrameTypeSkipped() throws IOException { writeMedium(frame, 4); // has a 4-byte field frame.writeByte(99); // type 99 frame.writeByte(Http2.FLAG_NONE); frame.writeInt(expectedStreamId); frame.writeInt(111111111); // custom data fr.nextFrame(new BaseTestHandler()); // Should not callback. } @Test public void onlyOneLiteralHeadersFrame() throws IOException { final List
sentHeaders = headerEntries("name", "value"); Buffer headerBytes = literalHeaders(sentHeaders); writeMedium(frame, (int) headerBytes.size()); frame.writeByte(Http2.TYPE_HEADERS); frame.writeByte(FLAG_END_HEADERS | FLAG_END_STREAM); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeAll(headerBytes); assertEquals(frame, sendHeaderFrames(true, sentHeaders)); // Check writer sends the same bytes. fr.nextFrame(new BaseTestHandler() { @Override public void headers(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, List
headerBlock, HeadersMode headersMode) { assertFalse(outFinished); assertTrue(inFinished); assertEquals(expectedStreamId, streamId); assertEquals(-1, associatedStreamId); assertEquals(sentHeaders, headerBlock); assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode); } }); } @Test public void headersWithPriority() throws IOException { final List
sentHeaders = headerEntries("name", "value"); Buffer headerBytes = literalHeaders(sentHeaders); writeMedium(frame, (int) (headerBytes.size() + 5)); frame.writeByte(Http2.TYPE_HEADERS); frame.writeByte(FLAG_END_HEADERS | FLAG_PRIORITY); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeInt(0); // Independent stream. frame.writeByte(255); // Heaviest weight, zero-indexed. frame.writeAll(headerBytes); fr.nextFrame(new BaseTestHandler() { @Override public void priority(int streamId, int streamDependency, int weight, boolean exclusive) { assertEquals(0, streamDependency); assertEquals(256, weight); assertFalse(exclusive); } @Override public void headers(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, List
nameValueBlock, HeadersMode headersMode) { assertFalse(outFinished); assertFalse(inFinished); assertEquals(expectedStreamId, streamId); assertEquals(-1, associatedStreamId); assertEquals(sentHeaders, nameValueBlock); assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode); } }); } /** Headers are compressed, then framed. */ @Test public void headersFrameThenContinuation() throws IOException { final List
sentHeaders = largeHeaders(); Buffer headerBlock = literalHeaders(sentHeaders); // Write the first headers frame. writeMedium(frame, Http2.INITIAL_MAX_FRAME_SIZE); frame.writeByte(Http2.TYPE_HEADERS); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(expectedStreamId & 0x7fffffff); frame.write(headerBlock, Http2.INITIAL_MAX_FRAME_SIZE); // Write the continuation frame, specifying no more frames are expected. writeMedium(frame, (int) headerBlock.size()); frame.writeByte(Http2.TYPE_CONTINUATION); frame.writeByte(FLAG_END_HEADERS); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeAll(headerBlock); assertEquals(frame, sendHeaderFrames(false, sentHeaders)); // Check writer sends the same bytes. // Reading the above frames should result in a concatenated headerBlock. fr.nextFrame(new BaseTestHandler() { @Override public void headers(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, List
headerBlock, HeadersMode headersMode) { assertFalse(outFinished); assertFalse(inFinished); assertEquals(expectedStreamId, streamId); assertEquals(-1, associatedStreamId); assertEquals(sentHeaders, headerBlock); assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode); } }); } @Test public void pushPromise() throws IOException { final int expectedPromisedStreamId = 11; final List
pushPromise = Arrays.asList( new Header(Header.TARGET_METHOD, "GET"), new Header(Header.TARGET_SCHEME, "https"), new Header(Header.TARGET_AUTHORITY, "squareup.com"), new Header(Header.TARGET_PATH, "/") ); // Write the push promise frame, specifying the associated stream ID. Buffer headerBytes = literalHeaders(pushPromise); writeMedium(frame, (int) (headerBytes.size() + 4)); frame.writeByte(Http2.TYPE_PUSH_PROMISE); frame.writeByte(Http2.FLAG_END_PUSH_PROMISE); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeInt(expectedPromisedStreamId & 0x7fffffff); frame.writeAll(headerBytes); assertEquals(frame, sendPushPromiseFrames(expectedPromisedStreamId, pushPromise)); fr.nextFrame(new BaseTestHandler() { @Override public void pushPromise(int streamId, int promisedStreamId, List
headerBlock) { assertEquals(expectedStreamId, streamId); assertEquals(expectedPromisedStreamId, promisedStreamId); assertEquals(pushPromise, headerBlock); } }); } /** Headers are compressed, then framed. */ @Test public void pushPromiseThenContinuation() throws IOException { final int expectedPromisedStreamId = 11; final List
pushPromise = largeHeaders(); // Decoding the first header will cross frame boundaries. Buffer headerBlock = literalHeaders(pushPromise); // Write the first headers frame. writeMedium(frame, Http2.INITIAL_MAX_FRAME_SIZE); frame.writeByte(Http2.TYPE_PUSH_PROMISE); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeInt(expectedPromisedStreamId & 0x7fffffff); frame.write(headerBlock, Http2.INITIAL_MAX_FRAME_SIZE - 4); // Write the continuation frame, specifying no more frames are expected. writeMedium(frame, (int) headerBlock.size()); frame.writeByte(Http2.TYPE_CONTINUATION); frame.writeByte(FLAG_END_HEADERS); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeAll(headerBlock); assertEquals(frame, sendPushPromiseFrames(expectedPromisedStreamId, pushPromise)); // Reading the above frames should result in a concatenated headerBlock. fr.nextFrame(new BaseTestHandler() { @Override public void pushPromise(int streamId, int promisedStreamId, List
headerBlock) { assertEquals(expectedStreamId, streamId); assertEquals(expectedPromisedStreamId, promisedStreamId); assertEquals(pushPromise, headerBlock); } }); } @Test public void readRstStreamFrame() throws IOException { writeMedium(frame, 4); frame.writeByte(Http2.TYPE_RST_STREAM); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeInt(ErrorCode.COMPRESSION_ERROR.httpCode); fr.nextFrame(new BaseTestHandler() { @Override public void rstStream(int streamId, ErrorCode errorCode) { assertEquals(expectedStreamId, streamId); assertEquals(ErrorCode.COMPRESSION_ERROR, errorCode); } }); } @Test public void readSettingsFrame() throws IOException { final int reducedTableSizeBytes = 16; writeMedium(frame, 12); // 2 settings * 6 bytes (2 for the code and 4 for the value). frame.writeByte(Http2.TYPE_SETTINGS); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(0); // Settings are always on the connection stream 0. frame.writeShort(1); // SETTINGS_HEADER_TABLE_SIZE frame.writeInt(reducedTableSizeBytes); frame.writeShort(2); // SETTINGS_ENABLE_PUSH frame.writeInt(0); fr.nextFrame(new BaseTestHandler() { @Override public void settings(boolean clearPrevious, Settings settings) { assertFalse(clearPrevious); // No clearPrevious in HTTP/2. assertEquals(reducedTableSizeBytes, settings.getHeaderTableSize()); assertEquals(false, settings.getEnablePush(true)); } }); } @Test public void readSettingsFrameInvalidPushValue() throws IOException { writeMedium(frame, 6); // 2 for the code and 4 for the value frame.writeByte(Http2.TYPE_SETTINGS); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(0); // Settings are always on the connection stream 0. frame.writeShort(2); frame.writeInt(2); try { fr.nextFrame(new BaseTestHandler()); fail(); } catch (IOException e) { assertEquals("PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1", e.getMessage()); } } @Test public void readSettingsFrameInvalidSettingId() throws IOException { writeMedium(frame, 6); // 2 for the code and 4 for the value frame.writeByte(Http2.TYPE_SETTINGS); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(0); // Settings are always on the connection stream 0. frame.writeShort(7); // old number for SETTINGS_INITIAL_WINDOW_SIZE frame.writeInt(1); try { fr.nextFrame(new BaseTestHandler()); fail(); } catch (IOException e) { assertEquals("PROTOCOL_ERROR invalid settings id: 7", e.getMessage()); } } @Test public void readSettingsFrameNegativeWindowSize() throws IOException { writeMedium(frame, 6); // 2 for the code and 4 for the value frame.writeByte(Http2.TYPE_SETTINGS); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(0); // Settings are always on the connection stream 0. frame.writeShort(4); // SETTINGS_INITIAL_WINDOW_SIZE frame.writeInt(Integer.MIN_VALUE); try { fr.nextFrame(new BaseTestHandler()); fail(); } catch (IOException e) { assertEquals("PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1", e.getMessage()); } } @Test public void readSettingsFrameNegativeFrameLength() throws IOException { writeMedium(frame, 6); // 2 for the code and 4 for the value frame.writeByte(Http2.TYPE_SETTINGS); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(0); // Settings are always on the connection stream 0. frame.writeShort(5); // SETTINGS_MAX_FRAME_SIZE frame.writeInt(Integer.MIN_VALUE); try { fr.nextFrame(new BaseTestHandler()); fail(); } catch (IOException e) { assertEquals("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: -2147483648", e.getMessage()); } } @Test public void readSettingsFrameTooShortFrameLength() throws IOException { writeMedium(frame, 6); // 2 for the code and 4 for the value frame.writeByte(Http2.TYPE_SETTINGS); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(0); // Settings are always on the connection stream 0. frame.writeShort(5); // SETTINGS_MAX_FRAME_SIZE frame.writeInt((int) Math.pow(2, 14) - 1); try { fr.nextFrame(new BaseTestHandler()); fail(); } catch (IOException e) { assertEquals("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: 16383", e.getMessage()); } } @Test public void readSettingsFrameTooLongFrameLength() throws IOException { writeMedium(frame, 6); // 2 for the code and 4 for the value frame.writeByte(Http2.TYPE_SETTINGS); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(0); // Settings are always on the connection stream 0. frame.writeShort(5); // SETTINGS_MAX_FRAME_SIZE frame.writeInt((int) Math.pow(2, 24)); try { fr.nextFrame(new BaseTestHandler()); fail(); } catch (IOException e) { assertEquals("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: 16777216", e.getMessage()); } } @Test public void pingRoundTrip() throws IOException { final int expectedPayload1 = 7; final int expectedPayload2 = 8; writeMedium(frame, 8); // length frame.writeByte(Http2.TYPE_PING); frame.writeByte(Http2.FLAG_ACK); frame.writeInt(0); // connection-level frame.writeInt(expectedPayload1); frame.writeInt(expectedPayload2); // Check writer sends the same bytes. assertEquals(frame, sendPingFrame(true, expectedPayload1, expectedPayload2)); fr.nextFrame(new BaseTestHandler() { @Override public void ping(boolean ack, int payload1, int payload2) { assertTrue(ack); assertEquals(expectedPayload1, payload1); assertEquals(expectedPayload2, payload2); } }); } @Test public void maxLengthDataFrame() throws IOException { final byte[] expectedData = new byte[Http2.INITIAL_MAX_FRAME_SIZE]; Arrays.fill(expectedData, (byte) 2); writeMedium(frame, expectedData.length); frame.writeByte(Http2.TYPE_DATA); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(expectedStreamId & 0x7fffffff); frame.write(expectedData); // Check writer sends the same bytes. assertEquals(frame, sendDataFrame(new Buffer().write(expectedData))); fr.nextFrame(new BaseTestHandler() { @Override public void data(boolean inFinished, int streamId, BufferedSource source, int length) throws IOException { assertFalse(inFinished); assertEquals(expectedStreamId, streamId); assertEquals(Http2.INITIAL_MAX_FRAME_SIZE, length); ByteString data = source.readByteString(length); for (byte b : data.toByteArray()) { assertEquals(2, b); } } }); } /** We do not send SETTINGS_COMPRESS_DATA = 1, nor want to. Let's make sure we error. */ @Test public void compressedDataFrameWhenSettingDisabled() throws IOException { byte[] expectedData = new byte[Http2.INITIAL_MAX_FRAME_SIZE]; Arrays.fill(expectedData, (byte) 2); Buffer zipped = gzip(expectedData); int zippedSize = (int) zipped.size(); writeMedium(frame, zippedSize); frame.writeByte(Http2.TYPE_DATA); frame.writeByte(FLAG_COMPRESSED); frame.writeInt(expectedStreamId & 0x7fffffff); zipped.readAll(frame); try { fr.nextFrame(new BaseTestHandler()); fail(); } catch (IOException e) { assertEquals("PROTOCOL_ERROR: FLAG_COMPRESSED without SETTINGS_COMPRESS_DATA", e.getMessage()); } } @Test public void readPaddedDataFrame() throws IOException { int dataLength = 1123; byte[] expectedData = new byte[dataLength]; Arrays.fill(expectedData, (byte) 2); int paddingLength = 254; byte[] padding = new byte[paddingLength]; Arrays.fill(padding, (byte) 0); writeMedium(frame, dataLength + paddingLength + 1); frame.writeByte(Http2.TYPE_DATA); frame.writeByte(FLAG_PADDED); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeByte(paddingLength); frame.write(expectedData); frame.write(padding); fr.nextFrame(assertData()); assertTrue(frame.exhausted()); // Padding was skipped. } @Test public void readPaddedDataFrameZeroPadding() throws IOException { int dataLength = 1123; byte[] expectedData = new byte[dataLength]; Arrays.fill(expectedData, (byte) 2); writeMedium(frame, dataLength + 1); frame.writeByte(Http2.TYPE_DATA); frame.writeByte(FLAG_PADDED); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeByte(0); frame.write(expectedData); fr.nextFrame(assertData()); } @Test public void readPaddedHeadersFrame() throws IOException { int paddingLength = 254; byte[] padding = new byte[paddingLength]; Arrays.fill(padding, (byte) 0); Buffer headerBlock = literalHeaders(headerEntries("foo", "barrr", "baz", "qux")); writeMedium(frame, (int) headerBlock.size() + paddingLength + 1); frame.writeByte(Http2.TYPE_HEADERS); frame.writeByte(FLAG_END_HEADERS | FLAG_PADDED); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeByte(paddingLength); frame.writeAll(headerBlock); frame.write(padding); fr.nextFrame(assertHeaderBlock()); assertTrue(frame.exhausted()); // Padding was skipped. } @Test public void readPaddedHeadersFrameZeroPadding() throws IOException { Buffer headerBlock = literalHeaders(headerEntries("foo", "barrr", "baz", "qux")); writeMedium(frame, (int) headerBlock.size() + 1); frame.writeByte(Http2.TYPE_HEADERS); frame.writeByte(FLAG_END_HEADERS | FLAG_PADDED); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeByte(0); frame.writeAll(headerBlock); fr.nextFrame(assertHeaderBlock()); } /** Headers are compressed, then framed. */ @Test public void readPaddedHeadersFrameThenContinuation() throws IOException { int paddingLength = 254; byte[] padding = new byte[paddingLength]; Arrays.fill(padding, (byte) 0); // Decoding the first header will cross frame boundaries. Buffer headerBlock = literalHeaders(headerEntries("foo", "barrr", "baz", "qux")); // Write the first headers frame. writeMedium(frame, (int) (headerBlock.size() / 2) + paddingLength + 1); frame.writeByte(Http2.TYPE_HEADERS); frame.writeByte(FLAG_PADDED); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeByte(paddingLength); frame.write(headerBlock, headerBlock.size() / 2); frame.write(padding); // Write the continuation frame, specifying no more frames are expected. writeMedium(frame, (int) headerBlock.size()); frame.writeByte(Http2.TYPE_CONTINUATION); frame.writeByte(FLAG_END_HEADERS); frame.writeInt(expectedStreamId & 0x7fffffff); frame.writeAll(headerBlock); fr.nextFrame(assertHeaderBlock()); assertTrue(frame.exhausted()); } @Test public void tooLargeDataFrame() throws IOException { try { sendDataFrame(new Buffer().write(new byte[0x1000000])); fail(); } catch (IllegalArgumentException e) { assertEquals("FRAME_SIZE_ERROR length > 16384: 16777216", e.getMessage()); } } @Test public void windowUpdateRoundTrip() throws IOException { final long expectedWindowSizeIncrement = 0x7fffffff; writeMedium(frame, 4); // length frame.writeByte(Http2.TYPE_WINDOW_UPDATE); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(expectedStreamId); frame.writeInt((int) expectedWindowSizeIncrement); // Check writer sends the same bytes. assertEquals(frame, windowUpdate(expectedWindowSizeIncrement)); fr.nextFrame(new BaseTestHandler() { @Override public void windowUpdate(int streamId, long windowSizeIncrement) { assertEquals(expectedStreamId, streamId); assertEquals(expectedWindowSizeIncrement, windowSizeIncrement); } }); } @Test public void badWindowSizeIncrement() throws IOException { try { windowUpdate(0); fail(); } catch (IllegalArgumentException e) { assertEquals("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: 0", e.getMessage()); } try { windowUpdate(0x80000000L); fail(); } catch (IllegalArgumentException e) { assertEquals("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: 2147483648", e.getMessage()); } } @Test public void goAwayWithoutDebugDataRoundTrip() throws IOException { final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR; writeMedium(frame, 8); // Without debug data there's only 2 32-bit fields. frame.writeByte(Http2.TYPE_GOAWAY); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(0); // connection-scope frame.writeInt(expectedStreamId); // last good stream. frame.writeInt(expectedError.httpCode); // Check writer sends the same bytes. assertEquals(frame, sendGoAway(expectedStreamId, expectedError, Util.EMPTY_BYTE_ARRAY)); fr.nextFrame(new BaseTestHandler() { @Override public void goAway( int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) { assertEquals(expectedStreamId, lastGoodStreamId); assertEquals(expectedError, errorCode); assertEquals(0, debugData.size()); } }); } @Test public void goAwayWithDebugDataRoundTrip() throws IOException { final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR; final ByteString expectedData = ByteString.encodeUtf8("abcdefgh"); // Compose the expected GOAWAY frame without debug data. writeMedium(frame, 8 + expectedData.size()); frame.writeByte(Http2.TYPE_GOAWAY); frame.writeByte(Http2.FLAG_NONE); frame.writeInt(0); // connection-scope frame.writeInt(0); // never read any stream! frame.writeInt(expectedError.httpCode); frame.write(expectedData.toByteArray()); // Check writer sends the same bytes. assertEquals(frame, sendGoAway(0, expectedError, expectedData.toByteArray())); fr.nextFrame(new BaseTestHandler() { @Override public void goAway( int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) { assertEquals(0, lastGoodStreamId); assertEquals(expectedError, errorCode); assertEquals(expectedData, debugData); } }); } @Test public void frameSizeError() throws IOException { Http2.Writer writer = new Http2.Writer(new Buffer(), true); try { writer.frameHeader(0, 16777216, Http2.TYPE_DATA, FLAG_NONE); fail(); } catch (IllegalArgumentException e) { // TODO: real max is based on settings between 16384 and 16777215 assertEquals("FRAME_SIZE_ERROR length > 16384: 16777216", e.getMessage()); } } @Test public void ackSettingsAppliesMaxFrameSize() throws IOException { int newMaxFrameSize = 16777215; Http2.Writer writer = new Http2.Writer(new Buffer(), true); writer.ackSettings(new Settings().set(Settings.MAX_FRAME_SIZE, 0, newMaxFrameSize)); assertEquals(newMaxFrameSize, writer.maxDataLength()); writer.frameHeader(0, newMaxFrameSize, Http2.TYPE_DATA, FLAG_NONE); } @Test public void streamIdHasReservedBit() throws IOException { Http2.Writer writer = new Http2.Writer(new Buffer(), true); try { int streamId = 3; streamId |= 1L << 31; // set reserved bit writer.frameHeader(streamId, Http2.INITIAL_MAX_FRAME_SIZE, Http2.TYPE_DATA, FLAG_NONE); fail(); } catch (IllegalArgumentException e) { assertEquals("reserved bit set: -2147483645", e.getMessage()); } } private Buffer literalHeaders(List
sentHeaders) throws IOException { Buffer out = new Buffer(); new Hpack.Writer(out).writeHeaders(sentHeaders); return out; } private Buffer sendHeaderFrames(boolean outFinished, List
headers) throws IOException { Buffer out = new Buffer(); new Http2.Writer(out, true).headers(outFinished, expectedStreamId, headers); return out; } private Buffer sendPushPromiseFrames(int streamId, List
headers) throws IOException { Buffer out = new Buffer(); new Http2.Writer(out, true).pushPromise(expectedStreamId, streamId, headers); return out; } private Buffer sendPingFrame(boolean ack, int payload1, int payload2) throws IOException { Buffer out = new Buffer(); new Http2.Writer(out, true).ping(ack, payload1, payload2); return out; } private Buffer sendGoAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) throws IOException { Buffer out = new Buffer(); new Http2.Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData); return out; } private Buffer sendDataFrame(Buffer data) throws IOException { Buffer out = new Buffer(); new Http2.Writer(out, true).dataFrame(expectedStreamId, FLAG_NONE, data, (int) data.size()); return out; } private Buffer windowUpdate(long windowSizeIncrement) throws IOException { Buffer out = new Buffer(); new Http2.Writer(out, true).windowUpdate(expectedStreamId, windowSizeIncrement); return out; } private FrameReader.Handler assertHeaderBlock() { return new BaseTestHandler() { @Override public void headers(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, List
headerBlock, HeadersMode headersMode) { assertFalse(outFinished); assertFalse(inFinished); assertEquals(expectedStreamId, streamId); assertEquals(-1, associatedStreamId); assertEquals(headerEntries("foo", "barrr", "baz", "qux"), headerBlock); assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode); } }; } private FrameReader.Handler assertData() { return new BaseTestHandler() { @Override public void data(boolean inFinished, int streamId, BufferedSource source, int length) throws IOException { assertFalse(inFinished); assertEquals(expectedStreamId, streamId); assertEquals(1123, length); ByteString data = source.readByteString(length); for (byte b : data.toByteArray()) { assertEquals(2, b); } } }; } private static Buffer gzip(byte[] data) throws IOException { Buffer buffer = new Buffer(); Okio.buffer(new GzipSink(buffer)).write(data).close(); return buffer; } /** Create a sufficiently large header set to overflow Http20Draft12.INITIAL_MAX_FRAME_SIZE bytes. */ private static List
largeHeaders() { String[] nameValues = new String[32]; char[] chars = new char[512]; for (int i = 0; i < nameValues.length;) { Arrays.fill(chars, (char) i); nameValues[i++] = nameValues[i++] = String.valueOf(chars); } return headerEntries(nameValues); } private static void writeMedium(BufferedSink sink, int i) throws IOException { sink.writeByte((i >>> 16) & 0xff); sink.writeByte((i >>> 8) & 0xff); sink.writeByte( i & 0xff); } }