@Override
 public void onException(
     final HttpServerExchange exchange, final Sender sender, final IOException exception) {
   cache.dereference();
   exchange.endExchange();
 }
  /**
   * Attempts to serve the response from a cache.
   *
   * <p>If this fails, and the markCachable parameter is true then the response will be considered
   * cachable, and may be cached to be served by future handlers.
   *
   * <p>If this returns true then the caller should not modify the exchange any more, as this can
   * result in a handoff to an IO thread
   *
   * @param markCacheable If this is true then the resulting response will be considered cachable
   * @return <code>true</code> if serving suceeded,
   */
  public boolean tryServeResponse(boolean markCacheable) {
    final CachedHttpRequest key = new CachedHttpRequest(exchange);
    DirectBufferCache.CacheEntry<CachedHttpRequest> entry = cache.get(key);

    // we only cache get and head requests
    if (!exchange.getRequestMethod().equals(GET) && !exchange.getRequestMethod().equals(HEAD)) {
      return false;
    }

    if (entry == null) {
      this.responseCachable = markCacheable;
      return false;
    }

    // It's loading retry later
    if (!entry.enabled() || !entry.reference()) {
      this.responseCachable = markCacheable;
      return false;
    }

    CachedHttpRequest existingKey = entry.key();
    // if any of the header matches fail we just return
    // we don't can the request, as it is possible the underlying handler
    // may have additional etags
    final ETag etag = existingKey.getEtag();
    if (!ETagUtils.handleIfMatch(exchange, etag, false)) {
      return false;
    }
    // we do send a 304 if the if-none-match header matches
    if (!ETagUtils.handleIfNoneMatch(exchange, etag, true)) {
      exchange.setResponseCode(304);
      exchange.endExchange();
      return true;
    }
    // the server may have a more up to date representation
    if (!DateUtils.handleIfUnmodifiedSince(exchange, existingKey.getLastModified())) {
      return false;
    }
    if (!DateUtils.handleIfModifiedSince(exchange, existingKey.getLastModified())) {
      exchange.setResponseCode(304);
      exchange.endExchange();
      return true;
    }

    // we are going to proceed. Set the appropriate headers
    if (existingKey.getContentType() != null) {
      exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, existingKey.getContentType());
    }
    if (existingKey.getContentEncoding() != null
        && !Headers.IDENTITY.equals(HttpString.tryFromString(existingKey.getContentEncoding()))) {
      exchange.getResponseHeaders().put(Headers.CONTENT_ENCODING, existingKey.getContentEncoding());
    }
    if (existingKey.getLastModified() != null) {
      exchange
          .getResponseHeaders()
          .put(Headers.LAST_MODIFIED, DateUtils.toDateString(existingKey.getLastModified()));
    }
    if (existingKey.getContentLocation() != null) {
      exchange.getResponseHeaders().put(Headers.CONTENT_LOCATION, existingKey.getContentLocation());
    }
    if (existingKey.getLanguage() != null) {
      exchange.getResponseHeaders().put(Headers.CONTENT_LANGUAGE, existingKey.getLanguage());
    }
    if (etag != null) {
      exchange.getResponseHeaders().put(Headers.CONTENT_LANGUAGE, etag.toString());
    }

    // TODO: support if-range
    exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, Long.toString(entry.size()));
    if (exchange.getRequestMethod().equals(HEAD)) {
      exchange.endExchange();
      return true;
    }

    final ByteBuffer[] buffers;

    boolean ok = false;
    try {
      LimitedBufferSlicePool.PooledByteBuffer[] pooled = entry.buffers();
      buffers = new ByteBuffer[pooled.length];
      for (int i = 0; i < buffers.length; i++) {
        // Keep position from mutating
        buffers[i] = pooled[i].getResource().duplicate();
      }
      ok = true;
    } finally {
      if (!ok) {
        entry.dereference();
      }
    }

    // Transfer Inline, or register and continue transfer
    // Pass off the entry dereference call to the listener
    exchange.getResponseSender().send(buffers, new DereferenceCallback(entry));
    return true;
  }
 @Override
 public void onComplete(final HttpServerExchange exchange, final Sender sender) {
   cache.dereference();
   exchange.endExchange();
 }