Repository: square/okhttp
File: okhttp/src/jvmMain/kotlin/okhttp3/internal/connection/ExchangeFinder.kt (Kotlin)

Code before:
    }
  }

  /**
   * Returns true if the current route has a failure that retrying could fix, and that there's
   * a route to retry on.
   */
  fun retryAfterFailure(): Boolean {
    if (refusedStreamCount == 0 && connectionShutdownCount == 0 && otherFailureCount == 0) {
      return false // Nothing to recover from.
    }

    if (nextRouteToTry != null) {
      return true
    }

    val retryRoute = retryRoute()
    if (retryRoute != null) {
      // Lock in the route because retryRoute() is racy and we don't want to call it twice.
      nextRouteToTry = retryRoute
      return true
    }

    // If we have a routes left, use 'em.
    if (routeSelection?.hasNext() == true) return true

    // If we haven't initialized the route selector yet, assume it'll have at least one route.
    val localRouteSelector = routeSelector ?: return true

    // If we do have a route selector, use its routes.
    return localRouteSelector.hasNext()
  }

  /**
   * Return the route from the current connection if it should be retried, even if the connection
   * itself is unhealthy. The biggest gotcha here is that we shouldn't reuse routes from coalesced
   * connections.
   */
  private fun retryRoute(): Route? {
    if (refusedStreamCount > 1 || connectionShutdownCount > 1 || otherFailureCount > 0) {
      return null // This route has too many problems to retry.
    }

    val connection = call.connection ?: return null

    synchronized(connection) {
      if (connection.routeFailureCount != 0) return null
      if (!connection.route().address.url.canReuseConnectionFor(address.url)) return null
      return connection.route()
    }
  }

Review comment (bug):
I don’t think this made any sense. We were clearing out a stashed route when an unrelated route failed. For example, if we coalesced connections this would clear out the original route that we coalesced from. (This could be the cause of our difficult-to-reproduce `exhausted all routes` errors.)

Code after:
    }
  }

  /**
   * Returns true if the current route has a failure that retrying could fix, and that there's
   * a route to retry on.
   */
  fun retryAfterFailure(): Boolean {
    if (refusedStreamCount == 0 && connectionShutdownCount == 0 && otherFailureCount == 0) {
      return false // Nothing to recover from.
    }

    if (nextRouteToTry != null) {
      return true
    }

    val retryRoute = retryRoute()
    if (retryRoute != null) {
      // Lock in the route because retryRoute() is racy and we don't want to call it twice.
      nextRouteToTry = retryRoute
      return true
    }

    // If we have a routes left, use 'em.
    if (routeSelection?.hasNext() == true) return true

    // If we haven't initialized the route selector yet, assume it'll have at least one route.
    val localRouteSelector = routeSelector ?: return true

    // If we do have a route selector, use its routes.
    return localRouteSelector.hasNext()
  }

  /**
   * Return the route from the current connection if it should be retried, even if the connection
   * itself is unhealthy. The biggest gotcha here is that we shouldn't reuse routes from coalesced
   * connections.
   */
  private fun retryRoute(): Route? {
    if (refusedStreamCount > 1 || connectionShutdownCount > 1 || otherFailureCount > 0) {
      return null // This route has too many problems to retry.
    }

    val connection = call.connection ?: return null

    synchronized(connection) {
      if (connection.routeFailureCount != 0) return null
      if (!connection.noNewExchanges) return null // This route is still in use.
      if (!connection.route().address.url.canReuseConnectionFor(address.url)) return null
      return connection.route()
    }