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