private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
    // Handle a bad request.
    if (!req.getDecoderResult().isSuccess()) {
      sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
      req.release();
      return;
    }

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

    // Handshake
    WebSocketServerHandshakerFactory wsFactory =
        new WebSocketServerHandshakerFactory(
            getWebSocketLocation(req), null, false, Integer.MAX_VALUE);
    handshaker = wsFactory.newHandshaker(req);
    if (handshaker == null) {
      WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
    } else {
      handshaker.handshake(ctx.channel(), req);
    }
    req.release();
  }
  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 handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {

    if (!req.getDecoderResult().isSuccess()
        || (!"websocket".equals(req.headers().get("Upgrade")))) {

      sendHttpResponse(
          ctx,
          req,
          new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));

      return;
    }

    WebSocketServerHandshakerFactory wsFactory =
        new WebSocketServerHandshakerFactory("ws://localhost:7397/websocket", null, false);

    handshaker = wsFactory.newHandshaker(req);

    if (handshaker == null) {
      WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
    } else {
      handshaker.handshake(ctx.channel(), req);
    }
  }
  public void channelRead0(final ChannelHandlerContext ctx, final FullHttpRequest nettyRequest)
      throws Exception {
    if (!nettyRequest.getDecoderResult().isSuccess()) {
      sendError(ctx, HttpResponseStatus.BAD_REQUEST);
      return;
    }

    final long startTime = System.nanoTime();

    final Request request =
        new DefaultRequest(
            new NettyHeadersBackedHeaders(nettyRequest.headers()),
            nettyRequest.getMethod().name(),
            nettyRequest.getUri(),
            nettyRequest.content());

    final Channel channel = ctx.channel();

    final DefaultMutableStatus responseStatus = new DefaultMutableStatus();
    final HttpHeaders httpHeaders = new DefaultHttpHeaders(false);
    final MutableHeaders responseHeaders = new NettyHeadersBackedMutableHeaders(httpHeaders);
    FileHttpTransmitter fileHttpTransmitter =
        new DefaultFileHttpTransmitter(
            nettyRequest,
            httpHeaders,
            channel,
            compressResponses,
            addResponseTimeHeader ? startTime : -1);

    final DefaultEventController<RequestOutcome> requestOutcomeEventController =
        new DefaultEventController<>();

    // We own the lifecycle
    nettyRequest.content().retain();

    final Response response =
        new DefaultResponse(
            responseStatus,
            responseHeaders,
            fileHttpTransmitter,
            ctx.alloc(),
            new Action<ByteBuf>() {
              @Override
              public void execute(final ByteBuf byteBuf) throws Exception {
                final HttpResponse nettyResponse =
                    new CustomHttpResponse(responseStatus.getResponseStatus(), httpHeaders);

                nettyRequest.content().release();
                responseHeaders.set(HttpHeaderConstants.CONTENT_LENGTH, byteBuf.writerIndex());
                boolean shouldClose = true;
                if (channel.isOpen()) {
                  if (isKeepAlive(nettyRequest)) {
                    responseHeaders.set(
                        HttpHeaderConstants.CONNECTION, HttpHeaderConstants.KEEP_ALIVE);
                    shouldClose = false;
                  }

                  long stopTime = System.nanoTime();
                  if (addResponseTimeHeader) {
                    responseHeaders.set(
                        "X-Response-Time", NumberUtil.toMillisDiffString(startTime, stopTime));
                  }

                  execController.getExecution().complete();

                  channel.writeAndFlush(nettyResponse);
                  channel.write(new DefaultHttpContent(byteBuf));
                  ChannelFuture future = channel.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);

                  if (requestOutcomeEventController.isHasListeners()) {
                    Headers headers = new DelegatingHeaders(responseHeaders);
                    Status status =
                        new DefaultStatus(responseStatus.getCode(), responseStatus.getMessage());
                    SentResponse sentResponse = new DefaultSentResponse(headers, status);
                    RequestOutcome requestOutcome =
                        new DefaultRequestOutcome(
                            request, sentResponse, System.currentTimeMillis());
                    requestOutcomeEventController.fire(requestOutcome);
                  }
                  if (shouldClose) {
                    future.addListener(ChannelFutureListener.CLOSE);
                  }
                }
              }
            });

    InetSocketAddress socketAddress = (InetSocketAddress) channel.localAddress();
    final BindAddress bindAddress = new InetSocketAddressBackedBindAddress(socketAddress);

    Action<Action<Object>> subscribeHandler =
        new Action<Action<Object>>() {
          @Override
          public void execute(Action<Object> thing) throws Exception {
            channelSubscriptions.put(channel, thing);
            channel
                .closeFuture()
                .addListener(
                    new ChannelFutureListener() {
                      @Override
                      public void operationComplete(ChannelFuture future) throws Exception {
                        channelSubscriptions.remove(channel);
                      }
                    });
          }
        };

    final DirectChannelAccess directChannelAccess =
        new DefaultDirectChannelAccess(channel, subscribeHandler);

    execController.start(
        new Action<Execution>() {
          @Override
          public void execute(Execution execution) throws Exception {
            DefaultContext.RequestConstants requestConstants =
                new DefaultContext.RequestConstants(
                    applicationConstants,
                    bindAddress,
                    request,
                    response,
                    directChannelAccess,
                    requestOutcomeEventController.getRegistry(),
                    execution);

            new DefaultContext(requestConstants, registry, handlers, 0, return404).next();
          }
        });
  }
  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;
  }
  @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
  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);
    }
  }
  public void channelRead0(final ChannelHandlerContext ctx, final FullHttpRequest nettyRequest)
      throws Exception {
    if (!nettyRequest.getDecoderResult().isSuccess()) {
      sendError(ctx, HttpResponseStatus.BAD_REQUEST);
      return;
    }

    final long startTime = addResponseTimeHeader ? System.nanoTime() : 0;
    final Request request =
        new DefaultRequest(
            new NettyHeadersBackedHeaders(nettyRequest.headers()),
            nettyRequest.getMethod().name(),
            nettyRequest.getUri(),
            nettyRequest.content());
    final Channel channel = ctx.channel();
    final DefaultMutableStatus responseStatus = new DefaultMutableStatus();
    final HttpHeaders nettyHeaders = new DefaultHttpHeaders(false);
    final MutableHeaders responseHeaders = new NettyHeadersBackedMutableHeaders(nettyHeaders);
    final MimeTypes mimeTypes = registry.get(MimeTypes.class);
    final DefaultEventController<RequestOutcome> requestOutcomeEventController =
        new DefaultEventController<>();
    final AtomicBoolean transmitted = new AtomicBoolean(false);

    final ResponseTransmitter responseTransmitter =
        new DefaultResponseTransmitter(
            transmitted,
            channel,
            nettyRequest,
            request,
            nettyHeaders,
            responseStatus,
            requestOutcomeEventController,
            startTime);
    final Action<Action<? super ResponseTransmitter>> responseTransmitterWrapper =
        Actions.wrap(responseTransmitter);

    final FileHttpTransmitter fileHttpTransmitter =
        new DefaultFileHttpTransmitter(
            nettyHeaders,
            mimeTypes,
            compressResponses,
            compressionMinSize,
            compressionMimeTypeWhiteList,
            compressionMimeTypeBlackList,
            responseTransmitterWrapper);
    StreamTransmitter streamTransmitter =
        new DefaultStreamTransmitter(nettyRequest, nettyHeaders, channel);

    final Response response =
        new DefaultResponse(
            responseStatus,
            responseHeaders,
            fileHttpTransmitter,
            streamTransmitter,
            ctx.alloc(),
            new Action<ByteBuf>() {
              @Override
              public void execute(final ByteBuf byteBuf) throws Exception {
                responseTransmitterWrapper.execute(
                    new Action<ResponseTransmitter>() {
                      @Override
                      public void execute(ResponseTransmitter responseTransmitter)
                          throws Exception {
                        nettyHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, byteBuf.writerIndex());
                        responseTransmitter.transmit(new DefaultHttpContent(byteBuf));
                      }
                    });
              }
            });

    InetSocketAddress socketAddress = (InetSocketAddress) channel.localAddress();
    final BindAddress bindAddress = new InetSocketAddressBackedBindAddress(socketAddress);

    Action<Action<Object>> subscribeHandler =
        new Action<Action<Object>>() {
          @Override
          public void execute(Action<Object> thing) throws Exception {
            transmitted.set(true);
            channelSubscriptions.put(channel, thing);
            channel
                .closeFuture()
                .addListener(
                    new ChannelFutureListener() {
                      @Override
                      public void operationComplete(ChannelFuture future) throws Exception {
                        channelSubscriptions.remove(channel);
                      }
                    });
          }
        };

    final DirectChannelAccess directChannelAccess =
        new DefaultDirectChannelAccess(channel, subscribeHandler);

    final DefaultContext.RequestConstants requestConstants =
        new DefaultContext.RequestConstants(
            applicationConstants,
            bindAddress,
            request,
            response,
            directChannelAccess,
            requestOutcomeEventController.getRegistry());

    DefaultContext.start(
        execController.getControl(),
        requestConstants,
        registry,
        handlers,
        return404,
        new Action<Execution>() {
          @Override
          public void execute(Execution execution) throws Exception {
            if (!transmitted.get()) {
              Handler lastHandler = requestConstants.handler;
              StringBuilder description = new StringBuilder();
              description
                  .append("No response sent for ")
                  .append(request.getMethod().getName())
                  .append(" request to ")
                  .append(request.getUri())
                  .append(" (last handler: ");

              if (lastHandler instanceof DescribingHandler) {
                ((DescribingHandler) lastHandler).describeTo(description);
              } else {
                DescribingHandlers.describeTo(lastHandler, description);
              }

              description.append(")");
              String message = description.toString();
              LOGGER.warn(message);

              response.status(500);

              if (launchConfig.isDevelopment()) {
                response.send(message);
              } else {
                response.send();
              }
            }
          }
        });
  }
