// tests ./httpclient/ but is in root as it needs access to test files in root
package forwardproxy

import (
	"crypto/tls"
	"fmt"
	"net"
	"sync"
	"testing"
	"time"

	"github.com/caddyserver/forwardproxy/httpclient"
)

func TestHttpClient(t *testing.T) {
	_test := func(urlSchemeAndCreds, urlAddress string) {
		for _, httpProxyVer := range testHTTPProxyVersions {
			for _, httpTargetVer := range testHTTPTargetVersions {
				for _, resource := range testResources {
					// always dial localhost for testing purposes
					proxyURL := fmt.Sprintf("%s@%s", urlSchemeAndCreds, urlAddress)

					dialer, err := httpclient.NewHTTPConnectDialer(proxyURL)
					if err != nil {
						t.Fatal(err)
					}
					dialer.DialTLS = func(network string, address string) (net.Conn, string, error) {
						// always dial localhost for testing purposes
						conn, err := tls.Dial(network, address, &tls.Config{
							InsecureSkipVerify: true,
							NextProtos:         []string{httpVersionToALPN[httpProxyVer]},
						})
						if err != nil {
							return nil, "", err
						}
						return conn, conn.ConnectionState().NegotiatedProtocol, nil
					}

					// always dial localhost for testing purposes
					conn, err := dialer.Dial("tcp", caddyTestTarget.addr)
					if err != nil {
						t.Fatal(err)
					}
					response, err := getResourceViaProxyConn(conn, caddyTestTarget.addr, resource, httpTargetVer, credentialsCorrect)
					if err != nil {
						t.Fatal(httpProxyVer, httpTargetVer, err)
					} else if err = responseExpected(response, caddyTestTarget.contents[resource]); err != nil {
						t.Fatal(httpProxyVer, httpTargetVer, err)
					}
				}
			}
		}
	}

	_test("https://"+credentialsCorrectPlain, caddyForwardProxyAuth.addr)
	_test("http://"+credentialsCorrectPlain, caddyHTTPForwardProxyAuth.addr)
}

func TestHttpClientH2Multiplexing(t *testing.T) {
	// doesn't actually confirm that it is multiplexed, just that it doesn't break things
	// but it was manually inspected in Wireshark when this code was committed
	httpProxyVer := "HTTP/2.0"
	httpTargetVer := "HTTP/1.1"

	dialer, err := httpclient.NewHTTPConnectDialer("https://" + credentialsCorrectPlain + "@" + caddyForwardProxyAuth.addr)
	if err != nil {
		t.Fatal(err)
	}
	dialer.DialTLS = func(network string, address string) (net.Conn, string, error) {
		// always dial localhost for testing purposes
		conn, err := tls.Dial(network, address, &tls.Config{
			InsecureSkipVerify: true,
			NextProtos:         []string{httpVersionToALPN[httpProxyVer]},
		})
		if err != nil {
			return nil, "", err
		}
		return conn, conn.ConnectionState().NegotiatedProtocol, nil
	}

	retries := 20
	sleepInterval := time.Millisecond * 100

	var wg sync.WaitGroup
	wg.Add(retries + 1) // + for one serial launch
	_test := func() {
		defer wg.Done()
		for _, resource := range testResources {
			// always dial localhost for testing purposes
			conn, err := dialer.Dial("tcp", caddyTestTarget.addr)
			if err != nil {
				t.Fatal(err)
			}
			response, err := getResourceViaProxyConn(conn, caddyTestTarget.addr, resource, httpTargetVer, credentialsCorrect)
			if err != nil {
				t.Fatal(httpProxyVer, httpTargetVer, err)
			} else if err = responseExpected(response, caddyTestTarget.contents[resource]); err != nil {
				t.Fatal(httpProxyVer, httpTargetVer, err)
			}
		}
	}

	_test() // do serially at least once

	for i := 0; i < retries; i++ {
		// nolint:govet // this is a test
		go _test()
		time.Sleep(sleepInterval)
	}
}
