func (r *request) setQueryOptions(q *QueryOptions) {
	if q == nil {
		return
	}
	if q.Namespace != "" {
		r.params.Set("ns", q.Namespace)
	}
	if q.Datacenter != "" {
		r.params.Set("dc", q.Datacenter)
	}
	if q.AllowStale {
		r.params.Set("stale", "")
	}
	if q.RequireConsistent {
		r.params.Set("consistent", "")
	}
	if q.WaitIndex != 0 {
		r.params.Set("index", strconv.FormatUint(q.WaitIndex, 10))
	}
	if q.WaitTime != 0 {
		r.params.Set("wait", durToMsec(q.WaitTime))
	}
	if q.WaitHash != "" {
		r.params.Set("hash", q.WaitHash)
	}
	if q.Token != "" {
		r.header.Set("X-Consul-Token", q.Token)
	}
	if q.Near != "" {
		r.params.Set("near", q.Near)
	}
	if q.Filter != "" {
		r.params.Set("filter", q.Filter)
	}
	if len(q.NodeMeta) > 0 {
		for key, value := range q.NodeMeta {
			r.params.Add("node-meta", key+":"+value)
		}
	}
	if q.RelayFactor != 0 {
		r.params.Set("relay-factor", strconv.Itoa(int(q.RelayFactor)))
	}
	if q.LocalOnly {
		r.params.Set("local-only", fmt.Sprintf("%t", q.LocalOnly))
	}
	if q.Connect {
		r.params.Set("connect", "true")
	}
	if q.UseCache && !q.RequireConsistent {
		r.params.Set("cached", "")
		cc := []string{}
		if q.MaxAge > 0 {
			cc = append(cc, fmt.Sprintf("max-age=%.0f", q.MaxAge.Seconds()))
		}
		if q.StaleIfError > 0 {
			cc = append(cc, fmt.Sprintf("stale-if-error=%.0f", q.StaleIfError.Seconds()))
		}
		if len(cc) > 0 {
			r.header.Set("Cache-Control", strings.Join(cc, ", "))
		}
	}
	r.ctx = q.ctx
}
setQueryOptions is used to annotate the request with
additional query options
package api
import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
	"github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/go-hclog"
	"github.com/hashicorp/go-rootcerts"
)
const (
	HTTPAddrEnvName = "CONSUL_HTTP_ADDR"
	HTTPTokenEnvName = "CONSUL_HTTP_TOKEN"
	HTTPTokenFileEnvName = "CONSUL_HTTP_TOKEN_FILE"
	HTTPAuthEnvName = "CONSUL_HTTP_AUTH"
	HTTPSSLEnvName = "CONSUL_HTTP_SSL"
	HTTPCAFile = "CONSUL_CACERT"
	HTTPCAPath = "CONSUL_CAPATH"
	HTTPClientCert = "CONSUL_CLIENT_CERT"
	HTTPClientKey = "CONSUL_CLIENT_KEY"
	HTTPTLSServerName = "CONSUL_TLS_SERVER_NAME"
	HTTPSSLVerifyEnvName = "CONSUL_HTTP_SSL_VERIFY"
	GRPCAddrEnvName = "CONSUL_GRPC_ADDR"
	HTTPNamespaceEnvName = "CONSUL_NAMESPACE"
)
type QueryOptions struct {
	Namespace string
	Datacenter string
	AllowStale bool
	RequireConsistent bool
	UseCache bool
	MaxAge time.Duration
	StaleIfError time.Duration
	WaitIndex uint64
	WaitHash string
	WaitTime time.Duration
	Token string
	Near string
	NodeMeta map[string]string
	RelayFactor uint8
	LocalOnly bool
	Connect bool
	ctx context.Context
	Filter string
}
func (o *QueryOptions) Context() context.Context {
	if o != nil && o.ctx != nil {
		return o.ctx
	}
	return context.Background()
}
func (o *QueryOptions) WithContext(ctx context.Context) *QueryOptions {
	o2 := new(QueryOptions)
	if o != nil {
		*o2 = *o
	}
	o2.ctx = ctx
	return o2
}
type WriteOptions struct {
	Namespace string
	Datacenter string
	Token string
	RelayFactor uint8
	ctx context.Context
}
func (o *WriteOptions) Context() context.Context {
	if o != nil && o.ctx != nil {
		return o.ctx
	}
	return context.Background()
}
func (o *WriteOptions) WithContext(ctx context.Context) *WriteOptions {
	o2 := new(WriteOptions)
	if o != nil {
		*o2 = *o
	}
	o2.ctx = ctx
	return o2
}
type QueryMeta struct {
	LastIndex uint64
	LastContentHash string
	LastContact time.Duration
	KnownLeader bool
	RequestTime time.Duration
	AddressTranslationEnabled bool
	CacheHit bool
	CacheAge time.Duration
}
type WriteMeta struct {
	RequestTime time.Duration
}
type HttpBasicAuth struct {
	Username string
	Password string
}
type Config struct {
	Address string
	Scheme string
	Datacenter string
	Transport *http.Transport
	HttpClient *http.Client
	HttpAuth *HttpBasicAuth
	WaitTime time.Duration
	Token string
	TokenFile string
	Namespace string
	TLSConfig TLSConfig
}
type TLSConfig struct {
	Address string
	CAFile string
	CAPath string
	CAPem []byte
	CertFile string
	CertPEM []byte
	KeyFile string
	KeyPEM []byte
	InsecureSkipVerify bool
}
func DefaultConfig() *Config {
	return defaultConfig(nil, cleanhttp.DefaultPooledTransport)
}
func DefaultConfigWithLogger(logger hclog.Logger) *Config {
	return defaultConfig(logger, cleanhttp.DefaultPooledTransport)
}
func DefaultNonPooledConfig() *Config {
	return defaultConfig(nil, cleanhttp.DefaultTransport)
}
func defaultConfig(logger hclog.Logger, transportFn func() *http.Transport) *Config {
	if logger == nil {
		logger = hclog.New(&hclog.LoggerOptions{
			Name: "consul-api",
		})
	}
	config := &Config{
		Address:   "127.0.0.1:8500",
		Scheme:    "http",
		Transport: transportFn(),
	}
	if addr := os.Getenv(HTTPAddrEnvName); addr != "" {
		config.Address = addr
	}
	if tokenFile := os.Getenv(HTTPTokenFileEnvName); tokenFile != "" {
		config.TokenFile = tokenFile
	}
	if token := os.Getenv(HTTPTokenEnvName); token != "" {
		config.Token = token
	}
	if auth := os.Getenv(HTTPAuthEnvName); auth != "" {
		var username, password string
		if strings.Contains(auth, ":") {
			split := strings.SplitN(auth, ":", 2)
			username = split[0]
			password = split[1]
		} else {
			username = auth
		}
		config.HttpAuth = &HttpBasicAuth{
			Username: username,
			Password: password,
		}
	}
	if ssl := os.Getenv(HTTPSSLEnvName); ssl != "" {
		enabled, err := strconv.ParseBool(ssl)
		if err != nil {
			logger.Warn(fmt.Sprintf("could not parse %s", HTTPSSLEnvName), "error", err)
		}
		if enabled {
			config.Scheme = "https"
		}
	}
	if v := os.Getenv(HTTPTLSServerName); v != "" {
		config.TLSConfig.Address = v
	}
	if v := os.Getenv(HTTPCAFile); v != "" {
		config.TLSConfig.CAFile = v
	}
	if v := os.Getenv(HTTPCAPath); v != "" {
		config.TLSConfig.CAPath = v
	}
	if v := os.Getenv(HTTPClientCert); v != "" {
		config.TLSConfig.CertFile = v
	}
	if v := os.Getenv(HTTPClientKey); v != "" {
		config.TLSConfig.KeyFile = v
	}
	if v := os.Getenv(HTTPSSLVerifyEnvName); v != "" {
		doVerify, err := strconv.ParseBool(v)
		if err != nil {
			logger.Warn(fmt.Sprintf("could not parse %s", HTTPSSLVerifyEnvName), "error", err)
		}
		if !doVerify {
			config.TLSConfig.InsecureSkipVerify = true
		}
	}
	if v := os.Getenv(HTTPNamespaceEnvName); v != "" {
		config.Namespace = v
	}
	return config
}
func SetupTLSConfig(tlsConfig *TLSConfig) (*tls.Config, error) {
	tlsClientConfig := &tls.Config{
		InsecureSkipVerify: tlsConfig.InsecureSkipVerify,
	}
	if tlsConfig.Address != "" {
		server := tlsConfig.Address
		hasPort := strings.LastIndex(server, ":") > strings.LastIndex(server, "]")
		if hasPort {
			var err error
			server, _, err = net.SplitHostPort(server)
			if err != nil {
				return nil, err
			}
		}
		tlsClientConfig.ServerName = server
	}
	if len(tlsConfig.CertPEM) != 0 && len(tlsConfig.KeyPEM) != 0 {
		tlsCert, err := tls.X509KeyPair(tlsConfig.CertPEM, tlsConfig.KeyPEM)
		if err != nil {
			return nil, err
		}
		tlsClientConfig.Certificates = []tls.Certificate{tlsCert}
	} else if len(tlsConfig.CertPEM) != 0 || len(tlsConfig.KeyPEM) != 0 {
		return nil, fmt.Errorf("both client cert and client key must be provided")
	}
	if tlsConfig.CertFile != "" && tlsConfig.KeyFile != "" {
		tlsCert, err := tls.LoadX509KeyPair(tlsConfig.CertFile, tlsConfig.KeyFile)
		if err != nil {
			return nil, err
		}
		tlsClientConfig.Certificates = []tls.Certificate{tlsCert}
	} else if tlsConfig.CertFile != "" || tlsConfig.KeyFile != "" {
		return nil, fmt.Errorf("both client cert and client key must be provided")
	}
	if tlsConfig.CAFile != "" || tlsConfig.CAPath != "" || len(tlsConfig.CAPem) != 0 {
		rootConfig := &rootcerts.Config{
			CAFile:        tlsConfig.CAFile,
			CAPath:        tlsConfig.CAPath,
			CACertificate: tlsConfig.CAPem,
		}
		if err := rootcerts.ConfigureTLS(tlsClientConfig, rootConfig); err != nil {
			return nil, err
		}
	}
	return tlsClientConfig, nil
}
func (c *Config) GenerateEnv() []string {
	env := make([]string, 0, 10)
	env = append(env,
		fmt.Sprintf("%s=%s", HTTPAddrEnvName, c.Address),
		fmt.Sprintf("%s=%s", HTTPTokenEnvName, c.Token),
		fmt.Sprintf("%s=%s", HTTPTokenFileEnvName, c.TokenFile),
		fmt.Sprintf("%s=%t", HTTPSSLEnvName, c.Scheme == "https"),
		fmt.Sprintf("%s=%s", HTTPCAFile, c.TLSConfig.CAFile),
		fmt.Sprintf("%s=%s", HTTPCAPath, c.TLSConfig.CAPath),
		fmt.Sprintf("%s=%s", HTTPClientCert, c.TLSConfig.CertFile),
		fmt.Sprintf("%s=%s", HTTPClientKey, c.TLSConfig.KeyFile),
		fmt.Sprintf("%s=%s", HTTPTLSServerName, c.TLSConfig.Address),
		fmt.Sprintf("%s=%t", HTTPSSLVerifyEnvName, !c.TLSConfig.InsecureSkipVerify))
	if c.HttpAuth != nil {
		env = append(env, fmt.Sprintf("%s=%s:%s", HTTPAuthEnvName, c.HttpAuth.Username, c.HttpAuth.Password))
	} else {
		env = append(env, fmt.Sprintf("%s=", HTTPAuthEnvName))
	}
	return env
}
type Client struct {
	config Config
}
func NewClient(config *Config) (*Client, error) {
	defConfig := DefaultConfig()
	if len(config.Address) == 0 {
		config.Address = defConfig.Address
	}
	if len(config.Scheme) == 0 {
		config.Scheme = defConfig.Scheme
	}
	if config.Transport == nil {
		config.Transport = defConfig.Transport
	}
	if config.TLSConfig.Address == "" {
		config.TLSConfig.Address = defConfig.TLSConfig.Address
	}
	if config.TLSConfig.CAFile == "" {
		config.TLSConfig.CAFile = defConfig.TLSConfig.CAFile
	}
	if config.TLSConfig.CAPath == "" {
		config.TLSConfig.CAPath = defConfig.TLSConfig.CAPath
	}
	if config.TLSConfig.CertFile == "" {
		config.TLSConfig.CertFile = defConfig.TLSConfig.CertFile
	}
	if config.TLSConfig.KeyFile == "" {
		config.TLSConfig.KeyFile = defConfig.TLSConfig.KeyFile
	}
	if !config.TLSConfig.InsecureSkipVerify {
		config.TLSConfig.InsecureSkipVerify = defConfig.TLSConfig.InsecureSkipVerify
	}
	if config.HttpClient == nil {
		var err error
		config.HttpClient, err = NewHttpClient(config.Transport, config.TLSConfig)
		if err != nil {
			return nil, err
		}
	}
	parts := strings.SplitN(config.Address, "://", 2)
	if len(parts) == 2 {
		switch parts[0] {
		case "http":
			config.Scheme = "http"
		case "https":
			config.Scheme = "https"
		case "unix":
			trans := cleanhttp.DefaultTransport()
			trans.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
				return net.Dial("unix", parts[1])
			}
			config.HttpClient = &http.Client{
				Transport: trans,
			}
		default:
			return nil, fmt.Errorf("Unknown protocol scheme: %s", parts[0])
		}
		config.Address = parts[1]
	}
	if config.TokenFile != "" {
		data, err := ioutil.ReadFile(config.TokenFile)
		if err != nil {
			return nil, fmt.Errorf("Error loading token file: %s", err)
		}
		if token := strings.TrimSpace(string(data)); token != "" {
			config.Token = token
		}
	}
	if config.Token == "" {
		config.Token = defConfig.Token
	}
	return &Client{config: *config}, nil
}
func NewHttpClient(transport *http.Transport, tlsConf TLSConfig) (*http.Client, error) {
	client := &http.Client{
		Transport: transport,
	}
	if transport.TLSClientConfig == nil {
		tlsClientConfig, err := SetupTLSConfig(&tlsConf)
		if err != nil {
			return nil, err
		}
		transport.TLSClientConfig = tlsClientConfig
	}
	return client, nil
}
type request struct {
	config *Config
	method string
	url    *url.URL
	params url.Values
	body   io.Reader
	header http.Header
	obj    interface{}
	ctx    context.Context
}