public void writeResponse(HttpVersion version, ByteBuf container) {
    HttpResponse response = new DefaultHttpResponse(version, HttpResponseStatus.OK);
    HttpHeaders.setContentLength(response, container.readableBytes());

    channel.write(response);
    channel.write(container);
    channel.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
  }
  private void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
    // Generate an error page if response status code is not OK (200).
    if (res.getStatus().getCode() != 200) {
      res.setContent(ChannelBuffers.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
      setContentLength(res, res.getContent().readableBytes());
    }

    // Send the response and close the connection if necessary.
    ChannelFuture f = ctx.getChannel().write(res);
    if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
      f.addListener(ChannelFutureListener.CLOSE);
    }
  }
  private static void sendHttpResponse(
      ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
    // Generate an error page if response getStatus code is not OK (200).
    if (res.getStatus().code() != 200) {
      ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
      res.content().writeBytes(buf);
      buf.release();
      HttpHeaders.setContentLength(res, res.content().readableBytes());
    }

    // Send the response and close the connection if necessary.
    ChannelFuture f = ctx.channel().writeAndFlush(res);
    if (!HttpHeaders.isKeepAlive(req) || res.getStatus().code() != 200) {
      f.addListener(ChannelFutureListener.CLOSE);
    }
  }
Example #4
0
 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   FullHttpResponse res;
   FullHttpRequest req = (FullHttpRequest) msg;
   String url = req.getUri();
   if ("/".equals(req.getUri())) {
     url = "/index.html";
   }
   Path path = Paths.get(root + url);
   if (Files.isReadable(path)) {
     ByteBuf content = Unpooled.copiedBuffer(Files.readAllBytes(path));
     res = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
     if (url.endsWith(".html")) res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
     HttpHeaders.setContentLength(res, content.readableBytes());
   } else {
     res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
   }
   sendHttpResponse(ctx, req, res);
 }
  @Override
  protected void doOnTerminate(
      ChannelHandlerContext ctx, ChannelFuture last, final ChannelPromise promise) {
    if (request.method() == Method.WS) {
      return;
    }

    ByteBuffer byteBuffer = body.flip().byteBuffer();

    if (request.checkHeader()) {
      HttpRequest req =
          new DefaultFullHttpRequest(
              request.getNettyRequest().getProtocolVersion(),
              request.getNettyRequest().getMethod(),
              request.getNettyRequest().getUri(),
              byteBuffer != null ? Unpooled.wrappedBuffer(byteBuffer) : Unpooled.EMPTY_BUFFER);

      req.headers().add(request.headers().delegate());

      if (byteBuffer != null) {
        HttpHeaders.setContentLength(req, body.limit());
      }

      ctx.writeAndFlush(req)
          .addListener(
              new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                  if (future.isSuccess()) {
                    promise.trySuccess();
                  } else {
                    promise.tryFailure(future.cause());
                  }
                }
              });
    } else {
      ctx.write(
          new DefaultHttpContent(
              byteBuffer != null ? Unpooled.wrappedBuffer(byteBuffer) : Unpooled.EMPTY_BUFFER));
    }
    body.reset();
  }
  private void sendMessage(
      HttpMessage msg, Channel channel, ByteBuf out, String type, ChannelPromise promise) {
    HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.OK);

    res.headers()
        .add(CONTENT_TYPE, type)
        .add("Set-Cookie", "io=" + msg.getSessionId())
        .add(CONNECTION, KEEP_ALIVE);

    addOriginHeaders(channel, res);
    HttpHeaders.setContentLength(res, out.readableBytes());

    // prevent XSS warnings on IE
    // https://github.com/LearnBoost/socket.io/pull/1333
    String userAgent = channel.attr(EncoderHandler.USER_AGENT).get();
    if (userAgent != null && (userAgent.contains(";MSIE") || userAgent.contains("Trident/"))) {
      res.headers().add("X-XSS-Protection", "0");
    }

    sendMessage(msg, channel, out, res, promise);
  }
  private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
    // Handle a bad request.
    if (!req.getDecoderResult().isSuccess()) {
      sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
      return;
    }

    // Allow only GET methods.
    if (req.getMethod() != GET) {
      sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
      return;
    }

    // Send the demo page and favicon.ico
    if ("/".equals(req.getUri())) {
      ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req));
      FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content);

      res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
      HttpHeaders.setContentLength(res, content.readableBytes());

      sendHttpResponse(ctx, req, res);
      return;
    }
    if ("/favicon.ico".equals(req.getUri())) {
      FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
      sendHttpResponse(ctx, req, res);
      return;
    }

    // Handshake
    WebSocketServerHandshakerFactory wsFactory =
        new WebSocketServerHandshakerFactory(getWebSocketLocation(req), null, true);
    handshaker = wsFactory.newHandshaker(req);
    if (handshaker == null) {
      WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
    } else {
      handshaker.handshake(ctx.channel(), req);
    }
  }
  private void processHead(ChannelHandlerContext context, FullHttpRequest request) {
    HttpHeaders headers = request.headers();
    FullHttpResponse response = null;

    if (headers.contains(Names.CONTENT_LENGTH)) {

      try {
        File file = getRequestedFile(request.getUri());

        response =
            new DefaultFullHttpResponse(
                HTTP_1_1, request.getDecoderResult().isSuccess() ? OK : BAD_REQUEST);

        HttpHeaders.setContentLength(response, file.length());

      } catch (FileNotFoundException | URISyntaxException e) {
        response = getBadRequest(e.getMessage());
      }
    }

    context.writeAndFlush(response);
  }
  private void processGet(ChannelHandlerContext context, FullHttpRequest request) {
    try {
      File file = getRequestedFile(request.getUri());

      RandomAccessFile raf = new RandomAccessFile(file, "r");
      long fileLength = raf.length();

      HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
      HttpHeaders.setContentLength(response, fileLength);
      setContentTypeHeader(response, file);

      context.write(response);

      context.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength));

      // Write the end marker.
      ChannelFuture future = context.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
      future.addListener(ChannelFutureListener.CLOSE);

    } catch (IOException | URISyntaxException e) {
      context.writeAndFlush(getBadRequest(e.getMessage()));
    }
  }
 @Override
 protected void encode(ChannelHandlerContext ctx, HttpXmlRequest msg, List<Object> out)
     throws Exception {
   ByteBuf body = encode0(ctx, msg.getBody());
   FullHttpRequest request = msg.getRequest();
   if (request == null) {
     request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/do", body);
     HttpHeaders headers = request.headers();
     headers.set(HttpHeaders.Names.HOST, InetAddress.getLocalHost().getHostAddress());
     headers.set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
     headers.set(
         HttpHeaders.Names.ACCEPT_ENCODING,
         HttpHeaders.Values.GZIP.toString() + ',' + HttpHeaders.Values.DEFLATE.toString());
     headers.set(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
     headers.set(HttpHeaders.Names.ACCEPT_LANGUAGE, "zh");
     headers.set(HttpHeaders.Names.USER_AGENT, "Netty xml Http Client side");
     headers.set(
         HttpHeaders.Names.ACCEPT,
         "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
   }
   HttpHeaders.setContentLength(request, body.readableBytes());
   out.add(request);
 }
 /**
  * Provided a cause, returns an error response with the right status and error message.
  *
  * @param cause the cause of the error.
  * @return a {@link FullHttpResponse} with the error message that can be sent to the client.
  */
 private FullHttpResponse getErrorResponse(Throwable cause) {
   HttpResponseStatus status;
   StringBuilder errReason = new StringBuilder();
   if (cause instanceof RestServiceException) {
     RestServiceErrorCode restServiceErrorCode = ((RestServiceException) cause).getErrorCode();
     status = getHttpResponseStatus(ResponseStatus.getResponseStatus(restServiceErrorCode));
     if (status == HttpResponseStatus.BAD_REQUEST) {
       errReason.append(" [").append(Utils.getRootCause(cause).getMessage()).append("]");
     }
   } else {
     nettyMetrics.internalServerErrorCount.inc();
     status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
   }
   String fullMsg = "Failure: " + status + errReason;
   logger.trace("Constructed error response for the client - [{}]", fullMsg);
   FullHttpResponse response;
   if (request != null && !request.getRestMethod().equals(RestMethod.HEAD)) {
     response =
         new DefaultFullHttpResponse(
             HttpVersion.HTTP_1_1, status, Unpooled.wrappedBuffer(fullMsg.getBytes()));
   } else {
     // for HEAD, we cannot send the actual body but we need to return what the length would have
     // been if this was GET.
     // https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html (Section 9.4)
     response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
   }
   HttpHeaders.setDate(response, new GregorianCalendar().getTime());
   HttpHeaders.setContentLength(response, fullMsg.length());
   HttpHeaders.setHeader(response, HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
   boolean keepAlive =
       request != null
           && !request.getRestMethod().equals(RestMethod.POST)
           && !CLOSE_CONNECTION_ERROR_STATUSES.contains(status);
   HttpHeaders.setKeepAlive(response, keepAlive);
   return response;
 }
  private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
    // Handle a bad request.
    if (!request.getDecoderResult().isSuccess()) {
      sendHttpResponse(
          ctx, request, new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
      return;
    }

    if (RouterHits.checkIfMappingExit(request)) {
      // Do Router Mapping First
      RouterHits.execute(ctx, request);
      return;
    }

    if ("/websocket".equals(request.getUri())) {
      // Handshake
      WebSocketServerHandshakerFactory wsFactory =
          new WebSocketServerHandshakerFactory(getWebSocketLocation(request), null, true);
      handshaker = wsFactory.newHandshaker(request);
      if (handshaker == null) {
        WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
      } else {
        handshaker.handshake(ctx.channel(), request);
        channels.add(ctx.channel());
      }
      return;
    }

    final String uri = request.getUri();
    // System.out.println("uri: " + uri);
    final String path = sanitizeUri("www", uri);
    // System.out.println("path: " + path);
    if (path == null) {
      sendHttpResponse(
          ctx, request, new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN));

      return;
    }

    File file = new File(path);
    if (file.isHidden() || !file.exists()) {
      sendHttpResponse(
          ctx, request, new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.NOT_FOUND));
      return;
    }

    if (file.isDirectory()) {
      if (uri.endsWith("/")) {

        File checkIndexFile = new File(file.getAbsolutePath() + File.separator + "index.html");

        System.out.println(checkIndexFile.exists());
        if (checkIndexFile.exists()) {
          file = checkIndexFile;
        } else {
          sendListing(ctx, file);
          return;
        }
      } else {
        sendRedirect(ctx, uri + '/');
      }
    }

    if (!file.isFile()) {
      sendHttpResponse(
          ctx, request, new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN));
      return;
    }

    // Cache Validation
    String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE);
    if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
      SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
      Date ifModifiedSinceDate = null;
      try {
        ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
      } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }

      // Only compare up to the second because the datetime format we send to the client
      // does not have milliseconds
      long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
      long fileLastModifiedSeconds = file.lastModified() / 1000;
      if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
        sendNotModified(ctx);
        return;
      }
    }

    RandomAccessFile raf;
    try {
      raf = new RandomAccessFile(file, "r");
    } catch (FileNotFoundException ignore) {
      sendHttpResponse(
          ctx, request, new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.NOT_FOUND));
      return;
    }
    long fileLength = 0;
    try {
      fileLength = raf.length();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
    HttpHeaders.setContentLength(response, fileLength);
    setContentTypeHeader(response, file);
    setDateAndCacheHeaders(response, file);
    if (HttpHeaders.isKeepAlive(request)) {
      response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
    }

    // Write the initial line and the header.
    ctx.write(response);

    // Write the content.
    ChannelFuture sendFileFuture = null;
    ChannelFuture lastContentFuture;
    if (ctx.pipeline().get(SslHandler.class) == null) {
      sendFileFuture =
          ctx.write(
              new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
      // Write the end marker.
      lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
    } else {
      try {
        sendFileFuture =
            ctx.writeAndFlush(
                new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)),
                ctx.newProgressivePromise());
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      // HttpChunkedInput will write the end marker (LastHttpContent) for us.
      lastContentFuture = sendFileFuture;
    }

    sendFileFuture.addListener(
        new ChannelProgressiveFutureListener() {
          @Override
          public void operationProgressed(
              ChannelProgressiveFuture future, long progress, long total) {
            if (total < 0) { // total unknown
              System.err.println(future.channel() + " Transfer progress: " + progress);
            } else {
              System.err.println(
                  future.channel() + " Transfer progress: " + progress + " / " + total);
            }
          }

          @Override
          public void operationComplete(ChannelProgressiveFuture future) {
            System.err.println(future.channel() + " Transfer complete.");
          }
        });

    // Decide whether to close the connection or not.
    if (!HttpHeaders.isKeepAlive(request)) {
      // Close the connection when the whole content is written out.
      lastContentFuture.addListener(ChannelFutureListener.CLOSE);
    }

    //        // Send the demo page and favicon.ico
    //        if ("/".equals(req.getUri()) && req.getMethod() == GET) {
    //            ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req));
    //            FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
    //
    //            res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
    //            HttpHeaders.setContentLength(res, content.readableBytes());
    //
    //            sendHttpResponse(ctx, req, res);
    //            return;
    //        }
    //
    //        if ("/favicon.ico".equals(req.getUri())) {
    //            FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
    //            sendHttpResponse(ctx, req, res);
    //            return;
    //        }

    sendHttpResponse(
        ctx, request, new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN));
    return;
  }
