github.com/codemac/docker@v1.2.1-0.20150518222241-6a18412d5b9c/pkg/plugins/client.go (about) 1 package plugins 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "net" 9 "net/http" 10 "strings" 11 "time" 12 13 "github.com/Sirupsen/logrus" 14 ) 15 16 const ( 17 versionMimetype = "application/vnd.docker.plugins.v1+json" 18 defaultTimeOut = 30 19 ) 20 21 func NewClient(addr string) *Client { 22 tr := &http.Transport{} 23 protoAndAddr := strings.Split(addr, "://") 24 configureTCPTransport(tr, protoAndAddr[0], protoAndAddr[1]) 25 return &Client{&http.Client{Transport: tr}, protoAndAddr[1]} 26 } 27 28 type Client struct { 29 http *http.Client 30 addr string 31 } 32 33 func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error { 34 var buf bytes.Buffer 35 if err := json.NewEncoder(&buf).Encode(args); err != nil { 36 return err 37 } 38 39 req, err := http.NewRequest("POST", "/"+serviceMethod, &buf) 40 if err != nil { 41 return err 42 } 43 req.Header.Add("Accept", versionMimetype) 44 req.URL.Scheme = "http" 45 req.URL.Host = c.addr 46 47 var retries int 48 start := time.Now() 49 50 for { 51 resp, err := c.http.Do(req) 52 if err != nil { 53 timeOff := backoff(retries) 54 if timeOff+time.Since(start) > defaultTimeOut { 55 return err 56 } 57 retries++ 58 logrus.Warn("Unable to connect to plugin: %s, retrying in %ds\n", c.addr, timeOff) 59 time.Sleep(timeOff) 60 continue 61 } 62 63 if resp.StatusCode != http.StatusOK { 64 remoteErr, err := ioutil.ReadAll(resp.Body) 65 if err != nil { 66 return nil 67 } 68 return fmt.Errorf("Plugin Error: %s", remoteErr) 69 } 70 71 return json.NewDecoder(resp.Body).Decode(&ret) 72 } 73 } 74 75 func backoff(retries int) time.Duration { 76 b, max := float64(1), float64(defaultTimeOut) 77 for b < max && retries > 0 { 78 b *= 2 79 retries-- 80 } 81 if b > max { 82 b = max 83 } 84 return time.Duration(b) 85 } 86 87 func configureTCPTransport(tr *http.Transport, proto, addr string) { 88 // Why 32? See https://github.com/docker/docker/pull/8035. 89 timeout := 32 * time.Second 90 if proto == "unix" { 91 // No need for compression in local communications. 92 tr.DisableCompression = true 93 tr.Dial = func(_, _ string) (net.Conn, error) { 94 return net.DialTimeout(proto, addr, timeout) 95 } 96 } else { 97 tr.Proxy = http.ProxyFromEnvironment 98 tr.Dial = (&net.Dialer{Timeout: timeout}).Dial 99 } 100 }