github.com/kjdelisle/consul@v1.4.5/agent/http.go (about)

     1  package agent
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"net/http"
     9  	"net/http/pprof"
    10  	"net/url"
    11  	"os"
    12  	"reflect"
    13  	"regexp"
    14  	"strconv"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/NYTimes/gziphandler"
    19  	metrics "github.com/armon/go-metrics"
    20  	"github.com/hashicorp/consul/acl"
    21  	"github.com/hashicorp/consul/agent/cache"
    22  	"github.com/hashicorp/consul/agent/consul"
    23  	"github.com/hashicorp/consul/agent/structs"
    24  	"github.com/hashicorp/consul/api"
    25  	cleanhttp "github.com/hashicorp/go-cleanhttp"
    26  	"github.com/mitchellh/mapstructure"
    27  	"github.com/pkg/errors"
    28  )
    29  
    30  // MethodNotAllowedError should be returned by a handler when the HTTP method is not allowed.
    31  type MethodNotAllowedError struct {
    32  	Method string
    33  	Allow  []string
    34  }
    35  
    36  func (e MethodNotAllowedError) Error() string {
    37  	return fmt.Sprintf("method %s not allowed", e.Method)
    38  }
    39  
    40  // BadRequestError should be returned by a handler when parameters or the payload are not valid
    41  type BadRequestError struct {
    42  	Reason string
    43  }
    44  
    45  func (e BadRequestError) Error() string {
    46  	return fmt.Sprintf("Bad request: %s", e.Reason)
    47  }
    48  
    49  // CodeWithPayloadError allow returning non HTTP 200
    50  // Error codes while not returning PlainText payload
    51  type CodeWithPayloadError struct {
    52  	Reason      string
    53  	StatusCode  int
    54  	ContentType string
    55  }
    56  
    57  func (e CodeWithPayloadError) Error() string {
    58  	return e.Reason
    59  }
    60  
    61  type ForbiddenError struct {
    62  }
    63  
    64  func (e ForbiddenError) Error() string {
    65  	return "Access is restricted"
    66  }
    67  
    68  // HTTPServer provides an HTTP api for an agent.
    69  type HTTPServer struct {
    70  	*http.Server
    71  	ln        net.Listener
    72  	agent     *Agent
    73  	blacklist *Blacklist
    74  
    75  	// proto is filled by the agent to "http" or "https".
    76  	proto string
    77  }
    78  
    79  type redirectFS struct {
    80  	fs http.FileSystem
    81  }
    82  
    83  func (fs *redirectFS) Open(name string) (http.File, error) {
    84  	file, err := fs.fs.Open(name)
    85  	if err != nil {
    86  		file, err = fs.fs.Open("/index.html")
    87  	}
    88  	return file, err
    89  }
    90  
    91  // endpoint is a Consul-specific HTTP handler that takes the usual arguments in
    92  // but returns a response object and error, both of which are handled in a
    93  // common manner by Consul's HTTP server.
    94  type endpoint func(resp http.ResponseWriter, req *http.Request) (interface{}, error)
    95  
    96  // unboundEndpoint is an endpoint method on a server.
    97  type unboundEndpoint func(s *HTTPServer, resp http.ResponseWriter, req *http.Request) (interface{}, error)
    98  
    99  // endpoints is a map from URL pattern to unbound endpoint.
   100  var endpoints map[string]unboundEndpoint
   101  
   102  // allowedMethods is a map from endpoint prefix to supported HTTP methods.
   103  // An empty slice means an endpoint handles OPTIONS requests and MethodNotFound errors itself.
   104  var allowedMethods map[string][]string
   105  
   106  // registerEndpoint registers a new endpoint, which should be done at package
   107  // init() time.
   108  func registerEndpoint(pattern string, methods []string, fn unboundEndpoint) {
   109  	if endpoints == nil {
   110  		endpoints = make(map[string]unboundEndpoint)
   111  	}
   112  	if endpoints[pattern] != nil || allowedMethods[pattern] != nil {
   113  		panic(fmt.Errorf("Pattern %q is already registered", pattern))
   114  	}
   115  
   116  	endpoints[pattern] = fn
   117  	allowedMethods[pattern] = methods
   118  }
   119  
   120  // wrappedMux hangs on to the underlying mux for unit tests.
   121  type wrappedMux struct {
   122  	mux     *http.ServeMux
   123  	handler http.Handler
   124  }
   125  
   126  // ServeHTTP implements the http.Handler interface.
   127  func (w *wrappedMux) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
   128  	w.handler.ServeHTTP(resp, req)
   129  }
   130  
   131  // handler is used to attach our handlers to the mux
   132  func (s *HTTPServer) handler(enableDebug bool) http.Handler {
   133  	mux := http.NewServeMux()
   134  
   135  	// handleFuncMetrics takes the given pattern and handler and wraps to produce
   136  	// metrics based on the pattern and request.
   137  	handleFuncMetrics := func(pattern string, handler http.HandlerFunc) {
   138  		// Get the parts of the pattern. We omit any initial empty for the
   139  		// leading slash, and put an underscore as a "thing" placeholder if we
   140  		// see a trailing slash, which means the part after is parsed. This lets
   141  		// us distinguish from things like /v1/query and /v1/query/<query id>.
   142  		var parts []string
   143  		for i, part := range strings.Split(pattern, "/") {
   144  			if part == "" {
   145  				if i == 0 {
   146  					continue
   147  				}
   148  				part = "_"
   149  			}
   150  			parts = append(parts, part)
   151  		}
   152  
   153  		// Register the wrapper, which will close over the expensive-to-compute
   154  		// parts from above.
   155  		// TODO (kyhavlov): Convert this to utilize metric labels in a major release
   156  		wrapper := func(resp http.ResponseWriter, req *http.Request) {
   157  			start := time.Now()
   158  			handler(resp, req)
   159  			key := append([]string{"http", req.Method}, parts...)
   160  			metrics.MeasureSince(key, start)
   161  		}
   162  
   163  		gzipWrapper, _ := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(0))
   164  		gzipHandler := gzipWrapper(http.HandlerFunc(wrapper))
   165  		mux.Handle(pattern, gzipHandler)
   166  	}
   167  
   168  	// handlePProf takes the given pattern and pprof handler
   169  	// and wraps it to add authorization and metrics
   170  	handlePProf := func(pattern string, handler http.HandlerFunc) {
   171  		wrapper := func(resp http.ResponseWriter, req *http.Request) {
   172  			var token string
   173  			s.parseToken(req, &token)
   174  
   175  			rule, err := s.agent.resolveToken(token)
   176  			if err != nil {
   177  				resp.WriteHeader(http.StatusForbidden)
   178  				return
   179  			}
   180  
   181  			// If enableDebug is not set, and ACLs are disabled, write
   182  			// an unauthorized response
   183  			if !enableDebug {
   184  				if s.checkACLDisabled(resp, req) {
   185  					return
   186  				}
   187  			}
   188  
   189  			// If the token provided does not have the necessary permissions,
   190  			// write a forbidden response
   191  			if rule != nil && !rule.OperatorRead() {
   192  				resp.WriteHeader(http.StatusForbidden)
   193  				return
   194  			}
   195  
   196  			// Call the pprof handler
   197  			handler(resp, req)
   198  		}
   199  
   200  		handleFuncMetrics(pattern, http.HandlerFunc(wrapper))
   201  	}
   202  
   203  	mux.HandleFunc("/", s.Index)
   204  	for pattern, fn := range endpoints {
   205  		thisFn := fn
   206  		methods, _ := allowedMethods[pattern]
   207  		bound := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   208  			return thisFn(s, resp, req)
   209  		}
   210  		handleFuncMetrics(pattern, s.wrap(bound, methods))
   211  	}
   212  
   213  	// Register wrapped pprof handlers
   214  	handlePProf("/debug/pprof/", pprof.Index)
   215  	handlePProf("/debug/pprof/cmdline", pprof.Cmdline)
   216  	handlePProf("/debug/pprof/profile", pprof.Profile)
   217  	handlePProf("/debug/pprof/symbol", pprof.Symbol)
   218  	handlePProf("/debug/pprof/trace", pprof.Trace)
   219  
   220  	if s.IsUIEnabled() {
   221  		legacy_ui, err := strconv.ParseBool(os.Getenv("CONSUL_UI_LEGACY"))
   222  		if err != nil {
   223  			legacy_ui = false
   224  		}
   225  		var uifs http.FileSystem
   226  
   227  		// Use the custom UI dir if provided.
   228  		if s.agent.config.UIDir != "" {
   229  			uifs = http.Dir(s.agent.config.UIDir)
   230  		} else {
   231  			fs := assetFS()
   232  
   233  			if legacy_ui {
   234  				fs.Prefix += "/v1/"
   235  			} else {
   236  				fs.Prefix += "/v2/"
   237  			}
   238  			uifs = fs
   239  		}
   240  
   241  		if !legacy_ui {
   242  			uifs = &redirectFS{fs: uifs}
   243  		}
   244  
   245  		mux.Handle("/robots.txt", http.FileServer(uifs))
   246  		mux.Handle("/ui/", http.StripPrefix("/ui/", http.FileServer(uifs)))
   247  	}
   248  
   249  	// Wrap the whole mux with a handler that bans URLs with non-printable
   250  	// characters, unless disabled explicitly to deal with old keys that fail this
   251  	// check.
   252  	h := cleanhttp.PrintablePathCheckHandler(mux, nil)
   253  	if s.agent.config.DisableHTTPUnprintableCharFilter {
   254  		h = mux
   255  	}
   256  	return &wrappedMux{
   257  		mux:     mux,
   258  		handler: h,
   259  	}
   260  }
   261  
   262  // nodeName returns the node name of the agent
   263  func (s *HTTPServer) nodeName() string {
   264  	return s.agent.config.NodeName
   265  }
   266  
   267  // aclEndpointRE is used to find old ACL endpoints that take tokens in the URL
   268  // so that we can redact them. The ACL endpoints that take the token in the URL
   269  // are all of the form /v1/acl/<verb>/<token>, and can optionally include query
   270  // parameters which are indicated by a question mark. We capture the part before
   271  // the token, the token, and any query parameters after, and then reassemble as
   272  // $1<hidden>$3 (the token in $2 isn't used), which will give:
   273  //
   274  // /v1/acl/clone/foo           -> /v1/acl/clone/<hidden>
   275  // /v1/acl/clone/foo?token=bar -> /v1/acl/clone/<hidden>?token=<hidden>
   276  //
   277  // The query parameter in the example above is obfuscated like any other, after
   278  // this regular expression is applied, so the regular expression substitution
   279  // results in:
   280  //
   281  // /v1/acl/clone/foo?token=bar -> /v1/acl/clone/<hidden>?token=bar
   282  //                                ^---- $1 ----^^- $2 -^^-- $3 --^
   283  //
   284  // And then the loop that looks for parameters called "token" does the last
   285  // step to get to the final redacted form.
   286  var (
   287  	aclEndpointRE = regexp.MustCompile("^(/v1/acl/(create|update|destroy|info|clone|list)/)([^?]+)([?]?.*)$")
   288  )
   289  
   290  // wrap is used to wrap functions to make them more convenient
   291  func (s *HTTPServer) wrap(handler endpoint, methods []string) http.HandlerFunc {
   292  	return func(resp http.ResponseWriter, req *http.Request) {
   293  		setHeaders(resp, s.agent.config.HTTPResponseHeaders)
   294  		setTranslateAddr(resp, s.agent.config.TranslateWANAddrs)
   295  
   296  		// Obfuscate any tokens from appearing in the logs
   297  		formVals, err := url.ParseQuery(req.URL.RawQuery)
   298  		if err != nil {
   299  			s.agent.logger.Printf("[ERR] http: Failed to decode query: %s from=%s", err, req.RemoteAddr)
   300  			resp.WriteHeader(http.StatusInternalServerError)
   301  			return
   302  		}
   303  		logURL := req.URL.String()
   304  		if tokens, ok := formVals["token"]; ok {
   305  			for _, token := range tokens {
   306  				if token == "" {
   307  					logURL += "<hidden>"
   308  					continue
   309  				}
   310  				logURL = strings.Replace(logURL, token, "<hidden>", -1)
   311  			}
   312  		}
   313  		logURL = aclEndpointRE.ReplaceAllString(logURL, "$1<hidden>$4")
   314  
   315  		if s.blacklist.Block(req.URL.Path) {
   316  			errMsg := "Endpoint is blocked by agent configuration"
   317  			s.agent.logger.Printf("[ERR] http: Request %s %v, error: %v from=%s", req.Method, logURL, err, req.RemoteAddr)
   318  			resp.WriteHeader(http.StatusForbidden)
   319  			fmt.Fprint(resp, errMsg)
   320  			return
   321  		}
   322  
   323  		isForbidden := func(err error) bool {
   324  			if acl.IsErrPermissionDenied(err) || acl.IsErrNotFound(err) {
   325  				return true
   326  			}
   327  			_, ok := err.(ForbiddenError)
   328  			return ok
   329  		}
   330  
   331  		isMethodNotAllowed := func(err error) bool {
   332  			_, ok := err.(MethodNotAllowedError)
   333  			return ok
   334  		}
   335  
   336  		isBadRequest := func(err error) bool {
   337  			_, ok := err.(BadRequestError)
   338  			return ok
   339  		}
   340  
   341  		isTooManyRequests := func(err error) bool {
   342  			// Sadness net/rpc can't do nice typed errors so this is all we got
   343  			return err.Error() == consul.ErrRateLimited.Error()
   344  		}
   345  
   346  		addAllowHeader := func(methods []string) {
   347  			resp.Header().Add("Allow", strings.Join(methods, ","))
   348  		}
   349  
   350  		handleErr := func(err error) {
   351  			s.agent.logger.Printf("[ERR] http: Request %s %v, error: %v from=%s", req.Method, logURL, err, req.RemoteAddr)
   352  			switch {
   353  			case isForbidden(err):
   354  				resp.WriteHeader(http.StatusForbidden)
   355  				fmt.Fprint(resp, err.Error())
   356  			case structs.IsErrRPCRateExceeded(err):
   357  				resp.WriteHeader(http.StatusTooManyRequests)
   358  			case isMethodNotAllowed(err):
   359  				// RFC2616 states that for 405 Method Not Allowed the response
   360  				// MUST include an Allow header containing the list of valid
   361  				// methods for the requested resource.
   362  				// https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
   363  				addAllowHeader(err.(MethodNotAllowedError).Allow)
   364  				resp.WriteHeader(http.StatusMethodNotAllowed) // 405
   365  				fmt.Fprint(resp, err.Error())
   366  			case isBadRequest(err):
   367  				resp.WriteHeader(http.StatusBadRequest)
   368  				fmt.Fprint(resp, err.Error())
   369  			case isTooManyRequests(err):
   370  				resp.WriteHeader(http.StatusTooManyRequests)
   371  				fmt.Fprint(resp, err.Error())
   372  			default:
   373  				resp.WriteHeader(http.StatusInternalServerError)
   374  				fmt.Fprint(resp, err.Error())
   375  			}
   376  		}
   377  
   378  		start := time.Now()
   379  		defer func() {
   380  			s.agent.logger.Printf("[DEBUG] http: Request %s %v (%v) from=%s", req.Method, logURL, time.Since(start), req.RemoteAddr)
   381  		}()
   382  
   383  		var obj interface{}
   384  
   385  		// if this endpoint has declared methods, respond appropriately to OPTIONS requests. Otherwise let the endpoint handle that.
   386  		if req.Method == "OPTIONS" && len(methods) > 0 {
   387  			addAllowHeader(append([]string{"OPTIONS"}, methods...))
   388  			return
   389  		}
   390  
   391  		// if this endpoint has declared methods, check the request method. Otherwise let the endpoint handle that.
   392  		methodFound := len(methods) == 0
   393  		for _, method := range methods {
   394  			if method == req.Method {
   395  				methodFound = true
   396  				break
   397  			}
   398  		}
   399  
   400  		if !methodFound {
   401  			err = MethodNotAllowedError{req.Method, append([]string{"OPTIONS"}, methods...)}
   402  		} else {
   403  			err = s.checkWriteAccess(req)
   404  
   405  			if err == nil {
   406  				// Invoke the handler
   407  				obj, err = handler(resp, req)
   408  			}
   409  		}
   410  		contentType := "application/json"
   411  		httpCode := http.StatusOK
   412  		if err != nil {
   413  			if errPayload, ok := err.(CodeWithPayloadError); ok {
   414  				httpCode = errPayload.StatusCode
   415  				if errPayload.ContentType != "" {
   416  					contentType = errPayload.ContentType
   417  				}
   418  				if errPayload.Reason != "" {
   419  					resp.Header().Add("X-Consul-Reason", errPayload.Reason)
   420  				}
   421  			} else {
   422  				handleErr(err)
   423  				return
   424  			}
   425  		}
   426  		if obj == nil {
   427  			return
   428  		}
   429  		var buf []byte
   430  		if contentType == "application/json" {
   431  			buf, err = s.marshalJSON(req, obj)
   432  			if err != nil {
   433  				handleErr(err)
   434  				return
   435  			}
   436  		} else {
   437  			if strings.HasPrefix(contentType, "text/") {
   438  				if val, ok := obj.(string); ok {
   439  					buf = []byte(val)
   440  				}
   441  			}
   442  		}
   443  		resp.Header().Set("Content-Type", contentType)
   444  		resp.WriteHeader(httpCode)
   445  		resp.Write(buf)
   446  	}
   447  }
   448  
   449  // marshalJSON marshals the object into JSON, respecting the user's pretty-ness
   450  // configuration.
   451  func (s *HTTPServer) marshalJSON(req *http.Request, obj interface{}) ([]byte, error) {
   452  	if _, ok := req.URL.Query()["pretty"]; ok || s.agent.config.DevMode {
   453  		buf, err := json.MarshalIndent(obj, "", "    ")
   454  		if err != nil {
   455  			return nil, err
   456  		}
   457  		buf = append(buf, "\n"...)
   458  		return buf, nil
   459  	}
   460  
   461  	buf, err := json.Marshal(obj)
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  	return buf, err
   466  }
   467  
   468  // Returns true if the UI is enabled.
   469  func (s *HTTPServer) IsUIEnabled() bool {
   470  	return s.agent.config.UIDir != "" || s.agent.config.EnableUI
   471  }
   472  
   473  // Renders a simple index page
   474  func (s *HTTPServer) Index(resp http.ResponseWriter, req *http.Request) {
   475  	// Check if this is a non-index path
   476  	if req.URL.Path != "/" {
   477  		resp.WriteHeader(http.StatusNotFound)
   478  		return
   479  	}
   480  
   481  	// Give them something helpful if there's no UI so they at least know
   482  	// what this server is.
   483  	if !s.IsUIEnabled() {
   484  		fmt.Fprint(resp, "Consul Agent")
   485  		return
   486  	}
   487  
   488  	// Redirect to the UI endpoint
   489  	http.Redirect(resp, req, "/ui/", http.StatusMovedPermanently) // 301
   490  }
   491  
   492  // decodeBody is used to decode a JSON request body
   493  func decodeBody(req *http.Request, out interface{}, cb func(interface{}) error) error {
   494  	// This generally only happens in tests since real HTTP requests set
   495  	// a non-nil body with no content. We guard against it anyways to prevent
   496  	// a panic. The EOF response is the same behavior as an empty reader.
   497  	if req.Body == nil {
   498  		return io.EOF
   499  	}
   500  
   501  	var raw interface{}
   502  	dec := json.NewDecoder(req.Body)
   503  	if err := dec.Decode(&raw); err != nil {
   504  		return err
   505  	}
   506  
   507  	// Invoke the callback prior to decode
   508  	if cb != nil {
   509  		if err := cb(raw); err != nil {
   510  			return err
   511  		}
   512  	}
   513  
   514  	decodeConf := &mapstructure.DecoderConfig{
   515  		DecodeHook: mapstructure.ComposeDecodeHookFunc(
   516  			mapstructure.StringToTimeDurationHookFunc(),
   517  			stringToReadableDurationFunc(),
   518  		),
   519  		Result: &out,
   520  	}
   521  
   522  	decoder, err := mapstructure.NewDecoder(decodeConf)
   523  	if err != nil {
   524  		return err
   525  	}
   526  
   527  	return decoder.Decode(raw)
   528  }
   529  
   530  // stringToReadableDurationFunc is a mapstructure hook for decoding a string
   531  // into an api.ReadableDuration for backwards compatibility.
   532  func stringToReadableDurationFunc() mapstructure.DecodeHookFunc {
   533  	return func(
   534  		f reflect.Type,
   535  		t reflect.Type,
   536  		data interface{}) (interface{}, error) {
   537  		var v api.ReadableDuration
   538  		if t != reflect.TypeOf(v) {
   539  			return data, nil
   540  		}
   541  
   542  		switch {
   543  		case f.Kind() == reflect.String:
   544  			if dur, err := time.ParseDuration(data.(string)); err != nil {
   545  				return nil, err
   546  			} else {
   547  				v = api.ReadableDuration(dur)
   548  			}
   549  			return v, nil
   550  		default:
   551  			return data, nil
   552  		}
   553  	}
   554  }
   555  
   556  // setTranslateAddr is used to set the address translation header. This is only
   557  // present if the feature is active.
   558  func setTranslateAddr(resp http.ResponseWriter, active bool) {
   559  	if active {
   560  		resp.Header().Set("X-Consul-Translate-Addresses", "true")
   561  	}
   562  }
   563  
   564  // setIndex is used to set the index response header
   565  func setIndex(resp http.ResponseWriter, index uint64) {
   566  	// If we ever return X-Consul-Index of 0 blocking clients will go into a busy
   567  	// loop and hammer us since ?index=0 will never block. It's always safe to
   568  	// return index=1 since the very first Raft write is always an internal one
   569  	// writing the raft config for the cluster so no user-facing blocking query
   570  	// will ever legitimately have an X-Consul-Index of 1.
   571  	if index == 0 {
   572  		index = 1
   573  	}
   574  	resp.Header().Set("X-Consul-Index", strconv.FormatUint(index, 10))
   575  }
   576  
   577  // setKnownLeader is used to set the known leader header
   578  func setKnownLeader(resp http.ResponseWriter, known bool) {
   579  	s := "true"
   580  	if !known {
   581  		s = "false"
   582  	}
   583  	resp.Header().Set("X-Consul-KnownLeader", s)
   584  }
   585  
   586  func setConsistency(resp http.ResponseWriter, consistency string) {
   587  	if consistency != "" {
   588  		resp.Header().Set("X-Consul-Effective-Consistency", consistency)
   589  	}
   590  }
   591  
   592  // setLastContact is used to set the last contact header
   593  func setLastContact(resp http.ResponseWriter, last time.Duration) {
   594  	if last < 0 {
   595  		last = 0
   596  	}
   597  	lastMsec := uint64(last / time.Millisecond)
   598  	resp.Header().Set("X-Consul-LastContact", strconv.FormatUint(lastMsec, 10))
   599  }
   600  
   601  // setMeta is used to set the query response meta data
   602  func setMeta(resp http.ResponseWriter, m *structs.QueryMeta) {
   603  	setIndex(resp, m.Index)
   604  	setLastContact(resp, m.LastContact)
   605  	setKnownLeader(resp, m.KnownLeader)
   606  	setConsistency(resp, m.ConsistencyLevel)
   607  }
   608  
   609  // setCacheMeta sets http response headers to indicate cache status.
   610  func setCacheMeta(resp http.ResponseWriter, m *cache.ResultMeta) {
   611  	if m == nil {
   612  		return
   613  	}
   614  	str := "MISS"
   615  	if m.Hit {
   616  		str = "HIT"
   617  	}
   618  	resp.Header().Set("X-Cache", str)
   619  	if m.Hit {
   620  		resp.Header().Set("Age", fmt.Sprintf("%.0f", m.Age.Seconds()))
   621  	}
   622  }
   623  
   624  // setHeaders is used to set canonical response header fields
   625  func setHeaders(resp http.ResponseWriter, headers map[string]string) {
   626  	for field, value := range headers {
   627  		resp.Header().Set(http.CanonicalHeaderKey(field), value)
   628  	}
   629  }
   630  
   631  // parseWait is used to parse the ?wait and ?index query params
   632  // Returns true on error
   633  func parseWait(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool {
   634  	query := req.URL.Query()
   635  	if wait := query.Get("wait"); wait != "" {
   636  		dur, err := time.ParseDuration(wait)
   637  		if err != nil {
   638  			resp.WriteHeader(http.StatusBadRequest)
   639  			fmt.Fprint(resp, "Invalid wait time")
   640  			return true
   641  		}
   642  		b.MaxQueryTime = dur
   643  	}
   644  	if idx := query.Get("index"); idx != "" {
   645  		index, err := strconv.ParseUint(idx, 10, 64)
   646  		if err != nil {
   647  			resp.WriteHeader(http.StatusBadRequest)
   648  			fmt.Fprint(resp, "Invalid index")
   649  			return true
   650  		}
   651  		b.MinQueryIndex = index
   652  	}
   653  	return false
   654  }
   655  
   656  // parseCacheControl parses the CacheControl HTTP header value. So far we only
   657  // support maxage directive.
   658  func parseCacheControl(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool {
   659  	raw := strings.ToLower(req.Header.Get("Cache-Control"))
   660  
   661  	if raw == "" {
   662  		return false
   663  	}
   664  
   665  	// Didn't want to import a full parser for this. While quoted strings are
   666  	// allowed in some directives, max-age does not allow them per
   667  	// https://tools.ietf.org/html/rfc7234#section-5.2.2.8 so we assume all
   668  	// well-behaved clients use the exact token form of max-age=<delta-seconds>
   669  	// where delta-seconds is a non-negative decimal integer.
   670  	directives := strings.Split(raw, ",")
   671  
   672  	parseDurationOrFail := func(raw string) (time.Duration, bool) {
   673  		i, err := strconv.Atoi(raw)
   674  		if err != nil {
   675  			resp.WriteHeader(http.StatusBadRequest)
   676  			fmt.Fprint(resp, "Invalid Cache-Control header.")
   677  			return 0, true
   678  		}
   679  		return time.Duration(i) * time.Second, false
   680  	}
   681  
   682  	for _, d := range directives {
   683  		d = strings.ToLower(strings.TrimSpace(d))
   684  
   685  		if d == "must-revalidate" {
   686  			b.MustRevalidate = true
   687  		}
   688  
   689  		if strings.HasPrefix(d, "max-age=") {
   690  			d, failed := parseDurationOrFail(d[8:])
   691  			if failed {
   692  				return true
   693  			}
   694  			b.MaxAge = d
   695  			if d == 0 {
   696  				// max-age=0 specifically means that we need to consider the cache stale
   697  				// immediately however MaxAge = 0 is indistinguishable from the default
   698  				// where MaxAge is unset.
   699  				b.MustRevalidate = true
   700  			}
   701  		}
   702  		if strings.HasPrefix(d, "stale-if-error=") {
   703  			d, failed := parseDurationOrFail(d[15:])
   704  			if failed {
   705  				return true
   706  			}
   707  			b.StaleIfError = d
   708  		}
   709  	}
   710  
   711  	return false
   712  }
   713  
   714  // parseConsistency is used to parse the ?stale and ?consistent query params.
   715  // Returns true on error
   716  func (s *HTTPServer) parseConsistency(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool {
   717  	query := req.URL.Query()
   718  	defaults := true
   719  	if _, ok := query["stale"]; ok {
   720  		b.AllowStale = true
   721  		defaults = false
   722  	}
   723  	if _, ok := query["consistent"]; ok {
   724  		b.RequireConsistent = true
   725  		defaults = false
   726  	}
   727  	if _, ok := query["leader"]; ok {
   728  		defaults = false
   729  	}
   730  	if _, ok := query["cached"]; ok {
   731  		b.UseCache = true
   732  		defaults = false
   733  	}
   734  	if maxStale := query.Get("max_stale"); maxStale != "" {
   735  		dur, err := time.ParseDuration(maxStale)
   736  		if err != nil {
   737  			resp.WriteHeader(http.StatusBadRequest)
   738  			fmt.Fprintf(resp, "Invalid max_stale value %q", maxStale)
   739  			return true
   740  		}
   741  		b.MaxStaleDuration = dur
   742  		if dur.Nanoseconds() > 0 {
   743  			b.AllowStale = true
   744  			defaults = false
   745  		}
   746  	}
   747  	// No specific Consistency has been specified by caller
   748  	if defaults {
   749  		path := req.URL.Path
   750  		if strings.HasPrefix(path, "/v1/catalog") || strings.HasPrefix(path, "/v1/health") {
   751  			if s.agent.config.DiscoveryMaxStale.Nanoseconds() > 0 {
   752  				b.MaxStaleDuration = s.agent.config.DiscoveryMaxStale
   753  				b.AllowStale = true
   754  			}
   755  		}
   756  	}
   757  	if b.AllowStale && b.RequireConsistent {
   758  		resp.WriteHeader(http.StatusBadRequest)
   759  		fmt.Fprint(resp, "Cannot specify ?stale with ?consistent, conflicting semantics.")
   760  		return true
   761  	}
   762  	if b.UseCache && b.RequireConsistent {
   763  		resp.WriteHeader(http.StatusBadRequest)
   764  		fmt.Fprint(resp, "Cannot specify ?cached with ?consistent, conflicting semantics.")
   765  		return true
   766  	}
   767  	return false
   768  }
   769  
   770  // parseDC is used to parse the ?dc query param
   771  func (s *HTTPServer) parseDC(req *http.Request, dc *string) {
   772  	if other := req.URL.Query().Get("dc"); other != "" {
   773  		*dc = other
   774  	} else if *dc == "" {
   775  		*dc = s.agent.config.Datacenter
   776  	}
   777  }
   778  
   779  // parseTokenInternal is used to parse the ?token query param or the X-Consul-Token header or
   780  // Authorization Bearer token (RFC6750) and
   781  // optionally resolve proxy tokens to real ACL tokens. If the token is invalid or not specified it will populate
   782  // the token with the agents UserToken (acl_token in the consul configuration)
   783  // Parsing has the following priority: ?token, X-Consul-Token and last "Authorization: Bearer "
   784  func (s *HTTPServer) parseTokenInternal(req *http.Request, token *string, resolveProxyToken bool) {
   785  	tok := ""
   786  	if other := req.URL.Query().Get("token"); other != "" {
   787  		tok = other
   788  	} else if other := req.Header.Get("X-Consul-Token"); other != "" {
   789  		tok = other
   790  	} else if other := req.Header.Get("Authorization"); other != "" {
   791  		// HTTP Authorization headers are in the format: <Scheme>[SPACE]<Value>
   792  		// Ref. https://tools.ietf.org/html/rfc7236#section-3
   793  		parts := strings.Split(other, " ")
   794  
   795  		// Authorization Header is invalid if containing 1 or 0 parts, e.g.:
   796  		// "" || "<Scheme><Value>" || "<Scheme>" || "<Value>"
   797  		if len(parts) > 1 {
   798  			scheme := parts[0]
   799  			// Everything after "<Scheme>" is "<Value>", trimmed
   800  			value := strings.TrimSpace(strings.Join(parts[1:], " "))
   801  
   802  			// <Scheme> must be "Bearer"
   803  			if scheme == "Bearer" {
   804  				// Since Bearer tokens shouldnt contain spaces (rfc6750#section-2.1)
   805  				// "value" is tokenized, only the first item is used
   806  				tok = strings.TrimSpace(strings.Split(value, " ")[0])
   807  			}
   808  		}
   809  	}
   810  
   811  	if tok != "" {
   812  		if resolveProxyToken {
   813  			if p := s.agent.resolveProxyToken(tok); p != nil {
   814  				*token = s.agent.State.ServiceToken(p.Proxy.TargetServiceID)
   815  				return
   816  			}
   817  		}
   818  
   819  		*token = tok
   820  		return
   821  	}
   822  
   823  	*token = s.agent.tokens.UserToken()
   824  }
   825  
   826  // parseToken is used to parse the ?token query param or the X-Consul-Token header or
   827  // Authorization Bearer token header (RFC6750) and resolve proxy tokens to real ACL tokens
   828  func (s *HTTPServer) parseToken(req *http.Request, token *string) {
   829  	s.parseTokenInternal(req, token, true)
   830  }
   831  
   832  // parseTokenWithoutResolvingProxyToken is used to parse the ?token query param or the X-Consul-Token header
   833  // or Authorization Bearer header token (RFC6750) and
   834  func (s *HTTPServer) parseTokenWithoutResolvingProxyToken(req *http.Request, token *string) {
   835  	s.parseTokenInternal(req, token, false)
   836  }
   837  
   838  func sourceAddrFromRequest(req *http.Request) string {
   839  	xff := req.Header.Get("X-Forwarded-For")
   840  	forwardHosts := strings.Split(xff, ",")
   841  	if len(forwardHosts) > 0 {
   842  		forwardIp := net.ParseIP(strings.TrimSpace(forwardHosts[0]))
   843  		if forwardIp != nil {
   844  			return forwardIp.String()
   845  		}
   846  	}
   847  
   848  	host, _, err := net.SplitHostPort(req.RemoteAddr)
   849  	if err != nil {
   850  		return ""
   851  	}
   852  
   853  	ip := net.ParseIP(host)
   854  	if ip != nil {
   855  		return ip.String()
   856  	} else {
   857  		return ""
   858  	}
   859  }
   860  
   861  // parseSource is used to parse the ?near=<node> query parameter, used for
   862  // sorting by RTT based on a source node. We set the source's DC to the target
   863  // DC in the request, if given, or else the agent's DC.
   864  func (s *HTTPServer) parseSource(req *http.Request, source *structs.QuerySource) {
   865  	s.parseDC(req, &source.Datacenter)
   866  	source.Ip = sourceAddrFromRequest(req)
   867  	if node := req.URL.Query().Get("near"); node != "" {
   868  		if node == "_agent" {
   869  			source.Node = s.agent.config.NodeName
   870  		} else {
   871  			source.Node = node
   872  		}
   873  	}
   874  }
   875  
   876  // parseMetaFilter is used to parse the ?node-meta=key:value query parameter, used for
   877  // filtering results to nodes with the given metadata key/value
   878  func (s *HTTPServer) parseMetaFilter(req *http.Request) map[string]string {
   879  	if filterList, ok := req.URL.Query()["node-meta"]; ok {
   880  		filters := make(map[string]string)
   881  		for _, filter := range filterList {
   882  			key, value := ParseMetaPair(filter)
   883  			filters[key] = value
   884  		}
   885  		return filters
   886  	}
   887  	return nil
   888  }
   889  
   890  // parseInternal is a convenience method for endpoints that need
   891  // to use both parseWait and parseDC.
   892  func (s *HTTPServer) parseInternal(resp http.ResponseWriter, req *http.Request, dc *string, b *structs.QueryOptions, resolveProxyToken bool) bool {
   893  	s.parseDC(req, dc)
   894  	s.parseTokenInternal(req, &b.Token, resolveProxyToken)
   895  	if s.parseConsistency(resp, req, b) {
   896  		return true
   897  	}
   898  	if parseCacheControl(resp, req, b) {
   899  		return true
   900  	}
   901  	return parseWait(resp, req, b)
   902  }
   903  
   904  // parse is a convenience method for endpoints that need
   905  // to use both parseWait and parseDC.
   906  func (s *HTTPServer) parse(resp http.ResponseWriter, req *http.Request, dc *string, b *structs.QueryOptions) bool {
   907  	return s.parseInternal(resp, req, dc, b, true)
   908  }
   909  
   910  // parseWithoutResolvingProxyToken is a convenience method similar to parse except that it disables resolving proxy tokens
   911  func (s *HTTPServer) parseWithoutResolvingProxyToken(resp http.ResponseWriter, req *http.Request, dc *string, b *structs.QueryOptions) bool {
   912  	return s.parseInternal(resp, req, dc, b, false)
   913  }
   914  
   915  func (s *HTTPServer) checkWriteAccess(req *http.Request) error {
   916  	if req.Method == http.MethodGet || req.Method == http.MethodHead || req.Method == http.MethodOptions {
   917  		return nil
   918  	}
   919  
   920  	allowed := s.agent.config.AllowWriteHTTPFrom
   921  	if len(allowed) == 0 {
   922  		return nil
   923  	}
   924  
   925  	ipStr, _, err := net.SplitHostPort(req.RemoteAddr)
   926  	if err != nil {
   927  		return errors.Wrap(err, "unable to parse remote addr")
   928  	}
   929  
   930  	ip := net.ParseIP(ipStr)
   931  
   932  	for _, n := range allowed {
   933  		if n.Contains(ip) {
   934  			return nil
   935  		}
   936  	}
   937  
   938  	return ForbiddenError{}
   939  }