package main import ( "fmt" "io" "strings" "net" "net/http" "net/url" log "github.com/sirupsen/logrus" ) func main() { const addr = "localhost:3333" log.Info("configuring reverse proxy...") configureEndpoint("/proxy1", "http://localhost:8000") log.Infof("serving on %v", addr) log.Fatal(http.ListenAndServe(addr, nil)) } type Endpoint struct { Path string Origin *url.URL } func configureEndpoint(path, address string) { log.Infof("proxying endpoint %v to %v", path, address) origin, err := url.Parse(address) if err != nil { log.Fatal(err) } end := Endpoint{ Path: path, Origin: origin, } http.HandleFunc(path + "/", func(w http.ResponseWriter, req *http.Request) { log.Infof("REQ: %v", req.URL.Path) proxy(w, req, end) }) } func proxy(w http.ResponseWriter, req *http.Request, end Endpoint) { response := proxyRequest(w, req, end) proxyResponse(w, response) } func proxyRequest(w http.ResponseWriter, req *http.Request, end Endpoint) *http.Response { // configure host address req.Host = end.Origin.Host req.URL.Host = end.Origin.Host // strip proxy endpoint path from request path req.URL.Path = strings.TrimPrefix(req.URL.Path, end.Path) // set X-Forwarded-For forwardedFor, _, _ := net.SplitHostPort(req.RemoteAddr) req.Header.Set("X-Forwarded-For", forwardedFor) // misc request cleanups req.URL.Scheme = end.Origin.Scheme req.RequestURI = "" // make request response, err := http.DefaultClient.Do(req) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "%v", err) log.Fatal(err) } return response } func proxyResponse(w http.ResponseWriter, response *http.Response) { // copy header for key, values := range response.Header { for _, value := range values { w.Header().Add(key, value) } } // get trailer keys trailerKeys := []string{} for key := range response.Trailer { trailerKeys = append(trailerKeys, key) } if (len(trailerKeys) > 0) { w.Header().Set("Trailer", strings.Join(trailerKeys, ",")) } w.WriteHeader(response.StatusCode) // copy body to client io.Copy(w, response.Body) // write trailers for key, values := range response.Trailer { for _, value := range values { w.Header().Set(key, value) } } }