From ce6509ec9790848b056c7ff06e32db496f0d9edf Mon Sep 17 00:00:00 2001 From: Camilo Aguilar Date: Fri, 27 Dec 2013 14:22:18 -0500 Subject: [PATCH] First working function to recursively resolve, download and unmarshal external xml schemas --- gowsdl.go | 204 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 160 insertions(+), 44 deletions(-) diff --git a/gowsdl.go b/gowsdl.go index 5da1404..23757cf 100644 --- a/gowsdl.go +++ b/gowsdl.go @@ -1,24 +1,64 @@ package main import ( + "crypto/tls" "encoding/xml" + "errors" + "fmt" "io/ioutil" "log" + "net/http" + "net/url" "os" + "path/filepath" "strings" "sync" ) +const maxRecursion uint8 = 5 + type GoWsdl struct { - file, pkg string - logger *log.Logger - wsdl *Wsdl + file, pkg string + wsdl *Wsdl + resolvedXsdExternals map[string]bool + currentRecursionLevel uint8 +} + +var cacheDir = os.TempDir() + "gowsdl-cache" + +func init() { + err := os.MkdirAll(cacheDir, 0700) + if err != nil { + log.Fatalf("Unable to reate cache directory") + } +} + +func downloadFile(url string) ([]byte, error) { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + client := &http.Client{Transport: tr} + + resp, err := client.Get(url) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return data, nil } -func NewGoWsdl(file, pkg string, logger *log.Logger) (*GoWsdl, error) { +func NewGoWsdl(file, pkg string) (*GoWsdl, error) { file = strings.TrimSpace(file) if file == "" { - logger.Fatalln("WSDL file is required to generate Go proxy") + log.Fatalln("WSDL file is required to generate Go proxy") } pkg = strings.TrimSpace(pkg) @@ -26,21 +66,16 @@ func NewGoWsdl(file, pkg string, logger *log.Logger) (*GoWsdl, error) { pkg = "main" } - if logger == nil { - logger = log.New(os.Stdout, "", 0) - } - return &GoWsdl{ - file: file, - pkg: pkg, - logger: logger, + file: file, + pkg: pkg, }, nil } func (g *GoWsdl) Start() (map[string][]byte, error) { gocode := make(map[string][]byte) - err := g.Unmarshal() + err := g.unmarshal() if err != nil { return nil, err } @@ -52,9 +87,20 @@ func (g *GoWsdl) Start() (map[string][]byte, error) { defer wg.Done() var err error - gocode["types"], err = g.GenTypes() + gocode["types"], err = g.genTypes() + if err != nil { + log.Println(err) + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + var err error + + gocode["messages"], err = g.genMessages() if err != nil { - g.logger.Println(err) + log.Println(err) } }() @@ -63,9 +109,9 @@ func (g *GoWsdl) Start() (map[string][]byte, error) { defer wg.Done() var err error - gocode["operations"], err = g.GenOperations() + gocode["operations"], err = g.genOperations() if err != nil { - g.logger.Println(err) + log.Println(err) } }() @@ -74,9 +120,9 @@ func (g *GoWsdl) Start() (map[string][]byte, error) { defer wg.Done() var err error - gocode["proxy"], err = g.GenSoapProxy() + gocode["proxy"], err = g.genSoapProxy() if err != nil { - g.logger.Println(err) + log.Println(err) } }() @@ -85,15 +131,24 @@ func (g *GoWsdl) Start() (map[string][]byte, error) { return gocode, nil } -func (g *GoWsdl) Unmarshal() error { - g.logger.Printf("Using %s...\n", g.file) +func (g *GoWsdl) unmarshal() error { + var data []byte - //g.file is URL or local file? - //if URL, download! + parsedUrl, err := url.Parse(g.file) + if parsedUrl.Scheme == "" { + log.Printf("Reading file %s...\n", g.file) - data, err := ioutil.ReadFile(g.file) - if err != nil { - return err + data, err = ioutil.ReadFile(g.file) + if err != nil { + return err + } + } else { + log.Printf("Downloading %s...\n", g.file) + + data, err = downloadFile(g.file) + if err != nil { + return err + } } g.wsdl = &Wsdl{} @@ -102,49 +157,110 @@ func (g *GoWsdl) Unmarshal() error { return err } - //Resolve wsdl imports - //Resolve xsd includes - //Resolve xsd imports + for _, schema := range g.wsdl.Types.Schemas { + err = g.resolveXsdExternals(schema, parsedUrl) + if err != nil { + return err + } + } return nil } -func (g *GoWsdl) GenTypes() ([]byte, error) { - if g.wsdl == nil { - g.logger.Fatalln("You have to unmarshal the WSDL file first") +func (g *GoWsdl) resolveXsdExternals(schema *XsdSchema, url *url.URL) error { + + for _, incl := range schema.Includes { + location, err := url.Parse(incl.SchemaLocation) + if err != nil { + return err + } + + _, schemaName := filepath.Split(location.Path) + if g.resolvedXsdExternals[schemaName] { + continue + } + + schemaLocation := location.String() + if !location.IsAbs() { + if !url.IsAbs() { + return errors.New(fmt.Sprintf("Unable to resolve external schema %s through WSDL URL %s", schemaLocation, url)) + } + schemaLocation = url.Scheme + "://" + url.Host + schemaLocation + } + + log.Printf("Downloading external schema: %s\n", schemaLocation) + + data, err := downloadFile(schemaLocation) + newschema := &XsdSchema{} + + err = xml.Unmarshal(data, newschema) + if err != nil { + return err + } + + if len(newschema.Includes) > 0 && + 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) + + if g.resolvedXsdExternals == nil { + g.resolvedXsdExternals = make(map[string]bool, maxRecursion) + } + g.resolvedXsdExternals[schemaName] = true } + // for _, imp := range schema.Imports { + + // } + return nil +} + +func (g *GoWsdl) genTypes() ([]byte, error) { //element > complexType for _, schema := range g.wsdl.Types.Schemas { - g.logger.Println(schema.XMLName) + embed := 0 + refcount := 0 + for _, el := range schema.Elements { + if el.Type != "" { + embed++ + } else { + refcount++ + } + } + + // log.Printf("embedded types: %d\n", embed) + // log.Printf("referenced types: %d\n", refcount) // for _, element := range schema.Elements { // g.logger.Printf("Type: %s\n", element.Name) // } - g.logger.Printf("Total types: %d\n", len(schema.Elements)) + } return nil, nil } -func (g *GoWsdl) GenOperations() ([]byte, error) { - if g.wsdl == nil { - g.logger.Fatalln("You have to unmarshal the WSDL file first") - } - +func (g *GoWsdl) genOperations() ([]byte, error) { for _, pt := range g.wsdl.PortTypes { // for _, o := range pt.Operations { // g.logger.Printf("Operation: %s", o.Name) // } - g.logger.Printf("Total ops: %d\n", len(pt.Operations)) + log.Printf("Total ops: %d\n", len(pt.Operations)) } return nil, nil } -func (g *GoWsdl) GenSoapProxy() ([]byte, error) { - if g.wsdl == nil { - g.logger.Fatalln("You have to unmarshal the WSDL file first") - } +func (g *GoWsdl) genMessages() ([]byte, error) { + return nil, nil +} + +func (g *GoWsdl) genSoapProxy() ([]byte, error) { return nil, nil }