func (c *Client) dial(network, addr string) (net.Conn, error) {
	conn, err := net.DialTimeout(network, addr, c.config.DialTimeout)
	if err != nil {
		return nil, err
	}
	tcpConn, ok := conn.(*net.TCPConn)
	if !ok {
		return nil, errors.New("Failed type-assertion of net.Conn as *net.TCPConn")
	}
	if err = tcpConn.SetKeepAlive(true); err != nil {
		return nil, err
	}
	if err = tcpConn.SetKeepAlivePeriod(time.Second); err != nil {
		return nil, err
	}
	return tcpConn, nil
}
dial attempts to open a TCP connection to the provided address, explicitly
enabling keep-alives with a one-second interval.
package etcd
import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"time"
)
const (
	STRONG_CONSISTENCY = "STRONG"
	WEAK_CONSISTENCY   = "WEAK"
)
const (
	defaultBufferSize = 10
)
type Config struct {
	CertFile    string        `json:"certFile"`
	KeyFile     string        `json:"keyFile"`
	CaCertFile  []string      `json:"caCertFiles"`
	DialTimeout time.Duration `json:"timeout"`
	Consistency string        `json:"consistency"`
}
type Client struct {
	config      Config   `json:"config"`
	cluster     *Cluster `json:"cluster"`
	httpClient  *http.Client
	persistence io.Writer
	cURLch      chan string
	CheckRetry func(cluster *Cluster, numReqs int,
		lastResp http.Response, err error) error
}
func NewClient(machines []string) *Client {
	config := Config{
		DialTimeout: time.Second,
		Consistency: STRONG_CONSISTENCY,
	}
	client := &Client{
		cluster: NewCluster(machines),
		config:  config,
	}
	client.initHTTPClient()
	client.saveConfig()
	return client
}
func NewTLSClient(machines []string, cert, key, caCert string) (*Client, error) {
	if len(machines) == 0 {
		machines = []string{"https://127.0.0.1:4001"}
	}
	config := Config{
		DialTimeout: time.Second,
		Consistency: STRONG_CONSISTENCY,
		CertFile:    cert,
		KeyFile:     key,
		CaCertFile:  make([]string, 0),
	}
	client := &Client{
		cluster: NewCluster(machines),
		config:  config,
	}
	err := client.initHTTPSClient(cert, key)
	if err != nil {
		return nil, err
	}
	err = client.AddRootCA(caCert)
	client.saveConfig()
	return client, nil
}
func NewClientFromFile(fpath string) (*Client, error) {
	fi, err := os.Open(fpath)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err := fi.Close(); err != nil {
			panic(err)
		}
	}()
	return NewClientFromReader(fi)
}
func NewClientFromReader(reader io.Reader) (*Client, error) {
	c := new(Client)
	b, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(b, c)
	if err != nil {
		return nil, err
	}
	if c.config.CertFile == "" {
		c.initHTTPClient()
	} else {
		err = c.initHTTPSClient(c.config.CertFile, c.config.KeyFile)
	}
	if err != nil {
		return nil, err
	}
	for _, caCert := range c.config.CaCertFile {
		if err := c.AddRootCA(caCert); err != nil {
			return nil, err
		}
	}
	return c, nil
}
func (c *Client) SetTransport(tr *http.Transport) {
	c.httpClient.Transport = tr
}
func (c *Client) initHTTPClient() {
	tr := &http.Transport{
		Dial: c.dial,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}
	c.httpClient = &http.Client{Transport: tr}
}
func (c *Client) initHTTPSClient(cert, key string) error {
	if cert == "" || key == "" {
		return errors.New("Require both cert and key path")
	}
	tlsCert, err := tls.LoadX509KeyPair(cert, key)
	if err != nil {
		return err
	}
	tlsConfig := &tls.Config{
		Certificates:       []tls.Certificate{tlsCert},
		InsecureSkipVerify: true,
	}
	tr := &http.Transport{
		TLSClientConfig: tlsConfig,
		Dial:            c.dial,
	}
	c.httpClient = &http.Client{Transport: tr}
	return nil
}
func (c *Client) SetPersistence(writer io.Writer) {
	c.persistence = writer
}
func (c *Client) SetConsistency(consistency string) error {
	if !(consistency == STRONG_CONSISTENCY || consistency == WEAK_CONSISTENCY) {
		return errors.New("The argument must be either STRONG_CONSISTENCY or WEAK_CONSISTENCY.")
	}
	c.config.Consistency = consistency
	return nil
}
func (c *Client) SetDialTimeout(d time.Duration) {
	c.config.DialTimeout = d
}
func (c *Client) AddRootCA(caCert string) error {
	if c.httpClient == nil {
		return errors.New("Client has not been initialized yet!")
	}
	certBytes, err := ioutil.ReadFile(caCert)
	if err != nil {
		return err
	}
	tr, ok := c.httpClient.Transport.(*http.Transport)
	if !ok {
		panic("AddRootCA(): Transport type assert should not fail")
	}
	if tr.TLSClientConfig.RootCAs == nil {
		caCertPool := x509.NewCertPool()
		ok = caCertPool.AppendCertsFromPEM(certBytes)
		if ok {
			tr.TLSClientConfig.RootCAs = caCertPool
		}
		tr.TLSClientConfig.InsecureSkipVerify = false
	} else {
		ok = tr.TLSClientConfig.RootCAs.AppendCertsFromPEM(certBytes)
	}
	if !ok {
		err = errors.New("Unable to load caCert")
	}
	c.config.CaCertFile = append(c.config.CaCertFile, caCert)
	c.saveConfig()
	return err
}
func (c *Client) SetCluster(machines []string) bool {
	success := c.internalSyncCluster(machines)
	return success
}
func (c *Client) GetCluster() []string {
	return c.cluster.Machines
}
func (c *Client) SyncCluster() bool {
	return c.internalSyncCluster(c.cluster.Machines)
}
func (c *Client) internalSyncCluster(machines []string) bool {
	for _, machine := range machines {
		httpPath := c.createHttpPath(machine, path.Join(version, "machines"))
		resp, err := c.httpClient.Get(httpPath)
		if err != nil {
			continue
		} else {
			b, err := ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			if err != nil {
				continue
			}
			c.cluster.updateFromStr(string(b))
			c.cluster.switchLeader(0)
			logger.Debug("sync.machines ", c.cluster.Machines)
			c.saveConfig()
			return true
		}
	}
	return false
}
func (c *Client) createHttpPath(serverName string, _path string) string {
	u, err := url.Parse(serverName)
	if err != nil {
		panic(err)
	}
	u.Path = path.Join(u.Path, _path)
	if u.Scheme == "" {
		u.Scheme = "http"
	}
	return u.String()
}