@Test public void peerHttp2ClientDisablesPush() throws Exception { boolean client = false; // Peer is client, so we are server. Settings settings = new Settings(); settings.set(ENABLE_PUSH, 0, 0); // The peer client disables push. FramedConnection connection = sendHttp2SettingsAndCheckForAck(client, settings); // verify the peer's settings were read and applied. assertFalse(connection.peerSettings.getEnablePush(true)); }
@Test public void peerIncreasesMaxFrameSize() throws Exception { int newMaxFrameSize = 0x4001; Settings settings = new Settings(); settings.set(MAX_FRAME_SIZE, 0, newMaxFrameSize); FramedConnection connection = sendHttp2SettingsAndCheckForAck(true, settings); // verify the peer's settings were read and applied. assertEquals(newMaxFrameSize, connection.peerSettings.getMaxFrameSize(-1)); assertEquals(newMaxFrameSize, connection.frameWriter.maxDataLength()); }
@Test public void peerHttp2ServerZerosCompressionTable() throws Exception { boolean client = false; // Peer is server, so we are client. Settings settings = new Settings(); settings.set(HEADER_TABLE_SIZE, PERSIST_VALUE, 0); FramedConnection connection = sendHttp2SettingsAndCheckForAck(client, settings); // verify the peer's settings were read and applied. assertEquals(0, connection.peerSettings.getHeaderTableSize()); Http2.Reader frameReader = (Http2.Reader) connection.readerRunnable.frameReader; assertEquals(0, frameReader.hpackReader.maxDynamicTableByteCount()); // TODO: when supported, check the frameWriter's compression table is unaffected. }
private FramedConnection(Builder builder) throws IOException { protocol = builder.protocol; pushObserver = builder.pushObserver; client = builder.client; listener = builder.listener; // http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-5.1.1 nextStreamId = builder.client ? 1 : 2; if (builder.client && protocol == Protocol.HTTP_2) { nextStreamId += 2; // In HTTP/2, 1 on client is reserved for Upgrade. } nextPingId = builder.client ? 1 : 2; // Flow control was designed more for servers, or proxies than edge clients. // If we are a client, set the flow control window to 16MiB. This avoids // thrashing window updates every 64KiB, yet small enough to avoid blowing // up the heap. if (builder.client) { okHttpSettings.set(Settings.INITIAL_WINDOW_SIZE, 0, OKHTTP_CLIENT_WINDOW_SIZE); } hostname = builder.hostname; if (protocol == Protocol.HTTP_2) { variant = new Http2(); // Like newSingleThreadExecutor, except lazy creates the thread. pushExecutor = new ThreadPoolExecutor( 0, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), Util.threadFactory(Util.format("OkHttp %s Push Observer", hostname), true)); // 1 less than SPDY http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-6.9.2 peerSettings.set(Settings.INITIAL_WINDOW_SIZE, 0, 65535); peerSettings.set(Settings.MAX_FRAME_SIZE, 0, Http2.INITIAL_MAX_FRAME_SIZE); } else if (protocol == Protocol.SPDY_3) { variant = new Spdy3(); pushExecutor = null; } else { throw new AssertionError(protocol); } bytesLeftInWriteWindow = peerSettings.getInitialWindowSize(DEFAULT_INITIAL_WINDOW_SIZE); socket = builder.socket; frameWriter = variant.newWriter(builder.sink, client); readerRunnable = new Reader(variant.newReader(builder.source, client)); }
/** Merges {@code settings} into this peer's settings and sends them to the remote peer. */ public void setSettings(Settings settings) throws IOException { synchronized (frameWriter) { synchronized (this) { if (shutdown) { throw new IOException("shutdown"); } okHttpSettings.merge(settings); frameWriter.settings(settings); } } }
/** * @param sendConnectionPreface true to send connection preface frames. This should always be true * except for in tests that don't check for a connection preface. */ void start(boolean sendConnectionPreface) throws IOException { if (sendConnectionPreface) { frameWriter.connectionPreface(); frameWriter.settings(okHttpSettings); int windowSize = okHttpSettings.getInitialWindowSize(Settings.DEFAULT_INITIAL_WINDOW_SIZE); if (windowSize != Settings.DEFAULT_INITIAL_WINDOW_SIZE) { frameWriter.windowUpdate(0, windowSize - Settings.DEFAULT_INITIAL_WINDOW_SIZE); } } new Thread(readerRunnable).start(); // Not a daemon thread. }
@Test public void peerHttp2ServerLowersInitialWindowSize() throws Exception { peer.setVariantAndClient(HTTP_2, false); Settings initial = new Settings(); initial.set(INITIAL_WINDOW_SIZE, PERSIST_VALUE, 1684); Settings shouldntImpactConnection = new Settings(); shouldntImpactConnection.set(INITIAL_WINDOW_SIZE, PERSIST_VALUE, 3368); peer.sendFrame().settings(initial); peer.acceptFrame(); // ACK peer.sendFrame().settings(shouldntImpactConnection); peer.acceptFrame(); // ACK 2 peer.acceptFrame(); // HEADERS peer.play(); FramedConnection connection = connection(peer, HTTP_2); // Default is 64KiB - 1. assertEquals(65535, connection.peerSettings.getInitialWindowSize(-1)); // Verify the peer received the ACK. MockSpdyPeer.InFrame ackFrame = peer.takeFrame(); assertEquals(TYPE_SETTINGS, ackFrame.type); assertEquals(0, ackFrame.streamId); assertTrue(ackFrame.ack); ackFrame = peer.takeFrame(); assertEquals(TYPE_SETTINGS, ackFrame.type); assertEquals(0, ackFrame.streamId); assertTrue(ackFrame.ack); // This stream was created *after* the connection settings were adjusted. FramedStream stream = connection.newStream(headerEntries("a", "android"), false, true); assertEquals(3368, connection.peerSettings.getInitialWindowSize(DEFAULT_INITIAL_WINDOW_SIZE)); assertEquals(1684, connection.bytesLeftInWriteWindow); // initial wasn't affected. // New Stream is has the most recent initial window size. assertEquals(3368, stream.bytesLeftInWriteWindow); }
public synchronized int maxConcurrentStreams() { return peerSettings.getMaxConcurrentStreams(Integer.MAX_VALUE); }