forked from tiaguinho/gosoap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
soap.go
156 lines (127 loc) · 3.21 KB
/
soap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package gosoap
import (
"bytes"
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
// HeaderParams holds params specific to the header
type HeaderParams map[string]string
// Params type is used to set the params in soap request
type Params map[string]interface{}
// SoapClient return new *Client to handle the requests with the WSDL
func SoapClient(wsdl string) (*Client, error) {
_, err := url.Parse(wsdl)
if err != nil {
return nil, err
}
d, err := getWsdlDefinitions(wsdl)
if err != nil {
return nil, err
}
c := &Client{
WSDL: wsdl,
URL: strings.TrimSuffix(d.TargetNamespace, "/"),
Definitions: d,
}
return c, nil
}
// Client struct hold all the informations about WSDL,
// request and response of the server
type Client struct {
HttpClient *http.Client
WSDL string
URL string
Method string
SoapAction string
Params Params
HeaderName string
HeaderParams HeaderParams
Definitions *wsdlDefinitions
Body []byte
Header []byte
Username string
Password string
payload []byte
}
// GetLastRequest returns the last request
func (c *Client) GetLastRequest() []byte {
return c.payload
}
// Call call's the method m with Params p
func (c *Client) Call(m string, p Params) (err error) {
c.Method = m
c.Params = p
c.SoapAction = c.Definitions.GetSoapActionFromWsdlOperation(c.Method)
if c.SoapAction == "" {
c.SoapAction = fmt.Sprintf("%s/%s", c.URL, c.Method)
}
c.payload, err = xml.MarshalIndent(c, "", " ")
if err != nil {
return err
}
b, err := c.doRequest(c.Definitions.Services[0].Ports[0].SoapAddresses[0].Location)
if err != nil {
return err
}
var soap SoapEnvelope
err = xml.Unmarshal(b, &soap)
c.Body = soap.Body.Contents
c.Header = soap.Header.Contents
return err
}
// Unmarshal get the body and unmarshal into the interface
func (c *Client) Unmarshal(v interface{}) error {
if len(c.Body) == 0 {
return fmt.Errorf("Body is empty")
}
var f Fault
xml.Unmarshal(c.Body, &f)
if f.Code != "" {
return fmt.Errorf("[%s]: %s", f.Code, f.Description)
}
return xml.Unmarshal(c.Body, v)
}
// doRequest makes new request to the server using the c.Method, c.URL and the body.
// body is enveloped in Call method
func (c *Client) doRequest(url string) ([]byte, error) {
req, err := http.NewRequest("POST", url, bytes.NewBuffer(c.payload))
if err != nil {
return nil, err
}
if c.Username != "" && c.Password != "" {
req.SetBasicAuth(c.Username, c.Password)
}
if c.HttpClient == nil {
c.HttpClient = &http.Client{}
}
req.ContentLength = int64(len(c.payload))
req.Header.Add("Content-Type", "text/xml;charset=UTF-8")
req.Header.Add("Accept", "text/xml")
req.Header.Add("SOAPAction", c.SoapAction)
resp, err := c.HttpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
// SoapEnvelope struct
type SoapEnvelope struct {
XMLName struct{} `xml:"Envelope"`
Header SoapHeader
Body SoapBody
}
// SoapHeader struct
type SoapHeader struct {
XMLName struct{} `xml:"Header"`
Contents []byte `xml:",innerxml"`
}
// SoapBody struct
type SoapBody struct {
XMLName struct{} `xml:"Body"`
Contents []byte `xml:",innerxml"`
}