/**
   * Report and attempt to recover from {@code e}. Returns true if the HTTP engine was replaced and
   * the request should be retried. Otherwise the failure is permanent.
   */
  private boolean handleFailure(IOException e) throws IOException {
    RouteSelector routeSelector = httpEngine.routeSelector;
    if (routeSelector != null && httpEngine.connection != null) {
      routeSelector.connectFailed(httpEngine.connection, e);
    }

    OutputStream requestBody = httpEngine.getRequestBody();
    boolean canRetryRequestBody =
        requestBody == null
            || requestBody instanceof RetryableOutputStream
            || (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable());
    if (routeSelector == null && httpEngine.connection == null // No connection.
        || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt.
        || !isRecoverable(e)
        || !canRetryRequestBody) {
      httpEngineFailure = e;
      return false;
    }

    httpEngine.release(true);
    RetryableOutputStream retryableOutputStream =
        requestBody instanceof RetryableOutputStream ? (RetryableOutputStream) requestBody : null;
    httpEngine = newHttpEngine(method, rawRequestHeaders, null, retryableOutputStream);
    httpEngine.routeSelector = routeSelector; // Keep the same routeSelector.
    if (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable()) {
      httpEngine.sendRequest();
      faultRecoveringRequestBody.replaceStream(httpEngine.getRequestBody());
    }
    return true;
  }
 /**
  * Sends a request and optionally reads a response. Returns true if the request was successfully
  * executed, and false if the request can be retried. Throws an exception if the request failed
  * permanently.
  */
 private boolean execute(boolean readResponse) throws IOException {
   try {
     httpEngine.sendRequest();
     if (readResponse) {
       httpEngine.readResponse();
     }
     return true;
   } catch (IOException e) {
     if (handleFailure(e)) {
       return false;
     } else {
       throw e;
     }
   }
 }