aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Kochie <superq@gmail.com>2018-08-06 16:54:46 +0200
committerGitHub <noreply@github.com>2018-08-06 16:54:46 +0200
commit5d23ad0ca7aea9efb4115af73490da6f8e9d7175 (patch)
treed06f1ef2512fff1b09ce41a074b53a8ecb1d765d
parent2c52b8c76147f0f0671029e9fd7454b9866ace0d (diff)
downloadprometheus_node_collector-5d23ad0ca7aea9efb4115af73490da6f8e9d7175.tar.bz2
prometheus_node_collector-5d23ad0ca7aea9efb4115af73490da6f8e9d7175.tar.xz
prometheus_node_collector-5d23ad0ca7aea9efb4115af73490da6f8e9d7175.zip
Fix supervisord collector (#978)
* Replace supervisord xmlrpc library * Use `github.com/mattn/go-xmlrpc` that doesn't leak goroutines. * Fix uptime metric * Use Prometheus best practices for uptime metric. * Use "start time" rather than "uptime". * Don't emit a start time if the process is down. * Add changelog entry. * Add example compatibility rules. Signed-off-by: Ben Kochie <superq@gmail.com>
-rw-r--r--CHANGELOG.md4
-rw-r--r--collector/supervisord.go55
-rw-r--r--docs/example-17-compatibility-rules-new-to-old.yml5
-rw-r--r--docs/example-17-compatibility-rules.yml5
-rw-r--r--vendor/github.com/kolo/xmlrpc/README.md79
-rw-r--r--vendor/github.com/kolo/xmlrpc/client.go144
-rw-r--r--vendor/github.com/kolo/xmlrpc/decoder.go449
-rw-r--r--vendor/github.com/kolo/xmlrpc/encoder.go164
-rw-r--r--vendor/github.com/kolo/xmlrpc/request.go57
-rw-r--r--vendor/github.com/kolo/xmlrpc/response.go52
-rw-r--r--vendor/github.com/kolo/xmlrpc/test_server.rb25
-rw-r--r--vendor/github.com/kolo/xmlrpc/xmlrpc.go19
-rw-r--r--vendor/github.com/mattn/go-xmlrpc/LICENSE (renamed from vendor/github.com/kolo/xmlrpc/LICENSE)12
-rw-r--r--vendor/github.com/mattn/go-xmlrpc/README.md48
-rw-r--r--vendor/github.com/mattn/go-xmlrpc/xmlrpc.go365
-rw-r--r--vendor/vendor.json12
16 files changed, 477 insertions, 1018 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45f7e26..78f4d95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
2 2
3**Breaking changes** 3**Breaking changes**
4 4
5supvervisord collector reports "start_time_seconds" rather than "uptime"
6
5* [CHANGE] Filter out non-installed units when collecting all systemd units #1011 7* [CHANGE] Filter out non-installed units when collecting all systemd units #1011
6* [FEATURE] Collect NRefused property for systemd socket units (available as of systemd v239) 8* [FEATURE] Collect NRefused property for systemd socket units (available as of systemd v239)
7* [FEATURE] Collect NRestarts property for systemd service units 9* [FEATURE] Collect NRestarts property for systemd service units
@@ -10,6 +12,8 @@
10* [ENHANCEMENT] 12* [ENHANCEMENT]
11* [BUGFIX] 13* [BUGFIX]
12 14
15* [BUGFIX] Fix goroutine leak in supervisord collector
16
13## 0.16.0 / 2018-05-15 17## 0.16.0 / 2018-05-15
14 18
15**Breaking changes** 19**Breaking changes**
diff --git a/collector/supervisord.go b/collector/supervisord.go
index 20fa36f..eb6720b 100644
--- a/collector/supervisord.go
+++ b/collector/supervisord.go
@@ -16,7 +16,9 @@
16package collector 16package collector
17 17
18import ( 18import (
19 "github.com/kolo/xmlrpc" 19 "fmt"
20
21 "github.com/mattn/go-xmlrpc"
20 "github.com/prometheus/client_golang/prometheus" 22 "github.com/prometheus/client_golang/prometheus"
21 "github.com/prometheus/common/log" 23 "github.com/prometheus/common/log"
22 "gopkg.in/alecthomas/kingpin.v2" 24 "gopkg.in/alecthomas/kingpin.v2"
@@ -27,11 +29,10 @@ var (
27) 29)
28 30
29type supervisordCollector struct { 31type supervisordCollector struct {
30 client *xmlrpc.Client
31 upDesc *prometheus.Desc 32 upDesc *prometheus.Desc
32 stateDesc *prometheus.Desc 33 stateDesc *prometheus.Desc
33 exitStatusDesc *prometheus.Desc 34 exitStatusDesc *prometheus.Desc
34 uptimeDesc *prometheus.Desc 35 startTimeDesc *prometheus.Desc
35} 36}
36 37
37func init() { 38func init() {
@@ -40,17 +41,11 @@ func init() {
40 41
41// NewSupervisordCollector returns a new Collector exposing supervisord statistics. 42// NewSupervisordCollector returns a new Collector exposing supervisord statistics.
42func NewSupervisordCollector() (Collector, error) { 43func NewSupervisordCollector() (Collector, error) {
43 client, err := xmlrpc.NewClient(*supervisordURL, nil)
44 if err != nil {
45 return nil, err
46 }
47
48 var ( 44 var (
49 subsystem = "supervisord" 45 subsystem = "supervisord"
50 labelNames = []string{"name", "group"} 46 labelNames = []string{"name", "group"}
51 ) 47 )
52 return &supervisordCollector{ 48 return &supervisordCollector{
53 client: client,
54 upDesc: prometheus.NewDesc( 49 upDesc: prometheus.NewDesc(
55 prometheus.BuildFQName(namespace, subsystem, "up"), 50 prometheus.BuildFQName(namespace, subsystem, "up"),
56 "Process Up", 51 "Process Up",
@@ -69,9 +64,9 @@ func NewSupervisordCollector() (Collector, error) {
69 labelNames, 64 labelNames,
70 nil, 65 nil,
71 ), 66 ),
72 uptimeDesc: prometheus.NewDesc( 67 startTimeDesc: prometheus.NewDesc(
73 prometheus.BuildFQName(namespace, subsystem, "uptime"), 68 prometheus.BuildFQName(namespace, subsystem, "start_time_seconds"),
74 "Process Uptime", 69 "Process start time",
75 labelNames, 70 labelNames,
76 nil, 71 nil,
77 ), 72 ),
@@ -98,7 +93,7 @@ func (c *supervisordCollector) isRunning(state int) bool {
98} 93}
99 94
100func (c *supervisordCollector) Update(ch chan<- prometheus.Metric) error { 95func (c *supervisordCollector) Update(ch chan<- prometheus.Metric) error {
101 var infos []struct { 96 var info struct {
102 Name string `xmlrpc:"name"` 97 Name string `xmlrpc:"name"`
103 Group string `xmlrpc:"group"` 98 Group string `xmlrpc:"group"`
104 Start int `xmlrpc:"start"` 99 Start int `xmlrpc:"start"`
@@ -112,10 +107,35 @@ func (c *supervisordCollector) Update(ch chan<- prometheus.Metric) error {
112 StderrLogfile string `xmlrcp:"stderr_logfile"` 107 StderrLogfile string `xmlrcp:"stderr_logfile"`
113 PID int `xmlrpc:"pid"` 108 PID int `xmlrpc:"pid"`
114 } 109 }
115 if err := c.client.Call("supervisor.getAllProcessInfo", nil, &infos); err != nil { 110
116 return err 111 res, err := xmlrpc.Call(*supervisordURL, "supervisor.getAllProcessInfo")
112 if err != nil {
113 return fmt.Errorf("unable to call supervisord: %s", err)
117 } 114 }
118 for _, info := range infos { 115
116 for _, p := range res.(xmlrpc.Array) {
117 for k, v := range p.(xmlrpc.Struct) {
118 switch k {
119 case "name":
120 info.Name = v.(string)
121 case "group":
122 info.Group = v.(string)
123 case "start":
124 info.Start = v.(int)
125 case "stop":
126 info.Stop = v.(int)
127 case "now":
128 info.Now = v.(int)
129 case "state":
130 info.State = v.(int)
131 case "statename":
132 info.StateName = v.(string)
133 case "exitstatus":
134 info.ExitStatus = v.(int)
135 case "pid":
136 info.PID = v.(int)
137 }
138 }
119 labels := []string{info.Name, info.Group} 139 labels := []string{info.Name, info.Group}
120 140
121 ch <- prometheus.MustNewConstMetric(c.stateDesc, prometheus.GaugeValue, float64(info.State), labels...) 141 ch <- prometheus.MustNewConstMetric(c.stateDesc, prometheus.GaugeValue, float64(info.State), labels...)
@@ -123,10 +143,9 @@ func (c *supervisordCollector) Update(ch chan<- prometheus.Metric) error {
123 143
124 if c.isRunning(info.State) { 144 if c.isRunning(info.State) {
125 ch <- prometheus.MustNewConstMetric(c.upDesc, prometheus.GaugeValue, 1, labels...) 145 ch <- prometheus.MustNewConstMetric(c.upDesc, prometheus.GaugeValue, 1, labels...)
126 ch <- prometheus.MustNewConstMetric(c.uptimeDesc, prometheus.CounterValue, float64(info.Now-info.Start), labels...) 146 ch <- prometheus.MustNewConstMetric(c.startTimeDesc, prometheus.CounterValue, float64(info.Start), labels...)
127 } else { 147 } else {
128 ch <- prometheus.MustNewConstMetric(c.upDesc, prometheus.GaugeValue, 0, labels...) 148 ch <- prometheus.MustNewConstMetric(c.upDesc, prometheus.GaugeValue, 0, labels...)
129 ch <- prometheus.MustNewConstMetric(c.uptimeDesc, prometheus.CounterValue, 0, labels...)
130 } 149 }
131 log.Debugf("%s:%s is %s on pid %d", info.Group, info.Name, info.StateName, info.PID) 150 log.Debugf("%s:%s is %s on pid %d", info.Group, info.Name, info.StateName, info.PID)
132 } 151 }
diff --git a/docs/example-17-compatibility-rules-new-to-old.yml b/docs/example-17-compatibility-rules-new-to-old.yml
new file mode 100644
index 0000000..c6db522
--- /dev/null
+++ b/docs/example-17-compatibility-rules-new-to-old.yml
@@ -0,0 +1,5 @@
1groups:
2- name: node_exporter-17-supervisord
3 rules:
4 - record: node_supervisord_start_time_seconds
5 expr: node_supervisord_uptime + time()
diff --git a/docs/example-17-compatibility-rules.yml b/docs/example-17-compatibility-rules.yml
new file mode 100644
index 0000000..6fbeaa9
--- /dev/null
+++ b/docs/example-17-compatibility-rules.yml
@@ -0,0 +1,5 @@
1groups:
2- name: node_exporter-17-supervisord
3 rules:
4 - record: node_supervisord_uptime
5 expr: time() - node_supervisord_start_time_seconds
diff --git a/vendor/github.com/kolo/xmlrpc/README.md b/vendor/github.com/kolo/xmlrpc/README.md
deleted file mode 100644
index 12b7692..0000000
--- a/vendor/github.com/kolo/xmlrpc/README.md
+++ /dev/null
@@ -1,79 +0,0 @@
1## Overview
2
3xmlrpc is an implementation of client side part of XMLRPC protocol in Go language.
4
5## Installation
6
7To install xmlrpc package run `go get github.com/kolo/xmlrpc`. To use
8it in application add `"github.com/kolo/xmlrpc"` string to `import`
9statement.
10
11## Usage
12
13 client, _ := xmlrpc.NewClient("https://bugzilla.mozilla.org/xmlrpc.cgi", nil)
14 result := struct{
15 Version string `xmlrpc:"version"`
16 }{}
17 client.Call("Bugzilla.version", nil, &result)
18 fmt.Printf("Version: %s\n", result.Version) // Version: 4.2.7+
19
20Second argument of NewClient function is an object that implements
21[http.RoundTripper](http://golang.org/pkg/net/http/#RoundTripper)
22interface, it can be used to get more control over connection options.
23By default it initialized by http.DefaultTransport object.
24
25### Arguments encoding
26
27xmlrpc package supports encoding of native Go data types to method
28arguments.
29
30Data types encoding rules:
31* int, int8, int16, int32, int64 encoded to int;
32* float32, float64 encoded to double;
33* bool encoded to boolean;
34* string encoded to string;
35* time.Time encoded to datetime.iso8601;
36* xmlrpc.Base64 encoded to base64;
37* slice decoded to array;
38
39Structs decoded to struct by following rules:
40* all public field become struct members;
41* field name become member name;
42* if field has xmlrpc tag, its value become member name.
43
44Server method can accept few arguments, to handle this case there is
45special approach to handle slice of empty interfaces (`[]interface{}`).
46Each value of such slice encoded as separate argument.
47
48### Result decoding
49
50Result of remote function is decoded to native Go data type.
51
52Data types decoding rules:
53* int, i4 decoded to int, int8, int16, int32, int64;
54* double decoded to float32, float64;
55* boolean decoded to bool;
56* string decoded to string;
57* array decoded to slice;
58* structs decoded following the rules described in previous section;
59* datetime.iso8601 decoded as time.Time data type;
60* base64 decoded to string.
61
62## Implementation details
63
64xmlrpc package contains clientCodec type, that implements [rpc.ClientCodec](http://golang.org/pkg/net/rpc/#ClientCodec)
65interface of [net/rpc](http://golang.org/pkg/net/rpc) package.
66
67xmlrpc package works over HTTP protocol, but some internal functions
68and data type were made public to make it easier to create another
69implementation of xmlrpc that works over another protocol. To encode
70request body there is EncodeMethodCall function. To decode server
71response Response data type can be used.
72
73## Contribution
74
75Feel free to fork the project, submit pull requests, ask questions.
76
77## Authors
78
79Dmitry Maksimov (dmtmax@gmail.com)
diff --git a/vendor/github.com/kolo/xmlrpc/client.go b/vendor/github.com/kolo/xmlrpc/client.go
deleted file mode 100644
index fb66b65..0000000
--- a/vendor/github.com/kolo/xmlrpc/client.go
+++ /dev/null
@@ -1,144 +0,0 @@
1package xmlrpc
2
3import (
4 "fmt"
5 "io/ioutil"
6 "net/http"
7 "net/http/cookiejar"
8 "net/rpc"
9 "net/url"
10)
11
12type Client struct {
13 *rpc.Client
14}
15
16// clientCodec is rpc.ClientCodec interface implementation.
17type clientCodec struct {
18 // url presents url of xmlrpc service
19 url *url.URL
20
21 // httpClient works with HTTP protocol
22 httpClient *http.Client
23
24 // cookies stores cookies received on last request
25 cookies http.CookieJar
26
27 // responses presents map of active requests. It is required to return request id, that
28 // rpc.Client can mark them as done.
29 responses map[uint64]*http.Response
30
31 response *Response
32
33 // ready presents channel, that is used to link request and it`s response.
34 ready chan uint64
35}
36
37func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
38 httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)
39
40 if codec.cookies != nil {
41 for _, cookie := range codec.cookies.Cookies(codec.url) {
42 httpRequest.AddCookie(cookie)
43 }
44 }
45
46 if err != nil {
47 return err
48 }
49
50 var httpResponse *http.Response
51 httpResponse, err = codec.httpClient.Do(httpRequest)
52
53 if err != nil {
54 return err
55 }
56
57 if codec.cookies != nil {
58 codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
59 }
60
61 codec.responses[request.Seq] = httpResponse
62 codec.ready <- request.Seq
63
64 return nil
65}
66
67func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
68 seq := <-codec.ready
69 httpResponse := codec.responses[seq]
70
71 if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
72 return fmt.Errorf("request error: bad status code - %d", httpResponse.StatusCode)
73 }
74
75 respData, err := ioutil.ReadAll(httpResponse.Body)
76
77 if err != nil {
78 return err
79 }
80
81 httpResponse.Body.Close()
82
83 resp := NewResponse(respData)
84
85 if resp.Failed() {
86 response.Error = fmt.Sprintf("%v", resp.Err())
87 }
88
89 codec.response = resp
90
91 response.Seq = seq
92 delete(codec.responses, seq)
93
94 return nil
95}
96
97func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) {
98 if v == nil {
99 return nil
100 }
101
102 if err = codec.response.Unmarshal(v); err != nil {
103 return err
104 }
105
106 return nil
107}
108
109func (codec *clientCodec) Close() error {
110 transport := codec.httpClient.Transport.(*http.Transport)
111 transport.CloseIdleConnections()
112 return nil
113}
114
115// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service.
116func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
117 if transport == nil {
118 transport = http.DefaultTransport
119 }
120
121 httpClient := &http.Client{Transport: transport}
122
123 jar, err := cookiejar.New(nil)
124
125 if err != nil {
126 return nil, err
127 }
128
129 u, err := url.Parse(requrl)
130
131 if err != nil {
132 return nil, err
133 }
134
135 codec := clientCodec{
136 url: u,
137 httpClient: httpClient,
138 ready: make(chan uint64),
139 responses: make(map[uint64]*http.Response),
140 cookies: jar,
141 }
142
143 return &Client{rpc.NewClientWithCodec(&codec)}, nil
144}
diff --git a/vendor/github.com/kolo/xmlrpc/decoder.go b/vendor/github.com/kolo/xmlrpc/decoder.go
deleted file mode 100644
index b739559..0000000
--- a/vendor/github.com/kolo/xmlrpc/decoder.go
+++ /dev/null
@@ -1,449 +0,0 @@
1package xmlrpc
2
3import (
4 "bytes"
5 "encoding/xml"
6 "errors"
7 "fmt"
8 "io"
9 "reflect"
10 "strconv"
11 "strings"
12 "time"
13)
14
15const iso8601 = "20060102T15:04:05"
16
17var (
18 // CharsetReader is a function to generate reader which converts a non UTF-8
19 // charset into UTF-8.
20 CharsetReader func(string, io.Reader) (io.Reader, error)
21
22 invalidXmlError = errors.New("invalid xml")
23)
24
25type TypeMismatchError string
26
27func (e TypeMismatchError) Error() string { return string(e) }
28
29type decoder struct {
30 *xml.Decoder
31}
32
33func unmarshal(data []byte, v interface{}) (err error) {
34 dec := &decoder{xml.NewDecoder(bytes.NewBuffer(data))}
35
36 if CharsetReader != nil {
37 dec.CharsetReader = CharsetReader
38 }
39
40 var tok xml.Token
41 for {
42 if tok, err = dec.Token(); err != nil {
43 return err
44 }
45
46 if t, ok := tok.(xml.StartElement); ok {
47 if t.Name.Local == "value" {
48 val := reflect.ValueOf(v)
49 if val.Kind() != reflect.Ptr {
50 return errors.New("non-pointer value passed to unmarshal")
51 }
52 if err = dec.decodeValue(val.Elem()); err != nil {
53 return err
54 }
55
56 break
57 }
58 }
59 }
60
61 // read until end of document
62 err = dec.Skip()
63 if err != nil && err != io.EOF {
64 return err
65 }
66
67 return nil
68}
69
70func (dec *decoder) decodeValue(val reflect.Value) error {
71 var tok xml.Token
72 var err error
73
74 if val.Kind() == reflect.Ptr {
75 if val.IsNil() {
76 val.Set(reflect.New(val.Type().Elem()))
77 }
78 val = val.Elem()
79 }
80
81 var typeName string
82 for {
83 if tok, err = dec.Token(); err != nil {
84 return err
85 }
86
87 if t, ok := tok.(xml.EndElement); ok {
88 if t.Name.Local == "value" {
89 return nil
90 } else {
91 return invalidXmlError
92 }
93 }
94
95 if t, ok := tok.(xml.StartElement); ok {
96 typeName = t.Name.Local
97 break
98 }
99
100 // Treat value data without type identifier as string
101 if t, ok := tok.(xml.CharData); ok {
102 if value := strings.TrimSpace(string(t)); value != "" {
103 if err = checkType(val, reflect.String); err != nil {
104 return err
105 }
106
107 val.SetString(value)
108 return nil
109 }
110 }
111 }
112
113 switch typeName {
114 case "struct":
115 ismap := false
116 pmap := val
117 valType := val.Type()
118
119 if err = checkType(val, reflect.Struct); err != nil {
120 if checkType(val, reflect.Map) == nil {
121 if valType.Key().Kind() != reflect.String {
122 return fmt.Errorf("only maps with string key type can be unmarshalled")
123 }
124 ismap = true
125 } else if checkType(val, reflect.Interface) == nil && val.IsNil() {
126 var dummy map[string]interface{}
127 pmap = reflect.New(reflect.TypeOf(dummy)).Elem()
128 valType = pmap.Type()
129 ismap = true
130 } else {
131 return err
132 }
133 }
134
135 var fields map[string]reflect.Value
136
137 if !ismap {
138 fields = make(map[string]reflect.Value)
139
140 for i := 0; i < valType.NumField(); i++ {
141 field := valType.Field(i)
142 fieldVal := val.FieldByName(field.Name)
143
144 if fieldVal.CanSet() {
145 if fn := field.Tag.Get("xmlrpc"); fn != "" {
146 fields[fn] = fieldVal
147 } else {
148 fields[field.Name] = fieldVal
149 }
150 }
151 }
152 } else {
153 // Create initial empty map
154 pmap.Set(reflect.MakeMap(valType))
155 }
156
157 // Process struct members.
158 StructLoop:
159 for {
160 if tok, err = dec.Token(); err != nil {
161 return err
162 }
163 switch t := tok.(type) {
164 case xml.StartElement:
165 if t.Name.Local != "member" {
166 return invalidXmlError
167 }
168
169 tagName, fieldName, err := dec.readTag()
170 if err != nil {
171 return err
172 }
173 if tagName != "name" {
174 return invalidXmlError
175 }
176
177 var fv reflect.Value
178 ok := true
179
180 if !ismap {
181 fv, ok = fields[string(fieldName)]
182 } else {
183 fv = reflect.New(valType.Elem())
184 }
185
186 if ok {
187 for {
188 if tok, err = dec.Token(); err != nil {
189 return err
190 }
191 if t, ok := tok.(xml.StartElement); ok && t.Name.Local == "value" {
192 if err = dec.decodeValue(fv); err != nil {
193 return err
194 }
195
196 // </value>
197 if err = dec.Skip(); err != nil {
198 return err
199 }
200
201 break
202 }
203 }
204 }
205
206 // </member>
207 if err = dec.Skip(); err != nil {
208 return err
209 }
210
211 if ismap {
212 pmap.SetMapIndex(reflect.ValueOf(string(fieldName)), reflect.Indirect(fv))
213 val.Set(pmap)
214 }
215 case xml.EndElement:
216 break StructLoop
217 }
218 }
219 case "array":
220 pslice := val
221 if checkType(val, reflect.Interface) == nil && val.IsNil() {
222 var dummy []interface{}
223 pslice = reflect.New(reflect.TypeOf(dummy)).Elem()
224 } else if err = checkType(val, reflect.Slice); err != nil {
225 return err
226 }
227
228 ArrayLoop:
229 for {
230 if tok, err = dec.Token(); err != nil {
231 return err
232 }
233
234 switch t := tok.(type) {
235 case xml.StartElement:
236 if t.Name.Local != "data" {
237 return invalidXmlError
238 }
239
240 slice := reflect.MakeSlice(pslice.Type(), 0, 0)
241
242 DataLoop:
243 for {
244 if tok, err = dec.Token(); err != nil {
245 return err
246 }
247
248 switch tt := tok.(type) {
249 case xml.StartElement:
250 if tt.Name.Local != "value" {
251 return invalidXmlError
252 }
253
254 v := reflect.New(pslice.Type().Elem())
255 if err = dec.decodeValue(v); err != nil {
256 return err
257 }
258
259 slice = reflect.Append(slice, v.Elem())
260
261 // </value>
262 if err = dec.Skip(); err != nil {
263 return err
264 }
265 case xml.EndElement:
266 pslice.Set(slice)
267 val.Set(pslice)
268 break DataLoop
269 }
270 }
271 case xml.EndElement:
272 break ArrayLoop
273 }
274 }
275 default:
276 if tok, err = dec.Token(); err != nil {
277 return err
278 }
279
280 var data []byte
281
282 switch t := tok.(type) {
283 case xml.EndElement:
284 return nil
285 case xml.CharData:
286 data = []byte(t.Copy())
287 default:
288 return invalidXmlError
289 }
290
291 switch typeName {
292 case "int", "i4", "i8":
293 if checkType(val, reflect.Interface) == nil && val.IsNil() {
294 i, err := strconv.ParseInt(string(data), 10, 64)
295 if err != nil {
296 return err
297 }
298
299 pi := reflect.New(reflect.TypeOf(i)).Elem()
300 pi.SetInt(i)
301 val.Set(pi)
302 } else if err = checkType(val, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64); err != nil {
303 return err
304 } else {
305 i, err := strconv.ParseInt(string(data), 10, val.Type().Bits())
306 if err != nil {
307 return err
308 }
309
310 val.SetInt(i)
311 }
312 case "string", "base64":
313 str := string(data)
314 if checkType(val, reflect.Interface) == nil && val.IsNil() {
315 pstr := reflect.New(reflect.TypeOf(str)).Elem()
316 pstr.SetString(str)
317 val.Set(pstr)
318 } else if err = checkType(val, reflect.String); err != nil {
319 return err
320 } else {
321 val.SetString(str)
322 }
323 case "dateTime.iso8601":
324 t, err := time.Parse(iso8601, string(data))
325 if err != nil {
326 return err
327 }
328
329 if checkType(val, reflect.Interface) == nil && val.IsNil() {
330 ptime := reflect.New(reflect.TypeOf(t)).Elem()
331 ptime.Set(reflect.ValueOf(t))
332 val.Set(ptime)
333 } else if _, ok := val.Interface().(time.Time); !ok {
334 return TypeMismatchError(fmt.Sprintf("error: type mismatch error - can't decode %v to time", val.Kind()))
335 } else {
336 val.Set(reflect.ValueOf(t))
337 }
338 case "boolean":
339 v, err := strconv.ParseBool(string(data))
340 if err != nil {
341 return err
342 }
343
344 if checkType(val, reflect.Interface) == nil && val.IsNil() {
345 pv := reflect.New(reflect.TypeOf(v)).Elem()
346 pv.SetBool(v)
347 val.Set(pv)
348 } else if err = checkType(val, reflect.Bool); err != nil {
349 return err
350 } else {
351 val.SetBool(v)
352 }
353 case "double":
354 if checkType(val, reflect.Interface) == nil && val.IsNil() {
355 i, err := strconv.ParseFloat(string(data), 64)
356 if err != nil {
357 return err
358 }
359
360 pdouble := reflect.New(reflect.TypeOf(i)).Elem()
361 pdouble.SetFloat(i)
362 val.Set(pdouble)
363 } else if err = checkType(val, reflect.Float32, reflect.Float64); err != nil {
364 return err
365 } else {
366 i, err := strconv.ParseFloat(string(data), val.Type().Bits())
367 if err != nil {
368 return err
369 }
370
371 val.SetFloat(i)
372 }
373 default:
374 return errors.New("unsupported type")
375 }
376
377 // </type>
378 if err = dec.Skip(); err != nil {
379 return err
380 }
381 }
382
383 return nil
384}
385
386func (dec *decoder) readTag() (string, []byte, error) {
387 var tok xml.Token
388 var err error
389
390 var name string
391 for {
392 if tok, err = dec.Token(); err != nil {
393 return "", nil, err
394 }
395
396 if t, ok := tok.(xml.StartElement); ok {
397 name = t.Name.Local
398 break
399 }
400 }
401
402 value, err := dec.readCharData()
403 if err != nil {
404 return "", nil, err
405 }
406
407 return name, value, dec.Skip()
408}
409
410func (dec *decoder) readCharData() ([]byte, error) {
411 var tok xml.Token
412 var err error
413
414 if tok, err = dec.Token(); err != nil {
415 return nil, err
416 }
417
418 if t, ok := tok.(xml.CharData); ok {
419 return []byte(t.Copy()), nil
420 } else {
421 return nil, invalidXmlError
422 }
423}
424
425func checkType(val reflect.Value, kinds ...reflect.Kind) error {
426 if len(kinds) == 0 {
427 return nil
428 }
429
430 if val.Kind() == reflect.Ptr {
431 val = val.Elem()
432 }
433
434 match := false
435
436 for _, kind := range kinds {
437 if val.Kind() == kind {
438 match = true
439 break
440 }
441 }
442
443 if !match {
444 return TypeMismatchError(fmt.Sprintf("error: type mismatch - can't unmarshal %v to %v",
445 val.Kind(), kinds[0]))
446 }
447
448 return nil
449}
diff --git a/vendor/github.com/kolo/xmlrpc/encoder.go b/vendor/github.com/kolo/xmlrpc/encoder.go
deleted file mode 100644
index bb1285f..0000000
--- a/vendor/github.com/kolo/xmlrpc/encoder.go
+++ /dev/null
@@ -1,164 +0,0 @@
1package xmlrpc
2
3import (
4 "bytes"
5 "encoding/xml"
6 "fmt"
7 "reflect"
8 "strconv"
9 "time"
10)
11
12type encodeFunc func(reflect.Value) ([]byte, error)
13
14func marshal(v interface{}) ([]byte, error) {
15 if v == nil {
16 return []byte{}, nil
17 }
18
19 val := reflect.ValueOf(v)
20 return encodeValue(val)
21}
22
23func encodeValue(val reflect.Value) ([]byte, error) {
24 var b []byte
25 var err error
26
27 if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
28 if val.IsNil() {
29 return []byte("<value/>"), nil
30 }
31
32 val = val.Elem()
33 }
34
35 switch val.Kind() {
36 case reflect.Struct:
37 switch val.Interface().(type) {
38 case time.Time:
39 t := val.Interface().(time.Time)
40 b = []byte(fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", t.Format(iso8601)))
41 default:
42 b, err = encodeStruct(val)
43 }
44 case reflect.Map:
45 b, err = encodeMap(val)
46 case reflect.Slice:
47 b, err = encodeSlice(val)
48 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
49 b = []byte(fmt.Sprintf("<int>%s</int>", strconv.FormatInt(val.Int(), 10)))
50 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
51 b = []byte(fmt.Sprintf("<i4>%s</i4>", strconv.FormatUint(val.Uint(), 10)))
52 case reflect.Float32, reflect.Float64:
53 b = []byte(fmt.Sprintf("<double>%s</double>",
54 strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())))
55 case reflect.Bool:
56 if val.Bool() {
57 b = []byte("<boolean>1</boolean>")
58 } else {
59 b = []byte("<boolean>0</boolean>")
60 }
61 case reflect.String:
62 var buf bytes.Buffer
63
64 xml.Escape(&buf, []byte(val.String()))
65
66 if _, ok := val.Interface().(Base64); ok {
67 b = []byte(fmt.Sprintf("<base64>%s</base64>", buf.String()))
68 } else {
69 b = []byte(fmt.Sprintf("<string>%s</string>", buf.String()))
70 }
71 default:
72 return nil, fmt.Errorf("xmlrpc encode error: unsupported type")
73 }
74
75 if err != nil {
76 return nil, err
77 }
78
79 return []byte(fmt.Sprintf("<value>%s</value>", string(b))), nil
80}
81
82func encodeStruct(val reflect.Value) ([]byte, error) {
83 var b bytes.Buffer
84
85 b.WriteString("<struct>")
86
87 t := val.Type()
88 for i := 0; i < t.NumField(); i++ {
89 b.WriteString("<member>")
90 f := t.Field(i)
91
92 name := f.Tag.Get("xmlrpc")
93 if name == "" {
94 name = f.Name
95 }
96 b.WriteString(fmt.Sprintf("<name>%s</name>", name))
97
98 p, err := encodeValue(val.FieldByName(f.Name))
99 if err != nil {
100 return nil, err
101 }
102 b.Write(p)
103
104 b.WriteString("</member>")
105 }
106
107 b.WriteString("</struct>")
108
109 return b.Bytes(), nil
110}
111
112func encodeMap(val reflect.Value) ([]byte, error) {
113 var t = val.Type()
114
115 if t.Key().Kind() != reflect.String {
116 return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
117 }
118
119 var b bytes.Buffer
120
121 b.WriteString("<struct>")
122
123 keys := val.MapKeys()
124
125 for i := 0; i < val.Len(); i++ {
126 key := keys[i]
127 kval := val.MapIndex(key)
128
129 b.WriteString("<member>")
130 b.WriteString(fmt.Sprintf("<name>%s</name>", key.String()))
131
132 p, err := encodeValue(kval)
133
134 if err != nil {
135 return nil, err
136 }
137
138 b.Write(p)
139 b.WriteString("</member>")
140 }
141
142 b.WriteString("</struct>")
143
144 return b.Bytes(), nil
145}
146
147func encodeSlice(val reflect.Value) ([]byte, error) {
148 var b bytes.Buffer
149
150 b.WriteString("<array><data>")
151
152 for i := 0; i < val.Len(); i++ {
153 p, err := encodeValue(val.Index(i))
154 if err != nil {
155 return nil, err
156 }
157
158 b.Write(p)
159 }
160
161 b.WriteString("</data></array>")
162
163 return b.Bytes(), nil
164}
diff --git a/vendor/github.com/kolo/xmlrpc/request.go b/vendor/github.com/kolo/xmlrpc/request.go
deleted file mode 100644
index acb8251..0000000
--- a/vendor/github.com/kolo/xmlrpc/request.go
+++ /dev/null
@@ -1,57 +0,0 @@
1package xmlrpc
2
3import (
4 "bytes"
5 "fmt"
6 "net/http"
7)
8
9func NewRequest(url string, method string, args interface{}) (*http.Request, error) {
10 var t []interface{}
11 var ok bool
12 if t, ok = args.([]interface{}); !ok {
13 if args != nil {
14 t = []interface{}{args}
15 }
16 }
17
18 body, err := EncodeMethodCall(method, t...)
19 if err != nil {
20 return nil, err
21 }
22
23 request, err := http.NewRequest("POST", url, bytes.NewReader(body))
24 if err != nil {
25 return nil, err
26 }
27
28 request.Header.Set("Content-Type", "text/xml")
29 request.Header.Set("Content-Length", fmt.Sprintf("%d", len(body)))
30
31 return request, nil
32}
33
34func EncodeMethodCall(method string, args ...interface{}) ([]byte, error) {
35 var b bytes.Buffer
36 b.WriteString(`<?xml version="1.0" encoding="UTF-8"?>`)
37 b.WriteString(fmt.Sprintf("<methodCall><methodName>%s</methodName>", method))
38
39 if args != nil {
40 b.WriteString("<params>")
41
42 for _, arg := range args {
43 p, err := marshal(arg)
44 if err != nil {
45 return nil, err
46 }
47
48 b.WriteString(fmt.Sprintf("<param>%s</param>", string(p)))
49 }
50
51 b.WriteString("</params>")
52 }
53
54 b.WriteString("</methodCall>")
55
56 return b.Bytes(), nil
57}
diff --git a/vendor/github.com/kolo/xmlrpc/response.go b/vendor/github.com/kolo/xmlrpc/response.go
deleted file mode 100644
index 6742a1c..0000000
--- a/vendor/github.com/kolo/xmlrpc/response.go
+++ /dev/null
@@ -1,52 +0,0 @@
1package xmlrpc
2
3import (
4 "regexp"
5)
6
7var (
8 faultRx = regexp.MustCompile(`<fault>(\s|\S)+</fault>`)
9)
10
11type failedResponse struct {
12 Code int `xmlrpc:"faultCode"`
13 Error string `xmlrpc:"faultString"`
14}
15
16func (r *failedResponse) err() error {
17 return &xmlrpcError{
18 code: r.Code,
19 err: r.Error,
20 }
21}
22
23type Response struct {
24 data []byte
25}
26
27func NewResponse(data []byte) *Response {
28 return &Response{
29 data: data,
30 }
31}
32
33func (r *Response) Failed() bool {
34 return faultRx.Match(r.data)
35}
36
37func (r *Response) Err() error {
38 failedResp := new(failedResponse)
39 if err := unmarshal(r.data, failedResp); err != nil {
40 return err
41 }
42
43 return failedResp.err()
44}
45
46func (r *Response) Unmarshal(v interface{}) error {
47 if err := unmarshal(r.data, v); err != nil {
48 return err
49 }
50
51 return nil
52}
diff --git a/vendor/github.com/kolo/xmlrpc/test_server.rb b/vendor/github.com/kolo/xmlrpc/test_server.rb
deleted file mode 100644
index 1b1ff87..0000000
--- a/vendor/github.com/kolo/xmlrpc/test_server.rb
+++ /dev/null
@@ -1,25 +0,0 @@
1# encoding: utf-8
2
3require "xmlrpc/server"
4
5class Service
6 def time
7 Time.now
8 end
9
10 def upcase(s)
11 s.upcase
12 end
13
14 def sum(x, y)
15 x + y
16 end
17
18 def error
19 raise XMLRPC::FaultException.new(500, "Server error")
20 end
21end
22
23server = XMLRPC::Server.new 5001, 'localhost'
24server.add_handler "service", Service.new
25server.serve
diff --git a/vendor/github.com/kolo/xmlrpc/xmlrpc.go b/vendor/github.com/kolo/xmlrpc/xmlrpc.go
deleted file mode 100644
index 8766403..0000000
--- a/vendor/github.com/kolo/xmlrpc/xmlrpc.go
+++ /dev/null
@@ -1,19 +0,0 @@
1package xmlrpc
2
3import (
4 "fmt"
5)
6
7// xmlrpcError represents errors returned on xmlrpc request.
8type xmlrpcError struct {
9 code int
10 err string
11}
12
13// Error() method implements Error interface
14func (e *xmlrpcError) Error() string {
15 return fmt.Sprintf("error: \"%s\" code: %d", e.err, e.code)
16}
17
18// Base64 represents value in base64 encoding
19type Base64 string
diff --git a/vendor/github.com/kolo/xmlrpc/LICENSE b/vendor/github.com/mattn/go-xmlrpc/LICENSE
index 8103dd1..740fa93 100644
--- a/vendor/github.com/kolo/xmlrpc/LICENSE
+++ b/vendor/github.com/mattn/go-xmlrpc/LICENSE
@@ -1,4 +1,6 @@
1Copyright (C) 2012 Dmitry Maksimov 1The MIT License (MIT)
2
3Copyright (c) 2017 Yasuhiro Matsumoto
2 4
3Permission is hereby granted, free of charge, to any person obtaining a copy 5Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal 6of this software and associated documentation files (the "Software"), to deal
@@ -7,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is 9copies of the Software, and to permit persons to whom the Software is
8furnished to do so, subject to the following conditions: 10furnished to do so, subject to the following conditions:
9 11
10The above copyright notice and this permission notice shall be included in 12The above copyright notice and this permission notice shall be included in all
11all copies or substantial portions of the Software. 13copies or substantial portions of the Software.
12 14
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19THE SOFTWARE. 21SOFTWARE.
diff --git a/vendor/github.com/mattn/go-xmlrpc/README.md b/vendor/github.com/mattn/go-xmlrpc/README.md
new file mode 100644
index 0000000..de6cb1d
--- /dev/null
+++ b/vendor/github.com/mattn/go-xmlrpc/README.md
@@ -0,0 +1,48 @@
1# go-xmlrpc
2
3xmlrpc interface for go
4
5## Usage
6
7```go
8package main
9
10import (
11 "github.com/mattn/go-xmlrpc"
12 "fmt"
13 "log"
14)
15
16func main() {
17 res, e := xmlrpc.Call(
18 "http://your-blog.example.com/xmlrpc.php",
19 "metaWeblog.getRecentPosts",
20 "blog-id",
21 "user-id",
22 "password",
23 10)
24 if e != nil {
25 log.Fatal(e)
26 }
27 for _, p := range res.(xmlrpc.Array) {
28 for k, v := range p.(xmlrpc.Struct) {
29 fmt.Printf("%s=%v\n", k, v)
30 }
31 fmt.Println()
32 }
33}
34```
35
36## Installation
37
38```
39$ go get github.com/mattn/go-xmlrpc
40```
41
42## License
43
44MIT
45
46## Author
47
48Yasuhiro Matsumoto (a.k.a. mattn)
diff --git a/vendor/github.com/mattn/go-xmlrpc/xmlrpc.go b/vendor/github.com/mattn/go-xmlrpc/xmlrpc.go
new file mode 100644
index 0000000..cfbe4d8
--- /dev/null
+++ b/vendor/github.com/mattn/go-xmlrpc/xmlrpc.go
@@ -0,0 +1,365 @@
1package xmlrpc
2
3import (
4 "bytes"
5 "encoding/base64"
6 "encoding/xml"
7 "errors"
8 "fmt"
9 "io"
10 "io/ioutil"
11 "net/http"
12 "reflect"
13 "strconv"
14 "strings"
15 "time"
16)
17
18type Array []interface{}
19type Struct map[string]interface{}
20
21var xmlSpecial = map[byte]string{
22 '<': "&lt;",
23 '>': "&gt;",
24 '"': "&quot;",
25 '\'': "&apos;",
26 '&': "&amp;",
27}
28
29func xmlEscape(s string) string {
30 var b bytes.Buffer
31 for i := 0; i < len(s); i++ {
32 c := s[i]
33 if s, ok := xmlSpecial[c]; ok {
34 b.WriteString(s)
35 } else {
36 b.WriteByte(c)
37 }
38 }
39 return b.String()
40}
41
42type valueNode struct {
43 Type string `xml:"attr"`
44 Body string `xml:"chardata"`
45}
46
47func next(p *xml.Decoder) (xml.Name, interface{}, error) {
48 se, e := nextStart(p)
49 if e != nil {
50 return xml.Name{}, nil, e
51 }
52
53 var nv interface{}
54 switch se.Name.Local {
55 case "string":
56 var s string
57 if e = p.DecodeElement(&s, &se); e != nil {
58 return xml.Name{}, nil, e
59 }
60 return xml.Name{}, s, nil
61 case "boolean":
62 var s string
63 if e = p.DecodeElement(&s, &se); e != nil {
64 return xml.Name{}, nil, e
65 }
66 s = strings.TrimSpace(s)
67 var b bool
68 switch s {
69 case "true", "1":
70 b = true
71 case "false", "0":
72 b = false
73 default:
74 e = errors.New("invalid boolean value")
75 }
76 return xml.Name{}, b, e
77 case "int", "i1", "i2", "i4", "i8":
78 var s string
79 var i int
80 if e = p.DecodeElement(&s, &se); e != nil {
81 return xml.Name{}, nil, e
82 }
83 i, e = strconv.Atoi(strings.TrimSpace(s))
84 return xml.Name{}, i, e
85 case "double":
86 var s string
87 var f float64
88 if e = p.DecodeElement(&s, &se); e != nil {
89 return xml.Name{}, nil, e
90 }
91 f, e = strconv.ParseFloat(strings.TrimSpace(s), 64)
92 return xml.Name{}, f, e
93 case "dateTime.iso8601":
94 var s string
95 if e = p.DecodeElement(&s, &se); e != nil {
96 return xml.Name{}, nil, e
97 }
98 t, e := time.Parse("20060102T15:04:05", s)
99 if e != nil {
100 t, e = time.Parse("2006-01-02T15:04:05-07:00", s)
101 if e != nil {
102 t, e = time.Parse("2006-01-02T15:04:05", s)
103 }
104 }
105 return xml.Name{}, t, e
106 case "base64":
107 var s string
108 if e = p.DecodeElement(&s, &se); e != nil {
109 return xml.Name{}, nil, e
110 }
111 if b, e := base64.StdEncoding.DecodeString(s); e != nil {
112 return xml.Name{}, nil, e
113 } else {
114 return xml.Name{}, b, nil
115 }
116 case "member":
117 nextStart(p)
118 return next(p)
119 case "value":
120 nextStart(p)
121 return next(p)
122 case "name":
123 nextStart(p)
124 return next(p)
125 case "struct":
126 st := Struct{}
127
128 se, e = nextStart(p)
129 for e == nil && se.Name.Local == "member" {
130 // name
131 se, e = nextStart(p)
132 if se.Name.Local != "name" {
133 return xml.Name{}, nil, errors.New("invalid response")
134 }
135 if e != nil {
136 break
137 }
138 var name string
139 if e = p.DecodeElement(&name, &se); e != nil {
140 return xml.Name{}, nil, e
141 }
142 se, e = nextStart(p)
143 if e != nil {
144 break
145 }
146
147 // value
148 _, value, e := next(p)
149 if se.Name.Local != "value" {
150 return xml.Name{}, nil, errors.New("invalid response")
151 }
152 if e != nil {
153 break
154 }
155 st[name] = value
156
157 se, e = nextStart(p)
158 if e != nil {
159 break
160 }
161 }
162 return xml.Name{}, st, nil
163 case "array":
164 var ar Array
165 nextStart(p) // data
166 for {
167 nextStart(p) // top of value
168 _, value, e := next(p)
169 if e != nil {
170 break
171 }
172 ar = append(ar, value)
173 }
174 return xml.Name{}, ar, nil
175 case "nil":
176 return xml.Name{}, nil, nil
177 }
178
179 if e = p.DecodeElement(nv, &se); e != nil {
180 return xml.Name{}, nil, e
181 }
182 return se.Name, nv, e
183}
184func nextStart(p *xml.Decoder) (xml.StartElement, error) {
185 for {
186 t, e := p.Token()
187 if e != nil {
188 return xml.StartElement{}, e
189 }
190 switch t := t.(type) {
191 case xml.StartElement:
192 return t, nil
193 }
194 }
195 panic("unreachable")
196}
197
198func toXml(v interface{}, typ bool) (s string) {
199 if v == nil {
200 return "<nil/>"
201 }
202 r := reflect.ValueOf(v)
203 t := r.Type()
204 k := t.Kind()
205
206 if b, ok := v.([]byte); ok {
207 return "<base64>" + base64.StdEncoding.EncodeToString(b) + "</base64>"
208 }
209
210 switch k {
211 case reflect.Invalid:
212 panic("unsupported type")
213 case reflect.Bool:
214 return fmt.Sprintf("<boolean>%v</boolean>", v)
215 case reflect.Int,
216 reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
217 reflect.Uint,
218 reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
219 if typ {
220 return fmt.Sprintf("<int>%v</int>", v)
221 }
222 return fmt.Sprintf("%v", v)
223 case reflect.Uintptr:
224 panic("unsupported type")
225 case reflect.Float32, reflect.Float64:
226 if typ {
227 return fmt.Sprintf("<double>%v</double>", v)
228 }
229 return fmt.Sprintf("%v", v)
230 case reflect.Complex64, reflect.Complex128:
231 panic("unsupported type")
232 case reflect.Array:
233 s = "<array><data>"
234 for n := 0; n < r.Len(); n++ {
235 s += "<value>"
236 s += toXml(r.Index(n).Interface(), typ)
237 s += "</value>"
238 }
239 s += "</data></array>"
240 return s
241 case reflect.Chan:
242 panic("unsupported type")
243 case reflect.Func:
244 panic("unsupported type")
245 case reflect.Interface:
246 return toXml(r.Elem(), typ)
247 case reflect.Map:
248 s = "<struct>"
249 for _, key := range r.MapKeys() {
250 s += "<member>"
251 s += "<name>" + xmlEscape(key.Interface().(string)) + "</name>"
252 s += "<value>" + toXml(r.MapIndex(key).Interface(), typ) + "</value>"
253 s += "</member>"
254 }
255 s += "</struct>"
256 return s
257 case reflect.Ptr:
258 panic("unsupported type")
259 case reflect.Slice:
260 s = "<array><data>"
261 for n := 0; n < r.Len(); n++ {
262 s += "<value>"
263 s += toXml(r.Index(n).Interface(), typ)
264 s += "</value>"
265 }
266 s += "</data></array>"
267 return s
268 case reflect.String:
269 if typ {
270 return fmt.Sprintf("<string>%v</string>", xmlEscape(v.(string)))
271 }
272 return xmlEscape(v.(string))
273 case reflect.Struct:
274 s = "<struct>"
275 for n := 0; n < r.NumField(); n++ {
276 s += "<member>"
277 s += "<name>" + t.Field(n).Name + "</name>"
278 s += "<value>" + toXml(r.FieldByIndex([]int{n}).Interface(), true) + "</value>"
279 s += "</member>"
280 }
281 s += "</struct>"
282 return s
283 case reflect.UnsafePointer:
284 return toXml(r.Elem(), typ)
285 }
286 return
287}
288
289// Client is client of XMLRPC
290type Client struct {
291 HttpClient *http.Client
292 url string
293}
294
295// NewClient create new Client
296func NewClient(url string) *Client {
297 return &Client{
298 HttpClient: &http.Client{Transport: http.DefaultTransport, Timeout: 10 * time.Second},
299 url: url,
300 }
301}
302
303func makeRequest(name string, args ...interface{}) *bytes.Buffer {
304 buf := new(bytes.Buffer)
305 buf.WriteString(`<?xml version="1.0"?><methodCall>`)
306 buf.WriteString("<methodName>" + xmlEscape(name) + "</methodName>")
307 buf.WriteString("<params>")
308 for _, arg := range args {
309 buf.WriteString("<param><value>")
310 buf.WriteString(toXml(arg, true))
311 buf.WriteString("</value></param>")
312 }
313 buf.WriteString("</params></methodCall>")
314 return buf
315}
316
317func call(client *http.Client, url, name string, args ...interface{}) (v interface{}, e error) {
318 r, e := httpClient.Post(url, "text/xml", makeRequest(name, args...))
319 if e != nil {
320 return nil, e
321 }
322
323 // Since we do not always read the entire body, discard the rest, which
324 // allows the http transport to reuse the connection.
325 defer io.Copy(ioutil.Discard, r.Body)
326 defer r.Body.Close()
327
328 if r.StatusCode/100 != 2 {
329 return nil, errors.New(http.StatusText(http.StatusBadRequest))
330 }
331
332 p := xml.NewDecoder(r.Body)
333 se, e := nextStart(p) // methodResponse
334 if se.Name.Local != "methodResponse" {
335 return nil, errors.New("invalid response: missing methodResponse")
336 }
337 se, e = nextStart(p) // params
338 if se.Name.Local != "params" {
339 return nil, errors.New("invalid response: missing params")
340 }
341 se, e = nextStart(p) // param
342 if se.Name.Local != "param" {
343 return nil, errors.New("invalid response: missing param")
344 }
345 se, e = nextStart(p) // value
346 if se.Name.Local != "value" {
347 return nil, errors.New("invalid response: missing value")
348 }
349 _, v, e = next(p)
350 return v, e
351}
352
353// Call call remote procedures function name with args
354func (c *Client) Call(name string, args ...interface{}) (v interface{}, e error) {
355 return call(c.HttpClient, c.url, name, args...)
356}
357
358// Global httpClient allows us to pool/reuse connections and not wastefully
359// re-create transports for each request.
360var httpClient = &http.Client{Transport: http.DefaultTransport, Timeout: 10 * time.Second}
361
362// Call call remote procedures function name with args
363func Call(url, name string, args ...interface{}) (v interface{}, e error) {
364 return call(httpClient, url, name, args...)
365}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 8dbff89..c8cad52 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -65,18 +65,18 @@
65 "versionExact": "v1.0.0" 65 "versionExact": "v1.0.0"
66 }, 66 },
67 { 67 {
68 "checksumSHA1": "tw3ocqSpa9ikzUV6qhcKBAAO6WU=",
69 "path": "github.com/kolo/xmlrpc",
70 "revision": "0826b98aaa29c0766956cb40d45cf7482a597671",
71 "revisionTime": "2015-04-13T19:18:30Z"
72 },
73 {
74 "checksumSHA1": "IdBAvtVSv0sbi8sEsLovnZubims=", 68 "checksumSHA1": "IdBAvtVSv0sbi8sEsLovnZubims=",
75 "path": "github.com/lufia/iostat", 69 "path": "github.com/lufia/iostat",
76 "revision": "9f7362b77ad333b26c01c99de52a11bdb650ded2", 70 "revision": "9f7362b77ad333b26c01c99de52a11bdb650ded2",
77 "revisionTime": "2017-06-05T15:08:45Z" 71 "revisionTime": "2017-06-05T15:08:45Z"
78 }, 72 },
79 { 73 {
74 "checksumSHA1": "8uAFFK5p8F39X1MOLsHrgTI0hzw=",
75 "path": "github.com/mattn/go-xmlrpc",
76 "revision": "b7a1b57d9142f44a3a8f5a80aadf8d2d6ea2ca22",
77 "revisionTime": "2018-04-20T00:08:13Z"
78 },
79 {
80 "checksumSHA1": "aodj/cITRyuaZSh84DDhrZjh76U=", 80 "checksumSHA1": "aodj/cITRyuaZSh84DDhrZjh76U=",
81 "path": "github.com/matttproud/golang_protobuf_extensions/pbutil", 81 "path": "github.com/matttproud/golang_protobuf_extensions/pbutil",
82 "revision": "3247c84500bff8d9fb6d579d800f20b3e091582c", 82 "revision": "3247c84500bff8d9fb6d579d800f20b3e091582c",