@SuppressWarnings("unchecked")
  public boolean sendRequest(
      final FilterChainContext ctx, final Request request, final HttpRequestPacket requestPacket)
      throws IOException {

    boolean isWriteComplete = true;

    if (Utils.requestHasEntityBody(request)) {
      final HttpTxContext context = HttpTxContext.get(ctx);
      BodyHandler handler = bodyHandlerFactory.getBodyHandler(request);
      if (requestPacket.getHeaders().contains(Header.Expect)
          && requestPacket.getHeaders().getValue(1).equalsIgnoreCase("100-Continue")) {
        // We have to set the content-length now as the headers will be flushed
        // before the FileBodyHandler is invoked.  If we don't do it here, and
        // the user didn't explicitly set the length, then the transfer-encoding
        // will be chunked and zero-copy file transfer will not occur.
        final File f = request.getFile();
        if (f != null) {
          requestPacket.setContentLengthLong(f.length());
        }
        handler = new ExpectHandler(handler);
      }
      context.setBodyHandler(handler);
      if (logger.isDebugEnabled()) {
        logger.debug("REQUEST: {}", requestPacket);
      }
      isWriteComplete = handler.doHandle(ctx, request, requestPacket);
    } else {
      HttpContent content = HttpContent.builder(requestPacket).last(true).build();
      if (logger.isDebugEnabled()) {
        logger.debug("REQUEST: {}", requestPacket);
      }
      ctx.write(content, ctx.getTransportContext().getCompletionHandler());
    }

    return isWriteComplete;
  }
  private boolean sendAsGrizzlyRequest(
      final RequestInfoHolder requestInfoHolder, final FilterChainContext ctx) throws IOException {

    HttpTxContext httpTxContext = requestInfoHolder.getHttpTxContext();
    if (httpTxContext == null) {
      httpTxContext = HttpTxContext.create(requestInfoHolder);
    }

    if (checkProxyAuthFailure(ctx, httpTxContext)) {
      return true;
    }

    final Request request = httpTxContext.getRequest();
    final URI uri = request.isUseRawUrl() ? request.getRawURI() : request.getURI();
    boolean secure = Utils.isSecure(uri);

    // If the request is secure, check to see if an error occurred during
    // the handshake.  We have to do this here, as the error would occur
    // out of the scope of a HttpTxContext so there would be
    // no good way to communicate the problem to the caller.
    if (secure && checkHandshakeError(ctx, httpTxContext)) {
      return true;
    }

    if (isUpgradeRequest(httpTxContext.getHandler())
        && isWSRequest(httpTxContext.getRequestUrl())) {
      httpTxContext.setWSRequest(true);
      convertToUpgradeRequest(httpTxContext);
    }

    HttpRequestPacket requestPacket = requestCache.poll();
    if (requestPacket == null) {
      requestPacket = new HttpRequestPacketImpl();
    }
    requestPacket.setMethod(request.getMethod());
    requestPacket.setProtocol(Protocol.HTTP_1_1);

    // Special handling for CONNECT.
    if (Method.CONNECT.matchesMethod(request.getMethod())) {
      final int port = uri.getPort();
      requestPacket.setRequestURI(uri.getHost() + ':' + (port == -1 ? 443 : port));
    } else {
      requestPacket.setRequestURI(uri.getPath());
    }

    if (Utils.requestHasEntityBody(request)) {
      final long contentLength = request.getContentLength();
      if (contentLength >= 0) {
        requestPacket.setContentLengthLong(contentLength);
        requestPacket.setChunked(false);
      } else {
        requestPacket.setChunked(true);
      }
    }

    if (httpTxContext.isWSRequest()) {
      try {
        final URI wsURI = new URI(httpTxContext.getWsRequestURI());
        httpTxContext.setProtocolHandler(Version.RFC6455.createHandler(true));
        httpTxContext.setHandshake(httpTxContext.getProtocolHandler().createHandShake(wsURI));
        requestPacket =
            (HttpRequestPacket) httpTxContext.getHandshake().composeHeaders().getHttpHeader();
      } catch (URISyntaxException e) {
        throw new IllegalArgumentException("Invalid WS URI: " + httpTxContext.getWsRequestURI());
      }
    }

    requestPacket.setSecure(secure);
    addQueryString(request, requestPacket);
    addHostHeader(request, uri, requestPacket);
    addGeneralHeaders(request, requestPacket);
    addCookies(request, requestPacket);

    initTransferCompletionHandler(request, httpTxContext.getHandler());

    final HttpRequestPacket requestPacketLocal = requestPacket;
    FilterChainContext sendingCtx = ctx;

    if (secure) {
      // Check to see if the ProtocolNegotiator has given
      // us a different FilterChain to use.  If so, we need
      // use a different FilterChainContext when invoking sendRequest().
      sendingCtx = checkAndHandleFilterChainUpdate(ctx, sendingCtx);
    }
    final Connection c = ctx.getConnection();
    if (!Utils.isSpdyConnection(c)) {
      HttpContext.newInstance(ctx, c, c, c);
    } else {
      SpdySession session = SpdySession.get(c);
      final Lock lock = session.getNewClientStreamLock();
      try {
        lock.lock();
        SpdyStream stream =
            session.openStream(
                requestPacketLocal,
                session.getNextLocalStreamId(),
                0,
                0,
                0,
                false,
                !requestPacketLocal.isExpectContent());
        HttpContext.newInstance(ctx, stream, stream, stream);
      } finally {
        lock.unlock();
      }
    }
    HttpTxContext.set(ctx, httpTxContext);
    return sendRequest(sendingCtx, request, requestPacketLocal);
  }