Make rest Set methods safe for concurrent calling

This commit is contained in:
Nick Craig-Wood 2016-02-23 21:16:13 +00:00
parent f46304e8ae
commit 70902b4051

View file

@ -1,4 +1,6 @@
// Package rest implements a simple REST wrapper // Package rest implements a simple REST wrapper
//
// All methods are safe for concurrent calling.
package rest package rest
import ( import (
@ -8,12 +10,14 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"sync"
"github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs"
) )
// Client contains the info to sustain the API // Client contains the info to sustain the API
type Client struct { type Client struct {
mu sync.RWMutex
c *http.Client c *http.Client
rootURL string rootURL string
errorHandler func(resp *http.Response) error errorHandler func(resp *http.Response) error
@ -45,18 +49,24 @@ func defaultErrorHandler(resp *http.Response) (err error) {
// SetErrorHandler sets the handler to decode an error response when // SetErrorHandler sets the handler to decode an error response when
// the HTTP status code is not 2xx. The handler should close resp.Body. // the HTTP status code is not 2xx. The handler should close resp.Body.
func (api *Client) SetErrorHandler(fn func(resp *http.Response) error) *Client { func (api *Client) SetErrorHandler(fn func(resp *http.Response) error) *Client {
api.mu.Lock()
defer api.mu.Unlock()
api.errorHandler = fn api.errorHandler = fn
return api return api
} }
// SetRoot sets the default root URL // SetRoot sets the default root URL
func (api *Client) SetRoot(RootURL string) *Client { func (api *Client) SetRoot(RootURL string) *Client {
api.mu.Lock()
defer api.mu.Unlock()
api.rootURL = RootURL api.rootURL = RootURL
return api return api
} }
// SetHeader sets a header for all requests // SetHeader sets a header for all requests
func (api *Client) SetHeader(key, value string) *Client { func (api *Client) SetHeader(key, value string) *Client {
api.mu.Lock()
defer api.mu.Unlock()
api.headers[key] = value api.headers[key] = value
return api return api
} }
@ -89,6 +99,8 @@ func DecodeJSON(resp *http.Response, result interface{}) (err error) {
// //
// it will return resp if at all possible, even if err is set // it will return resp if at all possible, even if err is set
func (api *Client) Call(opts *Opts) (resp *http.Response, err error) { func (api *Client) Call(opts *Opts) (resp *http.Response, err error) {
api.mu.RLock()
defer api.mu.RUnlock()
if opts == nil { if opts == nil {
return nil, fmt.Errorf("call() called with nil opts") return nil, fmt.Errorf("call() called with nil opts")
} }
@ -127,12 +139,16 @@ func (api *Client) Call(opts *Opts) (resp *http.Response, err error) {
} }
// Now set the headers // Now set the headers
for k, v := range headers { for k, v := range headers {
req.Header.Add(k, v) if v != "" {
req.Header.Add(k, v)
}
} }
if opts.UserName != "" || opts.Password != "" { if opts.UserName != "" || opts.Password != "" {
req.SetBasicAuth(opts.UserName, opts.Password) req.SetBasicAuth(opts.UserName, opts.Password)
} }
api.mu.RUnlock()
resp, err = api.c.Do(req) resp, err = api.c.Do(req)
api.mu.RLock()
if err != nil { if err != nil {
return nil, err return nil, err
} }