Пример #1
0
  @Override
  public Object encode(ChannelHandlerContext ctx, Object msg) throws Exception {

    List<Object> out = new ArrayList<Object>();
    if (msg instanceof HttpRequest) {

      HttpRequest httpRequest = (HttpRequest) msg;
      SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
      int streamID = spdySynStreamFrame.getStreamID();
      out.add(spdySynStreamFrame);
      addContent(out, streamID, httpRequest);

    } else if (msg instanceof HttpResponse) {

      HttpResponse httpResponse = (HttpResponse) msg;
      if (httpResponse.containsHeader(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
        SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse);
        int streamID = spdySynStreamFrame.getStreamID();
        out.add(spdySynStreamFrame);
        addContent(out, streamID, httpResponse);
      } else {
        SpdySynReplyFrame spdySynReplyFrame = createSynReplyFrame(httpResponse);
        int streamID = spdySynReplyFrame.getStreamID();
        out.add(spdySynReplyFrame);
        addContent(out, streamID, httpResponse);
      }

    } else if (msg instanceof HttpChunk) {

      HttpChunk chunk = (HttpChunk) msg;
      SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamID);
      spdyDataFrame.setData(chunk.getContent());
      spdyDataFrame.setLast(chunk.isLast());

      if (chunk instanceof HttpChunkTrailer) {
        HttpChunkTrailer trailer = (HttpChunkTrailer) chunk;
        List<Map.Entry<String, String>> trailers = trailer.getHeaders();
        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.addHeader(entry.getKey(), entry.getValue());
          }

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

    return out.toArray();
  }
  private SpdyHeadersFrame readHeaderBlockFrame(ByteBuf buffer) {
    int streamId;
    switch (type) {
      case SPDY_SYN_STREAM_FRAME:
        if (buffer.readableBytes() < 10) {
          return null;
        }

        int offset = buffer.readerIndex();
        streamId = getUnsignedInt(buffer, offset);
        int associatedToStreamId = getUnsignedInt(buffer, offset + 4);
        byte priority = (byte) (buffer.getByte(offset + 8) >> 5 & 0x07);
        buffer.skipBytes(10);
        length -= 10;

        SpdySynStreamFrame spdySynStreamFrame =
            new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority);
        spdySynStreamFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
        spdySynStreamFrame.setUnidirectional((flags & SPDY_FLAG_UNIDIRECTIONAL) != 0);

        return spdySynStreamFrame;

      case SPDY_SYN_REPLY_FRAME:
        if (buffer.readableBytes() < 4) {
          return null;
        }

        streamId = getUnsignedInt(buffer, buffer.readerIndex());
        buffer.skipBytes(4);
        length -= 4;

        SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
        spdySynReplyFrame.setLast((flags & SPDY_FLAG_FIN) != 0);

        return spdySynReplyFrame;

      case SPDY_HEADERS_FRAME:
        if (buffer.readableBytes() < 4) {
          return null;
        }

        streamId = getUnsignedInt(buffer, buffer.readerIndex());
        buffer.skipBytes(4);
        length -= 4;

        SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId);
        spdyHeadersFrame.setLast((flags & SPDY_FLAG_FIN) != 0);

        return spdyHeadersFrame;

      default:
        throw new Error("Shouldn't reach here.");
    }
  }
Пример #3
0
  private static void testSpdySessionHandlerGoAway(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;

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

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

    // Send an initial request
    sessionHandler.writeInbound(spdySynStreamFrame);
    assertSynReply(
        sessionHandler.readOutbound(), localStreamId, false, spdySynStreamFrame.headers());
    assertNull(sessionHandler.readOutbound());
    sessionHandler.writeInbound(spdyDataFrame);
    assertDataFrame(sessionHandler.readOutbound(), localStreamId, true);
    assertNull(sessionHandler.readOutbound());

    // Check if session handler sends a GOAWAY frame when closing
    sessionHandler.writeInbound(closeMessage);
    assertGoAway(sessionHandler.readOutbound(), localStreamId);
    assertNull(sessionHandler.readOutbound());
    localStreamId += 2;

    // Check if session handler returns REFUSED_STREAM if it receives
    // SYN_STREAM frames after sending a GOAWAY frame
    spdySynStreamFrame.setStreamId(localStreamId);
    sessionHandler.writeInbound(spdySynStreamFrame);
    assertRstStream(sessionHandler.readOutbound(), localStreamId, SpdyStreamStatus.REFUSED_STREAM);
    assertNull(sessionHandler.readOutbound());

    // Check if session handler ignores Data frames after sending
    // a GOAWAY frame
    spdyDataFrame.setStreamId(localStreamId);
    sessionHandler.writeInbound(spdyDataFrame);
    assertNull(sessionHandler.readOutbound());

    sessionHandler.finish();
  }
