예제 #1
0
 private static void assertHeaders(Object msg, int streamId, boolean last, SpdyHeaders headers) {
   assertNotNull(msg);
   assertTrue(msg instanceof SpdyHeadersFrame);
   SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
   assertEquals(spdyHeadersFrame.getStreamId(), streamId);
   assertEquals(spdyHeadersFrame.isLast(), last);
   for (String name : headers.names()) {
     List<String> expectedValues = headers.getAll(name);
     List<String> receivedValues = spdyHeadersFrame.headers().getAll(name);
     assertTrue(receivedValues.containsAll(expectedValues));
     receivedValues.removeAll(expectedValues);
     assertTrue(receivedValues.isEmpty());
     spdyHeadersFrame.headers().remove(name);
   }
   assertTrue(spdyHeadersFrame.headers().isEmpty());
 }
예제 #2
0
  @Override
  protected void decode(
      ChannelHandlerContext ctx, SpdyDataOrControlFrame msg, MessageBuf<Object> out)
      throws Exception {
    if (msg instanceof SpdySynStreamFrame) {

      // HTTP requests/responses are mapped one-to-one to SPDY streams.
      SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
      int streamId = spdySynStreamFrame.getStreamId();

      if (SpdyCodecUtil.isServerId(streamId)) {
        // SYN_STREAM frames initiated by the server are pushed resources
        int associatedToStreamId = spdySynStreamFrame.getAssociatedToStreamId();

        // If a client receives a SYN_STREAM with an Associated-To-Stream-ID of 0
        // it must reply with a RST_STREAM with error code INVALID_STREAM
        if (associatedToStreamId == 0) {
          SpdyRstStreamFrame spdyRstStreamFrame =
              new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INVALID_STREAM);
          ctx.write(spdyRstStreamFrame);
        }

        String URL = SpdyHeaders.getUrl(spdyVersion, spdySynStreamFrame);

        // If a client receives a SYN_STREAM without a 'url' header
        // it must reply with a RST_STREAM with error code PROTOCOL_ERROR
        if (URL == null) {
          SpdyRstStreamFrame spdyRstStreamFrame =
              new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
          ctx.write(spdyRstStreamFrame);
        }

        try {
          FullHttpResponse httpResponseWithEntity =
              createHttpResponse(spdyVersion, spdySynStreamFrame);

          // Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
          SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamId);
          SpdyHttpHeaders.setAssociatedToStreamId(httpResponseWithEntity, associatedToStreamId);
          SpdyHttpHeaders.setPriority(httpResponseWithEntity, spdySynStreamFrame.getPriority());
          SpdyHttpHeaders.setUrl(httpResponseWithEntity, URL);

          if (spdySynStreamFrame.isLast()) {
            HttpHeaders.setContentLength(httpResponseWithEntity, 0);
            out.add(httpResponseWithEntity);
          } else {
            // Response body will follow in a series of Data Frames
            putMessage(streamId, httpResponseWithEntity);
          }
        } catch (Exception e) {
          SpdyRstStreamFrame spdyRstStreamFrame =
              new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
          ctx.write(spdyRstStreamFrame);
        }
      } else {
        // SYN_STREAM frames initiated by the client are HTTP requests
        try {
          FullHttpRequest httpRequestWithEntity =
              createHttpRequest(spdyVersion, spdySynStreamFrame);

          // Set the Stream-ID as a header
          SpdyHttpHeaders.setStreamId(httpRequestWithEntity, streamId);

          if (spdySynStreamFrame.isLast()) {
            out.add(httpRequestWithEntity);
          } else {
            // Request body will follow in a series of Data Frames
            putMessage(streamId, httpRequestWithEntity);
          }
        } catch (Exception e) {
          // If a client sends a SYN_STREAM without all of the getMethod, url (host and path),
          // scheme, and version headers the server must reply with a HTTP 400 BAD REQUEST reply.
          // Also sends HTTP 400 BAD REQUEST reply if header name/value pairs are invalid
          SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
          spdySynReplyFrame.setLast(true);
          SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, HttpResponseStatus.BAD_REQUEST);
          SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
          ctx.write(spdySynReplyFrame);
        }
      }

    } else if (msg instanceof SpdySynReplyFrame) {

      SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
      int streamId = spdySynReplyFrame.getStreamId();

      try {
        FullHttpResponse httpResponseWithEntity =
            createHttpResponse(spdyVersion, spdySynReplyFrame);

        // Set the Stream-ID as a header
        SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamId);

        if (spdySynReplyFrame.isLast()) {
          HttpHeaders.setContentLength(httpResponseWithEntity, 0);
          out.add(httpResponseWithEntity);
        } else {
          // Response body will follow in a series of Data Frames
          putMessage(streamId, httpResponseWithEntity);
        }
      } catch (Exception e) {
        // If a client receives a SYN_REPLY without valid getStatus and version headers
        // the client must reply with a RST_STREAM frame indicating a PROTOCOL_ERROR
        SpdyRstStreamFrame spdyRstStreamFrame =
            new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
        ctx.write(spdyRstStreamFrame);
      }

    } else if (msg instanceof SpdyHeadersFrame) {

      SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
      int streamId = spdyHeadersFrame.getStreamId();
      FullHttpMessage fullHttpMessage = getMessage(streamId);

      // If message is not in map discard HEADERS frame.
      if (fullHttpMessage == null) {
        return;
      }

      for (Map.Entry<String, String> e : spdyHeadersFrame.headers().entries()) {
        fullHttpMessage.headers().add(e.getKey(), e.getValue());
      }

      if (spdyHeadersFrame.isLast()) {
        HttpHeaders.setContentLength(fullHttpMessage, fullHttpMessage.content().readableBytes());
        removeMessage(streamId);
        out.add(fullHttpMessage);
      }

    } else if (msg instanceof SpdyDataFrame) {

      SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
      int streamId = spdyDataFrame.getStreamId();
      FullHttpMessage fullHttpMessage = getMessage(streamId);

      // If message is not in map discard Data Frame.
      if (fullHttpMessage == null) {
        return;
      }

      ByteBuf content = fullHttpMessage.content();
      if (content.readableBytes() > maxContentLength - spdyDataFrame.content().readableBytes()) {
        removeMessage(streamId);
        throw new TooLongFrameException(
            "HTTP content length exceeded " + maxContentLength + " bytes.");
      }

      ByteBuf spdyDataFrameData = spdyDataFrame.content();
      int spdyDataFrameDataLen = spdyDataFrameData.readableBytes();
      content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);

      if (spdyDataFrame.isLast()) {
        HttpHeaders.setContentLength(fullHttpMessage, content.readableBytes());
        removeMessage(streamId);
        out.add(fullHttpMessage);
      }

    } else if (msg instanceof SpdyRstStreamFrame) {

      SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
      int streamId = spdyRstStreamFrame.getStreamId();
      removeMessage(streamId);
    }
  }
