Exemple #1
0
// 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
}
Exemple #2
0
// 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)
}
Exemple #3
0
// 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)
}