From 72bbe4087c2f67f9b7a1e5ddcf59bd73d8f3ddd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Gul=C3=A1csi?= Date: Sun, 20 Dec 2015 15:47:09 +0100 Subject: [PATCH 1/2] Don't parse the whole response if it is not a Fault Also, don't use ioutil.ReadAll. --- soaptrip.go | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/soaptrip.go b/soaptrip.go index c418e06..ac6554c 100644 --- a/soaptrip.go +++ b/soaptrip.go @@ -5,7 +5,7 @@ import ( "bytes" "encoding/xml" "fmt" - "io/ioutil" + "io" "net/http" "strings" ) @@ -61,22 +61,21 @@ func (sf SoapFault) Error() string { // ParseFault attempts to parse a Soap Fault from an http.Response. If a fault is found, it will return an error // of type SoapFault, otherwise it will return nil func ParseFault(resp *http.Response) error { - // read the response, but don't close it - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } + var buf bytes.Buffer - // replace the read closer that we just used - // TODO: theres a more elegant way, maybe a MultiReader? - resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + defer func() { + resp.Body = struct { + io.Reader + io.Closer + }{io.MultiReader(bytes.NewReader(buf.Bytes()), resp.Body), resp.Body} + }() - reader := bytes.NewReader(b) - d := xml.NewDecoder(reader) + d := xml.NewDecoder(io.TeeReader(resp.Body, &buf)) var start xml.StartElement - fault := &SoapFault{} + fault := &SoapFault{Response: resp} found := false + depth := 0 // iterate through the tokens for { @@ -89,15 +88,20 @@ func ParseFault(resp *http.Response) error { switch t := tok.(type) { case xml.StartElement: start = t.Copy() + depth++ + if depth > 2 { // don't descend beyond Envelope>Body>Fault + break + } case xml.EndElement: start = xml.StartElement{} + depth-- case xml.CharData: - key := strings.ToLower(start.Name.Local) // fault was found, capture the values and mark as found - if key == "faultcode" { + switch strings.ToLower(start.Name.Local) { + case "faultcode": found = true fault.FaultCode = string(t) - } else if key == "faultstring" { + case "faultstring": found = true fault.FaultString = string(t) } From 3cf783fcf5c5b251f919784a9c607889a0880f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Gul=C3=A1csi?= Date: Sun, 20 Dec 2015 19:51:00 +0100 Subject: [PATCH 2/2] simplify: no defer is needed in ParseFault --- soaptrip.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/soaptrip.go b/soaptrip.go index ac6554c..b86e9fd 100644 --- a/soaptrip.go +++ b/soaptrip.go @@ -62,14 +62,6 @@ func (sf SoapFault) Error() string { // of type SoapFault, otherwise it will return nil func ParseFault(resp *http.Response) error { var buf bytes.Buffer - - defer func() { - resp.Body = struct { - io.Reader - io.Closer - }{io.MultiReader(bytes.NewReader(buf.Bytes()), resp.Body), resp.Body} - }() - d := xml.NewDecoder(io.TeeReader(resp.Body, &buf)) var start xml.StartElement @@ -108,6 +100,11 @@ func ParseFault(resp *http.Response) error { } } + resp.Body = struct { + io.Reader + io.Closer + }{io.MultiReader(bytes.NewReader(buf.Bytes()), resp.Body), resp.Body} + if found { fault.Response = resp return fault