// Copyright (c) 2010 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 "base/macros.h"
#include "net/proxy/proxy_server.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

// Test the creation of ProxyServer using ProxyServer::FromURI, which parses
// inputs of the form [<scheme>"://"]<host>[":"<port>]. Verify that each part
// was labelled correctly, and the accessors all give the right data.
TEST(ProxyServerTest, FromURI) {
  const struct {
    const char* const input_uri;
    const char* const expected_uri;
    ProxyServer::Scheme expected_scheme;
    const char* const expected_host;
    int expected_port;
    const char* const expected_pac_string;
  } tests[] = {
      // HTTP proxy URIs:
      {"foopy:10",  // No scheme.
       "foopy:10",
       ProxyServer::SCHEME_HTTP,
       "foopy",
       10,
       "PROXY foopy:10"},
      {"http://foopy",  // No port.
       "foopy:80",
       ProxyServer::SCHEME_HTTP,
       "foopy",
       80,
       "PROXY foopy:80"},
      {"http://foopy:10",
       "foopy:10",
       ProxyServer::SCHEME_HTTP,
       "foopy",
       10,
       "PROXY foopy:10"},

      // IPv6 HTTP proxy URIs:
      {"[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:10",  // No scheme.
       "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:10",
       ProxyServer::SCHEME_HTTP,
       "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210",
       10,
       "PROXY [FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:10"},
      {"http://[3ffe:2a00:100:7031::1]",  // No port.
       "[3ffe:2a00:100:7031::1]:80",
       ProxyServer::SCHEME_HTTP,
       "3ffe:2a00:100:7031::1",
       80,
       "PROXY [3ffe:2a00:100:7031::1]:80"},
      {"http://[::192.9.5.5]",
       "[::192.9.5.5]:80",
       ProxyServer::SCHEME_HTTP,
       "::192.9.5.5",
       80,
       "PROXY [::192.9.5.5]:80"},
      {"http://[::FFFF:129.144.52.38]:80",
       "[::FFFF:129.144.52.38]:80",
       ProxyServer::SCHEME_HTTP,
       "::FFFF:129.144.52.38",
       80,
       "PROXY [::FFFF:129.144.52.38]:80"},

      // SOCKS4 proxy URIs:
      {"socks4://foopy",  // No port.
       "socks4://foopy:1080",
       ProxyServer::SCHEME_SOCKS4,
       "foopy",
       1080,
       "SOCKS foopy:1080"},
      {"socks4://foopy:10",
       "socks4://foopy:10",
       ProxyServer::SCHEME_SOCKS4,
       "foopy",
       10,
       "SOCKS foopy:10"},

      // SOCKS5 proxy URIs
      {"socks5://foopy",  // No port.
       "socks5://foopy:1080",
       ProxyServer::SCHEME_SOCKS5,
       "foopy",
       1080,
       "SOCKS5 foopy:1080"},
      {"socks5://foopy:10",
       "socks5://foopy:10",
       ProxyServer::SCHEME_SOCKS5,
       "foopy",
       10,
       "SOCKS5 foopy:10"},

      // SOCKS proxy URIs (should default to SOCKS5)
      {"socks://foopy",  // No port.
       "socks5://foopy:1080",
       ProxyServer::SCHEME_SOCKS5,
       "foopy",
       1080,
       "SOCKS5 foopy:1080"},
      {"socks://foopy:10",
       "socks5://foopy:10",
       ProxyServer::SCHEME_SOCKS5,
       "foopy",
       10,
       "SOCKS5 foopy:10"},

      // HTTPS proxy URIs:
      {"https://foopy",  // No port
       "https://foopy:443",
       ProxyServer::SCHEME_HTTPS,
       "foopy",
       443,
       "HTTPS foopy:443"},
      {"https://foopy:10",  // Non-standard port
       "https://foopy:10",
       ProxyServer::SCHEME_HTTPS,
       "foopy",
       10,
       "HTTPS foopy:10"},
      {"https://1.2.3.4:10",  // IP Address
       "https://1.2.3.4:10",
       ProxyServer::SCHEME_HTTPS,
       "1.2.3.4",
       10,
       "HTTPS 1.2.3.4:10"},
  };

  for (size_t i = 0; i < arraysize(tests); ++i) {
    ProxyServer uri =
        ProxyServer::FromURI(tests[i].input_uri, ProxyServer::SCHEME_HTTP);
    EXPECT_TRUE(uri.is_valid());
    EXPECT_FALSE(uri.is_direct());
    EXPECT_EQ(tests[i].expected_uri, uri.ToURI());
    EXPECT_EQ(tests[i].expected_scheme, uri.scheme());
    EXPECT_EQ(tests[i].expected_host, uri.host_port_pair().host());
    EXPECT_EQ(tests[i].expected_port, uri.host_port_pair().port());
    EXPECT_EQ(tests[i].expected_pac_string, uri.ToPacString());
  }
}

TEST(ProxyServerTest, DefaultConstructor) {
  ProxyServer proxy_server;
  EXPECT_FALSE(proxy_server.is_valid());
}