Пример #4
0
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      if (msg instanceof SpdySynStreamFrame) {

        SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
        if (!spdySynStreamFrame.isUnidirectional()) {
          int streamId = spdySynStreamFrame.getStreamId();
          SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
          spdySynReplyFrame.setLast(spdySynStreamFrame.isLast());
          for (Map.Entry<String, String> entry : spdySynStreamFrame.headers()) {
            spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
          }

          ctx.writeAndFlush(spdySynReplyFrame);
        }
        return;
      }

      if (msg instanceof SpdySynReplyFrame) {
        return;
      }

      if (msg instanceof SpdyDataFrame
          || msg instanceof SpdyPingFrame
          || msg instanceof SpdyHeadersFrame) {

        ctx.writeAndFlush(msg);
        return;
      }

      if (msg instanceof SpdySettingsFrame) {
        SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
        if (spdySettingsFrame.isSet(closeSignal)) {
          ctx.close();
        }
      }
    }
Пример #5
0
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
      // Initiate 4 new streams
      int streamId = server ? 2 : 1;
      SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(streamId, 0, (byte) 0);
      spdySynStreamFrame.setLast(true);
      ctx.writeAndFlush(spdySynStreamFrame);
      spdySynStreamFrame.setStreamId(spdySynStreamFrame.getStreamId() + 2);
      ctx.writeAndFlush(spdySynStreamFrame);
      spdySynStreamFrame.setStreamId(spdySynStreamFrame.getStreamId() + 2);
      ctx.writeAndFlush(spdySynStreamFrame);
      spdySynStreamFrame.setStreamId(spdySynStreamFrame.getStreamId() + 2);
      ctx.writeAndFlush(spdySynStreamFrame);

      // Limit the number of concurrent streams to 1
      SpdySettingsFrame spdySettingsFrame = new DefaultSpdySettingsFrame();
      spdySettingsFrame.setValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS, 1);
      ctx.writeAndFlush(spdySettingsFrame);
    }
Пример #6
0
  private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage) throws Exception {
    boolean chunked = httpMessage.isChunked();

    // Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers
    int streamID = SpdyHttpHeaders.getStreamID(httpMessage);
    int associatedToStreamID = SpdyHttpHeaders.getAssociatedToStreamID(httpMessage);
    byte priority = SpdyHttpHeaders.getPriority(httpMessage);
    String URL = SpdyHttpHeaders.getUrl(httpMessage);
    String scheme = SpdyHttpHeaders.getScheme(httpMessage);
    SpdyHttpHeaders.removeStreamID(httpMessage);
    SpdyHttpHeaders.removeAssociatedToStreamID(httpMessage);
    SpdyHttpHeaders.removePriority(httpMessage);
    SpdyHttpHeaders.removeUrl(httpMessage);
    SpdyHttpHeaders.removeScheme(httpMessage);

    // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
    // headers are not valid and MUST not be sent.
    httpMessage.removeHeader(HttpHeaders.Names.CONNECTION);
    httpMessage.removeHeader("Keep-Alive");
    httpMessage.removeHeader("Proxy-Connection");
    httpMessage.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);

    SpdySynStreamFrame spdySynStreamFrame =
        new DefaultSpdySynStreamFrame(streamID, associatedToStreamID, priority);

    // Unfold the first line of the message into name/value pairs
    if (httpMessage instanceof HttpRequest) {
      HttpRequest httpRequest = (HttpRequest) httpMessage;
      SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.getMethod());
      SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.getUri());
      SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion());
    }
    if (httpMessage instanceof HttpResponse) {
      HttpResponse httpResponse = (HttpResponse) httpMessage;
      SpdyHeaders.setStatus(spdyVersion, spdySynStreamFrame, httpResponse.getStatus());
      SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, URL);
      spdySynStreamFrame.setUnidirectional(true);
    }

    // Replace the HTTP host header with the SPDY host header
    if (spdyVersion >= 3) {
      String host = HttpHeaders.getHost(httpMessage);
      httpMessage.removeHeader(HttpHeaders.Names.HOST);
      SpdyHeaders.setHost(spdySynStreamFrame, host);
    }

    // Set the SPDY scheme header
    if (scheme == null) {
      scheme = "https";
    }
    SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme);

    // Transfer the remaining HTTP headers
    for (Map.Entry<String, String> entry : httpMessage.getHeaders()) {
      spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue());
    }

    if (chunked) {
      currentStreamID = streamID;
      spdySynStreamFrame.setLast(false);
    } else {
      spdySynStreamFrame.setLast(httpMessage.getContent().readableBytes() == 0);
    }

    return spdySynStreamFrame;
  }
Пример #7
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);
    }
  }
