package main import ( "log" "net/http" "net/http/httputil" "net/url" "gopkg.in/gcfg.v1" ) /* example config.ini [Proxy] Listen = :8001 RemoteBase = https://some.service.com/protected/path Username = username Password = password */ type Config struct { Proxy struct { RemoteBase string Username string Password string Listen string } } // like httputil.NewSingleHostReverseProxy but with BasicAuth and Host header rewrite func NewReverseProxy(target *url.URL, user string, pass string) *httputil.ReverseProxy { targetQuery := target.RawQuery director := func(req *http.Request) { req.URL.Scheme = target.Scheme req.URL.Host = target.Host req.URL.Path = req.URL.Path req.URL.RawQuery = targetQuery + req.URL.RawQuery if _, ok := req.Header["User-Agent"]; !ok { req.Header.Set("User-Agent", "") // no default agent } // extensions to NewSingleHostReverseProxy: req.SetBasicAuth(user, pass) req.Host = target.Host } return &httputil.ReverseProxy{Director: director} } func main() { config := &Config{} err := gcfg.ReadFileInto(config, "config.ini") if err != nil { panic(err) } remote, err := url.Parse(config.Proxy.RemoteBase) if err != nil { panic(err) } // turn remote path into our route, must begin and end with a slash to match patterns route := remote.Path remote.Path = "" if route == "" { route = "/" } if route[0] != '/' { route = "/" + route } if route[len(route)-1] != '/' { route = route + "/" } proxy := NewReverseProxy(remote, config.Proxy.Username, config.Proxy.Password) proxy.ModifyResponse = func(r *http.Response) error { if r.StatusCode != http.StatusOK { log.Printf("request for %s got %v", r.Request.URL.String(), r.StatusCode) } if location, err := r.Location(); err == nil { log.Printf("fetching: %+v", location.String()) resp, _ := http.DefaultClient.Get(location.String()) *r = *resp } return nil } log.Println("proxy for " + route) http.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) { log.Print(r.URL.Path) proxy.ServeHTTP(w, r) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { log.Print("unhandled: " + r.URL.Path) }) http.ListenAndServe(config.Proxy.Listen, nil) }