Skip to content

Commit

Permalink
Merge pull request #109 from saucelabs/mmt/clean-proxy
Browse files Browse the repository at this point in the history
proxy extreme makeover
  • Loading branch information
mmatczuk authored Oct 28, 2022
2 parents b90f02f + 75dfe51 commit 06c69d8
Show file tree
Hide file tree
Showing 28 changed files with 506 additions and 642 deletions.
55 changes: 38 additions & 17 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,57 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// APIHandler returns http.Handler serving API endpoints.
// APIHandler serves API endpoints.
// It provides health and readiness endpoints prometheus metrics, and pprof debug endpoints.
func APIHandler(s *HTTPServer, r prometheus.Gatherer) http.Handler {
m := http.NewServeMux()
m.HandleFunc("/api/v1/health", healthHandler)
m.HandleFunc("/api/v1/ready", readyHandler(s))
type APIHandler struct {
mux *http.ServeMux
proxy *HTTPServer
script string
}

func NewAPIHandler(r prometheus.Gatherer, proxy *HTTPServer, pac string) *APIHandler {
m := http.NewServeMux()
a := &APIHandler{
mux: m,
proxy: proxy,
}
m.HandleFunc("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}).ServeHTTP)

m.HandleFunc("/healthz", a.healthz)
m.HandleFunc("/readyz", a.readyz)
m.HandleFunc("/configz", a.configz)
m.HandleFunc("/pac", a.pac)
m.HandleFunc("/debug/pprof/", pprof.Index)
m.HandleFunc("/debug/pprof/profile", pprof.Profile)
m.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
m.HandleFunc("/debug/pprof/trace", pprof.Trace)

return m
return a
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
func (h *APIHandler) healthz(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK")) //nolint:errcheck // ignore error
}

func readyHandler(s *HTTPServer) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if s.Addr() != "" {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK")) //nolint:errcheck // ignore error
} else {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte("Service Unavailable")) //nolint:errcheck // ignore error
}
func (h *APIHandler) readyz(w http.ResponseWriter, r *http.Request) {
if h.proxy.Addr() != "" {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK")) //nolint:errcheck // ignore error
} else {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte("Service Unavailable")) //nolint:errcheck // ignore error
}
}

func (h *APIHandler) configz(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}

func (h *APIHandler) pac(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/x-ns-proxy-autoconfig")
w.Write([]byte(h.script)) //nolint:errcheck // ignore it
}

func (h *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.mux.ServeHTTP(w, r)
}
10 changes: 1 addition & 9 deletions bind/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,6 @@ func PAC(fs *pflag.FlagSet, pac **url.URL) {
func HTTPProxyConfig(fs *pflag.FlagSet, cfg *forwarder.HTTPProxyConfig) {
fs.VarP(anyflag.NewValue[*url.URL](cfg.UpstreamProxy, &cfg.UpstreamProxy, forwarder.ParseProxyURL),
"upstream-proxy", "u", "upstream proxy URL")
fs.VarP(anyflag.NewValue[*url.Userinfo](cfg.UpstreamProxyCredentials, &cfg.UpstreamProxyCredentials, forwarder.ParseUserInfo),
"upstream-proxy-credentials", "", "upstream proxy credentials in the form of `username:password`, it overrides credentials embedded in upstream-proxy URL")
fs.VarP(anyflag.NewValue[*url.URL](cfg.PAC, &cfg.PAC, url.ParseRequestURI),
"pac", "p", "local file `path or URL` to PAC content")
fs.StringSliceVarP(&cfg.PACProxiesCredentials, "pac-proxies-credentials", "d", cfg.PACProxiesCredentials,
"PAC proxies credentials in URL format ex. http://user:pass@host:port (can be specified multiple times)")
fs.StringSliceVar(&cfg.SiteCredentials, "site-credentials", cfg.SiteCredentials,
"target site credentials")
fs.BoolVarP(&cfg.ProxyLocalhost, "proxy-localhost", "t", cfg.ProxyLocalhost,
"proxy localhost requests to an upstream proxy")
}
Expand Down Expand Up @@ -85,7 +77,7 @@ func HTTPServerConfig(fs *pflag.FlagSet, cfg *forwarder.HTTPServerConfig, prefix
fs.DurationVar(&cfg.ReadTimeout,
namePrefix+"read-timeout", cfg.ReadTimeout, usagePrefix+"HTTP server read timeout")
fs.VarP(anyflag.NewValue[*url.Userinfo](cfg.BasicAuth, &cfg.BasicAuth, forwarder.ParseUserInfo),
namePrefix+"basic-auth", "", usagePrefix+"basic-auth in the form of `username:password`")
namePrefix+"basic-auth", "", usagePrefix+"HTTP server basic-auth in the form of `username:password`")
}

func TLSConfig(fs *pflag.FlagSet, cfg *forwarder.TLSConfig) {
Expand Down
16 changes: 16 additions & 0 deletions cmd/forwarder/wrap.go → cmd/forwarder/decorators.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,25 @@ import (
"regexp"
"strings"

"github.com/saucelabs/forwarder/pac"
"github.com/spf13/cobra"
)

func withPACSupportedFunctions(cmd *cobra.Command) *cobra.Command {
cmd.Example += "\n" + pacSupportedFunctions()
return cmd
}

func pacSupportedFunctions() string {
var sb strings.Builder
sb.WriteString("Supported PAC util functions:")
for _, fn := range pac.SupportedFunctions() {
sb.WriteString("\n ")
sb.WriteString(fn)
}
return sb.String()
}

func wrapLongAt(cmd *cobra.Command, width int) {
cmd.Long = wrapTextAt(cmd.Long, width)
}
Expand Down
15 changes: 2 additions & 13 deletions cmd/forwarder/paceval/paceval.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"net"
"net/url"
"os"
"strings"

"github.com/saucelabs/forwarder"
"github.com/saucelabs/forwarder/bind"
Expand Down Expand Up @@ -49,7 +48,7 @@ func (c *command) RunE(cmd *cobra.Command, args []string) error {
if err != nil {
return fmt.Errorf("parse URL: %w", err)
}
proxy, err := pr.FindProxyForURL(u)
proxy, err := pr.FindProxyForURL(u, "")
if err != nil {
return err
}
Expand Down Expand Up @@ -80,7 +79,7 @@ func Command() (cmd *cobra.Command) {
Short: "Evaluate a PAC file for given URLs",
Long: long,
RunE: c.RunE,
Example: example + "\n" + supportedFunctions(),
Example: example,
}
}

Expand All @@ -104,13 +103,3 @@ const example = ` # Evaluate a PAC file for a URL
# Evaluate a PAC file for multiple URLs using a PAC file from a URL
forwarder pac-eval --pac https://example.com/pac.js https://www.google.com https://www.facebook.com
`

func supportedFunctions() string {
var sb strings.Builder
sb.WriteString("Supported PAC util functions:")
for _, fn := range pac.SupportedFunctions() {
sb.WriteString("\n ")
sb.WriteString(fn)
}
return sb.String()
}
21 changes: 5 additions & 16 deletions cmd/forwarder/pacserver/pacserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"net/http"
"net/url"
"os/signal"
"strings"
"syscall"

"github.com/saucelabs/forwarder"
Expand Down Expand Up @@ -80,7 +79,7 @@ func Command() (cmd *cobra.Command) {
Short: "Start HTTP(S) server that serves a PAC file",
Long: long,
RunE: c.RunE,
Example: example + "\n" + supportedFunctions(),
Example: example,
}
}

Expand All @@ -96,24 +95,14 @@ If you start an HTTPS server and you don't provide a certificate, the server wil
`

const example = ` # Start a HTTP server serving a PAC file
pac-server --pac pac.js --protocol http --address localhost:8080
forwarder pac-server --pac pac.js --protocol http --address localhost:8080
# Start a HTTPS server serving a PAC file
pac-server --pac pac.js --protocol https --address localhost:80443
forwarder pac-server --pac pac.js --protocol https --address localhost:80443
# Start a HTTPS server serving a PAC file with custom certificate
pac-server --pac pac.js --protocol https --address localhost:80443 --cert-file cert.pem --key-file key.pem
forwarder pac-server --pac pac.js --protocol https --address localhost:80443 --cert-file cert.pem --key-file key.pem
# Start a HTTPS server serving a PAC file with basic authentication
pac-server --pac pac.js --protocol https --address localhost:80443 --basic-auth user:pass
forwarder pac-server --pac pac.js --protocol https --address localhost:80443 --basic-auth user:pass
`

func supportedFunctions() string {
var sb strings.Builder
sb.WriteString("Supported PAC util functions:")
for _, fn := range pac.SupportedFunctions() {
sb.WriteString("\n ")
sb.WriteString(fn)
}
return sb.String()
}
Loading

0 comments on commit 06c69d8

Please sign in to comment.