xmlrpc/client.go

145 строки
2.9 KiB
Go
Исходник Обычный вид История

package xmlrpc
import (
"fmt"
2012-11-08 23:07:21 +04:00
"io/ioutil"
"net/http"
2014-06-26 00:17:00 +04:00
"net/http/cookiejar"
2012-11-08 23:07:21 +04:00
"net/rpc"
2014-06-26 00:17:00 +04:00
"net/url"
)
type Client struct {
2012-11-08 23:07:21 +04:00
*rpc.Client
}
// clientCodec is rpc.ClientCodec interface implementation.
type clientCodec struct {
2012-11-08 23:07:21 +04:00
// url presents url of xmlrpc service
2014-06-26 00:17:00 +04:00
url *url.URL
2012-11-08 23:07:21 +04:00
// httpClient works with HTTP protocol
httpClient *http.Client
2012-11-08 23:07:21 +04:00
// cookies stores cookies received on last request
2014-06-26 00:17:00 +04:00
cookies http.CookieJar
2012-11-08 23:07:21 +04:00
// responses presents map of active requests. It is required to return request id, that
// rpc.Client can mark them as done.
responses map[uint64]*http.Response
response *Response
2012-11-08 23:07:21 +04:00
// ready presents channel, that is used to link request and it`s response.
ready chan uint64
}
2014-03-06 23:44:08 +04:00
func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
2014-06-26 00:17:00 +04:00
httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)
2012-11-08 23:07:21 +04:00
if codec.cookies != nil {
2014-06-26 00:17:00 +04:00
for _, cookie := range codec.cookies.Cookies(codec.url) {
2012-11-08 23:07:21 +04:00
httpRequest.AddCookie(cookie)
}
}
2012-11-08 23:07:21 +04:00
if err != nil {
return err
}
2012-11-08 23:07:21 +04:00
var httpResponse *http.Response
httpResponse, err = codec.httpClient.Do(httpRequest)
2012-11-08 23:07:21 +04:00
if err != nil {
return err
}
2014-06-26 00:17:00 +04:00
if codec.cookies != nil {
codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
2012-11-08 23:07:21 +04:00
}
2012-11-08 23:07:21 +04:00
codec.responses[request.Seq] = httpResponse
codec.ready <- request.Seq
2012-11-08 23:07:21 +04:00
return nil
}
func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
2012-11-08 23:07:21 +04:00
seq := <-codec.ready
httpResponse := codec.responses[seq]
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
2014-03-09 14:44:17 +04:00
return fmt.Errorf("request error: bad status code - %d", httpResponse.StatusCode)
}
respData, err := ioutil.ReadAll(httpResponse.Body)
2012-11-08 23:07:21 +04:00
if err != nil {
return err
}
2012-11-08 23:07:21 +04:00
httpResponse.Body.Close()
resp := NewResponse(respData)
if resp.Failed() {
response.Error = fmt.Sprintf("%v", resp.Err())
}
codec.response = resp
2012-11-08 23:07:21 +04:00
response.Seq = seq
delete(codec.responses, seq)
2012-11-08 23:07:21 +04:00
return nil
}
2014-03-05 21:10:17 +04:00
func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) {
if v == nil {
return nil
}
2014-03-05 21:10:17 +04:00
if err = codec.response.Unmarshal(v); err != nil {
2012-11-08 23:07:21 +04:00
return err
}
2012-11-08 23:07:21 +04:00
return nil
}
func (codec *clientCodec) Close() error {
2012-11-08 23:07:21 +04:00
transport := codec.httpClient.Transport.(*http.Transport)
transport.CloseIdleConnections()
return nil
}
// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service.
2014-06-26 00:17:00 +04:00
func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
2012-11-08 23:07:21 +04:00
if transport == nil {
transport = http.DefaultTransport
2012-11-08 23:07:21 +04:00
}
2012-11-08 23:07:21 +04:00
httpClient := &http.Client{Transport: transport}
2014-06-26 00:17:00 +04:00
jar, err := cookiejar.New(nil)
if err != nil {
return nil, err
}
u, err := url.Parse(requrl)
if err != nil {
return nil, err
}
2012-11-08 23:07:21 +04:00
codec := clientCodec{
2014-06-26 00:17:00 +04:00
url: u,
2012-11-08 23:07:21 +04:00
httpClient: httpClient,
ready: make(chan uint64),
responses: make(map[uint64]*http.Response),
2014-06-26 00:17:00 +04:00
cookies: jar,
2012-11-08 23:07:21 +04:00
}
2012-11-08 23:07:21 +04:00
return &Client{rpc.NewClientWithCodec(&codec)}, nil
}