Beispiel #10
0
  private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req)
      throws SuspendExecution {
    // Handle a bad request.
    if (!req.getDecoderResult().isSuccess()) {
      sendHttpResponse(
          ctx, req, new DefaultFullHttpResponse(req.getProtocolVersion(), BAD_REQUEST), false);
      return;
    }

    final String uri = req.getUri();

    final Context actorCtx = selector.get(ctx, req);
    assert actorCtx != null;

    final ReentrantLock lock = actorCtx.getLock();
    assert lock != null;

    lock.lock();

    try {
      final ActorRef<? extends WebMessage> userActorRef = actorCtx.getRef();
      ActorImpl internalActor = (ActorImpl) actorCtx.getAttachments().get(ACTOR_KEY);

      if (userActorRef != null) {
        if (actorCtx.handlesWithWebSocket(uri)) {
          if (internalActor == null || !(internalActor instanceof WebSocketActorAdapter)) {
            //noinspection unchecked
            webSocketActor =
                new WebSocketActorAdapter(ctx, (ActorRef<? super WebMessage>) userActorRef);
            addActorToContextAndUnlock(actorCtx, webSocketActor, lock);
          }
          // Handshake
          final WebSocketServerHandshakerFactory wsFactory =
              new WebSocketServerHandshakerFactory(uri, null, true);
          handshaker = wsFactory.newHandshaker(req);
          if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
          } else {
            @SuppressWarnings("unchecked")
            final ActorRef<WebMessage> userActorRef0 =
                (ActorRef<WebMessage>) webSocketActor.userActor;
            handshaker
                .handshake(ctx.channel(), req)
                .addListener(
                    new GenericFutureListener<ChannelFuture>() {
                      @Override
                      public void operationComplete(ChannelFuture future) throws Exception {
                        FiberUtil.runInFiber(
                            new SuspendableRunnable() {
                              @Override
                              public void run() throws SuspendExecution, InterruptedException {
                                userActorRef0.send(
                                    new WebSocketOpened(WebActorHandler.this.webSocketActor.ref()));
                              }
                            });
                      }
                    });
          }
          return;
        } else if (actorCtx.handlesWithHttp(uri)) {
          if (internalActor == null || !(internalActor instanceof HttpActorAdapter)) {
            //noinspection unchecked
            internalActor =
                new HttpActorAdapter(
                    (ActorRef<HttpRequest>) userActorRef, actorCtx, httpResponseEncoderName);
            addActorToContextAndUnlock(actorCtx, internalActor, lock);
          }
          //noinspection unchecked
          ((HttpActorAdapter) internalActor).service(ctx, req);
          return;
        }
      }
    } finally {
      if (lock.isHeldByCurrentStrand() && lock.isLocked()) lock.unlock();
    }

    sendHttpResponse(
        ctx, req, new DefaultFullHttpResponse(req.getProtocolVersion(), NOT_FOUND), false);
  }
  @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);
  }