// To attempts rewrite. It attempts to rewrite to first valid path // or the last path if none of the paths are valid. // Returns true if rewrite is successful and false otherwise. func To(fs http.FileSystem, r *http.Request, to string, replacer middleware.Replacer) Result { tos := strings.Fields(to) // try each rewrite paths t := "" for _, v := range tos { t = path.Clean(replacer.Replace(v)) // add trailing slash for directories, if present if strings.HasSuffix(v, "/") && !strings.HasSuffix(t, "/") { t += "/" } // validate file if isValidFile(fs, t) { break } } // validate resulting path u, err := url.Parse(t) if err != nil { // Let the user know we got here. Rewrite is expected but // the resulting url is invalid. log.Printf("[ERROR] rewrite: resulting path '%v' is invalid. error: %v", t, err) return RewriteIgnored } // take note of this rewrite for internal use by fastcgi // all we need is the URI, not full URL r.Header.Set(headerFieldName, r.URL.RequestURI()) // perform rewrite r.URL.Path = u.Path if u.RawQuery != "" { // overwrite query string if present r.URL.RawQuery = u.RawQuery } if u.Fragment != "" { // overwrite fragment if present r.URL.Fragment = u.Fragment } return RewriteDone }
// ServeHTTP satisfies the middleware.Handler interface. func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, upstream := range p.Upstreams { if middleware.Path(r.URL.Path).Matches(upstream.From()) && upstream.IsAllowedPath(r.URL.Path) { var replacer middleware.Replacer start := time.Now() requestHost := r.Host // Since Select() should give us "up" hosts, keep retrying // hosts until timeout (or until we get a nil host). for time.Now().Sub(start) < tryDuration { host := upstream.Select() if host == nil { return http.StatusBadGateway, errUnreachable } proxy := host.ReverseProxy r.Host = host.Name if baseURL, err := url.Parse(host.Name); err == nil { r.Host = baseURL.Host if proxy == nil { proxy = NewSingleHostReverseProxy(baseURL, host.WithoutPathPrefix) } } else if proxy == nil { return http.StatusInternalServerError, err } var extraHeaders http.Header if host.ExtraHeaders != nil { extraHeaders = make(http.Header) if replacer == nil { rHost := r.Host r.Host = requestHost replacer = middleware.NewReplacer(r, nil, "") r.Host = rHost } for header, values := range host.ExtraHeaders { for _, value := range values { extraHeaders.Add(header, replacer.Replace(value)) if header == "Host" { r.Host = replacer.Replace(value) } } } } atomic.AddInt64(&host.Conns, 1) backendErr := proxy.ServeHTTP(w, r, extraHeaders) atomic.AddInt64(&host.Conns, -1) if backendErr == nil { return 0, nil } timeout := host.FailTimeout if timeout == 0 { timeout = 10 * time.Second } atomic.AddInt32(&host.Fails, 1) go func(host *UpstreamHost, timeout time.Duration) { time.Sleep(timeout) atomic.AddInt32(&host.Fails, -1) }(host, timeout) } return http.StatusBadGateway, errUnreachable } } return p.Next.ServeHTTP(w, r) }
// ServeHTTP satisfies the middleware.Handler interface. func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, upstream := range p.Upstreams { if !middleware.Path(r.URL.Path).Matches(upstream.From()) || !upstream.AllowedPath(r.URL.Path) { continue } var replacer middleware.Replacer start := time.Now() outreq := createUpstreamRequest(r) // Since Select() should give us "up" hosts, keep retrying // hosts until timeout (or until we get a nil host). for time.Now().Sub(start) < tryDuration { host := upstream.Select() if host == nil { return http.StatusBadGateway, errUnreachable } if rr, ok := w.(*middleware.ResponseRecorder); ok && rr.Replacer != nil { rr.Replacer.Set("upstream", host.Name) } outreq.Host = host.Name if host.UpstreamHeaders != nil { if replacer == nil { rHost := r.Host replacer = middleware.NewReplacer(r, nil, "") outreq.Host = rHost } if v, ok := host.UpstreamHeaders["Host"]; ok { outreq.Host = replacer.Replace(v[len(v)-1]) } // Modify headers for request that will be sent to the upstream host upHeaders := createHeadersByRules(host.UpstreamHeaders, r.Header, replacer) for k, v := range upHeaders { outreq.Header[k] = v } } var downHeaderUpdateFn respUpdateFn if host.DownstreamHeaders != nil { if replacer == nil { rHost := r.Host replacer = middleware.NewReplacer(r, nil, "") outreq.Host = rHost } //Creates a function that is used to update headers the response received by the reverse proxy downHeaderUpdateFn = createRespHeaderUpdateFn(host.DownstreamHeaders, replacer) } proxy := host.ReverseProxy if baseURL, err := url.Parse(host.Name); err == nil { r.Host = baseURL.Host if proxy == nil { proxy = NewSingleHostReverseProxy(baseURL, host.WithoutPathPrefix) } } else if proxy == nil { return http.StatusInternalServerError, err } atomic.AddInt64(&host.Conns, 1) backendErr := proxy.ServeHTTP(w, outreq, downHeaderUpdateFn) atomic.AddInt64(&host.Conns, -1) if backendErr == nil { return 0, nil } timeout := host.FailTimeout if timeout == 0 { timeout = 10 * time.Second } atomic.AddInt32(&host.Fails, 1) go func(host *UpstreamHost, timeout time.Duration) { time.Sleep(timeout) atomic.AddInt32(&host.Fails, -1) }(host, timeout) } return http.StatusBadGateway, errUnreachable } return p.Next.ServeHTTP(w, r) }