// Test parsing of the special URI form "direct://". Analagous to the "DIRECT"
// entry in a PAC result.
TEST(ProxyServerTest, Direct) {
  ProxyServer uri = ProxyServer::FromURI("direct://", ProxyServer::SCHEME_HTTP);
  EXPECT_TRUE(uri.is_valid());
  EXPECT_TRUE(uri.is_direct());
  EXPECT_EQ("direct://", uri.ToURI());
  EXPECT_EQ("DIRECT", uri.ToPacString());
}

// Test parsing some invalid inputs.
TEST(ProxyServerTest, Invalid) {
  const char* const tests[] = {
    "",
    "   ",
    "dddf:",   // not a valid port
    "dddd:d",  // not a valid port
    "http://",  // not a valid host/port.
    "direct://xyz",  // direct is not allowed a host/port.
    "http:/",  // ambiguous, but will fail because of bad port.
    "http:",  // ambiguous, but will fail because of bad port.
  };

  for (size_t i = 0; i < arraysize(tests); ++i) {
    ProxyServer uri = ProxyServer::FromURI(tests[i], ProxyServer::SCHEME_HTTP);
    EXPECT_FALSE(uri.is_valid());
    EXPECT_FALSE(uri.is_direct());
    EXPECT_FALSE(uri.is_http());
    EXPECT_FALSE(uri.is_socks());
  }
}

// Test that LWS (SP | HT) is disregarded from the ends.
TEST(ProxyServerTest, Whitespace) {
  const char* const tests[] = {
    "  foopy:80",
    "foopy:80   \t",
    "  \tfoopy:80  ",
  };

  for (size_t i = 0; i < arraysize(tests); ++i) {
    ProxyServer uri = ProxyServer::FromURI(tests[i], ProxyServer::SCHEME_HTTP);
    EXPECT_EQ("foopy:80", uri.ToURI());
  }
}

// Test parsing a ProxyServer from a PAC representation.
TEST(ProxyServerTest, FromPACString) {
  const struct {
    const char* const input_pac;
    const char* const expected_uri;
  } tests[] = {
    {
       "PROXY foopy:10",
       "foopy:10",
    },
    {
       "   PROXY    foopy:10   ",
       "foopy:10",
    },
    {
       "pRoXy foopy:10",
       "foopy:10",
    },
    {
       "PROXY foopy",  // No port.
       "foopy:80",
    },
    {
       "socks foopy",
       "socks4://foopy:1080",
    },
    {
       "socks4 foopy",
       "socks4://foopy:1080",
    },
    {
       "socks5 foopy",
       "socks5://foopy:1080",
    },
    {
       "socks5 foopy:11",
       "socks5://foopy:11",
    },
    {
       " direct  ",
       "direct://",
    },
    {
       "https foopy",
       "https://foopy:443",
    },
    {
       "https foopy:10",
       "https://foopy:10",
    },
  };

  for (size_t i = 0; i < arraysize(tests); ++i) {
    ProxyServer uri = ProxyServer::FromPacString(tests[i].input_pac);
    EXPECT_TRUE(uri.is_valid());
    EXPECT_EQ(tests[i].expected_uri, uri.ToURI());
  }
}

// Test parsing a ProxyServer from an invalid PAC representation.
TEST(ProxyServerTest, FromPACStringInvalid) {
  const char* const tests[] = {
    "PROXY",  // missing host/port.
    "HTTPS",  // missing host/port.
    "SOCKS",  // missing host/port.
    "DIRECT foopy:10",  // direct cannot have host/port.
  };

  for (size_t i = 0; i < arraysize(tests); ++i) {
    ProxyServer uri = ProxyServer::FromPacString(tests[i]);
    EXPECT_FALSE(uri.is_valid());
  }
}

TEST(ProxyServerTest, ComparatorAndEquality) {
  struct {
    // Inputs.
    const char* const server1;
    const char* const server2;

    // Expectation.
    //   -1 means server1 is less than server2
    //    0 means server1 equals server2
    //    1 means server1 is greater than server2
    int expected_comparison;
  } tests[] = {
    { // Equal.
      "foo:11",
      "http://foo:11",
      0
    },
    { // Port is different.
      "foo:333",
      "foo:444",
      -1
    },
    { // Host is different.
      "foo:33",
      "bar:33",
      1
    },
    { // Scheme is different.
      "socks4://foo:33",
      "http://foo:33",
      1
    },
  };

  for (size_t i = 0; i < arraysize(tests); ++i) {
    // Parse the expected inputs to ProxyServer instances.
    const ProxyServer server1 =
        ProxyServer::FromURI(tests[i].server1, ProxyServer::SCHEME_HTTP);

    const ProxyServer server2 =
        ProxyServer::FromURI(tests[i].server2, ProxyServer::SCHEME_HTTP);

    switch (tests[i].expected_comparison) {
      case -1:
        EXPECT_TRUE(server1 < server2);
        EXPECT_FALSE(server2 < server1);
        EXPECT_FALSE(server2 == server1);
        break;
      case 0:
        EXPECT_FALSE(server1 < server2);
        EXPECT_FALSE(server2 < server1);
        EXPECT_TRUE(server2 == server1);
        break;
      case 1:
        EXPECT_FALSE(server1 < server2);
        EXPECT_TRUE(server2 < server1);
        EXPECT_FALSE(server2 == server1);
        break;
      default:
        FAIL() << "Invalid expectation. Can be only -1, 0, 1";
    }
  }
}

}  // namespace

}  // namespace net