Пример #8
0
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg instanceof SpdyDataFrame) {

      /*
       * SPDY Data frame processing requirements:
       *
       * If an endpoint receives a data frame for a Stream-ID which is not open
       * and the endpoint has not sent a GOAWAY frame, it must issue a stream error
       * with the error code INVALID_STREAM for the Stream-ID.
       *
       * If an endpoint which created the stream receives a data frame before receiving
       * a SYN_REPLY on that stream, it is a protocol error, and the recipient must
       * issue a stream error with the getStatus code PROTOCOL_ERROR for the Stream-ID.
       *
       * If an endpoint receives multiple data frames for invalid Stream-IDs,
       * it may close the session.
       *
       * If an endpoint refuses a stream it must ignore any data frames for that stream.
       *
       * If an endpoint receives a data frame after the stream is half-closed from the
       * sender, it must send a RST_STREAM frame with the getStatus STREAM_ALREADY_CLOSED.
       *
       * If an endpoint receives a data frame after the stream is closed, it must send
       * a RST_STREAM frame with the getStatus PROTOCOL_ERROR.
       */
      SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
      int streamId = spdyDataFrame.streamId();

      int deltaWindowSize = -1 * spdyDataFrame.content().readableBytes();
      int newSessionWindowSize =
          spdySession.updateReceiveWindowSize(SPDY_SESSION_STREAM_ID, deltaWindowSize);

      // Check if session window size is reduced beyond allowable lower bound
      if (newSessionWindowSize < 0) {
        issueSessionError(ctx, SpdySessionStatus.PROTOCOL_ERROR);
        return;
      }

      // Send a WINDOW_UPDATE frame if less than half the session window size remains
      if (newSessionWindowSize <= initialSessionReceiveWindowSize / 2) {
        int sessionDeltaWindowSize = initialSessionReceiveWindowSize - newSessionWindowSize;
        spdySession.updateReceiveWindowSize(SPDY_SESSION_STREAM_ID, sessionDeltaWindowSize);
        SpdyWindowUpdateFrame spdyWindowUpdateFrame =
            new DefaultSpdyWindowUpdateFrame(SPDY_SESSION_STREAM_ID, sessionDeltaWindowSize);
        ctx.writeAndFlush(spdyWindowUpdateFrame);
      }

      // Check if we received a data frame for a Stream-ID which is not open

      if (!spdySession.isActiveStream(streamId)) {
        spdyDataFrame.release();
        if (streamId <= lastGoodStreamId) {
          issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR);
        } else if (!sentGoAwayFrame) {
          issueStreamError(ctx, streamId, SpdyStreamStatus.INVALID_STREAM);
        }
        return;
      }

      // Check if we received a data frame for a stream which is half-closed

      if (spdySession.isRemoteSideClosed(streamId)) {
        spdyDataFrame.release();
        issueStreamError(ctx, streamId, SpdyStreamStatus.STREAM_ALREADY_CLOSED);
        return;
      }

      // Check if we received a data frame before receiving a SYN_REPLY
      if (!isRemoteInitiatedId(streamId) && !spdySession.hasReceivedReply(streamId)) {
        spdyDataFrame.release();
        issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR);
        return;
      }

      /*
       * SPDY Data frame flow control processing requirements:
       *
       * Recipient should not send a WINDOW_UPDATE frame as it consumes the last data frame.
       */

      // Update receive window size
      int newWindowSize = spdySession.updateReceiveWindowSize(streamId, deltaWindowSize);

      // Window size can become negative if we sent a SETTINGS frame that reduces the
      // size of the transfer window after the peer has written data frames.
      // The value is bounded by the length that SETTINGS frame decrease the window.
      // This difference is stored for the session when writing the SETTINGS frame
      // and is cleared once we send a WINDOW_UPDATE frame.
      if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamId)) {
        spdyDataFrame.release();
        issueStreamError(ctx, streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR);
        return;
      }

      // Window size became negative due to sender writing frame before receiving SETTINGS
      // Send data frames upstream in initialReceiveWindowSize chunks
      if (newWindowSize < 0) {
        while (spdyDataFrame.content().readableBytes() > initialReceiveWindowSize) {
          SpdyDataFrame partialDataFrame =
              new DefaultSpdyDataFrame(
                  streamId, spdyDataFrame.content().readSlice(initialReceiveWindowSize).retain());
          ctx.writeAndFlush(partialDataFrame);
        }
      }

      // Send a WINDOW_UPDATE frame if less than half the stream window size remains
      if (newWindowSize <= initialReceiveWindowSize / 2 && !spdyDataFrame.isLast()) {
        int streamDeltaWindowSize = initialReceiveWindowSize - newWindowSize;
        spdySession.updateReceiveWindowSize(streamId, streamDeltaWindowSize);
        SpdyWindowUpdateFrame spdyWindowUpdateFrame =
            new DefaultSpdyWindowUpdateFrame(streamId, streamDeltaWindowSize);
        ctx.writeAndFlush(spdyWindowUpdateFrame);
      }

      // Close the remote side of the stream if this is the last frame
      if (spdyDataFrame.isLast()) {
        halfCloseStream(streamId, true, ctx.newSucceededFuture());
      }

    } else if (msg instanceof SpdySynStreamFrame) {

      /*
       * SPDY SYN_STREAM frame processing requirements:
       *
       * If an endpoint receives a SYN_STREAM with a Stream-ID that is less than
       * any previously received SYN_STREAM, it must issue a session error with
       * the getStatus PROTOCOL_ERROR.
       *
       * If an endpoint receives multiple SYN_STREAM frames with the same active
       * Stream-ID, it must issue a stream error with the getStatus code PROTOCOL_ERROR.
       *
       * The recipient can reject a stream by sending a stream error with the
       * getStatus code REFUSED_STREAM.
       */

      SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
      int streamId = spdySynStreamFrame.streamId();

      // Check if we received a valid SYN_STREAM frame
      if (spdySynStreamFrame.isInvalid()
          || !isRemoteInitiatedId(streamId)
          || spdySession.isActiveStream(streamId)) {
        issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR);
        return;
      }

      // Stream-IDs must be monotonically increasing
      if (streamId <= lastGoodStreamId) {
        issueSessionError(ctx, SpdySessionStatus.PROTOCOL_ERROR);
        return;
      }

      // Try to accept the stream
      byte priority = spdySynStreamFrame.priority();
      boolean remoteSideClosed = spdySynStreamFrame.isLast();
      boolean localSideClosed = spdySynStreamFrame.isUnidirectional();
      if (!acceptStream(streamId, priority, remoteSideClosed, localSideClosed)) {
        issueStreamError(ctx, streamId, SpdyStreamStatus.REFUSED_STREAM);
        return;
      }

    } else if (msg instanceof SpdySynReplyFrame) {

      /*
       * SPDY SYN_REPLY frame processing requirements:
       *
       * If an endpoint receives multiple SYN_REPLY frames for the same active Stream-ID
       * it must issue a stream error with the getStatus code STREAM_IN_USE.
       */

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

      // Check if we received a valid SYN_REPLY frame
      if (spdySynReplyFrame.isInvalid()
          || isRemoteInitiatedId(streamId)
          || spdySession.isRemoteSideClosed(streamId)) {
        issueStreamError(ctx, streamId, SpdyStreamStatus.INVALID_STREAM);
        return;
      }

      // Check if we have received multiple frames for the same Stream-ID
      if (spdySession.hasReceivedReply(streamId)) {
        issueStreamError(ctx, streamId, SpdyStreamStatus.STREAM_IN_USE);
        return;
      }

      spdySession.receivedReply(streamId);

      // Close the remote side of the stream if this is the last frame
      if (spdySynReplyFrame.isLast()) {
        halfCloseStream(streamId, true, ctx.newSucceededFuture());
      }

    } else if (msg instanceof SpdyRstStreamFrame) {

      /*
       * SPDY RST_STREAM frame processing requirements:
       *
       * After receiving a RST_STREAM on a stream, the receiver must not send
       * additional frames on that stream.
       *
       * An endpoint must not send a RST_STREAM in response to a RST_STREAM.
       */

      SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
      removeStream(spdyRstStreamFrame.streamId(), ctx.newSucceededFuture());

    } else if (msg instanceof SpdySettingsFrame) {

      SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;

      int settingsMinorVersion =
          spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MINOR_VERSION);
      if (settingsMinorVersion >= 0 && settingsMinorVersion != minorVersion) {
        // Settings frame had the wrong minor version
        issueSessionError(ctx, SpdySessionStatus.PROTOCOL_ERROR);
        return;
      }

      int newConcurrentStreams =
          spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS);
      if (newConcurrentStreams >= 0) {
        remoteConcurrentStreams = newConcurrentStreams;
      }

      // Persistence flag are inconsistent with the use of SETTINGS to communicate
      // the initial window size. Remove flags from the sender requesting that the
      // value be persisted. Remove values that the sender indicates are persisted.
      if (spdySettingsFrame.isPersisted(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE)) {
        spdySettingsFrame.removeValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
      }
      spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);

      int newInitialWindowSize =
          spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
      if (newInitialWindowSize >= 0) {
        updateInitialSendWindowSize(newInitialWindowSize);
      }

    } else if (msg instanceof SpdyPingFrame) {

      /*
       * SPDY PING frame processing requirements:
       *
       * Receivers of a PING frame should send an identical frame to the sender
       * as soon as possible.
       *
       * Receivers of a PING frame must ignore frames that it did not initiate
       */

      SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;

      if (isRemoteInitiatedId(spdyPingFrame.id())) {
        ctx.writeAndFlush(spdyPingFrame);
        return;
      }

      // Note: only checks that there are outstanding pings since uniqueness is not enforced
      if (pings.get() == 0) {
        return;
      }
      pings.getAndDecrement();

    } else if (msg instanceof SpdyGoAwayFrame) {

      receivedGoAwayFrame = true;

    } else if (msg instanceof SpdyHeadersFrame) {

      SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
      int streamId = spdyHeadersFrame.streamId();

      // Check if we received a valid HEADERS frame
      if (spdyHeadersFrame.isInvalid()) {
        issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR);
        return;
      }

      if (spdySession.isRemoteSideClosed(streamId)) {
        issueStreamError(ctx, streamId, SpdyStreamStatus.INVALID_STREAM);
        return;
      }

      // Close the remote side of the stream if this is the last frame
      if (spdyHeadersFrame.isLast()) {
        halfCloseStream(streamId, true, ctx.newSucceededFuture());
      }

    } else if (msg instanceof SpdyWindowUpdateFrame) {

      /*
       * SPDY WINDOW_UPDATE frame processing requirements:
       *
       * Receivers of a WINDOW_UPDATE that cause the window size to exceed 2^31
       * must send a RST_STREAM with the getStatus code FLOW_CONTROL_ERROR.
       *
       * Sender should ignore all WINDOW_UPDATE frames associated with a stream
       * after sending the last frame for the stream.
       */

      SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
      int streamId = spdyWindowUpdateFrame.streamId();
      int deltaWindowSize = spdyWindowUpdateFrame.deltaWindowSize();

      // Ignore frames for half-closed streams
      if (streamId != SPDY_SESSION_STREAM_ID && spdySession.isLocalSideClosed(streamId)) {
        return;
      }

      // Check for numerical overflow
      if (spdySession.getSendWindowSize(streamId) > Integer.MAX_VALUE - deltaWindowSize) {
        if (streamId == SPDY_SESSION_STREAM_ID) {
          issueSessionError(ctx, SpdySessionStatus.PROTOCOL_ERROR);
        } else {
          issueStreamError(ctx, streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR);
        }
        return;
      }

      updateSendWindowSize(ctx, streamId, deltaWindowSize);
    }

    ctx.fireChannelRead(msg);
  }
