func GetPayloadFromError(err error) []byte {
	if err, ok := err.(ErrorWithPayload); ok {
		return err.Payload
	}
	return nil
}
GetPayloadFromError returns the payload of a ErrorWithPayload
package gosoap
import (
	"bytes"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"sync"
	"time"
	"golang.org/x/net/html/charset"
)
type SoapParams interface{}
type HeaderParams map[string]interface{}
type Params map[string]interface{}
type ArrayParams [][2]interface{}
type SliceParams []interface{}
type DumpLogger interface {
	LogRequest(method string, dump []byte)
	LogResponse(method string, dump []byte)
}
type fmtLogger struct{}
func (l *fmtLogger) LogRequest(method string, dump []byte) {
	fmt.Printf("Request:\n%v\n----\n", string(dump))
}
func (l *fmtLogger) LogResponse(method string, dump []byte) {
	fmt.Printf("Response:\n%v\n----\n", string(dump))
}
type Config struct {
	Dump   bool
	Logger DumpLogger
}
func SoapClient(wsdl string, httpClient *http.Client) (*Client, error) {
	return SoapClientWithConfig(wsdl, httpClient, &Config{Dump: false, Logger: &fmtLogger{}})
}
func SoapClientWithConfig(wsdl string, httpClient *http.Client, config *Config) (*Client, error) {
	_, err := url.Parse(wsdl)
	if err != nil {
		return nil, err
	}
	if httpClient == nil {
		httpClient = &http.Client{}
	}
	if config.Logger == nil {
		config.Logger = &fmtLogger{}
	}
	c := &Client{
		wsdl:       wsdl,
		config:     config,
		HTTPClient: httpClient,
		AutoAction: false,
	}
	return c, nil
}
type Client struct {
	HTTPClient   *http.Client
	AutoAction   bool
	URL          string
	HeaderName   string
	HeaderParams SoapParams
	Definitions  *wsdlDefinitions
	RefreshDefinitionsAfter time.Duration
	Username                string
	Password                string
	once                 sync.Once
	definitionsErr       error
	onRequest            sync.WaitGroup
	onDefinitionsRefresh sync.WaitGroup
	wsdl                 string
	config               *Config
}
func (c *Client) Call(m string, p SoapParams) (res *Response, err error) {
	return c.Do(NewRequest(m, p))
}
func (c *Client) CallByStruct(s RequestStruct) (res *Response, err error) {
	req, err := NewRequestByStruct(s)
	if err != nil {
		return nil, err
	}
	return c.Do(req)
}
func (c *Client) waitAndRefreshDefinitions(d time.Duration) {
	for {
		time.Sleep(d)
		c.onRequest.Wait()
		c.onDefinitionsRefresh.Add(1)
		c.initWsdl()
		c.onDefinitionsRefresh.Done()
	}
}
func (c *Client) initWsdl() {
	c.Definitions, c.definitionsErr = getWsdlDefinitions(c.wsdl, c.HTTPClient)
	if c.definitionsErr == nil {
		c.URL = strings.TrimSuffix(c.Definitions.TargetNamespace, "/")
	}
}
func (c *Client) SetWSDL(wsdl string) {
	c.onRequest.Wait()
	c.onDefinitionsRefresh.Wait()
	c.onRequest.Add(1)
	c.onDefinitionsRefresh.Add(1)
	defer c.onRequest.Done()
	defer c.onDefinitionsRefresh.Done()
	c.wsdl = wsdl
	c.initWsdl()
}
func (c *Client) Do(req *Request) (res *Response, err error) {
	c.onDefinitionsRefresh.Wait()
	c.onRequest.Add(1)
	defer c.onRequest.Done()
	c.once.Do(func() {
		c.initWsdl()
		if c.RefreshDefinitionsAfter >= 15*time.Minute {
			go c.waitAndRefreshDefinitions(c.RefreshDefinitionsAfter)
		}
	})
	if c.definitionsErr != nil {
		return nil, c.definitionsErr
	}
	if c.Definitions == nil {
		return nil, errors.New("wsdl definitions not found")
	}
	if c.Definitions.Services == nil {
		return nil, errors.New("No Services found in wsdl definitions")
	}
	p := &process{
		Client:     c,
		Request:    req,
		SoapAction: c.Definitions.GetSoapActionFromWsdlOperation(req.Method),
	}
	if p.SoapAction == "" && c.AutoAction {
		p.SoapAction = fmt.Sprintf("%s/%s/%s", c.URL, c.Definitions.Services[0].Name, req.Method)
	}
	p.Payload, err = xml.MarshalIndent(p, "", "    ")
	if err != nil {
		return nil, err
	}
	b, err := p.doRequest(c.Definitions.Services[0].Ports[0].SoapAddresses[0].Location)
	if err != nil {
		return nil, ErrorWithPayload{err, p.Payload}
	}
	var soap SoapEnvelope
	decoder := xml.NewDecoder(bytes.NewReader(b))
	decoder.CharsetReader = charset.NewReaderLabel
	err = decoder.Decode(&soap)
	res = &Response{
		Body:    soap.Body.Contents,
		Header:  soap.Header.Contents,
		Payload: p.Payload,
	}
	if err != nil {
		return res, ErrorWithPayload{err, p.Payload}
	}
	return res, nil
}
type process struct {
	Client     *Client
	Request    *Request
	SoapAction string
	Payload    []byte
}
func (p *process) doRequest(url string) ([]byte, error) {
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(p.Payload))
	if err != nil {
		return nil, err
	}
	if p.Client.config != nil && p.Client.config.Dump {
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			return nil, err
		}
		p.Client.config.Logger.LogRequest(p.Request.Method, dump)
	}
	if p.Client.Username != "" && p.Client.Password != "" {
		req.SetBasicAuth(p.Client.Username, p.Client.Password)
	}
	req.ContentLength = int64(len(p.Payload))
	req.Header.Add("Content-Type", "text/xml;charset=UTF-8")
	req.Header.Add("Accept", "text/xml")
	req.Header.Add("SOAPAction", p.SoapAction)
	resp, err := p.httpClient().Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if p.Client.config != nil && p.Client.config.Dump {
		dump, err := httputil.DumpResponse(resp, true)
		if err != nil {
			return nil, err
		}
		p.Client.config.Logger.LogResponse(p.Request.Method, dump)
	}
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		return nil, errors.New("unexpected status code: " + resp.Status)
	}
	return ioutil.ReadAll(resp.Body)
}
func (p *process) httpClient() *http.Client {
	if p.Client.HTTPClient != nil {
		return p.Client.HTTPClient
	}
	return http.DefaultClient
}
type ErrorWithPayload struct {
	error
	Payload []byte
}