@Override
  public void exchangeTerminated(HttpExchange exchange, Result result) {
    super.exchangeTerminated(exchange, result);

    Response response = result.getResponse();
    HttpFields responseHeaders = response.getHeaders();

    String closeReason = null;
    if (result.isFailed()) closeReason = "failure";
    else if (receiver.isShutdown()) closeReason = "server close";

    if (closeReason == null) {
      if (response.getVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
        // HTTP 1.0 must close the connection unless it has
        // an explicit keep alive or it's a CONNECT method.
        boolean keepAlive =
            responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
        boolean connect = HttpMethod.CONNECT.is(exchange.getRequest().getMethod());
        if (!keepAlive && !connect) closeReason = "http/1.0";
      } else {
        // HTTP 1.1 or greater closes only if it has an explicit close.
        if (responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()))
          closeReason = "http/1.1";
      }
    }

    if (closeReason != null) {
      if (LOG.isDebugEnabled()) LOG.debug("Closing, reason: {} - {}", closeReason, connection);
      connection.close();
    } else {
      if (response.getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101) connection.remove();
      else release();
    }
  }
 @Override
 public void onFillable() {
   HttpExchange exchange = channel.getHttpExchange();
   if (exchange != null) {
     channel.receive();
   } else {
     // If there is no exchange, then could be either a remote close,
     // or garbage bytes; in both cases we close the connection
     close();
   }
 }
 @Override
 protected boolean onReadTimeout() {
   if (LOG.isDebugEnabled()) LOG.debug("Idle timeout {}", this);
   close(new TimeoutException());
   return false;
 }
 @Override
 public void close() {
   close(new AsynchronousCloseException());
 }