pax_global_header 0000666 0000000 0000000 00000000064 12513013206 0014503 g ustar 00root root 0000000 0000000 52 comment=0826b98aaa29c0766956cb40d45cf7482a597671
golang-github-kolo-xmlrpc-0+git20150413.0826b98/ 0000775 0000000 0000000 00000000000 12513013206 0020520 5 ustar 00root root 0000000 0000000 golang-github-kolo-xmlrpc-0+git20150413.0826b98/LICENSE 0000664 0000000 0000000 00000002043 12513013206 0021524 0 ustar 00root root 0000000 0000000 Copyright (C) 2012 Dmitry Maksimov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
golang-github-kolo-xmlrpc-0+git20150413.0826b98/README.md 0000664 0000000 0000000 00000005127 12513013206 0022004 0 ustar 00root root 0000000 0000000 ## Overview
xmlrpc is an implementation of client side part of XMLRPC protocol in Go language.
## Installation
To install xmlrpc package run `go get github.com/kolo/xmlrpc`. To use
it in application add `"github.com/kolo/xmlrpc"` string to `import`
statement.
## Usage
client, _ := xmlrpc.NewClient("https://bugzilla.mozilla.org/xmlrpc.cgi", nil)
result := struct{
Version string `xmlrpc:"version"`
}{}
client.Call("Bugzilla.version", nil, &result)
fmt.Printf("Version: %s\n", result.Version) // Version: 4.2.7+
Second argument of NewClient function is an object that implements
[http.RoundTripper](http://golang.org/pkg/net/http/#RoundTripper)
interface, it can be used to get more control over connection options.
By default it initialized by http.DefaultTransport object.
### Arguments encoding
xmlrpc package supports encoding of native Go data types to method
arguments.
Data types encoding rules:
* int, int8, int16, int32, int64 encoded to int;
* float32, float64 encoded to double;
* bool encoded to boolean;
* string encoded to string;
* time.Time encoded to datetime.iso8601;
* xmlrpc.Base64 encoded to base64;
* slice decoded to array;
Structs decoded to struct by following rules:
* all public field become struct members;
* field name become member name;
* if field has xmlrpc tag, its value become member name.
Server method can accept few arguments, to handle this case there is
special approach to handle slice of empty interfaces (`[]interface{}`).
Each value of such slice encoded as separate argument.
### Result decoding
Result of remote function is decoded to native Go data type.
Data types decoding rules:
* int, i4 decoded to int, int8, int16, int32, int64;
* double decoded to float32, float64;
* boolean decoded to bool;
* string decoded to string;
* array decoded to slice;
* structs decoded following the rules described in previous section;
* datetime.iso8601 decoded as time.Time data type;
* base64 decoded to string.
## Implementation details
xmlrpc package contains clientCodec type, that implements [rpc.ClientCodec](http://golang.org/pkg/net/rpc/#ClientCodec)
interface of [net/rpc](http://golang.org/pkg/net/rpc) package.
xmlrpc package works over HTTP protocol, but some internal functions
and data type were made public to make it easier to create another
implementation of xmlrpc that works over another protocol. To encode
request body there is EncodeMethodCall function. To decode server
response Response data type can be used.
## Contribution
Feel free to fork the project, submit pull requests, ask questions.
## Authors
Dmitry Maksimov (dmtmax@gmail.com)
golang-github-kolo-xmlrpc-0+git20150413.0826b98/client.go 0000664 0000000 0000000 00000005614 12513013206 0022333 0 ustar 00root root 0000000 0000000 package xmlrpc
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/rpc"
"net/url"
)
type Client struct {
*rpc.Client
}
// clientCodec is rpc.ClientCodec interface implementation.
type clientCodec struct {
// url presents url of xmlrpc service
url *url.URL
// httpClient works with HTTP protocol
httpClient *http.Client
// cookies stores cookies received on last request
cookies http.CookieJar
// 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
// ready presents channel, that is used to link request and it`s response.
ready chan uint64
}
func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)
if codec.cookies != nil {
for _, cookie := range codec.cookies.Cookies(codec.url) {
httpRequest.AddCookie(cookie)
}
}
if err != nil {
return err
}
var httpResponse *http.Response
httpResponse, err = codec.httpClient.Do(httpRequest)
if err != nil {
return err
}
if codec.cookies != nil {
codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
}
codec.responses[request.Seq] = httpResponse
codec.ready <- request.Seq
return nil
}
func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
seq := <-codec.ready
httpResponse := codec.responses[seq]
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
return fmt.Errorf("request error: bad status code - %d", httpResponse.StatusCode)
}
respData, err := ioutil.ReadAll(httpResponse.Body)
if err != nil {
return err
}
httpResponse.Body.Close()
resp := NewResponse(respData)
if resp.Failed() {
response.Error = fmt.Sprintf("%v", resp.Err())
}
codec.response = resp
response.Seq = seq
delete(codec.responses, seq)
return nil
}
func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) {
if v == nil {
return nil
}
if err = codec.response.Unmarshal(v); err != nil {
return err
}
return nil
}
func (codec *clientCodec) Close() error {
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.
func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
if transport == nil {
transport = http.DefaultTransport
}
httpClient := &http.Client{Transport: transport}
jar, err := cookiejar.New(nil)
if err != nil {
return nil, err
}
u, err := url.Parse(requrl)
if err != nil {
return nil, err
}
codec := clientCodec{
url: u,
httpClient: httpClient,
ready: make(chan uint64),
responses: make(map[uint64]*http.Response),
cookies: jar,
}
return &Client{rpc.NewClientWithCodec(&codec)}, nil
}
golang-github-kolo-xmlrpc-0+git20150413.0826b98/client_test.go 0000664 0000000 0000000 00000003276 12513013206 0023374 0 ustar 00root root 0000000 0000000 package xmlrpc
import (
"testing"
"time"
)
func Test_CallWithoutArgs(t *testing.T) {
client := newClient(t)
defer client.Close()
var result time.Time
if err := client.Call("service.time", nil, &result); err != nil {
t.Fatalf("service.time call error: %v", err)
}
}
func Test_CallWithOneArg(t *testing.T) {
client := newClient(t)
defer client.Close()
var result string
if err := client.Call("service.upcase", "xmlrpc", &result); err != nil {
t.Fatalf("service.upcase call error: %v", err)
}
if result != "XMLRPC" {
t.Fatalf("Unexpected result of service.upcase: %s != %s", "XMLRPC", result)
}
}
func Test_CallWithTwoArgs(t *testing.T) {
client := newClient(t)
defer client.Close()
var sum int
if err := client.Call("service.sum", []interface{}{2, 3}, &sum); err != nil {
t.Fatalf("service.sum call error: %v", err)
}
if sum != 5 {
t.Fatalf("Unexpected result of service.sum: %d != %d", 5, sum)
}
}
func Test_TwoCalls(t *testing.T) {
client := newClient(t)
defer client.Close()
var upcase string
if err := client.Call("service.upcase", "xmlrpc", &upcase); err != nil {
t.Fatalf("service.upcase call error: %v", err)
}
var sum int
if err := client.Call("service.sum", []interface{}{2, 3}, &sum); err != nil {
t.Fatalf("service.sum call error: %v", err)
}
}
func Test_FailedCall(t *testing.T) {
client := newClient(t)
defer client.Close()
var result int
if err := client.Call("service.error", nil, &result); err == nil {
t.Fatal("expected service.error returns error, but it didn't")
}
}
func newClient(t *testing.T) *Client {
client, err := NewClient("http://localhost:5001", nil)
if err != nil {
t.Fatalf("Can't create client: %v", err)
}
return client
}
golang-github-kolo-xmlrpc-0+git20150413.0826b98/decoder.go 0000664 0000000 0000000 00000021607 12513013206 0022462 0 ustar 00root root 0000000 0000000 package xmlrpc
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"reflect"
"strconv"
"strings"
"time"
)
const iso8601 = "20060102T15:04:05"
var (
// CharsetReader is a function to generate reader which converts a non UTF-8
// charset into UTF-8.
CharsetReader func(string, io.Reader) (io.Reader, error)
invalidXmlError = errors.New("invalid xml")
)
type TypeMismatchError string
func (e TypeMismatchError) Error() string { return string(e) }
type decoder struct {
*xml.Decoder
}
func unmarshal(data []byte, v interface{}) (err error) {
dec := &decoder{xml.NewDecoder(bytes.NewBuffer(data))}
if CharsetReader != nil {
dec.CharsetReader = CharsetReader
}
var tok xml.Token
for {
if tok, err = dec.Token(); err != nil {
return err
}
if t, ok := tok.(xml.StartElement); ok {
if t.Name.Local == "value" {
val := reflect.ValueOf(v)
if val.Kind() != reflect.Ptr {
return errors.New("non-pointer value passed to unmarshal")
}
if err = dec.decodeValue(val.Elem()); err != nil {
return err
}
break
}
}
}
// read until end of document
err = dec.Skip()
if err != nil && err != io.EOF {
return err
}
return nil
}
func (dec *decoder) decodeValue(val reflect.Value) error {
var tok xml.Token
var err error
if val.Kind() == reflect.Ptr {
if val.IsNil() {
val.Set(reflect.New(val.Type().Elem()))
}
val = val.Elem()
}
var typeName string
for {
if tok, err = dec.Token(); err != nil {
return err
}
if t, ok := tok.(xml.EndElement); ok {
if t.Name.Local == "value" {
return nil
} else {
return invalidXmlError
}
}
if t, ok := tok.(xml.StartElement); ok {
typeName = t.Name.Local
break
}
// Treat value data without type identifier as string
if t, ok := tok.(xml.CharData); ok {
if value := strings.TrimSpace(string(t)); value != "" {
if err = checkType(val, reflect.String); err != nil {
return err
}
val.SetString(value)
return nil
}
}
}
switch typeName {
case "struct":
ismap := false
pmap := val
valType := val.Type()
if err = checkType(val, reflect.Struct); err != nil {
if checkType(val, reflect.Map) == nil {
if valType.Key().Kind() != reflect.String {
return fmt.Errorf("only maps with string key type can be unmarshalled")
}
ismap = true
} else if checkType(val, reflect.Interface) == nil && val.IsNil() {
var dummy map[string]interface{}
pmap = reflect.New(reflect.TypeOf(dummy)).Elem()
valType = pmap.Type()
ismap = true
} else {
return err
}
}
var fields map[string]reflect.Value
if !ismap {
fields = make(map[string]reflect.Value)
for i := 0; i < valType.NumField(); i++ {
field := valType.Field(i)
fieldVal := val.FieldByName(field.Name)
if fieldVal.CanSet() {
if fn := field.Tag.Get("xmlrpc"); fn != "" {
fields[fn] = fieldVal
} else {
fields[field.Name] = fieldVal
}
}
}
} else {
// Create initial empty map
pmap.Set(reflect.MakeMap(valType))
}
// Process struct members.
StructLoop:
for {
if tok, err = dec.Token(); err != nil {
return err
}
switch t := tok.(type) {
case xml.StartElement:
if t.Name.Local != "member" {
return invalidXmlError
}
tagName, fieldName, err := dec.readTag()
if err != nil {
return err
}
if tagName != "name" {
return invalidXmlError
}
var fv reflect.Value
ok := true
if !ismap {
fv, ok = fields[string(fieldName)]
} else {
fv = reflect.New(valType.Elem())
}
if ok {
for {
if tok, err = dec.Token(); err != nil {
return err
}
if t, ok := tok.(xml.StartElement); ok && t.Name.Local == "value" {
if err = dec.decodeValue(fv); err != nil {
return err
}
//
if err = dec.Skip(); err != nil {
return err
}
break
}
}
}
//
if err = dec.Skip(); err != nil {
return err
}
if ismap {
pmap.SetMapIndex(reflect.ValueOf(string(fieldName)), reflect.Indirect(fv))
val.Set(pmap)
}
case xml.EndElement:
break StructLoop
}
}
case "array":
pslice := val
if checkType(val, reflect.Interface) == nil && val.IsNil() {
var dummy []interface{}
pslice = reflect.New(reflect.TypeOf(dummy)).Elem()
} else if err = checkType(val, reflect.Slice); err != nil {
return err
}
ArrayLoop:
for {
if tok, err = dec.Token(); err != nil {
return err
}
switch t := tok.(type) {
case xml.StartElement:
if t.Name.Local != "data" {
return invalidXmlError
}
slice := reflect.MakeSlice(pslice.Type(), 0, 0)
DataLoop:
for {
if tok, err = dec.Token(); err != nil {
return err
}
switch tt := tok.(type) {
case xml.StartElement:
if tt.Name.Local != "value" {
return invalidXmlError
}
v := reflect.New(pslice.Type().Elem())
if err = dec.decodeValue(v); err != nil {
return err
}
slice = reflect.Append(slice, v.Elem())
//
if err = dec.Skip(); err != nil {
return err
}
case xml.EndElement:
pslice.Set(slice)
val.Set(pslice)
break DataLoop
}
}
case xml.EndElement:
break ArrayLoop
}
}
default:
if tok, err = dec.Token(); err != nil {
return err
}
var data []byte
switch t := tok.(type) {
case xml.EndElement:
return nil
case xml.CharData:
data = []byte(t.Copy())
default:
return invalidXmlError
}
switch typeName {
case "int", "i4", "i8":
if checkType(val, reflect.Interface) == nil && val.IsNil() {
i, err := strconv.ParseInt(string(data), 10, 64)
if err != nil {
return err
}
pi := reflect.New(reflect.TypeOf(i)).Elem()
pi.SetInt(i)
val.Set(pi)
} else if err = checkType(val, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64); err != nil {
return err
} else {
i, err := strconv.ParseInt(string(data), 10, val.Type().Bits())
if err != nil {
return err
}
val.SetInt(i)
}
case "string", "base64":
str := string(data)
if checkType(val, reflect.Interface) == nil && val.IsNil() {
pstr := reflect.New(reflect.TypeOf(str)).Elem()
pstr.SetString(str)
val.Set(pstr)
} else if err = checkType(val, reflect.String); err != nil {
return err
} else {
val.SetString(str)
}
case "dateTime.iso8601":
t, err := time.Parse(iso8601, string(data))
if err != nil {
return err
}
if checkType(val, reflect.Interface) == nil && val.IsNil() {
ptime := reflect.New(reflect.TypeOf(t)).Elem()
ptime.Set(reflect.ValueOf(t))
val.Set(ptime)
} else if _, ok := val.Interface().(time.Time); !ok {
return TypeMismatchError(fmt.Sprintf("error: type mismatch error - can't decode %v to time", val.Kind()))
} else {
val.Set(reflect.ValueOf(t))
}
case "boolean":
v, err := strconv.ParseBool(string(data))
if err != nil {
return err
}
if checkType(val, reflect.Interface) == nil && val.IsNil() {
pv := reflect.New(reflect.TypeOf(v)).Elem()
pv.SetBool(v)
val.Set(pv)
} else if err = checkType(val, reflect.Bool); err != nil {
return err
} else {
val.SetBool(v)
}
case "double":
if checkType(val, reflect.Interface) == nil && val.IsNil() {
i, err := strconv.ParseFloat(string(data), 64)
if err != nil {
return err
}
pdouble := reflect.New(reflect.TypeOf(i)).Elem()
pdouble.SetFloat(i)
val.Set(pdouble)
} else if err = checkType(val, reflect.Float32, reflect.Float64); err != nil {
return err
} else {
i, err := strconv.ParseFloat(string(data), val.Type().Bits())
if err != nil {
return err
}
val.SetFloat(i)
}
default:
return errors.New("unsupported type")
}
//
if err = dec.Skip(); err != nil {
return err
}
}
return nil
}
func (dec *decoder) readTag() (string, []byte, error) {
var tok xml.Token
var err error
var name string
for {
if tok, err = dec.Token(); err != nil {
return "", nil, err
}
if t, ok := tok.(xml.StartElement); ok {
name = t.Name.Local
break
}
}
value, err := dec.readCharData()
if err != nil {
return "", nil, err
}
return name, value, dec.Skip()
}
func (dec *decoder) readCharData() ([]byte, error) {
var tok xml.Token
var err error
if tok, err = dec.Token(); err != nil {
return nil, err
}
if t, ok := tok.(xml.CharData); ok {
return []byte(t.Copy()), nil
} else {
return nil, invalidXmlError
}
}
func checkType(val reflect.Value, kinds ...reflect.Kind) error {
if len(kinds) == 0 {
return nil
}
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
match := false
for _, kind := range kinds {
if val.Kind() == kind {
match = true
break
}
}
if !match {
return TypeMismatchError(fmt.Sprintf("error: type mismatch - can't unmarshal %v to %v",
val.Kind(), kinds[0]))
}
return nil
}
golang-github-kolo-xmlrpc-0+git20150413.0826b98/decoder_test.go 0000664 0000000 0000000 00000011470 12513013206 0023516 0 ustar 00root root 0000000 0000000 package xmlrpc
import (
"fmt"
"io"
"io/ioutil"
"reflect"
"testing"
"time"
"golang.org/x/text/encoding/charmap"
"golang.org/x/text/transform"
)
type book struct {
Title string
Amount int
}
type bookUnexported struct {
title string
amount int
}
var unmarshalTests = []struct {
value interface{}
ptr interface{}
xml string
}{
{100, new(*int), "100"},
{int64(45659074), new(*int64), "45659074"},
{"Once upon a time", new(*string), "Once upon a time"},
{"Mike & Mick ", new(*string), "Mike & Mick <London, UK>"},
{"Once upon a time", new(*string), "Once upon a time"},
{"T25jZSB1cG9uIGEgdGltZQ==", new(*string), "T25jZSB1cG9uIGEgdGltZQ=="},
{true, new(*bool), "1"},
{false, new(*bool), "0"},
{12.134, new(*float32), "12.134"},
{-12.134, new(*float32), "-12.134"},
{time.Unix(1386622812, 0).UTC(), new(*time.Time), "20131209T21:00:12"},
{[]int{1, 5, 7}, new(*[]int), "157"},
{book{"War and Piece", 20}, new(*book), "TitleWar and PieceAmount20"},
{bookUnexported{}, new(*bookUnexported), "titleWar and Pieceamount20"},
{0, new(*int), ""},
{[]interface{}{"A", "5"}, new(interface{}), "A5"},
//{map[string]interface{}{"Name": "John Smith",
// "Age": 6,
// "Wight": []interface{}{66.67, 100.5}},
// new(interface{}), "NameJohn SmithAge6Wight66.67100.5"},
{map[string]interface{}{"Name": "John Smith"}, new(interface{}), "NameJohn Smith"},
}
func Test_unmarshal(t *testing.T) {
for _, tt := range unmarshalTests {
v := reflect.New(reflect.TypeOf(tt.value))
if err := unmarshal([]byte(tt.xml), v.Interface()); err != nil {
t.Fatalf("unmarshal error: %v", err)
}
v = v.Elem()
if v.Kind() == reflect.Slice {
vv := reflect.ValueOf(tt.value)
if vv.Len() != v.Len() {
t.Fatalf("unmarshal error:\nexpected: %v\n got: %v", tt.value, v.Interface())
}
for i := 0; i < v.Len(); i++ {
if v.Index(i).Interface() != vv.Index(i).Interface() {
t.Fatalf("unmarshal error:\nexpected: %v\n got: %v", tt.value, v.Interface())
}
}
} else {
a1 := v.Interface()
a2 := interface{}(tt.value)
if !reflect.DeepEqual(a1, a2) {
t.Fatalf("unmarshal error:\nexpected: %v\n got: %v", tt.value, v.Interface())
}
}
}
}
func Test_unmarshalToNil(t *testing.T) {
for _, tt := range unmarshalTests {
if err := unmarshal([]byte(tt.xml), tt.ptr); err != nil {
t.Fatalf("unmarshal error: %v", err)
}
}
}
func Test_typeMismatchError(t *testing.T) {
var s string
tt := unmarshalTests[0]
var err error
if err = unmarshal([]byte(tt.xml), &s); err == nil {
t.Fatal("unmarshal error: expected error, but didn't get it")
}
if _, ok := err.(TypeMismatchError); !ok {
t.Fatal("unmarshal error: expected type mistmatch error, but didn't get it")
}
}
func Test_unmarshalEmptyValueTag(t *testing.T) {
var v int
if err := unmarshal([]byte(""), &v); err != nil {
t.Fatalf("unmarshal error: %v", err)
}
}
func Test_decodeNonUTF8Response(t *testing.T) {
data, err := ioutil.ReadFile("fixtures/cp1251.xml")
if err != nil {
t.Fatal(err)
}
CharsetReader = decode
var s string
if err = unmarshal(data, &s); err != nil {
fmt.Println(err)
t.Fatal("unmarshal error: cannot decode non utf-8 response")
}
expected := "Л.Н. Толстой - Война и Мир"
if s != expected {
t.Fatalf("unmarshal error:\nexpected: %v\n got: %v", expected, s)
}
CharsetReader = nil
}
func decode(charset string, input io.Reader) (io.Reader, error) {
if charset != "cp1251" {
return nil, fmt.Errorf("unsupported charset")
}
return transform.NewReader(input, charmap.Windows1251.NewDecoder()), nil
}
golang-github-kolo-xmlrpc-0+git20150413.0826b98/encoder.go 0000664 0000000 0000000 00000006466 12513013206 0022502 0 ustar 00root root 0000000 0000000 package xmlrpc
import (
"bytes"
"encoding/xml"
"fmt"
"reflect"
"strconv"
"time"
)
type encodeFunc func(reflect.Value) ([]byte, error)
func marshal(v interface{}) ([]byte, error) {
if v == nil {
return []byte{}, nil
}
val := reflect.ValueOf(v)
return encodeValue(val)
}
func encodeValue(val reflect.Value) ([]byte, error) {
var b []byte
var err error
if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
if val.IsNil() {
return []byte(""), nil
}
val = val.Elem()
}
switch val.Kind() {
case reflect.Struct:
switch val.Interface().(type) {
case time.Time:
t := val.Interface().(time.Time)
b = []byte(fmt.Sprintf("%s", t.Format(iso8601)))
default:
b, err = encodeStruct(val)
}
case reflect.Map:
b, err = encodeMap(val)
case reflect.Slice:
b, err = encodeSlice(val)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
b = []byte(fmt.Sprintf("%s", strconv.FormatInt(val.Int(), 10)))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
b = []byte(fmt.Sprintf("%s", strconv.FormatUint(val.Uint(), 10)))
case reflect.Float32, reflect.Float64:
b = []byte(fmt.Sprintf("%s",
strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())))
case reflect.Bool:
if val.Bool() {
b = []byte("1")
} else {
b = []byte("0")
}
case reflect.String:
var buf bytes.Buffer
xml.Escape(&buf, []byte(val.String()))
if _, ok := val.Interface().(Base64); ok {
b = []byte(fmt.Sprintf("%s", buf.String()))
} else {
b = []byte(fmt.Sprintf("%s", buf.String()))
}
default:
return nil, fmt.Errorf("xmlrpc encode error: unsupported type")
}
if err != nil {
return nil, err
}
return []byte(fmt.Sprintf("%s", string(b))), nil
}
func encodeStruct(val reflect.Value) ([]byte, error) {
var b bytes.Buffer
b.WriteString("")
t := val.Type()
for i := 0; i < t.NumField(); i++ {
b.WriteString("")
f := t.Field(i)
name := f.Tag.Get("xmlrpc")
if name == "" {
name = f.Name
}
b.WriteString(fmt.Sprintf("%s", name))
p, err := encodeValue(val.FieldByName(f.Name))
if err != nil {
return nil, err
}
b.Write(p)
b.WriteString("")
}
b.WriteString("")
return b.Bytes(), nil
}
func encodeMap(val reflect.Value) ([]byte, error) {
var t = val.Type()
if t.Key().Kind() != reflect.String {
return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
}
var b bytes.Buffer
b.WriteString("")
keys := val.MapKeys()
for i := 0; i < val.Len(); i++ {
key := keys[i]
kval := val.MapIndex(key)
b.WriteString("")
b.WriteString(fmt.Sprintf("%s", key.String()))
p, err := encodeValue(kval)
if err != nil {
return nil, err
}
b.Write(p)
b.WriteString("")
}
b.WriteString("")
return b.Bytes(), nil
}
func encodeSlice(val reflect.Value) ([]byte, error) {
var b bytes.Buffer
b.WriteString("")
for i := 0; i < val.Len(); i++ {
p, err := encodeValue(val.Index(i))
if err != nil {
return nil, err
}
b.Write(p)
}
b.WriteString("")
return b.Bytes(), nil
}
golang-github-kolo-xmlrpc-0+git20150413.0826b98/encoder_test.go 0000664 0000000 0000000 00000005201 12513013206 0023523 0 ustar 00root root 0000000 0000000 package xmlrpc
import (
"testing"
"time"
)
var marshalTests = []struct {
value interface{}
xml string
}{
{100, "100"},
{"Once upon a time", "Once upon a time"},
{"Mike & Mick ", "Mike & Mick <London, UK>"},
{Base64("T25jZSB1cG9uIGEgdGltZQ=="), "T25jZSB1cG9uIGEgdGltZQ=="},
{true, "1"},
{false, "0"},
{12.134, "12.134"},
{-12.134, "-12.134"},
{time.Unix(1386622812, 0).UTC(), "20131209T21:00:12"},
{[]interface{}{1, "one"}, "1one"},
{&struct {
Title string
Amount int
}{"War and Piece", 20}, "TitleWar and PieceAmount20"},
{&struct {
Value interface{} `xmlrpc:"value"`
}{}, "value"},
{
map[string]interface{}{"title": "War and Piece", "amount": 20},
"titleWar and Pieceamount20",
},
{
map[string]interface{}{
"Name": "John Smith",
"Age": 6,
"Wight": []float32{66.67, 100.5},
"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)}},
"NameJohn SmithAge6Wight66.67100.5DatesBirth18291110T23:00:00Death20091110T23:00:00",
},
}
func Test_marshal(t *testing.T) {
for _, tt := range marshalTests {
b, err := marshal(tt.value)
if err != nil {
t.Fatalf("unexpected marshal error: %v", err)
}
if string(b) != tt.xml {
t.Fatalf("marshal error:\nexpected: %s\n got: %s", tt.xml, string(b))
}
}
}
golang-github-kolo-xmlrpc-0+git20150413.0826b98/fixtures/ 0000775 0000000 0000000 00000000000 12513013206 0022371 5 ustar 00root root 0000000 0000000 golang-github-kolo-xmlrpc-0+git20150413.0826b98/fixtures/cp1251.xml 0000664 0000000 0000000 00000000261 12513013206 0024025 0 ustar 00root root 0000000 0000000
.. -