Codebase list golang-github-kolo-xmlrpc / 9179795
Update upstream source from tag 'upstream/0.0_git20200310.e035052' Update to upstream version '0.0~git20200310.e035052' with Debian dir a6d39782099147317b1e65bbc4ad553d124fdb0c Shengjing Zhu 3 years ago
12 changed file(s) with 432 addition(s) and 135 deletion(s). Raw diff Collapse all Expand all
0 [![GoDoc](https://godoc.org/github.com/kolo/xmlrpc?status.svg)](https://godoc.org/github.com/kolo/xmlrpc)
1
02 ## Overview
13
24 xmlrpc is an implementation of client side part of XMLRPC protocol in Go language.
5
6 ## Status
7
8 This project is in minimal maintenance mode with no further development. Bug fixes
9 are accepted, but it might take some time until they will be merged.
310
411 ## Installation
512
2734 arguments.
2835
2936 Data types encoding rules:
37
3038 * int, int8, int16, int32, int64 encoded to int;
3139 * float32, float64 encoded to double;
3240 * bool encoded to boolean;
3341 * string encoded to string;
3442 * time.Time encoded to datetime.iso8601;
3543 * xmlrpc.Base64 encoded to base64;
36 * slice decoded to array;
44 * slice encoded to array;
3745
38 Structs decoded to struct by following rules:
46 Structs encoded to struct by following rules:
47
3948 * all public field become struct members;
4049 * field name become member name;
4150 * if field has xmlrpc tag, its value become member name.
51 * for fields tagged with `",omitempty"`, empty values are omitted;
4252
4353 Server method can accept few arguments, to handle this case there is
4454 special approach to handle slice of empty interfaces (`[]interface{}`).
4959 Result of remote function is decoded to native Go data type.
5060
5161 Data types decoding rules:
62
5263 * int, i4 decoded to int, int8, int16, int32, int64;
5364 * double decoded to float32, float64;
5465 * boolean decoded to bool;
7182
7283 ## Contribution
7384
74 Feel free to fork the project, submit pull requests, ask questions.
85 See [project status](#status).
7586
7687 ## Authors
7788
00 package xmlrpc
11
22 import (
3 "errors"
34 "fmt"
45 "io/ioutil"
56 "net/http"
67 "net/http/cookiejar"
78 "net/rpc"
89 "net/url"
10 "sync"
911 )
1012
1113 type Client struct {
2628 // responses presents map of active requests. It is required to return request id, that
2729 // rpc.Client can mark them as done.
2830 responses map[uint64]*http.Response
31 mutex sync.Mutex
2932
30 response *Response
33 response Response
3134
3235 // ready presents channel, that is used to link request and it`s response.
3336 ready chan uint64
37
38 // close notifies codec is closed.
39 close chan uint64
3440 }
3541
3642 func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
3743 httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)
3844
45 if err != nil {
46 return err
47 }
48
3949 if codec.cookies != nil {
4050 for _, cookie := range codec.cookies.Cookies(codec.url) {
4151 httpRequest.AddCookie(cookie)
4252 }
43 }
44
45 if err != nil {
46 return err
4753 }
4854
4955 var httpResponse *http.Response
5763 codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
5864 }
5965
66 codec.mutex.Lock()
6067 codec.responses[request.Seq] = httpResponse
68 codec.mutex.Unlock()
69
6170 codec.ready <- request.Seq
6271
6372 return nil
6473 }
6574
6675 func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
67 seq := <-codec.ready
76 var seq uint64
77 select {
78 case seq = <-codec.ready:
79 case <-codec.close:
80 return errors.New("codec is closed")
81 }
82 response.Seq = seq
83
84 codec.mutex.Lock()
6885 httpResponse := codec.responses[seq]
86 delete(codec.responses, seq)
87 codec.mutex.Unlock()
88
89 defer httpResponse.Body.Close()
6990
7091 if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
71 return fmt.Errorf("request error: bad status code - %d", httpResponse.StatusCode)
92 response.Error = fmt.Sprintf("request error: bad status code - %d", httpResponse.StatusCode)
93 return nil
7294 }
7395
74 respData, err := ioutil.ReadAll(httpResponse.Body)
75
96 body, err := ioutil.ReadAll(httpResponse.Body)
7697 if err != nil {
77 return err
98 response.Error = err.Error()
99 return nil
78100 }
79101
80 httpResponse.Body.Close()
81
82 resp := NewResponse(respData)
83
84 if resp.Failed() {
85 response.Error = fmt.Sprintf("%v", resp.Err())
102 resp := Response(body)
103 if err := resp.Err(); err != nil {
104 response.Error = err.Error()
105 return nil
86106 }
87107
88108 codec.response = resp
89
90 response.Seq = seq
91 delete(codec.responses, seq)
92109
93110 return nil
94111 }
97114 if v == nil {
98115 return nil
99116 }
100
101 if err = codec.response.Unmarshal(v); err != nil {
102 return err
103 }
104
105 return nil
117 return codec.response.Unmarshal(v)
106118 }
107119
108120 func (codec *clientCodec) Close() error {
109 transport := codec.httpClient.Transport.(*http.Transport)
110 transport.CloseIdleConnections()
121 if transport, ok := codec.httpClient.Transport.(*http.Transport); ok {
122 transport.CloseIdleConnections()
123 }
124
125 close(codec.close)
126
111127 return nil
112128 }
113129
134150 codec := clientCodec{
135151 url: u,
136152 httpClient: httpClient,
153 close: make(chan uint64),
137154 ready: make(chan uint64),
138155 responses: make(map[uint64]*http.Response),
139156 cookies: jar,
0 // +build integration
1
02 package xmlrpc
13
24 import (
5 "context"
6 "io"
7 "net/http"
8 "net/http/httptest"
9 "runtime"
10 "sync"
311 "testing"
412 "time"
513 )
6876 }
6977 }
7078
79 func Test_ConcurrentCalls(t *testing.T) {
80 client := newClient(t)
81
82 call := func() {
83 var result time.Time
84 client.Call("service.time", nil, &result)
85 }
86
87 var wg sync.WaitGroup
88 for i := 0; i < 100; i++ {
89 wg.Add(1)
90 go func() {
91 call()
92 wg.Done()
93 }()
94 }
95
96 wg.Wait()
97 client.Close()
98 }
99
100 func Test_CloseMemoryLeak(t *testing.T) {
101 expected := runtime.NumGoroutine()
102
103 for i := 0; i < 3; i++ {
104 client := newClient(t)
105 client.Call("service.time", nil, nil)
106 client.Close()
107 }
108
109 var actual int
110
111 // It takes some time to stop running goroutinges. This function checks number of
112 // running goroutines. It finishes execution if number is same as expected or timeout
113 // has been reached.
114 func() {
115 ctx, cancel := context.WithTimeout(context.Background(), time.Second)
116 defer cancel()
117
118 for {
119 select {
120 case <-ctx.Done():
121 return
122 default:
123 actual = runtime.NumGoroutine()
124 if actual == expected {
125 return
126 }
127 }
128 }
129 }()
130
131 if actual != expected {
132 t.Errorf("expected number of running goroutines to be %d, but got %d", expected, actual)
133 }
134 }
135
136 func Test_BadStatus(t *testing.T) {
137
138 // this is a mock xmlrpc server which sends an invalid status code on the first request
139 // and an empty methodResponse for all subsequence requests
140 first := true
141 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
142 if first {
143 first = false
144 http.Error(w, "bad status", http.StatusInternalServerError)
145 } else {
146 io.WriteString(w, `
147 <?xml version="1.0" encoding="UTF-8"?>
148 <methodResponse>
149 <params>
150 <param>
151 <value>
152 <struct></struct>
153 </value>
154 </param>
155 </params>
156 </methodResponse>
157 `)
158 }
159 }))
160
161 client, err := NewClient(ts.URL, nil)
162 if err != nil {
163 t.Fatalf("Can't create client: %v", err)
164 }
165 defer client.Close()
166
167 var result interface{}
168
169 // expect an error due to the bad status code
170 if err := client.Call("method", nil, &result); err == nil {
171 t.Fatalf("Bad status didn't result in error")
172 }
173
174 // expect subsequent calls to succeed
175 if err := client.Call("method", nil, &result); err != nil {
176 t.Fatalf("Failed to recover after bad status: %v", err)
177 }
178 }
179
71180 func newClient(t *testing.T) *Client {
72181 client, err := NewClient("http://localhost:5001", nil)
73182 if err != nil {
74183 t.Fatalf("Can't create client: %v", err)
75184 }
76
77185 return client
78186 }
1111 "time"
1212 )
1313
14 const iso8601 = "20060102T15:04:05"
14 const (
15 iso8601 = "20060102T15:04:05"
16 iso8601Z = "20060102T15:04:05Z07:00"
17 iso8601Hyphen = "2006-01-02T15:04:05"
18 iso8601HyphenZ = "2006-01-02T15:04:05Z07:00"
19 )
1520
1621 var (
1722 // CharsetReader is a function to generate reader which converts a non UTF-8
1823 // charset into UTF-8.
1924 CharsetReader func(string, io.Reader) (io.Reader, error)
2025
26 timeLayouts = []string{iso8601, iso8601Z, iso8601Hyphen, iso8601HyphenZ}
2127 invalidXmlError = errors.New("invalid xml")
2228 )
2329
123129 ismap = true
124130 } else if checkType(val, reflect.Interface) == nil && val.IsNil() {
125131 var dummy map[string]interface{}
126 pmap = reflect.New(reflect.TypeOf(dummy)).Elem()
127 valType = pmap.Type()
132 valType = reflect.TypeOf(dummy)
133 pmap = reflect.New(valType).Elem()
134 val.Set(pmap)
128135 ismap = true
129136 } else {
130137 return err
216223 }
217224 }
218225 case "array":
219 pslice := val
226 slice := val
220227 if checkType(val, reflect.Interface) == nil && val.IsNil() {
221 var dummy []interface{}
222 pslice = reflect.New(reflect.TypeOf(dummy)).Elem()
228 slice = reflect.ValueOf([]interface{}{})
223229 } else if err = checkType(val, reflect.Slice); err != nil {
224230 return err
225231 }
232238
233239 switch t := tok.(type) {
234240 case xml.StartElement:
241 var index int
235242 if t.Name.Local != "data" {
236243 return invalidXmlError
237244 }
238
239 slice := reflect.MakeSlice(pslice.Type(), 0, 0)
240
241245 DataLoop:
242246 for {
243247 if tok, err = dec.Token(); err != nil {
250254 return invalidXmlError
251255 }
252256
253 v := reflect.New(pslice.Type().Elem())
254 if err = dec.decodeValue(v); err != nil {
255 return err
257 if index < slice.Len() {
258 v := slice.Index(index)
259 if v.Kind() == reflect.Interface {
260 v = v.Elem()
261 }
262 if v.Kind() != reflect.Ptr {
263 return errors.New("error: cannot write to non-pointer array element")
264 }
265 if err = dec.decodeValue(v); err != nil {
266 return err
267 }
268 } else {
269 v := reflect.New(slice.Type().Elem())
270 if err = dec.decodeValue(v); err != nil {
271 return err
272 }
273 slice = reflect.Append(slice, v.Elem())
256274 }
257
258 slice = reflect.Append(slice, v.Elem())
259275
260276 // </value>
261277 if err = dec.Skip(); err != nil {
262278 return err
263279 }
280 index++
264281 case xml.EndElement:
265 pslice.Set(slice)
266 val.Set(pslice)
282 val.Set(slice)
267283 break DataLoop
268284 }
269285 }
320336 val.SetString(str)
321337 }
322338 case "dateTime.iso8601":
323 t, err := time.Parse(iso8601, string(data))
339 var t time.Time
340 var err error
341
342 for _, layout := range timeLayouts {
343 t, err = time.Parse(layout, string(data))
344 if err == nil {
345 break
346 }
347 }
324348 if err != nil {
325349 return err
326350 }
2626 ptr interface{}
2727 xml string
2828 }{
29 // int, i4, i8
30 {0, new(*int), "<value><int></int></value>"},
2931 {100, new(*int), "<value><int>100</int></value>"},
32 {389451, new(*int), "<value><i4>389451</i4></value>"},
3033 {int64(45659074), new(*int64), "<value><i8>45659074</i8></value>"},
34
35 // string
3136 {"Once upon a time", new(*string), "<value><string>Once upon a time</string></value>"},
3237 {"Mike & Mick <London, UK>", new(*string), "<value><string>Mike &amp; Mick &lt;London, UK&gt;</string></value>"},
3338 {"Once upon a time", new(*string), "<value>Once upon a time</value>"},
39
40 // base64
3441 {"T25jZSB1cG9uIGEgdGltZQ==", new(*string), "<value><base64>T25jZSB1cG9uIGEgdGltZQ==</base64></value>"},
42
43 // boolean
3544 {true, new(*bool), "<value><boolean>1</boolean></value>"},
3645 {false, new(*bool), "<value><boolean>0</boolean></value>"},
46
47 // double
3748 {12.134, new(*float32), "<value><double>12.134</double></value>"},
3849 {-12.134, new(*float32), "<value><double>-12.134</double></value>"},
39 {time.Unix(1386622812, 0).UTC(), new(*time.Time), "<value><dateTime.iso8601>20131209T21:00:12</dateTime.iso8601></value>"},
50
51 // datetime.iso8601
52 {_time("2013-12-09T21:00:12Z"), new(*time.Time), "<value><dateTime.iso8601>20131209T21:00:12</dateTime.iso8601></value>"},
53 {_time("2013-12-09T21:00:12Z"), new(*time.Time), "<value><dateTime.iso8601>20131209T21:00:12Z</dateTime.iso8601></value>"},
54 {_time("2013-12-09T21:00:12-01:00"), new(*time.Time), "<value><dateTime.iso8601>20131209T21:00:12-01:00</dateTime.iso8601></value>"},
55 {_time("2013-12-09T21:00:12+01:00"), new(*time.Time), "<value><dateTime.iso8601>20131209T21:00:12+01:00</dateTime.iso8601></value>"},
56 {_time("2013-12-09T21:00:12Z"), new(*time.Time), "<value><dateTime.iso8601>2013-12-09T21:00:12</dateTime.iso8601></value>"},
57 {_time("2013-12-09T21:00:12Z"), new(*time.Time), "<value><dateTime.iso8601>2013-12-09T21:00:12Z</dateTime.iso8601></value>"},
58 {_time("2013-12-09T21:00:12-01:00"), new(*time.Time), "<value><dateTime.iso8601>2013-12-09T21:00:12-01:00</dateTime.iso8601></value>"},
59 {_time("2013-12-09T21:00:12+01:00"), new(*time.Time), "<value><dateTime.iso8601>2013-12-09T21:00:12+01:00</dateTime.iso8601></value>"},
60
61 // array
4062 {[]int{1, 5, 7}, new(*[]int), "<value><array><data><value><int>1</int></value><value><int>5</int></value><value><int>7</int></value></data></array></value>"},
63 {[]interface{}{"A", "5"}, new(interface{}), "<value><array><data><value><string>A</string></value><value><string>5</string></value></data></array></value>"},
64 {[]interface{}{"A", int64(5)}, new(interface{}), "<value><array><data><value><string>A</string></value><value><int>5</int></value></data></array></value>"},
65
66 // struct
4167 {book{"War and Piece", 20}, new(*book), "<value><struct><member><name>Title</name><value><string>War and Piece</string></value></member><member><name>Amount</name><value><int>20</int></value></member></struct></value>"},
4268 {bookUnexported{}, new(*bookUnexported), "<value><struct><member><name>title</name><value><string>War and Piece</string></value></member><member><name>amount</name><value><int>20</int></value></member></struct></value>"},
43 {0, new(*int), "<value><int></int></value>"},
44 {[]interface{}{"A", "5"}, new(interface{}), "<value><array><data><value><string>A</string></value><value><string>5</string></value></data></array></value>"},
45 //{map[string]interface{}{"Name": "John Smith",
46 // "Age": 6,
47 // "Wight": []interface{}{66.67, 100.5}},
48 // new(interface{}), "<value><struct><member><name>Name</name><value><string>John Smith</string></value></member><member><name>Age</name><value><int>6</int></value></member><member><name>Wight</name><value><array><data><value><double>66.67</double></value><value><double>100.5</double></value></data></array></value></member></struct></value>"},
4969 {map[string]interface{}{"Name": "John Smith"}, new(interface{}), "<value><struct><member><name>Name</name><value><string>John Smith</string></value></member></struct></value>"},
70 {map[string]interface{}{}, new(interface{}), "<value><struct></struct></value>"},
71 }
72
73 func _time(s string) time.Time {
74 t, err := time.Parse(time.RFC3339, s)
75 if err != nil {
76 panic(fmt.Sprintf("time parsing error: %v", err))
77 }
78 return t
5079 }
5180
5281 func Test_unmarshal(t *testing.T) {
90119 func Test_typeMismatchError(t *testing.T) {
91120 var s string
92121
93 tt := unmarshalTests[0]
122 encoded := "<value><int>100</int></value>"
94123 var err error
95124
96 if err = unmarshal([]byte(tt.xml), &s); err == nil {
125 if err = unmarshal([]byte(encoded), &s); err == nil {
97126 t.Fatal("unmarshal error: expected error, but didn't get it")
98127 }
99128
107136
108137 if err := unmarshal([]byte("<value/>"), &v); err != nil {
109138 t.Fatalf("unmarshal error: %v", err)
139 }
140 }
141
142 const structEmptyXML = `
143 <value>
144 <struct>
145 </struct>
146 </value>
147 `
148
149 func Test_unmarshalEmptyStruct(t *testing.T) {
150 var v interface{}
151 if err := unmarshal([]byte(structEmptyXML), &v); err != nil {
152 t.Fatal(err)
153 }
154 if v == nil {
155 t.Fatalf("got nil map")
156 }
157 }
158
159 const arrayValueXML = `
160 <value>
161 <array>
162 <data>
163 <value><int>234</int></value>
164 <value><boolean>1</boolean></value>
165 <value><string>Hello World</string></value>
166 <value><string>Extra Value</string></value>
167 </data>
168 </array>
169 </value>
170 `
171
172 func Test_unmarshalExistingArray(t *testing.T) {
173
174 var (
175 v1 int
176 v2 bool
177 v3 string
178
179 v = []interface{}{&v1, &v2, &v3}
180 )
181 if err := unmarshal([]byte(arrayValueXML), &v); err != nil {
182 t.Fatal(err)
183 }
184
185 // check pre-existing values
186 if want := 234; v1 != want {
187 t.Fatalf("want %d, got %d", want, v1)
188 }
189 if want := true; v2 != want {
190 t.Fatalf("want %t, got %t", want, v2)
191 }
192 if want := "Hello World"; v3 != want {
193 t.Fatalf("want %s, got %s", want, v3)
194 }
195 // check the appended result
196 if n := len(v); n != 4 {
197 t.Fatalf("missing appended result")
198 }
199 if got, ok := v[3].(string); !ok || got != "Extra Value" {
200 t.Fatalf("got %s, want %s", got, "Extra Value")
110201 }
111202 }
112203
44 "encoding/xml"
55 "fmt"
66 "reflect"
7 "sort"
78 "strconv"
9 "strings"
810 "time"
911 )
12
13 // Base64 represents value in base64 encoding
14 type Base64 string
1015
1116 type encodeFunc func(reflect.Value) ([]byte, error)
1217
5055 b = []byte(fmt.Sprintf("<i4>%s</i4>", strconv.FormatUint(val.Uint(), 10)))
5156 case reflect.Float32, reflect.Float64:
5257 b = []byte(fmt.Sprintf("<double>%s</double>",
53 strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())))
58 strconv.FormatFloat(val.Float(), 'f', -1, val.Type().Bits())))
5459 case reflect.Bool:
5560 if val.Bool() {
5661 b = []byte("<boolean>1</boolean>")
7883 return []byte(fmt.Sprintf("<value>%s</value>", string(b))), nil
7984 }
8085
81 func encodeStruct(val reflect.Value) ([]byte, error) {
86 func encodeStruct(structVal reflect.Value) ([]byte, error) {
8287 var b bytes.Buffer
8388
8489 b.WriteString("<struct>")
8590
86 t := val.Type()
87 for i := 0; i < t.NumField(); i++ {
88 b.WriteString("<member>")
89 f := t.Field(i)
91 structType := structVal.Type()
92 for i := 0; i < structType.NumField(); i++ {
93 fieldVal := structVal.Field(i)
94 fieldType := structType.Field(i)
9095
91 name := f.Tag.Get("xmlrpc")
96 name := fieldType.Tag.Get("xmlrpc")
97 // if the tag has the omitempty property, skip it
98 if strings.HasSuffix(name, ",omitempty") && isZero(fieldVal) {
99 continue
100 }
101 name = strings.TrimSuffix(name, ",omitempty")
92102 if name == "" {
93 name = f.Name
103 name = fieldType.Name
94104 }
95 b.WriteString(fmt.Sprintf("<name>%s</name>", name))
96105
97 p, err := encodeValue(val.FieldByName(f.Name))
106 p, err := encodeValue(fieldVal)
98107 if err != nil {
99108 return nil, err
100109 }
110
111 b.WriteString("<member>")
112 b.WriteString(fmt.Sprintf("<name>%s</name>", name))
101113 b.Write(p)
102
103114 b.WriteString("</member>")
104115 }
105116
107118
108119 return b.Bytes(), nil
109120 }
121
122 var sortMapKeys bool
110123
111124 func encodeMap(val reflect.Value) ([]byte, error) {
112125 var t = val.Type()
120133 b.WriteString("<struct>")
121134
122135 keys := val.MapKeys()
136
137 if sortMapKeys {
138 sort.Slice(keys, func(i, j int) bool { return keys[i].String() < keys[j].String() })
139 }
123140
124141 for i := 0; i < val.Len(); i++ {
125142 key := keys[i]
1616 {false, "<value><boolean>0</boolean></value>"},
1717 {12.134, "<value><double>12.134</double></value>"},
1818 {-12.134, "<value><double>-12.134</double></value>"},
19 {738777323.0, "<value><double>738777323</double></value>"},
1920 {time.Unix(1386622812, 0).UTC(), "<value><dateTime.iso8601>20131209T21:00:12</dateTime.iso8601></value>"},
2021 {[]interface{}{1, "one"}, "<value><array><data><value><int>1</int></value><value><string>one</string></value></data></array></value>"},
2122 {&struct {
2728 }{}, "<value><struct><member><name>value</name><value/></member></struct></value>"},
2829 {
2930 map[string]interface{}{"title": "War and Piece", "amount": 20},
30 "<value><struct><member><name>title</name><value><string>War and Piece</string></value></member><member><name>amount</name><value><int>20</int></value></member></struct></value>",
31 "<value><struct><member><name>amount</name><value><int>20</int></value></member><member><name>title</name><value><string>War and Piece</string></value></member></struct></value>",
3132 },
3233 {
3334 map[string]interface{}{
3536 "Age": 6,
3637 "Wight": []float32{66.67, 100.5},
3738 "Dates": map[string]interface{}{"Birth": time.Date(1829, time.November, 10, 23, 0, 0, 0, time.UTC), "Death": time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)}},
38 "<value><struct><member><name>Name</name><value><string>John Smith</string></value></member><member><name>Age</name><value><int>6</int></value></member><member><name>Wight</name><value><array><data><value><double>66.67</double></value><value><double>100.5</double></value></data></array></value></member><member><name>Dates</name><value><struct><member><name>Birth</name><value><dateTime.iso8601>18291110T23:00:00</dateTime.iso8601></value></member><member><name>Death</name><value><dateTime.iso8601>20091110T23:00:00</dateTime.iso8601></value></member></struct></value></member></struct></value>",
39 "<value><struct><member><name>Age</name><value><int>6</int></value></member><member><name>Dates</name><value><struct><member><name>Birth</name><value><dateTime.iso8601>18291110T23:00:00</dateTime.iso8601></value></member><member><name>Death</name><value><dateTime.iso8601>20091110T23:00:00</dateTime.iso8601></value></member></struct></value></member><member><name>Name</name><value><string>John Smith</string></value></member><member><name>Wight</name><value><array><data><value><double>66.67</double></value><value><double>100.5</double></value></data></array></value></member></struct></value>",
3940 },
41 {&struct {
42 Title string
43 Amount int
44 Author string `xmlrpc:"author,omitempty"`
45 }{
46 Title: "War and Piece", Amount: 20,
47 }, "<value><struct><member><name>Title</name><value><string>War and Piece</string></value></member><member><name>Amount</name><value><int>20</int></value></member></struct></value>"},
48 {&struct {
49 Title string
50 Amount int
51 Author string `xmlrpc:"author,omitempty"`
52 }{
53 Title: "War and Piece", Amount: 20, Author: "Leo Tolstoy",
54 }, "<value><struct><member><name>Title</name><value><string>War and Piece</string></value></member><member><name>Amount</name><value><int>20</int></value></member><member><name>author</name><value><string>Leo Tolstoy</string></value></member></struct></value>"},
55 {&struct {
56 }{}, "<value><struct></struct></value>"},
4057 }
4158
4259 func Test_marshal(t *testing.T) {
60 sortMapKeys = true
61
4362 for _, tt := range marshalTests {
4463 b, err := marshal(tt.value)
4564 if err != nil {
0 package xmlrpc
1
2 import (
3 "math"
4 . "reflect"
5 )
6
7 func isZero(v Value) bool {
8 switch v.Kind() {
9 case Bool:
10 return !v.Bool()
11 case Int, Int8, Int16, Int32, Int64:
12 return v.Int() == 0
13 case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
14 return v.Uint() == 0
15 case Float32, Float64:
16 return math.Float64bits(v.Float()) == 0
17 case Complex64, Complex128:
18 c := v.Complex()
19 return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
20 case Array:
21 for i := 0; i < v.Len(); i++ {
22 if !isZero(v.Index(i)) {
23 return false
24 }
25 }
26 return true
27 case Chan, Func, Interface, Map, Ptr, Slice, UnsafePointer:
28 return v.IsNil()
29 case String:
30 return v.Len() == 0
31 case Struct:
32 for i := 0; i < v.NumField(); i++ {
33 if !isZero(v.Field(i)) {
34 return false
35 }
36 }
37 return true
38 default:
39 // This should never happens, but will act as a safeguard for
40 // later, as a default value doesn't makes sense here.
41 panic(&ValueError{"reflect.Value.IsZero", v.Kind()})
42 }
43 }
00 package xmlrpc
11
22 import (
3 "fmt"
34 "regexp"
45 )
56
78 faultRx = regexp.MustCompile(`<fault>(\s|\S)+</fault>`)
89 )
910
10 type failedResponse struct {
11 Code int `xmlrpc:"faultCode"`
12 Error string `xmlrpc:"faultString"`
11 // FaultError is returned from the server when an invalid call is made
12 type FaultError struct {
13 Code int `xmlrpc:"faultCode"`
14 String string `xmlrpc:"faultString"`
1315 }
1416
15 func (r *failedResponse) err() error {
16 return &xmlrpcError{
17 code: r.Code,
18 err: r.Error,
19 }
17 // Error implements the error interface
18 func (e FaultError) Error() string {
19 return fmt.Sprintf("Fault(%d): %s", e.Code, e.String)
2020 }
2121
22 type Response struct {
23 data []byte
22 type Response []byte
23
24 func (r Response) Err() error {
25 if !faultRx.Match(r) {
26 return nil
27 }
28 var fault FaultError
29 if err := unmarshal(r, &fault); err != nil {
30 return err
31 }
32 return fault
2433 }
2534
26 func NewResponse(data []byte) *Response {
27 return &Response{
28 data: data,
29 }
30 }
31
32 func (r *Response) Failed() bool {
33 return faultRx.Match(r.data)
34 }
35
36 func (r *Response) Err() error {
37 failedResp := new(failedResponse)
38 if err := unmarshal(r.data, failedResp); err != nil {
39 return err
40 }
41
42 return failedResp.err()
43 }
44
45 func (r *Response) Unmarshal(v interface{}) error {
46 if err := unmarshal(r.data, v); err != nil {
35 func (r Response) Unmarshal(v interface{}) error {
36 if err := unmarshal(r, v); err != nil {
4737 return err
4838 }
4939
2727 </methodResponse>`
2828
2929 func Test_failedResponse(t *testing.T) {
30 resp := NewResponse([]byte(faultRespXml))
31
32 if !resp.Failed() {
33 t.Fatal("Failed() error: expected true, got false")
34 }
30 resp := Response([]byte(faultRespXml))
3531
3632 if resp.Err() == nil {
3733 t.Fatal("Err() error: expected error, got nil")
3834 }
3935
40 err := resp.Err().(*xmlrpcError)
41 if err.code != 410 && err.err != "You must log in before using this part of Bugzilla." {
36 fault := resp.Err().(FaultError)
37 if fault.Code != 410 && fault.String != "You must log in before using this part of Bugzilla." {
4238 t.Fatal("Err() error: got wrong error")
4339 }
4440 }
6460 </params>
6561 </methodResponse>`
6662
63 func Test_responseWithEmptyValue(t *testing.T) {
64 resp := Response([]byte(emptyValResp))
6765
68 func Test_responseWithEmptyValue(t *testing.T) {
69 resp := NewResponse([]byte(emptyValResp))
70
71 result := struct{
72 User string `xmlrpc:"user"`
66 result := struct {
67 User string `xmlrpc:"user"`
7368 Token string `xmlrpc:"token"`
7469 }{}
7570
1414 x + y
1515 end
1616
17 def error
18 raise XMLRPC::FaultException.new(500, "Server error")
19 end
17 def error
18 raise XMLRPC::FaultException.new(500, "Server error")
19 end
2020 end
2121
2222 server = XMLRPC::Server.new 5001, 'localhost'
+0
-19
xmlrpc.go less more
0 package xmlrpc
1
2 import (
3 "fmt"
4 )
5
6 // xmlrpcError represents errors returned on xmlrpc request.
7 type xmlrpcError struct {
8 code int
9 err string
10 }
11
12 // Error() method implements Error interface
13 func (e *xmlrpcError) Error() string {
14 return fmt.Sprintf("error: \"%s\" code: %d", e.err, e.code)
15 }
16
17 // Base64 represents value in base64 encoding
18 type Base64 string