Пример #9
0
  private void handleOutboundMessage(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
      throws Exception {
    if (msg instanceof SpdyDataFrame) {

      SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
      int streamId = spdyDataFrame.streamId();

      // Frames must not be sent on half-closed streams
      if (spdySession.isLocalSideClosed(streamId)) {
        spdyDataFrame.release();
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      /*
       * SPDY Data frame flow control processing requirements:
       *
       * Sender must not send a data frame with data length greater
       * than the transfer window size.
       *
       * After sending each data frame, the sender decrements its
       * transfer window size by the amount of data transmitted.
       *
       * When the window size becomes less than or equal to 0, the
       * sender must pause transmitting data frames.
       */

      int dataLength = spdyDataFrame.content().readableBytes();
      int sendWindowSize = spdySession.getSendWindowSize(streamId);
      int sessionSendWindowSize = spdySession.getSendWindowSize(SPDY_SESSION_STREAM_ID);
      sendWindowSize = Math.min(sendWindowSize, sessionSendWindowSize);

      if (sendWindowSize <= 0) {
        // Stream is stalled -- enqueue Data frame and return
        spdySession.putPendingWrite(streamId, new SpdySession.PendingWrite(spdyDataFrame, promise));
        return;
      } else if (sendWindowSize < dataLength) {
        // Stream is not stalled but we cannot send the entire frame
        spdySession.updateSendWindowSize(streamId, -1 * sendWindowSize);
        spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * sendWindowSize);

        // Create a partial data frame whose length is the current window size
        SpdyDataFrame partialDataFrame =
            new DefaultSpdyDataFrame(
                streamId, spdyDataFrame.content().readSlice(sendWindowSize).retain());

        // Enqueue the remaining data (will be the first frame queued)
        spdySession.putPendingWrite(streamId, new SpdySession.PendingWrite(spdyDataFrame, promise));

        // The transfer window size is pre-decremented when sending a data frame downstream.
        // Close the session on write failures that leave the transfer window in a corrupt state.
        final ChannelHandlerContext context = ctx;
        ctx.write(partialDataFrame)
            .addListener(
                new ChannelFutureListener() {
                  @Override
                  public void operationComplete(ChannelFuture future) throws Exception {
                    if (!future.isSuccess()) {
                      issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
                    }
                  }
                });
        return;
      } else {
        // Window size is large enough to send entire data frame
        spdySession.updateSendWindowSize(streamId, -1 * dataLength);
        spdySession.updateSendWindowSize(SPDY_SESSION_STREAM_ID, -1 * dataLength);

        // The transfer window size is pre-decremented when sending a data frame downstream.
        // Close the session on write failures that leave the transfer window in a corrupt state.
        final ChannelHandlerContext context = ctx;
        promise.addListener(
            new ChannelFutureListener() {
              @Override
              public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                  issueSessionError(context, SpdySessionStatus.INTERNAL_ERROR);
                }
              }
            });
      }

      // Close the local side of the stream if this is the last frame
      if (spdyDataFrame.isLast()) {
        halfCloseStream(streamId, false, promise);
      }

    } else if (msg instanceof SpdySynStreamFrame) {

      SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
      int streamId = spdySynStreamFrame.streamId();

      if (isRemoteInitiatedId(streamId)) {
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      byte priority = spdySynStreamFrame.priority();
      boolean remoteSideClosed = spdySynStreamFrame.isUnidirectional();
      boolean localSideClosed = spdySynStreamFrame.isLast();
      if (!acceptStream(streamId, priority, remoteSideClosed, localSideClosed)) {
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

    } else if (msg instanceof SpdySynReplyFrame) {

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

      // Frames must not be sent on half-closed streams
      if (!isRemoteInitiatedId(streamId) || spdySession.isLocalSideClosed(streamId)) {
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      // Close the local side of the stream if this is the last frame
      if (spdySynReplyFrame.isLast()) {
        halfCloseStream(streamId, false, promise);
      }

    } else if (msg instanceof SpdyRstStreamFrame) {

      SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
      removeStream(spdyRstStreamFrame.streamId(), promise);

    } else if (msg instanceof SpdySettingsFrame) {

      SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;

      int settingsMinorVersion =
          spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MINOR_VERSION);
      if (settingsMinorVersion >= 0 && settingsMinorVersion != minorVersion) {
        // Settings frame had the wrong minor version
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      int newConcurrentStreams =
          spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS);
      if (newConcurrentStreams >= 0) {
        localConcurrentStreams = newConcurrentStreams;
      }

      // Persistence flag are inconsistent with the use of SETTINGS to communicate
      // the initial window size. Remove flags from the sender requesting that the
      // value be persisted. Remove values that the sender indicates are persisted.
      if (spdySettingsFrame.isPersisted(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE)) {
        spdySettingsFrame.removeValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
      }
      spdySettingsFrame.setPersistValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE, false);

      int newInitialWindowSize =
          spdySettingsFrame.getValue(SpdySettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
      if (newInitialWindowSize >= 0) {
        updateInitialReceiveWindowSize(newInitialWindowSize);
      }

    } else if (msg instanceof SpdyPingFrame) {

      SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
      if (isRemoteInitiatedId(spdyPingFrame.id())) {
        ctx.fireExceptionCaught(
            new IllegalArgumentException("invalid PING ID: " + spdyPingFrame.id()));
        return;
      }
      pings.getAndIncrement();

    } else if (msg instanceof SpdyGoAwayFrame) {

      // Why is this being sent? Intercept it and fail the write.
      // Should have sent a CLOSE ChannelStateEvent
      promise.setFailure(PROTOCOL_EXCEPTION);
      return;

    } else if (msg instanceof SpdyHeadersFrame) {

      SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
      int streamId = spdyHeadersFrame.streamId();

      // Frames must not be sent on half-closed streams
      if (spdySession.isLocalSideClosed(streamId)) {
        promise.setFailure(PROTOCOL_EXCEPTION);
        return;
      }

      // Close the local side of the stream if this is the last frame
      if (spdyHeadersFrame.isLast()) {
        halfCloseStream(streamId, false, promise);
      }

    } else if (msg instanceof SpdyWindowUpdateFrame) {

      // Why is this being sent? Intercept it and fail the write.
      promise.setFailure(PROTOCOL_EXCEPTION);
      return;
    }

    ctx.write(msg, promise);
  }
Пример #10
0
  @Override
  protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
    if (msg instanceof SpdyDataFrame) {

      SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
      ByteBuf data = spdyDataFrame.data();
      byte flags = spdyDataFrame.isLast() ? SPDY_DATA_FLAG_FIN : 0;
      out.ensureWritable(SPDY_HEADER_SIZE + data.readableBytes());
      out.writeInt(spdyDataFrame.getStreamId() & 0x7FFFFFFF);
      out.writeByte(flags);
      out.writeMedium(data.readableBytes());
      out.writeBytes(data, data.readerIndex(), data.readableBytes());

    } else if (msg instanceof SpdySynStreamFrame) {

      SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
      ByteBuf data = compressHeaderBlock(encodeHeaderBlock(version, spdySynStreamFrame));
      byte flags = spdySynStreamFrame.isLast() ? SPDY_FLAG_FIN : 0;
      if (spdySynStreamFrame.isUnidirectional()) {
        flags |= SPDY_FLAG_UNIDIRECTIONAL;
      }
      int headerBlockLength = data.readableBytes();
      int length;
      if (version < 3) {
        length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
      } else {
        length = 10 + headerBlockLength;
      }
      out.ensureWritable(SPDY_HEADER_SIZE + length);
      out.writeShort(version | 0x8000);
      out.writeShort(SPDY_SYN_STREAM_FRAME);
      out.writeByte(flags);
      out.writeMedium(length);
      out.writeInt(spdySynStreamFrame.getStreamId());
      out.writeInt(spdySynStreamFrame.getAssociatedToStreamId());
      if (version < 3) {
        // Restrict priorities for SPDY/2 to between 0 and 3
        byte priority = spdySynStreamFrame.getPriority();
        if (priority > 3) {
          priority = 3;
        }
        out.writeShort((priority & 0xFF) << 14);
      } else {
        out.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
      }
      if (version < 3 && data.readableBytes() == 0) {
        out.writeShort(0);
      }
      out.writeBytes(data, data.readerIndex(), headerBlockLength);

    } else if (msg instanceof SpdySynReplyFrame) {

      SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
      ByteBuf data = compressHeaderBlock(encodeHeaderBlock(version, spdySynReplyFrame));
      byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0;
      int headerBlockLength = data.readableBytes();
      int length;
      if (version < 3) {
        length = headerBlockLength == 0 ? 8 : 6 + headerBlockLength;
      } else {
        length = 4 + headerBlockLength;
      }
      out.ensureWritable(SPDY_HEADER_SIZE + length);
      out.writeShort(version | 0x8000);
      out.writeShort(SPDY_SYN_REPLY_FRAME);
      out.writeByte(flags);
      out.writeMedium(length);
      out.writeInt(spdySynReplyFrame.getStreamId());
      if (version < 3) {
        if (headerBlockLength == 0) {
          out.writeInt(0);
        } else {
          out.writeShort(0);
        }
      }
      out.writeBytes(data, data.readerIndex(), headerBlockLength);

    } else if (msg instanceof SpdyRstStreamFrame) {

      SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
      out.ensureWritable(SPDY_HEADER_SIZE + 8);
      out.writeShort(version | 0x8000);
      out.writeShort(SPDY_RST_STREAM_FRAME);
      out.writeInt(8);
      out.writeInt(spdyRstStreamFrame.getStreamId());
      out.writeInt(spdyRstStreamFrame.getStatus().getCode());

    } else if (msg instanceof SpdySettingsFrame) {

      SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
      byte flags = spdySettingsFrame.clearPreviouslyPersistedSettings() ? SPDY_SETTINGS_CLEAR : 0;
      Set<Integer> IDs = spdySettingsFrame.getIds();
      int numEntries = IDs.size();
      int length = 4 + numEntries * 8;
      out.ensureWritable(SPDY_HEADER_SIZE + length);
      out.writeShort(version | 0x8000);
      out.writeShort(SPDY_SETTINGS_FRAME);
      out.writeByte(flags);
      out.writeMedium(length);
      out.writeInt(numEntries);
      for (Integer ID : IDs) {
        int id = ID.intValue();
        byte ID_flags = 0;
        if (spdySettingsFrame.isPersistValue(id)) {
          ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
        }
        if (spdySettingsFrame.isPersisted(id)) {
          ID_flags |= SPDY_SETTINGS_PERSISTED;
        }
        if (version < 3) {
          // Chromium Issue 79156
          // SPDY setting ids are not written in network byte order
          // Write id assuming the architecture is little endian
          out.writeByte(id & 0xFF);
          out.writeByte(id >> 8 & 0xFF);
          out.writeByte(id >> 16 & 0xFF);
          out.writeByte(ID_flags);
        } else {
          out.writeByte(ID_flags);
          out.writeMedium(id);
        }
        out.writeInt(spdySettingsFrame.getValue(id));
      }

    } else if (msg instanceof SpdyNoOpFrame) {

      out.ensureWritable(SPDY_HEADER_SIZE);
      out.writeShort(version | 0x8000);
      out.writeShort(SPDY_NOOP_FRAME);
      out.writeInt(0);

    } else if (msg instanceof SpdyPingFrame) {

      SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
      out.ensureWritable(SPDY_HEADER_SIZE + 4);
      out.writeShort(version | 0x8000);
      out.writeShort(SPDY_PING_FRAME);
      out.writeInt(4);
      out.writeInt(spdyPingFrame.getId());

    } else if (msg instanceof SpdyGoAwayFrame) {

      SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
      int length = version < 3 ? 4 : 8;
      out.ensureWritable(SPDY_HEADER_SIZE + length);
      out.writeShort(version | 0x8000);
      out.writeShort(SPDY_GOAWAY_FRAME);
      out.writeInt(length);
      out.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
      if (version >= 3) {
        out.writeInt(spdyGoAwayFrame.getStatus().getCode());
      }

    } else if (msg instanceof SpdyHeadersFrame) {

      SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
      ByteBuf data = compressHeaderBlock(encodeHeaderBlock(version, spdyHeadersFrame));
      byte flags = spdyHeadersFrame.isLast() ? SPDY_FLAG_FIN : 0;
      int headerBlockLength = data.readableBytes();
      int length;
      if (version < 3) {
        length = headerBlockLength == 0 ? 4 : 6 + headerBlockLength;
      } else {
        length = 4 + headerBlockLength;
      }
      out.ensureWritable(SPDY_HEADER_SIZE + length);
      out.writeShort(version | 0x8000);
      out.writeShort(SPDY_HEADERS_FRAME);
      out.writeByte(flags);
      out.writeMedium(length);
      out.writeInt(spdyHeadersFrame.getStreamId());
      if (version < 3 && headerBlockLength != 0) {
        out.writeShort(0);
      }
      out.writeBytes(data, data.readerIndex(), headerBlockLength);

    } else if (msg instanceof SpdyWindowUpdateFrame) {

      SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
      out.ensureWritable(SPDY_HEADER_SIZE + 8);
      out.writeShort(version | 0x8000);
      out.writeShort(SPDY_WINDOW_UPDATE_FRAME);
      out.writeInt(8);
      out.writeInt(spdyWindowUpdateFrame.getStreamId());
      out.writeInt(spdyWindowUpdateFrame.getDeltaWindowSize());
    } else {
      throw new UnsupportedMessageTypeException(msg);
    }
  }
