13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/*
23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2014 Square, Inc.
33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * you may not use this file except in compliance with the License.
63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * You may obtain a copy of the License at
73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Unless required by applicable law or agreed to in writing, software
113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * See the License for the specific language governing permissions and
143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * limitations under the License.
153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage com.squareup.okhttp.internal.spdy;
173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.Util;
193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException;
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.ByteString;
213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.OkBuffer;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport org.junit.Test;
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertEquals;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.fail;
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic class Spdy3Test {
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  static final int expectedStreamId = 15;
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void tooLargeDataFrame() throws IOException {
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      sendDataFrame(new OkBuffer().write(new byte[0x1000000]));
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IllegalArgumentException e) {
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      assertEquals("FRAME_TOO_LARGE max size is 16Mib: " + 0x1000000L, e.getMessage());
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void badWindowSizeIncrement() throws IOException {
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      windowUpdate(0);
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IllegalArgumentException e) {
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      assertEquals("windowSizeIncrement must be between 1 and 0x7fffffff: 0", e.getMessage());
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      windowUpdate(0x80000000L);
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IllegalArgumentException e) {
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      assertEquals("windowSizeIncrement must be between 1 and 0x7fffffff: 2147483648",
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          e.getMessage());
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void goAwayRoundTrip() throws IOException {
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer frame = new OkBuffer();
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Compose the expected GOAWAY frame without debug data
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // |C| Version(15bits) | Type(16bits) |
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    frame.writeInt(0x80000000 | (Spdy3.VERSION & 0x7fff) << 16 | Spdy3.TYPE_GOAWAY & 0xffff);
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // | Flags (8)  |  Length (24 bits)   |
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    frame.writeInt(8); // no flags and length is 8.
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    frame.writeInt(expectedStreamId); // last good stream.
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    frame.writeInt(expectedError.spdyGoAwayCode);
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Check writer sends the same bytes.
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(frame, sendGoAway(expectedStreamId, expectedError, Util.EMPTY_BYTE_ARRAY));
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // SPDY/3 does not send debug data, so bytes should be same!
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(frame, sendGoAway(expectedStreamId, expectedError, new byte[8]));
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    FrameReader fr = new Spdy3.Reader(frame, false);
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    fr.nextFrame(new BaseTestHandler() { // Consume the goAway frame.
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override public void goAway(
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        assertEquals(expectedStreamId, lastGoodStreamId);
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        assertEquals(expectedError, errorCode);
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        assertEquals(0, debugData.size());
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    });
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void sendDataFrame(OkBuffer source) throws IOException {
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Spdy3.Writer writer = new Spdy3.Writer(new OkBuffer(), true);
883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    writer.sendDataFrame(expectedStreamId, 0, source, (int) source.size());
893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void windowUpdate(long increment) throws IOException {
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    new Spdy3.Writer(new OkBuffer(), true).windowUpdate(expectedStreamId, increment);
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private OkBuffer sendGoAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData)
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      throws IOException {
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer out = new OkBuffer();
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    new Spdy3.Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData);
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return out;
1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller}
102