# HG changeset patch # User Sascha L. Teichmann # Date 1549363146 -3600 # Node ID 8a6ed058af228473418525c6fad9344db9b170a9 # Parent 28fb26536b8ff1268e9155796d7a90f53590b1a4 WFS downloader: Use a custom XML streaming parser to detect error reports. diff -r 28fb26536b8f -r 8a6ed058af22 client/yarn.lock --- a/client/yarn.lock Tue Feb 05 09:38:01 2019 +0100 +++ b/client/yarn.lock Tue Feb 05 11:39:06 2019 +0100 @@ -4647,7 +4647,7 @@ loader-utils "^1.0.2" schema-utils "^1.0.0" -"file-saver@github:eligrey/FileSaver.js#1.3.8": +file-saver@eligrey/FileSaver.js#1.3.8: version "1.3.8" resolved "https://codeload.github.com/eligrey/FileSaver.js/tar.gz/e865e37af9f9947ddcced76b549e27dc45c1cb2e" diff -r 28fb26536b8f -r 8a6ed058af22 pkg/wfs/download.go --- a/pkg/wfs/download.go Tue Feb 05 09:38:01 2019 +0100 +++ b/pkg/wfs/download.go Tue Feb 05 11:39:06 2019 +0100 @@ -24,6 +24,7 @@ "net/http" "net/url" "strconv" + "strings" "gemma.intevation.de/gemma/pkg/config" "golang.org/x/net/html/charset" @@ -274,45 +275,109 @@ } type ExceptionReport struct { - XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 ExceptionReport"` - Exception struct { - ExceptionCode *string `xml:"exceptionCode,attr"` - ExceptionText struct { - Text *string `xml:",cdata"` - } `xml:"http://www.opengis.net/ows/1.1 ExceptionText"` - } `xml:"http://www.opengis.net/ows/1.1 Exception"` + Code string + Text string } func (er *ExceptionReport) Error() string { - nilString := func(s *string) string { - if s == nil { - return "" - } - return *s - } return fmt.Sprintf( "WFS GetFeature error: ExceptionCode: '%s' / ExceptionText: %s", - nilString(er.Exception.ExceptionCode), - nilString(er.Exception.ExceptionText.Text)) + er.Code, er.Text) } func scanExceptionReport(r io.Reader) *ExceptionReport { + const ows = "http://www.opengis.net/ows/1.1" + decoder := xml.NewDecoder(r) decoder.CharsetReader = charset.NewReaderLabel - var er ExceptionReport + var ( + isError bool + code string + depth int + text strings.Builder + ) + + type tokenFunc func(xml.Token) tokenFunc + + var exceptionReportFn, exceptionFn, exceptionTextFn, collectTextFn tokenFunc - if decoder.Decode(&er) != nil { - // Fine because of other kind of document - return nil + exceptionReportFn = func(t xml.Token) tokenFunc { + e, ok := t.(xml.StartElement) + if !ok { + return exceptionReportFn + } + if e.Name.Local != "ExceptionReport" && e.Name.Space != ows { + return nil + } + isError = true + return exceptionFn + } + + exceptionFn = func(t xml.Token) tokenFunc { + e, ok := t.(xml.StartElement) + if !ok { + return exceptionFn + } + if e.Name.Local == "Exception" && e.Name.Space == ows { + for i := range e.Attr { + at := &e.Attr[i] + if at.Name.Local == "code" || at.Name.Local == "exceptionCode" { + code = at.Value + break + } + } + return exceptionTextFn + } + return exceptionFn } - if er.Exception.ExceptionText.Text == nil && er.Exception.ExceptionCode == nil { - return nil + exceptionTextFn = func(t xml.Token) tokenFunc { + e, ok := t.(xml.StartElement) + if ok && e.Name.Local == "ExceptionText" && e.Name.Space == ows { + return collectTextFn + } + return exceptionTextFn } - return &er + collectTextFn = func(t xml.Token) tokenFunc { + switch e := t.(type) { + case xml.StartElement: + depth++ + case xml.CharData: + if depth == 0 { + text.Write(e) + } + case xml.EndElement: + if depth == 0 { + return nil + } + depth-- + } + return collectTextFn + } + +tokens: + for fn := exceptionReportFn; fn != nil; { + tok, err := decoder.Token() + switch { + case tok == nil && err == io.EOF: + break tokens + case err != nil: + return nil + } + fn = fn(tok) + } + + if isError { + return &ExceptionReport{ + Code: code, + Text: text.String(), + } + } + + return nil } // DownloadURLs does the actual GetFeature requests downloads