Blame vendor/github.com/kolo/xmlrpc/client.go

Packit 63bb0d
package xmlrpc
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"errors"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"io/ioutil"
Packit 63bb0d
	"net/http"
Packit 63bb0d
	"net/http/cookiejar"
Packit 63bb0d
	"net/rpc"
Packit 63bb0d
	"net/url"
Packit 63bb0d
	"sync"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
type Client struct {
Packit 63bb0d
	*rpc.Client
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// clientCodec is rpc.ClientCodec interface implementation.
Packit 63bb0d
type clientCodec struct {
Packit 63bb0d
	// url presents url of xmlrpc service
Packit 63bb0d
	url *url.URL
Packit 63bb0d
Packit 63bb0d
	// httpClient works with HTTP protocol
Packit 63bb0d
	httpClient *http.Client
Packit 63bb0d
Packit 63bb0d
	// cookies stores cookies received on last request
Packit 63bb0d
	cookies http.CookieJar
Packit 63bb0d
Packit 63bb0d
	// responses presents map of active requests. It is required to return request id, that
Packit 63bb0d
	// rpc.Client can mark them as done.
Packit 63bb0d
	responses map[uint64]*http.Response
Packit 63bb0d
	mutex     sync.Mutex
Packit 63bb0d
Packit 63bb0d
	response Response
Packit 63bb0d
Packit 63bb0d
	// ready presents channel, that is used to link request and it`s response.
Packit 63bb0d
	ready chan uint64
Packit 63bb0d
Packit 63bb0d
	// close notifies codec is closed.
Packit 63bb0d
	close chan uint64
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
Packit 63bb0d
	httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)
Packit 63bb0d
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if codec.cookies != nil {
Packit 63bb0d
		for _, cookie := range codec.cookies.Cookies(codec.url) {
Packit 63bb0d
			httpRequest.AddCookie(cookie)
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	var httpResponse *http.Response
Packit 63bb0d
	httpResponse, err = codec.httpClient.Do(httpRequest)
Packit 63bb0d
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if codec.cookies != nil {
Packit 63bb0d
		codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	codec.mutex.Lock()
Packit 63bb0d
	codec.responses[request.Seq] = httpResponse
Packit 63bb0d
	codec.mutex.Unlock()
Packit 63bb0d
Packit 63bb0d
	codec.ready <- request.Seq
Packit 63bb0d
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
Packit 63bb0d
	var seq uint64
Packit 63bb0d
	select {
Packit 63bb0d
	case seq = <-codec.ready:
Packit 63bb0d
	case <-codec.close:
Packit 63bb0d
		return errors.New("codec is closed")
Packit 63bb0d
	}
Packit 63bb0d
	response.Seq = seq
Packit 63bb0d
Packit 63bb0d
	codec.mutex.Lock()
Packit 63bb0d
	httpResponse := codec.responses[seq]
Packit 63bb0d
	delete(codec.responses, seq)
Packit 63bb0d
	codec.mutex.Unlock()
Packit 63bb0d
Packit 63bb0d
	defer httpResponse.Body.Close()
Packit 63bb0d
Packit 63bb0d
	if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
Packit 63bb0d
		response.Error = fmt.Sprintf("request error: bad status code - %d", httpResponse.StatusCode)
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	body, err := ioutil.ReadAll(httpResponse.Body)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		response.Error = err.Error()
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	resp := Response(body)
Packit 63bb0d
	if err := resp.Err(); err != nil {
Packit 63bb0d
		response.Error = err.Error()
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	codec.response = resp
Packit 63bb0d
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) {
Packit 63bb0d
	if v == nil {
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
	return codec.response.Unmarshal(v)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (codec *clientCodec) Close() error {
Packit 63bb0d
	if transport, ok := codec.httpClient.Transport.(*http.Transport); ok {
Packit 63bb0d
		transport.CloseIdleConnections()
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	close(codec.close)
Packit 63bb0d
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service.
Packit 63bb0d
func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
Packit 63bb0d
	if transport == nil {
Packit 63bb0d
		transport = http.DefaultTransport
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	httpClient := &http.Client{Transport: transport}
Packit 63bb0d
Packit 63bb0d
	jar, err := cookiejar.New(nil)
Packit 63bb0d
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return nil, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	u, err := url.Parse(requrl)
Packit 63bb0d
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return nil, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	codec := clientCodec{
Packit 63bb0d
		url:        u,
Packit 63bb0d
		httpClient: httpClient,
Packit 63bb0d
		close:      make(chan uint64),
Packit 63bb0d
		ready:      make(chan uint64),
Packit 63bb0d
		responses:  make(map[uint64]*http.Response),
Packit 63bb0d
		cookies:    jar,
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return &Client{rpc.NewClientWithCodec(&codec)}, nil
Packit 63bb0d
}