func (fp *ForwardProxy) dialContextCheckACL(ctx context.Context, network, hostPort string) (net.Conn, *ProxyError) {
	var conn net.Conn
	if network != "tcp" && network != "tcp4" && network != "tcp6" {
		return nil, &ProxyError{S: "Network " + network + " is not supported", Code: http.StatusBadRequest}
	}
	host, port, err := net.SplitHostPort(hostPort)
	if err != nil {
		return nil, &ProxyError{S: err.Error(), Code: http.StatusBadRequest}
	}
	if fp.upstream != nil {
		conn, err = fp.dialContext(ctx, network, hostPort)
		if err != nil {
			return conn, &ProxyError{S: err.Error(), Code: http.StatusBadGateway}
		}
		return conn, nil
	}
	if !fp.portIsAllowed(port) {
		return nil, &ProxyError{S: "port " + port + " is not allowed", Code: http.StatusForbidden}
	}
	IPs, err := net.LookupIP(host)
	if err != nil {
		return nil, &ProxyError{S: fmt.Sprintf("Lookup of %s failed: %v", host, err),
			Code: http.StatusBadGateway}
	}
	for _, ip := range IPs {
		if !fp.hostIsAllowed(host, ip) {
			continue
		}
		conn, err = fp.dialContext(ctx, network, net.JoinHostPort(ip.String(), port))
		if err == nil {
			return conn, nil
		}
	}
	return nil, &ProxyError{S: "No allowed IP addresses for " + host, Code: http.StatusForbidden}
}
dialContextCheckACL enforces Access Control List and calls fp.DialContext
package forwardproxy
import (
	"bufio"
	"bytes"
	"context"
	"crypto/subtle"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/caddyserver/caddy/caddyhttp/httpserver"
	"github.com/caddyserver/forwardproxy/httpclient"
)
type ForwardProxy struct {
	Next httpserver.Handler
	authRequired    bool
	authCredentials [][]byte  
	hideIP  bool
	hideVia bool
	pacFilePath string
	hostname string  
	port     string  
	probeResistDomain  string
	probeResistEnabled bool
	dialTimeout time.Duration  
	httpTransport http.Transport
	dialContext func(ctx context.Context, network, address string) (net.Conn, error)
	upstream    *url.URL  
	aclRules         []aclRule
	whitelistedPorts []int
}
var bufferPool sync.Pool
func (fp *ForwardProxy) hostIsAllowed(hostname string, ip net.IP) bool {
	for _, rule := range fp.aclRules {
		switch rule.tryMatch(ip, hostname) {
		case aclDecisionDeny:
			return false
		case aclDecisionAllow:
			return true
		}
	}
	fmt.Println("ERROR: no acl match for ", hostname, ip)  
	return false
}
func (fp *ForwardProxy) portIsAllowed(port string) bool {
	portInt, err := strconv.Atoi(port)
	if err != nil {
		return false
	}
	if portInt <= 0 || portInt > 65535 {
		return false
	}
	if len(fp.whitelistedPorts) == 0 {
		return true
	}
	isAllowed := false
	for _, p := range fp.whitelistedPorts {
		if p == portInt {
			isAllowed = true
			break
		}
	}
	return isAllowed
}
func dualStream(target net.Conn, clientReader io.ReadCloser, clientWriter io.Writer) error {
	stream := func(w io.Writer, r io.Reader) error {
		buf := bufferPool.Get().([]byte)
		buf = buf[0:cap(buf)]
		_, _err := flushingIoCopy(w, r, buf)
		if closeWriter, ok := w.(interface {
			CloseWrite() error
		}); ok {
			closeWriter.CloseWrite()
		}
		return _err
	}
	go stream(target, clientReader)
	return stream(clientWriter, target)
}
func serveHijack(w http.ResponseWriter, targetConn net.Conn) (int, error) {
	hijacker, ok := w.(http.Hijacker)
	if !ok {
		return http.StatusInternalServerError, errors.New("ResponseWriter does not implement Hijacker")
	}
	clientConn, bufReader, err := hijacker.Hijack()
	if err != nil {
		return http.StatusInternalServerError, errors.New("failed to hijack: " + err.Error())
	}
	defer clientConn.Close()
	if bufReader != nil {
		if n := bufReader.Reader.Buffered(); n > 0 {
			rbuf, err := bufReader.Reader.Peek(n)
			if err != nil {
				return http.StatusBadGateway, err
			}
			targetConn.Write(rbuf)
		}
	}
	res := &http.Response{StatusCode: http.StatusOK,
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
	}
	res.Header.Set("Server", "Caddy")
	err = res.Write(clientConn)
	if err != nil {
		return http.StatusInternalServerError, errors.New("failed to send response to client: " + err.Error())
	}
	return 0, dualStream(targetConn, clientConn, clientConn)
}
func (fp *ForwardProxy) checkCredentials(r *http.Request) error {
	pa := strings.Split(r.Header.Get("Proxy-Authorization"), " ")
	if len(pa) != 2 {
		return errors.New("Proxy-Authorization is required! Expected format: <type> <credentials>")
	}
	if strings.ToLower(pa[0]) != "basic" {
		return errors.New("Auth type is not supported")
	}
	for _, creds := range fp.authCredentials {
		if subtle.ConstantTimeCompare(creds, []byte(pa[1])) == 1 {
			return nil
		}
	}
	return errors.New("Invalid credentials")
}
func stripPort(address string) string {
	portIdx := strings.LastIndex(address, ":")
	ipv6Idx := strings.LastIndex(address, "]")
	if portIdx > ipv6Idx {
		address = address[:portIdx]
	}
	return address
}
func serveHiddenPage(w http.ResponseWriter, authErr error) (int, error) {
	const hiddenPage = `<html>
<head>
  <title>Hidden Proxy Page</title>
</head>
<body>
<h1>Hidden Proxy Page!</h1>
%s<br/>
</body>
</html>`
	const AuthFail = "Please authenticate yourself to the proxy."
	const AuthOk = "Congratulations, you are successfully authenticated to the proxy! Go browse all the things!"
	w.Header().Set("Content-Type", "text/html")
	if authErr != nil {
		w.Header().Set("Proxy-Authenticate", "Basic realm=\"Caddy Secure Web Proxy\"")
		w.WriteHeader(http.StatusProxyAuthRequired)
		w.Write([]byte(fmt.Sprintf(hiddenPage, AuthFail)))
		return 0, authErr
	}
	w.Write([]byte(fmt.Sprintf(hiddenPage, AuthOk)))
	return 0, nil
}
func (fp *ForwardProxy) shouldServePacFile(r *http.Request) bool {
	if len(fp.pacFilePath) > 0 && r.URL.Path == fp.pacFilePath {
		return true
	}
	return false
}
const pacFile = `
function FindProxyForURL(url, host) {
	if (host === "127.0.0.1" || host === "::1" || host === "localhost")
		return "DIRECT";
	return "HTTPS %s:%s";
}
`
func (fp *ForwardProxy) servePacFile(w http.ResponseWriter) (int, error) {
	fmt.Fprintf(w, pacFile, fp.hostname, fp.port)
	return 0, nil
}