soap/client.go

141 lines
2.8 KiB
Go

package soap
import (
"bytes"
"encoding/xml"
"io/ioutil"
"log"
"net"
"net/http"
"time"
)
// ClientDialTimeout default timeout 30s
var ClientDialTimeout = time.Duration(30 * time.Second)
// UserAgent is the default user agent
var UserAgent = "go-soap-0.1"
// Verbose be verbose
var Verbose = false
func l(m ...interface{}) {
if Verbose {
log.Println(m...)
}
}
// XMLMarshaller lets you inject your favourite custom xml implementation
type XMLMarshaller interface {
Marshal(v interface{}) ([]byte, error)
Unmarshal(xml []byte, v interface{}) error
}
type defaultMarshaller struct {
}
func (dm *defaultMarshaller) Marshal(v interface{}) (xmlBytes []byte, err error) {
return xml.Marshal(v)
}
func (dm *defaultMarshaller) Unmarshal(xmlBytes []byte, v interface{}) error {
return xml.Unmarshal(xmlBytes, v)
}
func newDefaultMarshaller() XMLMarshaller {
return &defaultMarshaller{}
}
func dialTimeout(network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, ClientDialTimeout)
}
// BasicAuth credentials for the client
type BasicAuth struct {
Login string
Password string
}
// Client generic SOAP client
type Client struct {
url string
tls bool
auth *BasicAuth
tr *http.Transport
Marshaller XMLMarshaller
}
// NewClient constructor
func NewClient(url string, auth *BasicAuth, tr *http.Transport) *Client {
return &Client{
url: url,
auth: auth,
tr: tr,
Marshaller: newDefaultMarshaller(),
}
}
// Call make a SOAP call
func (s *Client) Call(soapAction string, request, response interface{}) (httpResponse *http.Response, err error) {
envelope := Envelope{}
envelope.Body.Content = request
xmlBytes, err := s.Marshaller.Marshal(envelope)
if err != nil {
return
}
req, err := http.NewRequest("POST", s.url, bytes.NewBuffer(xmlBytes))
if err != nil {
return
}
if s.auth != nil {
req.SetBasicAuth(s.auth.Login, s.auth.Password)
}
req.Header.Add("Content-Type", "text/xml; charset=\"utf-8\"")
req.Header.Set("User-Agent", UserAgent)
if soapAction != "" {
req.Header.Add("SOAPAction", soapAction)
}
req.Close = true
tr := s.tr
if tr == nil {
tr = http.DefaultTransport.(*http.Transport)
}
client := &http.Client{Transport: tr}
l("POST to", s.url, "with", string(xmlBytes))
httpResponse, err = client.Do(req)
if err != nil {
return
}
defer httpResponse.Body.Close()
rawbody, err := ioutil.ReadAll(httpResponse.Body)
if err != nil {
return
}
if len(rawbody) == 0 {
l("empty response")
return
}
l("response", string(rawbody))
respEnvelope := new(Envelope)
respEnvelope.Body = Body{Content: response}
err = xml.Unmarshal(rawbody, respEnvelope)
if err != nil {
return
}
fault := respEnvelope.Body.Fault
if fault != nil {
err = fault
return
}
return
}