func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) {
	if e != nil {
		return d, resp, e
	}
	if resp.StatusCode != 200 {
		var buf bytes.Buffer
		io.Copy(&buf, resp.Body)
		return d, resp, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes())
	}
	return d, resp, e
}
requireOK is used to wrap doRequest and check for a 200
package api
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)
type QueryOptions struct {
	Datacenter string
	AllowStale bool
	RequireConsistent bool
	WaitIndex uint64
	WaitTime time.Duration
	Token string
}
type WriteOptions struct {
	Datacenter string
	Token string
}
type QueryMeta struct {
	LastIndex uint64
	LastContact time.Duration
	KnownLeader bool
	RequestTime time.Duration
}
type WriteMeta struct {
	RequestTime time.Duration
}
type HttpBasicAuth struct {
	Username string
	Password string
}
type Config struct {
	Address string
	Scheme string
	Datacenter string
	HttpClient *http.Client
	HttpAuth *HttpBasicAuth
	WaitTime time.Duration
	Token string
}
func DefaultConfig() *Config {
	config := &Config{
		Address:    "127.0.0.1:8500",
		Scheme:     "http",
		HttpClient: http.DefaultClient,
	}
	if addr := os.Getenv("CONSUL_HTTP_ADDR"); addr != "" {
		config.Address = addr
	}
	return config
}
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.HttpClient == nil {
		config.HttpClient = defConfig.HttpClient
	}
	if parts := strings.SplitN(config.Address, "unix://", 2); len(parts) == 2 {
		config.HttpClient = &http.Client{
			Transport: &http.Transport{
				Dial: func(_, _ string) (net.Conn, error) {
					return net.Dial("unix", parts[1])
				},
			},
		}
		config.Address = parts[1]
	}
	client := &Client{
		config: *config,
	}
	return client, nil
}
type request struct {
	config *Config
	method string
	url    *url.URL
	params url.Values
	body   io.Reader
	obj    interface{}
}
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.Token != "" {
		r.params.Set("token", q.Token)
	}
}
func durToMsec(dur time.Duration) string {
	return fmt.Sprintf("%dms", dur/time.Millisecond)
}
func (r *request) setWriteOptions(q *WriteOptions) {
	if q == nil {
		return
	}
	if q.Datacenter != "" {
		r.params.Set("dc", q.Datacenter)
	}
	if q.Token != "" {
		r.params.Set("token", q.Token)
	}
}
func (r *request) toHTTP() (*http.Request, error) {
	r.url.RawQuery = r.params.Encode()
	if r.body == nil && r.obj != nil {
		if b, err := encodeBody(r.obj); err != nil {
			return nil, err
		} else {
			r.body = b
		}
	}
	req, err := http.NewRequest(r.method, r.url.RequestURI(), r.body)
	if err != nil {
		return nil, err
	}
	req.URL.Host = r.url.Host
	req.URL.Scheme = r.url.Scheme
	req.Host = r.url.Host
	if r.config.HttpAuth != nil {
		req.SetBasicAuth(r.config.HttpAuth.Username, r.config.HttpAuth.Password)
	}
	return req, nil
}
func (c *Client) newRequest(method, path string) *request {
	r := &request{
		config: &c.config,
		method: method,
		url: &url.URL{
			Scheme: c.config.Scheme,
			Host:   c.config.Address,
			Path:   path,
		},
		params: make(map[string][]string),
	}
	if c.config.Datacenter != "" {
		r.params.Set("dc", c.config.Datacenter)
	}
	if c.config.WaitTime != 0 {
		r.params.Set("wait", durToMsec(r.config.WaitTime))
	}
	if c.config.Token != "" {
		r.params.Set("token", r.config.Token)
	}
	return r
}
func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
	req, err := r.toHTTP()
	if err != nil {
		return 0, nil, err
	}
	start := time.Now()
	resp, err := c.config.HttpClient.Do(req)
	diff := time.Now().Sub(start)
	return diff, resp, err
}
func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
	header := resp.Header
	index, err := strconv.ParseUint(header.Get("X-Consul-Index"), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse X-Consul-Index: %v", err)
	}
	q.LastIndex = index
	last, err := strconv.ParseUint(header.Get("X-Consul-LastContact"), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse X-Consul-LastContact: %v", err)
	}
	q.LastContact = time.Duration(last) * time.Millisecond
	switch header.Get("X-Consul-KnownLeader") {
	case "true":
		q.KnownLeader = true
	default:
		q.KnownLeader = false
	}
	return nil
}
func decodeBody(resp *http.Response, out interface{}) error {
	dec := json.NewDecoder(resp.Body)
	return dec.Decode(out)
}
func encodeBody(obj interface{}) (io.Reader, error) {
	buf := bytes.NewBuffer(nil)
	enc := json.NewEncoder(buf)
	if err := enc.Encode(obj); err != nil {
		return nil, err
	}
	return buf, nil
}