package main import ( "io" "strings" "net" "net/http" "net/url" "sanine.net/git/phlox/auth" "sanine.net/git/phlox/config" "sanine.net/git/phlox/page" log "github.com/sirupsen/logrus" ) type Endpoint struct { Path string Origin *url.URL } func addEndpoint(s *auth.Sessions, pages page.Pages, e config.Endpoint) { log.Infof("proxying endpoint %v to %v", e.Path, e.Address) origin, err := url.Parse(e.Address) if err != nil { log.Fatal(err) } end := Endpoint{ Path: e.Path, Origin: origin, } http.HandleFunc(e.Path, func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Location", e.Path + "/") w.WriteHeader(http.StatusPermanentRedirect) }) http.HandleFunc(e.Path + "/", func(w http.ResponseWriter, r *http.Request) { log.Infof("REQ: %v", r.URL.Path) proxy(w, r, s, pages, end) }) } func proxy(w http.ResponseWriter, r *http.Request, s *auth.Sessions, pages page.Pages, end Endpoint) { ok := authenticateRequest(r, s) if !ok { w.Header().Set("Location", "/phlox/login") w.WriteHeader(http.StatusTemporaryRedirect) return } response := proxyRequest(w, r, pages, end) if response != nil { proxyResponse(w, response) } } func proxyRequest(w http.ResponseWriter, r *http.Request, pages page.Pages, end Endpoint) *http.Response { // configure host address r.Host = end.Origin.Host r.URL.Host = end.Origin.Host // strip proxy endpoint path from request path r.URL.Path = strings.TrimPrefix(r.URL.Path, end.Path) // set X-Forwarded-For forwardedFor, _, _ := net.SplitHostPort(r.RemoteAddr) r.Header.Set("X-Forwarded-For", forwardedFor) // misc request cleanups r.URL.Scheme = end.Origin.Scheme r.RequestURI = "" // make request response, err := http.DefaultClient.Do(r) if err != nil { pages.ServeError500(w) log.Error(err) return nil } 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) } } }