예제 #3
0
  private static void testSpdySessionHandler(SpdyVersion version, boolean server) {
    EmbeddedChannel sessionHandler =
        new EmbeddedChannel(
            new SpdySessionHandler(version, server), new EchoHandler(closeSignal, server));

    while (sessionHandler.readOutbound() != null) {
      continue;
    }

    int localStreamId = server ? 1 : 2;
    int remoteStreamId = server ? 2 : 1;

    SpdySynStreamFrame spdySynStreamFrame =
        new DefaultSpdySynStreamFrame(localStreamId, 0, (byte) 0);
    spdySynStreamFrame.headers().set("Compression", "test");

    SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(localStreamId);
    spdyDataFrame.setLast(true);

    // Check if session handler returns INVALID_STREAM if it receives
    // a data frame for a Stream-ID that is not open
    sessionHandler.writeInbound(new DefaultSpdyDataFrame(localStreamId));
    assertRstStream(sessionHandler.readOutbound(), localStreamId, SpdyStreamStatus.INVALID_STREAM);
    assertNull(sessionHandler.readOutbound());

    // Check if session handler returns PROTOCOL_ERROR if it receives
    // a data frame for a Stream-ID before receiving a SYN_REPLY frame
    sessionHandler.writeInbound(new DefaultSpdyDataFrame(remoteStreamId));
    assertRstStream(sessionHandler.readOutbound(), remoteStreamId, SpdyStreamStatus.PROTOCOL_ERROR);
    assertNull(sessionHandler.readOutbound());
    remoteStreamId += 2;

    // Check if session handler returns PROTOCOL_ERROR if it receives
    // multiple SYN_REPLY frames for the same active Stream-ID
    sessionHandler.writeInbound(new DefaultSpdySynReplyFrame(remoteStreamId));
    assertNull(sessionHandler.readOutbound());
    sessionHandler.writeInbound(new DefaultSpdySynReplyFrame(remoteStreamId));
    assertRstStream(sessionHandler.readOutbound(), remoteStreamId, SpdyStreamStatus.STREAM_IN_USE);
    assertNull(sessionHandler.readOutbound());
    remoteStreamId += 2;

    // Check if frame codec correctly compresses/uncompresses headers
    sessionHandler.writeInbound(spdySynStreamFrame);
    assertSynReply(
        sessionHandler.readOutbound(), localStreamId, false, spdySynStreamFrame.headers());
    assertNull(sessionHandler.readOutbound());
    SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(localStreamId);

    spdyHeadersFrame.headers().add("HEADER", "test1");
    spdyHeadersFrame.headers().add("HEADER", "test2");

    sessionHandler.writeInbound(spdyHeadersFrame);
    assertHeaders(sessionHandler.readOutbound(), localStreamId, false, spdyHeadersFrame.headers());
    assertNull(sessionHandler.readOutbound());
    localStreamId += 2;

    // Check if session handler closed the streams using the number
    // of concurrent streams and that it returns REFUSED_STREAM
    // if it receives a SYN_STREAM frame it does not wish to accept
    spdySynStreamFrame.setStreamId(localStreamId);
    spdySynStreamFrame.setLast(true);
    spdySynStreamFrame.setUnidirectional(true);

    sessionHandler.writeInbound(spdySynStreamFrame);
    assertRstStream(sessionHandler.readOutbound(), localStreamId, SpdyStreamStatus.REFUSED_STREAM);
    assertNull(sessionHandler.readOutbound());

    // Check if session handler rejects HEADERS for closed streams
    int testStreamId = spdyDataFrame.getStreamId();
    sessionHandler.writeInbound(spdyDataFrame);
    assertDataFrame(sessionHandler.readOutbound(), testStreamId, spdyDataFrame.isLast());
    assertNull(sessionHandler.readOutbound());
    spdyHeadersFrame.setStreamId(testStreamId);

    sessionHandler.writeInbound(spdyHeadersFrame);
    assertRstStream(sessionHandler.readOutbound(), testStreamId, SpdyStreamStatus.INVALID_STREAM);
    assertNull(sessionHandler.readOutbound());

    // Check if session handler drops active streams if it receives
    // a RST_STREAM frame for that Stream-ID
    sessionHandler.writeInbound(new DefaultSpdyRstStreamFrame(remoteStreamId, 3));
    assertNull(sessionHandler.readOutbound());
    remoteStreamId += 2;

    // Check if session handler honors UNIDIRECTIONAL streams
    spdySynStreamFrame.setLast(false);
    sessionHandler.writeInbound(spdySynStreamFrame);
    assertNull(sessionHandler.readOutbound());
    spdySynStreamFrame.setUnidirectional(false);

    // Check if session handler returns PROTOCOL_ERROR if it receives
    // multiple SYN_STREAM frames for the same active Stream-ID
    sessionHandler.writeInbound(spdySynStreamFrame);
    assertRstStream(sessionHandler.readOutbound(), localStreamId, SpdyStreamStatus.PROTOCOL_ERROR);
    assertNull(sessionHandler.readOutbound());
    localStreamId += 2;

    // Check if session handler returns PROTOCOL_ERROR if it receives
    // a SYN_STREAM frame with an invalid Stream-ID
    spdySynStreamFrame.setStreamId(localStreamId - 1);
    sessionHandler.writeInbound(spdySynStreamFrame);
    assertRstStream(
        sessionHandler.readOutbound(), localStreamId - 1, SpdyStreamStatus.PROTOCOL_ERROR);
    assertNull(sessionHandler.readOutbound());
    spdySynStreamFrame.setStreamId(localStreamId);

    // Check if session handler returns PROTOCOL_ERROR if it receives
    // an invalid HEADERS frame
    spdyHeadersFrame.setStreamId(localStreamId);

    spdyHeadersFrame.setInvalid();
    sessionHandler.writeInbound(spdyHeadersFrame);
    assertRstStream(sessionHandler.readOutbound(), localStreamId, SpdyStreamStatus.PROTOCOL_ERROR);
    assertNull(sessionHandler.readOutbound());

    sessionHandler.finish();
  }
