Example #1
0
File: log.go Project: klauern/caddy
func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range l.Rules {
		if middleware.Path(r.URL.Path).Matches(rule.PathScope) {
			// Record the response
			responseRecorder := middleware.NewResponseRecorder(w)

			// Attach the Replacer we'll use so that other middlewares can
			// set their own placeholders if they want to.
			rep := middleware.NewReplacer(r, responseRecorder, CommonLogEmptyValue)
			responseRecorder.Replacer = rep

			// Bon voyage, request!
			status, err := l.Next.ServeHTTP(responseRecorder, r)

			if status >= 400 {
				// There was an error up the chain, but no response has been written yet.
				// The error must be handled here so the log entry will record the response size.
				if l.ErrorFunc != nil {
					l.ErrorFunc(responseRecorder, r, status)
				} else {
					// Default failover error handler
					responseRecorder.WriteHeader(status)
					fmt.Fprintf(responseRecorder, "%d %s", status, http.StatusText(status))
				}
				status = 0
			}

			// Write log entry
			rule.Log.Println(rep.Replace(rule.Format))

			return status, err
		}
	}
	return l.Next.ServeHTTP(w, r)
}
Example #2
0
func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range l.Rules {
		if middleware.Path(r.URL.Path).Matches(rule.PathScope) {
			responseRecorder := middleware.NewResponseRecorder(w)
			status, err := l.Next.ServeHTTP(responseRecorder, r)
			rep := middleware.NewReplacer(r, responseRecorder)
			rule.Log.Println(rep.Replace(rule.Format))
			return status, err
		}
	}
	return l.Next.ServeHTTP(w, r)
}
Example #3
0
// Rewrite rewrites the internal location of the current request.
func (s SimpleRule) Rewrite(r *http.Request) bool {
	if s.From == r.URL.Path {
		// 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())

		// replace variables
		to := path.Clean(middleware.NewReplacer(r, nil, "").Replace(s.To))

		r.URL.Path = to
		return true
	}
	return false
}
Example #4
0
// ServeHTTP implements the middleware.Handler interface.
func (rd Redirect) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range rd.Rules {
		if (rule.FromPath == "/" || r.URL.Path == rule.FromPath) && schemeMatches(rule, r) {
			to := middleware.NewReplacer(r, nil, "").Replace(rule.To)
			if rule.Meta {
				safeTo := html.EscapeString(to)
				fmt.Fprintf(w, metaRedir, safeTo, safeTo)
			} else {
				http.Redirect(w, r, to, rule.Code)
			}
			return 0, nil
		}
	}
	return rd.Next.ServeHTTP(w, r)
}
Example #5
0
// ServeHTTP implements the middleware.Handler interface and serves requests,
// setting headers on the response according to the configured rules.
func (h Headers) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	replacer := middleware.NewReplacer(r, nil, "")
	for _, rule := range h.Rules {
		if middleware.Path(r.URL.Path).Matches(rule.Path) {
			for _, header := range rule.Headers {
				if strings.HasPrefix(header.Name, "-") {
					w.Header().Del(strings.TrimLeft(header.Name, "-"))
				} else {
					w.Header().Set(header.Name, replacer.Replace(header.Value))
				}
			}
		}
	}
	return h.Next.ServeHTTP(w, r)
}
Example #6
0
// Rewrite rewrites the internal location of the current request.
func (r *RegexpRule) Rewrite(req *http.Request) bool {
	rPath := req.URL.Path

	// validate base
	if !middleware.Path(rPath).Matches(r.Base) {
		return false
	}

	// validate extensions
	if !r.matchExt(rPath) {
		return false
	}

	// include trailing slash in regexp if present
	start := len(r.Base)
	if strings.HasSuffix(r.Base, "/") {
		start -= 1
	}

	// validate regexp
	if !r.MatchString(rPath[start:]) {
		return false
	}

	// replace variables
	to := path.Clean(middleware.NewReplacer(req, nil, "").Replace(r.To))

	// validate resulting path
	url, err := url.Parse(to)
	if err != nil {
		return false
	}

	// take note of this rewrite for internal use by fastcgi
	// all we need is the URI, not full URL
	req.Header.Set("Caddy-Rewrite-Original-URI", req.URL.RequestURI())

	// perform rewrite
	req.URL.Path = url.Path
	if url.RawQuery != "" {
		// overwrite query string if present
		req.URL.RawQuery = url.RawQuery
	}
	return true
}
Example #7
0
// Rewrite rewrites the internal location of the current request.
func (r *RegexpRule) Rewrite(req *http.Request) bool {
	rPath := req.URL.Path

	// validate base
	if !middleware.Path(rPath).Matches(r.Base) {
		return false
	}

	// validate extensions
	if !r.matchExt(rPath) {
		return false
	}

	// include trailing slash in regexp if present
	start := len(r.Base)
	if strings.HasSuffix(r.Base, "/") {
		start -= 1
	}

	// validate regexp
	if !r.MatchString(rPath[start:]) {
		return false
	}

	// replace variables
	to := path.Clean(middleware.NewReplacer(req, nil, "").Replace(r.To))

	// validate resulting path
	url, err := url.Parse(to)
	if err != nil {
		return false
	}

	// perform rewrite
	req.URL.Path = url.Path
	if url.RawQuery != "" {
		// overwrite query string if present
		req.URL.RawQuery = url.RawQuery
	}
	return true
}
Example #8
0
func TestReverseProxy(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stderr)

	var requestReceived bool
	backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		requestReceived = true
		w.Write([]byte("Hello, client"))
	}))
	defer backend.Close()

	// set up proxy
	p := &Proxy{
		Upstreams: []Upstream{newFakeUpstream(backend.URL, false)},
	}

	// create request and response recorder
	r, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("Failed to create request: %v", err)
	}
	w := httptest.NewRecorder()

	p.ServeHTTP(w, r)

	if !requestReceived {
		t.Error("Expected backend to receive request, but it didn't")
	}

	// Make sure {upstream} placeholder is set
	rr := middleware.NewResponseRecorder(httptest.NewRecorder())
	rr.Replacer = middleware.NewReplacer(r, rr, "-")

	p.ServeHTTP(rr, r)

	if got, want := rr.Replacer.Replace("{upstream}"), backend.URL; got != want {
		t.Errorf("Expected custom placeholder {upstream} to be set (%s), but it wasn't; got: %s", want, got)
	}
}
Example #9
0
func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range l.Rules {
		if middleware.Path(r.URL.Path).Matches(rule.PathScope) {
			responseRecorder := middleware.NewResponseRecorder(w)
			status, err := l.Next.ServeHTTP(responseRecorder, r)
			if status >= 400 {
				// There was an error up the chain, but no response has been written yet.
				// The error must be handled here so the log entry will record the response size.
				if l.ErrorFunc != nil {
					l.ErrorFunc(responseRecorder, r, status)
				} else {
					// Default failover error handler
					responseRecorder.WriteHeader(status)
					fmt.Fprintf(responseRecorder, "%d %s", status, http.StatusText(status))
				}
				status = 0
			}
			rep := middleware.NewReplacer(r, responseRecorder)
			rule.Log.Println(rep.Replace(rule.Format))
			return status, err
		}
	}
	return l.Next.ServeHTTP(w, r)
}
Example #10
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)
}
Example #11
0
func newReplacer(r *http.Request) middleware.Replacer {
	return middleware.NewReplacer(r, nil, "")
}
Example #12
0
func TestDownstreamHeadersUpdate(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stderr)

	backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Merge-Me", "Initial")
		w.Header().Add("Remove-Me", "Remove-Value")
		w.Header().Add("Replace-Me", "Replace-Value")
		w.Write([]byte("Hello, client"))
	}))
	defer backend.Close()

	upstream := newFakeUpstream(backend.URL, false)
	upstream.host.DownstreamHeaders = http.Header{
		"+Merge-Me":  {"Merge-Value"},
		"+Add-Me":    {"Add-Value"},
		"-Remove-Me": {""},
		"Replace-Me": {"{hostname}"},
	}
	// set up proxy
	p := &Proxy{
		Upstreams: []Upstream{upstream},
	}

	// create request and response recorder
	r, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("Failed to create request: %v", err)
	}
	w := httptest.NewRecorder()

	p.ServeHTTP(w, r)

	replacer := middleware.NewReplacer(r, nil, "")
	actualHeaders := w.Header()

	headerKey := "Merge-Me"
	values, ok := actualHeaders[headerKey]
	if !ok {
		t.Errorf("Downstream response does not contain expected %v header. Expected header should be added", headerKey)
	} else if len(values) < 2 && (values[0] != "Initial" || values[1] != replacer.Replace("{hostname}")) {
		t.Errorf("Values for header `+Merge-Me` should be merged. Got %v", values)
	}

	headerKey = "Add-Me"
	if _, ok := actualHeaders[headerKey]; !ok {
		t.Errorf("Downstream response does not contain expected %v header", headerKey)
	}

	headerKey = "Remove-Me"
	if _, ok := actualHeaders[headerKey]; ok {
		t.Errorf("Downstream response should not contain %v header received from upstream", headerKey)
	}

	headerKey = "Replace-Me"
	headerValue := replacer.Replace("{hostname}")
	value, ok := actualHeaders[headerKey]
	if !ok {
		t.Errorf("Downstream response should contain %v header and not remove it", headerKey)
	} else if len(value) > 0 && headerValue != value[0] {
		t.Errorf("Downstream response should have header %v with value %v. Instead value was %v", headerKey, headerValue, value)
	}

}
Example #13
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)
}