Example #1
0
  private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse) throws Exception {
    boolean chunked = httpResponse.isChunked();

    // Get the Stream-ID from the headers
    int streamID = SpdyHttpHeaders.getStreamID(httpResponse);
    SpdyHttpHeaders.removeStreamID(httpResponse);

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

    SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);

    // Unfold the first line of the response into name/value pairs
    SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, httpResponse.getStatus());
    SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.getProtocolVersion());

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

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

    return spdySynReplyFrame;
  }
Example #2
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;
  }
Example #3
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);
    }
  }