예제 #4
0
  @Override
  protected void encode(ChannelHandlerContext ctx, HttpObject msg, List<Object> out)
      throws Exception {

    boolean valid = false;
    boolean last = false;

    if (msg instanceof HttpRequest) {

      HttpRequest httpRequest = (HttpRequest) msg;
      SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
      out.add(spdySynStreamFrame);

      last = spdySynStreamFrame.isLast();
      valid = true;
    }
    if (msg instanceof HttpResponse) {

      HttpResponse httpResponse = (HttpResponse) msg;
      if (httpResponse.headers().contains(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
        SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse);
        last = spdySynStreamFrame.isLast();
        out.add(spdySynStreamFrame);
      } else {
        SpdySynReplyFrame spdySynReplyFrame = createSynReplyFrame(httpResponse);
        last = spdySynReplyFrame.isLast();
        out.add(spdySynReplyFrame);
      }

      valid = true;
    }
    if (msg instanceof HttpContent && !last) {

      HttpContent chunk = (HttpContent) msg;

      chunk.content().retain();
      SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId, chunk.content());
      spdyDataFrame.setLast(chunk instanceof LastHttpContent);
      if (chunk instanceof LastHttpContent) {
        LastHttpContent trailer = (LastHttpContent) chunk;
        HttpHeaders trailers = trailer.trailingHeaders();
        if (trailers.isEmpty()) {
          out.add(spdyDataFrame);
        } else {
          // Create SPDY HEADERS frame out of trailers
          SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId);
          for (Map.Entry<String, String> entry : trailers) {
            spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue());
          }

          // Write HEADERS frame and append Data Frame
          out.add(spdyHeadersFrame);
          out.add(spdyDataFrame);
        }
      } else {
        out.add(spdyDataFrame);
      }

      valid = true;
    }

    if (!valid) {
      throw new UnsupportedMessageTypeException(msg);
    }
  }