func (r *request) setWriteOptions(q *WriteOptions) {
	if q == nil {
		return
	}
	if q.Datacenter != "" {
		r.params.Set("dc", q.Datacenter)
	}
	if q.Token != "" {
		r.header.Set("X-Consul-Token", q.Token)
	}
	if q.RelayFactor != 0 {
		r.params.Set("relay-factor", strconv.Itoa(int(q.RelayFactor)))
	}
	r.ctx = q.ctx
}
setWriteOptions is used to annotate the request with
additional write options
package api
import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
	"github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/go-rootcerts"
)
const (
	HTTPAddrEnvName = "CONSUL_HTTP_ADDR"
	HTTPTokenEnvName = "CONSUL_HTTP_TOKEN"
	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"
)
type QueryOptions struct {
	Datacenter string
	AllowStale bool
	RequireConsistent bool
	WaitIndex uint64
	WaitHash string
	WaitTime time.Duration
	Token string
	Near string
	NodeMeta map[string]string
	RelayFactor uint8
	Connect bool
	ctx context.Context
}
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 {
	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
}
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
	TLSConfig TLSConfig
}
type TLSConfig struct {
	Address string
	CAFile string
	CAPath string
	CertFile string
	KeyFile string
	InsecureSkipVerify bool
}
func DefaultConfig() *Config {
	return defaultConfig(cleanhttp.DefaultPooledTransport)
}
func DefaultNonPooledConfig() *Config {
	return defaultConfig(cleanhttp.DefaultTransport)
}
func defaultConfig(transportFn func() *http.Transport) *Config {
	config := &Config{
		Address:   "127.0.0.1:8500",
		Scheme:    "http",
		Transport: transportFn(),
	}
	if addr := os.Getenv(HTTPAddrEnvName); addr != "" {
		config.Address = addr
	}
	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 {
			log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLEnvName, 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 {
			log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLVerifyEnvName, err)
		}
		if !doVerify {
			config.TLSConfig.InsecureSkipVerify = true
		}
	}
	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 tlsConfig.CertFile != "" && tlsConfig.KeyFile != "" {
		tlsCert, err := tls.LoadX509KeyPair(tlsConfig.CertFile, tlsConfig.KeyFile)
		if err != nil {
			return nil, err
		}
		tlsClientConfig.Certificates = []tls.Certificate{tlsCert}
	}
	if tlsConfig.CAFile != "" || tlsConfig.CAPath != "" {
		rootConfig := &rootcerts.Config{
			CAFile: tlsConfig.CAFile,
			CAPath: tlsConfig.CAPath,
		}
		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=%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.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
}
func (r *request) setQueryOptions(q *QueryOptions) {
	if q == nil {
		return
	}
	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 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.Connect {
		r.params.Set("connect", "true")
	}
	r.ctx = q.ctx
}
func durToMsec(dur time.Duration) string {
	ms := dur / time.Millisecond
	if dur > 0 && ms == 0 {
		ms = 1
	}
	return fmt.Sprintf("%dms", ms)
}
const serverError = "Unexpected response code: 500"
func IsRetryableError(err error) bool {
	if err == nil {
		return false
	}
	if _, ok := err.(net.Error); ok {
		return true
	}
	return strings.Contains(err.Error(), serverError)
}