Example #13
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);
    }
  }
  @Override
  public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
    if (!request.getDecoderResult().isSuccess()) {
      sendError(ctx, BAD_REQUEST);
      return;
    }

    if (request.getMethod() != GET) {
      sendError(ctx, METHOD_NOT_ALLOWED);
      return;
    }

    final String uri = request.getUri();
    final String path = sanitizeUri(uri);
    if (path == null) {
      sendError(ctx, FORBIDDEN);
      return;
    }

    // Cache Validation
    String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE);
    if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
      SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
      Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);

      // Only compare up to the second because the datetime format we send to the client
      // does not have milliseconds
      long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
      // we use the start time of the JVM as last modified date
      long lastModifiedSeconds = ManagementFactory.getRuntimeMXBean().getStartTime();
      if (ifModifiedSinceDateSeconds == lastModifiedSeconds) {
        sendNotModified(ctx);
        return;
      }
    }

    ClassLoader classLoader = HttpClasspathServerHandler.class.getClassLoader();
    InputStream stream = classLoader.getResourceAsStream(path);

    if (stream == null) {
      sendError(ctx, NOT_FOUND);
      return;
    }

    HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
    HttpHeaders.setContentLength(response, stream.available());
    setContentTypeHeader(response, path);
    setDateAndCacheHeaders(response);
    if (HttpHeaders.isKeepAlive(request)) {
      response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
    }

    // Write the initial line and the header.
    ctx.write(response);

    // Write the content.

    ChannelFuture sendFileFuture =
        ctx.write(new ChunkedStream(stream), ctx.newProgressivePromise());
    // Write the end marker.
    ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);

    // Decide whether to close the connection or not.
    if (!HttpHeaders.isKeepAlive(request)) {
      // Close the connection when the whole content is written out.
      lastContentFuture.addListener(ChannelFutureListener.CLOSE);
    }
  }
 @Override
 public void setContentLengthLong(long len) {
   HttpHeaders.setContentLength(response, len);
 }
  @Override
  protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
    // TODO Auto-generated method stub
    if (!request.getDecoderResult().isSuccess()) {
      sendError(ctx, BAD_REQUEST);
      return;
    }
    if (request.getMethod() != GET) {
      sendError(ctx, METHOD_NOT_ALLOWED);
      return;
    }
    final String uri = request.getUri();
    final String path = sanitizeUri(uri);
    if (path == null) {
      sendError(ctx, FORBIDDEN);
      return;
    }
    File file = new File(path);
    if (file.isHidden() || !file.exists()) {
      sendError(ctx, NOT_FOUND);
      return;
    }
    if (file.isDirectory()) {
      if (uri.endsWith("/")) {
        sendListing(ctx, file);
      } else {
        sendRedirect(ctx, uri + '/');
      }
      return;
    }
    if (!file.isFile()) {
      sendError(ctx, FORBIDDEN);
      return;
    }
    RandomAccessFile randomAccessFile = null;
    try {
      randomAccessFile = new RandomAccessFile(file, "r"); // 以只读的方式打开文件
    } catch (FileNotFoundException fnfe) {
      sendError(ctx, NOT_FOUND);
      return;
    }
    long fileLength = randomAccessFile.length();
    HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
    setContentLength(response, fileLength);
    setContentTypeHeader(response, file);
    if (isKeepAlive(request)) {
      response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
    }
    ctx.write(response);
    ChannelFuture sendFileFuture;
    sendFileFuture =
        ctx.write(
            new ChunkedFile(randomAccessFile, 0, fileLength, 8192), ctx.newProgressivePromise());
    sendFileFuture.addListener(
        new ChannelProgressiveFutureListener() {
          @Override
          public void operationProgressed(
              ChannelProgressiveFuture future, long progress, long total) {
            if (total < 0) { // total unknown
              System.err.println("Transfer progress: " + progress);
            } else {
              System.err.println("Transfer progress: " + progress + " / " + total);
            }
          }

          @Override
          public void operationComplete(ChannelProgressiveFuture future) throws Exception {
            System.out.println("Transfer complete.");
          }
        });
    ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
    if (!isKeepAlive(request)) {
      lastContentFuture.addListener(ChannelFutureListener.CLOSE);
    }
  }
  @Override
  public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
    if (!request.getDecoderResult().isSuccess()) {
      sendError(ctx, BAD_REQUEST, request);
      return;
    }

    if (request.getMethod() != GET) {
      sendError(ctx, METHOD_NOT_ALLOWED, request);
      return;
    }

    File file = null;
    RandomAccessFile raf = null;
    boolean found = true;
    for (String p : paths) {
      String path = p + sanitizeUri(request.getUri());

      if (path == null) {
        path = "/index.html";
      }

      if (path.endsWith("/") || path.endsWith(File.separator)) {
        path += "index.html";
      }

      if (path.endsWith("/favicon.ico") || path.endsWith(File.separator)) {
        request.headers().add(SERVICED, "true");
        found = false;
        continue;
      }

      file = new File(path);
      if (file.isHidden() || !file.exists()) {
        found = false;
        continue;
      }

      //            if (file.isDirectory()) {
      //                if (uri.endsWith("/")) {
      //                    sendListing(ctx, file);
      //                } else {
      //                    sendRedirect(ctx, uri + '/');
      //                }
      //                return;
      //            }

      if (!file.isFile()) {
        found = false;
        continue;
      }

      try {
        raf = new RandomAccessFile(file, "r");
        found = true;
        break;
      } catch (FileNotFoundException ignore) {
        sendError(ctx, NOT_FOUND, request);
        return;
      }
    }

    if (!found) {
      sendError(ctx, NOT_FOUND, request);
      return;
    }
    request.headers().add(SERVICED, "true");

    // Cache Validation
    String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE);
    if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
      SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
      Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);

      // Only compare up to the second because the datetime format we send to the client
      // does not have milliseconds
      long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
      long fileLastModifiedSeconds = file.lastModified() / 1000;
      if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
        sendNotModified(ctx);
        return;
      }
    }

    long fileLength = raf.length();

    ctx.pipeline().addBefore(BridgeRuntime.class.getName(), "encoder", new HttpResponseEncoder());
    HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
    HttpHeaders.setContentLength(response, fileLength);
    contentType(request, response, file);
    setDateAndCacheHeaders(response, file);
    //        if (HttpHeaders.isKeepAlive(request)) {
    //            response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
    //        }

    // Write the initial line and the header.
    ctx.write(response);

    // Write the content.
    ChannelFuture sendFileFuture;
    ChannelFuture lastContentFuture;
    if (ctx.pipeline().get(SslHandler.class) == null) {
      sendFileFuture =
          ctx.write(
              new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
      // Write the end marker.
      lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
    } else {
      sendFileFuture =
          ctx.writeAndFlush(
              new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)),
              ctx.newProgressivePromise());
      // HttpChunkedInput will write the end marker (LastHttpContent) for us.
      lastContentFuture = sendFileFuture;
    }

    sendFileFuture.addListener(
        new ChannelProgressiveFutureListener() {
          @Override
          public void operationProgressed(
              ChannelProgressiveFuture future, long progress, long total) {
            if (total < 0) { // total unknown
              logger.trace(future.channel() + " Transfer progress: " + progress);
            } else {
              logger.trace(future.channel() + " Transfer progress: " + progress + " / " + total);
            }
          }

          @Override
          public void operationComplete(ChannelProgressiveFuture future) {
            logger.trace(future.channel() + " Transfer complete.");
          }
        });

    // Close the connection when the whole content is written out.
    lastContentFuture.addListener(ChannelFutureListener.CLOSE);
  }
  void handleStaticFileRequest(ChannelHandlerContext ctx, HttpRequest req, String path)
      throws Exception {
    log.debug("handling static file request for {}", path);
    path = sanitizePath(path);
    if (path == null) {
      sendError(ctx, FORBIDDEN);
      return;
    }

    File file = new File(path);
    if (file.isHidden() || !file.exists()) {
      log.warn("{} does not exist or is hidden", file.toString());
      sendError(ctx, NOT_FOUND);
      return;
    }
    if (!file.isFile()) {
      sendError(ctx, FORBIDDEN);
      return;
    }

    // Cache Validation
    String ifModifiedSince = req.headers().get(HttpHeaders.Names.IF_MODIFIED_SINCE);
    if (ifModifiedSince != null && !ifModifiedSince.equals("")) {
      SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT);
      Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);

      // Only compare up to the second because the datetime format we send to the client does not
      // have milliseconds
      long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
      long fileLastModifiedSeconds = file.lastModified() / 1000;
      if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
        sendNotModified(ctx);
        return;
      }
    }

    RandomAccessFile raf;
    try {
      raf = new RandomAccessFile(file, "r");
    } catch (FileNotFoundException ignore) {
      sendError(ctx, NOT_FOUND);
      return;
    }
    long fileLength = raf.length();

    HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
    HttpHeaders.setContentLength(response, fileLength);
    setContentTypeHeader(response, file);
    setDateAndCacheHeaders(response, file);
    if (HttpHeaders.isKeepAlive(req)) {
      response.headers().set(Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
    }

    // Write the initial line and the header.
    ctx.write(response);

    // Write the content.
    ChannelFuture sendFileFuture;
    ChannelFuture lastContentFuture;
    if (ctx.pipeline().get(SslHandler.class) == null) {
      sendFileFuture =
          ctx.write(
              new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
      // Write the end marker.
      lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
    } else {
      sendFileFuture =
          ctx.writeAndFlush(new ChunkedFile(raf, 0, fileLength, 8192), ctx.newProgressivePromise());
      // HttpChunkedInput will write the end marker (LastHttpContent) for us.
      lastContentFuture = sendFileFuture;
    }

    sendFileFuture.addListener(
        new ChannelProgressiveFutureListener() {
          @Override
          public void operationProgressed(
              ChannelProgressiveFuture future, long progress, long total) {
            if (total < 0) { // total unknown
              System.err.println(future.channel() + " Transfer progress: " + progress);
            } else {
              System.err.println(
                  future.channel() + " Transfer progress: " + progress + " / " + total);
            }
          }

          @Override
          public void operationComplete(ChannelProgressiveFuture future) {
            System.err.println(future.channel() + " Transfer complete.");
          }
        });

    // Decide whether to close the connection or not.
    if (!HttpHeaders.isKeepAlive(req)) {
      // Close the connection when the whole content is written out.
      lastContentFuture.addListener(ChannelFutureListener.CLOSE);
    }
  }