diff --git a/Makefile b/Makefile index 6622f1e..2eb27b0 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ GHACCOUNT := hooklift NAME := gowsdl -VERSION := v0.1.2 +VERSION := v0.2.0 include common.mk diff --git a/README.md b/README.md index ef57536..7ef9445 100644 --- a/README.md +++ b/README.md @@ -31,5 +31,6 @@ Usage: gowsdl [options] myservice.wsdl File where the generated code will be saved (default "myservice.go") -p string Package under which code will be generated (default "myservice") + -i Skips TLS Verification -v Shows gowsdl version ``` diff --git a/cmd/gowsdl/main.go b/cmd/gowsdl/main.go index b3e167d..7d5fd1f 100644 --- a/cmd/gowsdl/main.go +++ b/cmd/gowsdl/main.go @@ -66,6 +66,7 @@ var Name string var vers = flag.Bool("v", false, "Shows gowsdl version") var pkg = flag.String("p", "myservice", "Package under which code will be generated") var outFile = flag.String("o", "myservice.go", "File where the generated code will be saved") +var insecure = flag.Bool("i", false, "Skips TLS Verification") func init() { log.SetFlags(0) @@ -99,7 +100,7 @@ func main() { } // load wsdl - gowsdl, err := gen.NewGoWSDL(wsdlPath, *pkg, false) + gowsdl, err := gen.NewGoWSDL(wsdlPath, *pkg, *insecure) if err != nil { log.Fatalln(err) } diff --git a/gowsdl.go b/gowsdl.go index 1e5fe13..8cad47f 100644 --- a/gowsdl.go +++ b/gowsdl.go @@ -178,19 +178,19 @@ func (g *GoWSDL) unmarshal() error { return nil } -func (g *GoWSDL) resolveXSDExternals(schema *XSDSchema, url *url.URL) error { - for _, incl := range schema.Includes { - location, err := url.Parse(incl.SchemaLocation) +func (g *GoWSDL) getSchema(schemaLocation string, url *url.URL) error { + _, schemaFile := filepath.Split(schemaLocation) + + data, err := ioutil.ReadFile(schemaFile) + if err != nil { + + location, err := url.Parse(schemaLocation) if err != nil { return err } - _, schemaName := filepath.Split(location.Path) - if g.resolvedXSDExternals[schemaName] { - continue - } + schemaLocation = location.String() - schemaLocation := location.String() if !location.IsAbs() { if !url.IsAbs() { return fmt.Errorf("Unable to resolve external schema %s through WSDL URL %s", schemaLocation, url) @@ -200,55 +200,66 @@ func (g *GoWSDL) resolveXSDExternals(schema *XSDSchema, url *url.URL) error { log.Println("Downloading external schema", "location", schemaLocation) - data, err := downloadFile(schemaLocation, g.ignoreTLS) - newschema := new(XSDSchema) - - err = xml.Unmarshal(data, newschema) + data, err = downloadFile(schemaLocation, g.ignoreTLS) if err != nil { return err } + } + newschema := new(XSDSchema) - if len(newschema.Includes) > 0 && - maxRecursion > g.currentRecursionLevel { + err = xml.Unmarshal(data, newschema) + if err != nil { + return err + } + + if len(newschema.Includes) > 0 || len(newschema.Imports) > 0 { + if maxRecursion > g.currentRecursionLevel { g.currentRecursionLevel++ //log.Printf("Entering recursion %d\n", g.currentRecursionLevel) g.resolveXSDExternals(newschema, url) } + } - g.wsdl.Types.Schemas = append(g.wsdl.Types.Schemas, newschema) + g.wsdl.Types.Schemas = append(g.wsdl.Types.Schemas, newschema) + return nil +} + +func (g *GoWSDL) resolveXSDExternals(schema *XSDSchema, url *url.URL) error { + if len(schema.Includes) > 0 || len(schema.Imports) > 0 { if g.resolvedXSDExternals == nil { g.resolvedXSDExternals = make(map[string]bool, maxRecursion) } - g.resolvedXSDExternals[schemaName] = true } - for _, incl := range schema.Imports { - location, err := url.Parse(incl.SchemaLocation) + var err error + for _, incl := range schema.Includes { + if g.resolvedXSDExternals[incl.SchemaLocation] { + continue + } + + err = g.getSchema(incl.SchemaLocation, url) if err != nil { return err } - _, schemaName := filepath.Split(location.Path) - if g.resolvedXSDExternals[schemaName] { + g.resolvedXSDExternals[incl.SchemaLocation] = true + } + + for _, imp := range schema.Imports { + if g.resolvedXSDExternals[imp.SchemaLocation] { continue } - data, err := ioutil.ReadFile(schemaName) - newschema := new(XSDSchema) - err = xml.Unmarshal(data, newschema) + err = g.getSchema(imp.SchemaLocation, url) if err != nil { return err } - g.wsdl.Types.Schemas = append(g.wsdl.Types.Schemas, newschema) + g.resolvedXSDExternals[imp.SchemaLocation] = true - if g.resolvedXSDExternals == nil { - g.resolvedXSDExternals = make(map[string]bool, maxRecursion) - } - g.resolvedXSDExternals[schemaName] = true } return nil diff --git a/soap_tmpl.go b/soap_tmpl.go index 9a5fdb2..cc9a839 100644 --- a/soap_tmpl.go +++ b/soap_tmpl.go @@ -13,19 +13,26 @@ func dialTimeout(network, addr string) (net.Conn, error) { type SOAPEnvelope struct { XMLName xml.Name ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` + "`" + ` - Body SOAPBody ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` + "`" + ` + + Body SOAPBody } type SOAPHeader struct { + XMLName xml.Name ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header"` + "`" + ` + Header interface{} } type SOAPBody struct { - Fault *SOAPFault ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` + "`" + ` - Content string ` + "`" + `xml:",innerxml"` + "`" + ` + XMLName xml.Name ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` + "`" + ` + + Fault *SOAPFault ` + "`" + `xml:",omitempty"` + "`" + ` + Content interface{} ` + "`" + `xml:",omitempty"` + "`" + ` } type SOAPFault struct { + XMLName xml.Name ` + "`" + `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"` + "`" + ` + Code string ` + "`" + `xml:"faultcode,omitempty"` + "`" + ` String string ` + "`" + `xml:"faultstring,omitempty"` + "`" + ` Actor string ` + "`" + `xml:"faultactor,omitempty"` + "`" + ` @@ -43,6 +50,56 @@ type SOAPClient struct { auth *BasicAuth } +func (b *SOAPBody) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + if b.Content == nil { + return xml.UnmarshalError("Content must be a pointer to a struct") + } + + var ( + token xml.Token + err error + consumed bool + ) + +Loop: + for { + if token, err = d.Token(); err != nil { + return err + } + + if token == nil { + break + } + + switch se := token.(type) { + case xml.StartElement: + if consumed { + return xml.UnmarshalError("Found multiple elements inside SOAP body; not wrapped-document/literal WS-I compliant") + } else if se.Name.Space == "http://schemas.xmlsoap.org/soap/envelope/" && se.Name.Local == "Fault" { + b.Fault = &SOAPFault{} + b.Content = nil + + err = d.DecodeElement(b.Fault, &se) + if err != nil { + return err + } + + consumed = true + } else { + if err = d.DecodeElement(b.Content, &se); err != nil { + return err + } + + consumed = true + } + case xml.EndElement: + break Loop + } + } + + return nil +} + func (f *SOAPFault) Error() string { return f.String } @@ -60,14 +117,7 @@ func (s *SOAPClient) Call(soapAction string, request, response interface{}) erro //Header: SoapHeader{}, } - if request != nil { - reqXml, err := xml.Marshal(request) - if err != nil { - return err - } - - envelope.Body.Content = string(reqXml) - } + envelope.Body.Content = request buffer := new(bytes.Buffer) encoder := xml.NewEncoder(buffer) @@ -118,28 +168,17 @@ func (s *SOAPClient) Call(soapAction string, request, response interface{}) erro log.Println(string(rawbody)) respEnvelope := new(SOAPEnvelope) + respEnvelope.Body = SOAPBody{Content: response} err = xml.Unmarshal(rawbody, respEnvelope) if err != nil { return err } - body := respEnvelope.Body.Content fault := respEnvelope.Body.Fault - if body == "" { - log.Println("empty response body", "envelope", respEnvelope, "body", body) - return nil - } - - log.Println("response", "envelope", respEnvelope, "body", body) if fault != nil { return fault } - err = xml.Unmarshal([]byte(body), response) - if err != nil { - return err - } - return nil } `