// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/http/proxy_fallback.h"

#include "net/base/net_errors.h"
#include "net/base/proxy_server.h"

namespace net {

NET_EXPORT bool CanFalloverToNextProxy(const ProxyServer& proxy,
                                       int error,
                                       int* final_error) {
  *final_error = error;

  if (proxy.is_quic()) {
    switch (error) {
      case ERR_QUIC_PROTOCOL_ERROR:
      case ERR_QUIC_HANDSHAKE_FAILED:
      case ERR_MSG_TOO_BIG:
        return true;
    }
  }

  // TODO(eroman): Split up these error codes across the relevant proxy types.
  //
  // A failure to resolve the hostname or any error related to establishing a
  // TCP connection could be grounds for trying a new proxy configuration.
  //
  // Why do this when a hostname cannot be resolved?  Some URLs only make sense
  // to proxy servers.  The hostname in those URLs might fail to resolve if we
  // are still using a non-proxy config.  We need to check if a proxy config
  // now exists that corresponds to a proxy server that could load the URL.
  //
  // A failure while establishing a tunnel to the proxy
  // (ERR_TUNNEL_CONNECTION_FAILED) is NOT considered grounds for fallback.
  // Other browsers similarly don't fallback, and some client's PAC
  // configurations rely on this for some degree of content blocking.
  // See https://crbug.com/680837 for details.
  switch (error) {
    case ERR_PROXY_CONNECTION_FAILED:
    case ERR_NAME_NOT_RESOLVED:
    case ERR_INTERNET_DISCONNECTED:
    case ERR_ADDRESS_UNREACHABLE:
    case ERR_CONNECTION_CLOSED:
    case ERR_CONNECTION_TIMED_OUT:
    case ERR_CONNECTION_RESET:
    case ERR_CONNECTION_REFUSED:
    case ERR_CONNECTION_ABORTED:
    case ERR_TIMED_OUT:
    case ERR_SOCKS_CONNECTION_FAILED:
    // ERR_PROXY_CERTIFICATE_INVALID can happen in the case of trying to talk to
    // a proxy using SSL, and ending up talking to a captive portal that
    // supports SSL instead.
    case ERR_PROXY_CERTIFICATE_INVALID:
    // ERR_SSL_PROTOCOL_ERROR can happen when trying to talk SSL to a non-SSL
    // server (like a captive portal).
    case ERR_SSL_PROTOCOL_ERROR:
      return true;

    case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
      // Remap the SOCKS-specific "host unreachable" error to a more
      // generic error code (this way consumers like the link doctor
      // know to substitute their error page).
      //
      // Note that if the host resolving was done by the SOCKS5 proxy, we can't
      // differentiate between a proxy-side "host not found" versus a proxy-side
      // "address unreachable" error, and will report both of these failures as
      // ERR_ADDRESS_UNREACHABLE.
      *final_error = ERR_ADDRESS_UNREACHABLE;
      return false;
  }
  return false;
}

}  // namespace net