Пример #11
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();
  }
Пример #12
0
  @SuppressWarnings("deprecation")
  private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage) throws Exception {
    // Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers
    final HttpHeaders httpHeaders = httpMessage.headers();
    int streamID = httpHeaders.getInt(Names.STREAM_ID);
    int associatedToStreamId = httpHeaders.getInt(Names.ASSOCIATED_TO_STREAM_ID, 0);
    byte priority = (byte) httpHeaders.getInt(Names.PRIORITY, 0);
    String URL = httpHeaders.get(Names.URL);
    String scheme = httpHeaders.get(Names.SCHEME);
    httpHeaders.remove(Names.STREAM_ID);
    httpHeaders.remove(Names.ASSOCIATED_TO_STREAM_ID);
    httpHeaders.remove(Names.PRIORITY);
    httpHeaders.remove(Names.URL);
    httpHeaders.remove(Names.SCHEME);

    // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
    // headers are not valid and MUST not be sent.
    httpHeaders.remove(HttpHeaders.Names.CONNECTION);
    httpHeaders.remove(HttpHeaders.Names.KEEP_ALIVE);
    httpHeaders.remove(HttpHeaders.Names.PROXY_CONNECTION);
    httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING);

    SpdySynStreamFrame spdySynStreamFrame =
        new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority);

    // Unfold the first line of the message into name/value pairs
    SpdyHeaders frameHeaders = spdySynStreamFrame.headers();
    if (httpMessage instanceof FullHttpRequest) {
      HttpRequest httpRequest = (HttpRequest) httpMessage;
      frameHeaders.set(METHOD, httpRequest.method());
      frameHeaders.set(PATH, httpRequest.uri());
      frameHeaders.set(VERSION, httpMessage.protocolVersion());
    }
    if (httpMessage instanceof HttpResponse) {
      HttpResponse httpResponse = (HttpResponse) httpMessage;
      frameHeaders.set(STATUS, httpResponse.status());
      frameHeaders.set(PATH, URL);
      frameHeaders.set(VERSION, httpMessage.protocolVersion());
      spdySynStreamFrame.setUnidirectional(true);
    }

    // Replace the HTTP host header with the SPDY host header
    if (spdyVersion >= 3) {
      CharSequence host = httpHeaders.getUnconverted(HttpHeaders.Names.HOST);
      httpHeaders.remove(HttpHeaders.Names.HOST);
      frameHeaders.set(HOST, host);
    }

    // Set the SPDY scheme header
    if (scheme == null) {
      scheme = "https";
    }
    frameHeaders.set(SCHEME, scheme);

    // Transfer the remaining HTTP headers
    for (Map.Entry<String, String> entry : httpHeaders) {
      frameHeaders.add(entry.getKey(), entry.getValue());
    }
    currentStreamId = spdySynStreamFrame.streamId();
    spdySynStreamFrame.setLast(isLast(httpMessage));

    return spdySynStreamFrame;
  }
Пример #13
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);
    }
  }