Skip to content

Commit

Permalink
webdav: fix XML golden tests for encoding/xml xmlns change.
Browse files Browse the repository at this point in the history
The change to the standard encoding/xml library was
https://go-review.googlesource.com/#/c/2660/
which landed on 2015-02-14. An additional change was
https://go-review.googlesource.com/#/c/5910/ which
landed on 2015-03-03.

Fixes #9978.

Change-Id: I4f798f153e4e13b13eadc10e44b21a4b118251d3
Reviewed-on: https://go-review.googlesource.com/5714
Reviewed-by: Nigel Tao <nigeltao@golang.org>
  • Loading branch information
rsto authored and nigeltao committed Mar 4, 2015
1 parent 3d87fd6 commit 6dc0abc
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 95 deletions.
42 changes: 29 additions & 13 deletions webdav/xml.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,7 @@ type xmlError struct {

// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
type propstat struct {
// Prop requires DAV: to be the default namespace in the enclosing
// XML. This is due to the standard encoding/xml package currently
// not honoring namespace declarations inside a xmltag with a
// parent element for anonymous slice elements.
// Use of multistatusWriter takes care of this.
Prop []Property `xml:"prop>_ignored_"`
Prop []Property `xml:"DAV: prop>_ignored_"`
Status string `xml:"DAV: status"`
Error *xmlError `xml:"DAV: error"`
ResponseDescription string `xml:"DAV: responsedescription,omitempty"`
Expand Down Expand Up @@ -271,12 +266,24 @@ func (w *multistatusWriter) write(r *response) error {
if w.enc == nil {
w.w.Header().Add("Content-Type", "text/xml; charset=utf-8")
w.w.WriteHeader(StatusMulti)
_, err := fmt.Fprintf(w.w, `<?xml version="1.0" encoding="UTF-8"?>`+
`<D:multistatus xmlns:D="DAV:">`)
_, err := fmt.Fprintf(w.w, `<?xml version="1.0" encoding="UTF-8"?>`)
if err != nil {
return err
}
w.enc = xml.NewEncoder(w.w)
err = w.enc.EncodeToken(xml.StartElement{
Name: xml.Name{
Space: "DAV:",
Local: "multistatus",
},
Attr: []xml.Attr{{
Name: xml.Name{Local: "xmlns"},
Value: "DAV:",
}},
})
if err != nil {
return err
}
}
return w.enc.Encode(r)
}
Expand All @@ -289,14 +296,23 @@ func (w *multistatusWriter) close() error {
if w.enc == nil {
return nil
}
var end []xml.Token
if w.responseDescription != "" {
_, err := fmt.Fprintf(w.w,
"<D:responsedescription>%s</D:responsedescription>",
w.responseDescription)
name := xml.Name{Space: "DAV:", Local: "responsedescription"}
end = append(end,
xml.StartElement{Name: name},
xml.CharData(w.responseDescription),
xml.EndElement{Name: name},
)
}
end = append(end, xml.EndElement{
Name: xml.Name{Space: "DAV:", Local: "multistatus"},
})
for _, t := range end {
err := w.enc.EncodeToken(t)
if err != nil {
return err
}
}
_, err := fmt.Fprintf(w.w, "</D:multistatus>")
return err
return w.enc.Flush()
}
207 changes: 125 additions & 82 deletions webdav/xml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
package webdav

import (
"bytes"
"encoding/xml"
"io"
"net/http"
"net/http/httptest"
"reflect"
Expand Down Expand Up @@ -345,13 +347,6 @@ func TestReadPropfind(t *testing.T) {
func TestMultistatusWriter(t *testing.T) {
///The "section x.y.z" test cases come from section x.y.z of the spec at
// http://www.webdav.org/specs/rfc4918.html
//
// BUG:The following tests compare the actual and expected XML verbatim.
// Minor tweaks in the marshalling output of either standard encoding/xml
// or this package might break them. A more resilient approach could be
// to normalize both actual and expected XML content before comparison.
// This also would enhance readibility of the expected XML payload in the
// wantXML field.
testCases := []struct {
desc string
responses []response
Expand All @@ -365,38 +360,43 @@ func TestMultistatusWriter(t *testing.T) {
Href: []string{"http://example.com/foo"},
Propstat: []propstat{{
Prop: []Property{{
XMLName: xml.Name{Space: "http://ns.example.com/", Local: "Authors"},
XMLName: xml.Name{
Space: "http://ns.example.com/",
Local: "Authors",
},
}},
Status: "HTTP/1.1 424 Failed Dependency",
}, {
Prop: []Property{{
XMLName: xml.Name{Space: "http://ns.example.com/", Local: "Copyright-Owner"},
XMLName: xml.Name{
Space: "http://ns.example.com/",
Local: "Copyright-Owner",
},
}},
Status: "HTTP/1.1 409 Conflict",
}},
ResponseDescription: " Copyright Owner cannot be deleted or altered.",
ResponseDescription: "Copyright Owner cannot be deleted or altered.",
}},
wantXML: `<?xml version="1.0" encoding="UTF-8"?>` +
`<D:multistatus xmlns:D="DAV:">` +
`<response xmlns="DAV:">` +
`<href xmlns="DAV:">http://example.com/foo</href>` +
`<propstat xmlns="DAV:">` +
`<prop>` +
`<Authors xmlns="http://ns.example.com/"></Authors>` +
`</prop>` +
`<status xmlns="DAV:">HTTP/1.1 424 Failed Dependency</status>` +
`</propstat>` +
`<propstat xmlns="DAV:">` +
`<prop>` +
`<Copyright-Owner xmlns="http://ns.example.com/"></Copyright-Owner>` +
`</prop>` +
`<status xmlns="DAV:">HTTP/1.1 409 Conflict</status>` +
`</propstat>` +
`<responsedescription xmlns="DAV:">` +
` Copyright Owner cannot be deleted or altered.` +
`</responsedescription>` +
wantXML: `` +
`<?xml version="1.0" encoding="UTF-8"?>` +
`<multistatus xmlns="DAV:">` +
` <response>` +
` <href>http://example.com/foo</href>` +
` <propstat>` +
` <prop>` +
` <Authors xmlns="http://ns.example.com/"></Authors>` +
` </prop>` +
` <status>HTTP/1.1 424 Failed Dependency</status>` +
` </propstat>` +
` <propstat xmlns="DAV:">` +
` <prop>` +
` <Copyright-Owner xmlns="http://ns.example.com/"></Copyright-Owner>` +
` </prop>` +
` <status>HTTP/1.1 409 Conflict</status>` +
` </propstat>` +
` <responsedescription>Copyright Owner cannot be deleted or altered.</responsedescription>` +
`</response>` +
`</D:multistatus>`,
`</multistatus>`,
wantCode: StatusMulti,
}, {
desc: "section 9.6.2 (lock-token-submitted)",
Expand All @@ -407,14 +407,15 @@ func TestMultistatusWriter(t *testing.T) {
InnerXML: []byte(`<lock-token-submitted xmlns="DAV:"/>`),
},
}},
wantXML: `<?xml version="1.0" encoding="UTF-8"?>` +
`<D:multistatus xmlns:D="DAV:">` +
`<response xmlns="DAV:">` +
`<href xmlns="DAV:">http://example.com/foo</href>` +
`<status xmlns="DAV:">HTTP/1.1 423 Locked</status>` +
`<error xmlns="DAV:"><lock-token-submitted xmlns="DAV:"/></error>` +
`</response>` +
`</D:multistatus>`,
wantXML: `` +
`<?xml version="1.0" encoding="UTF-8"?>` +
`<multistatus xmlns="DAV:">` +
` <response>` +
` <href>http://example.com/foo</href>` +
` <status>HTTP/1.1 423 Locked</status>` +
` <error><lock-token-submitted xmlns="DAV:"/></error>` +
` </response>` +
`</multistatus>`,
wantCode: StatusMulti,
}, {
desc: "section 9.1.3",
Expand Down Expand Up @@ -442,42 +443,33 @@ func TestMultistatusWriter(t *testing.T) {
XMLName: xml.Name{Space: "http://ns.example.com/boxschema/", Local: "Random"},
}},
Status: "HTTP/1.1 403 Forbidden",
ResponseDescription: " The user does not have access to the DingALing property.",
ResponseDescription: "The user does not have access to the DingALing property.",
}},
}},
respdesc: " There has been an access violation error.",
wantXML: `<?xml version="1.0" encoding="UTF-8"?>` +
`<D:multistatus xmlns:D="DAV:">` +
`<response xmlns="DAV:">` +
`<href xmlns="DAV:">http://example.com/foo</href>` +
`<propstat xmlns="DAV:">` +
`<prop>` +
`<bigbox xmlns="http://ns.example.com/boxschema/">` +
`<BoxType xmlns="http://ns.example.com/boxschema/">Box type A</BoxType>` +
`</bigbox>` +
`<author xmlns="http://ns.example.com/boxschema/">` +
`<Name xmlns="http://ns.example.com/boxschema/">J.J. Johnson</Name>` +
`</author>` +
`</prop>` +
`<status xmlns="DAV:">HTTP/1.1 200 OK</status>` +
`</propstat>` +
`<propstat xmlns="DAV:">` +
`<prop>` +
`<DingALing xmlns="http://ns.example.com/boxschema/">` +
`</DingALing>` +
`<Random xmlns="http://ns.example.com/boxschema/">` +
`</Random>` +
`</prop>` +
`<status xmlns="DAV:">HTTP/1.1 403 Forbidden</status>` +
`<responsedescription xmlns="DAV:">` +
` The user does not have access to the DingALing property.` +
`</responsedescription>` +
`</propstat>` +
`</response>` +
`<D:responsedescription>` +
` There has been an access violation error.` +
`</D:responsedescription>` +
`</D:multistatus>`,
respdesc: "There has been an access violation error.",
wantXML: `` +
`<?xml version="1.0" encoding="UTF-8"?>` +
`<multistatus xmlns="DAV:">` +
` <response>` +
` <href>http://example.com/foo</href>` +
` <propstat>` +
` <prop>` +
` <bigbox xmlns="http://ns.example.com/boxschema/"><BoxType xmlns="http://ns.example.com/boxschema/">Box type A</BoxType></bigbox>` +
` <author xmlns="http://ns.example.com/boxschema/"><Name xmlns="http://ns.example.com/boxschema/">J.J. Johnson</Name></author>` +
` </prop>` +
` <status>HTTP/1.1 200 OK</status>` +
` </propstat>` +
` <propstat>` +
` <prop>` +
` <DingALing xmlns="http://ns.example.com/boxschema/"></DingALing>` +
` <Random xmlns="http://ns.example.com/boxschema/"></Random>` +
` </prop>` +
` <status>HTTP/1.1 403 Forbidden</status>` +
` <responsedescription>The user does not have access to the DingALing property.</responsedescription>` +
` </propstat>` +
` </response>` +
` <responsedescription>There has been an access violation error.</responsedescription>` +
`</multistatus>`,
wantCode: StatusMulti,
}, {
desc: "bad: no response written",
Expand All @@ -493,7 +485,10 @@ func TestMultistatusWriter(t *testing.T) {
responses: []response{{
Propstat: []propstat{{
Prop: []Property{{
XMLName: xml.Name{Space: "http://example.com/", Local: "foo"},
XMLName: xml.Name{
Space: "http://example.com/",
Local: "foo",
},
}},
Status: "HTTP/1.1 200 OK",
}},
Expand Down Expand Up @@ -523,7 +518,10 @@ func TestMultistatusWriter(t *testing.T) {
Href: []string{"http://example.com/foo"},
Propstat: []propstat{{
Prop: []Property{{
XMLName: xml.Name{Space: "http://example.com/", Local: "foo"},
XMLName: xml.Name{
Space: "http://example.com/",
Local: "foo",
},
}},
Status: "HTTP/1.1 200 OK",
}},
Expand All @@ -535,10 +533,16 @@ func TestMultistatusWriter(t *testing.T) {
}, {
desc: "bad: multiple hrefs and propstat",
responses: []response{{
Href: []string{"http://example.com/foo", "http://example.com/bar"},
Href: []string{
"http://example.com/foo",
"http://example.com/bar",
},
Propstat: []propstat{{
Prop: []Property{{
XMLName: xml.Name{Space: "http://example.com/", Local: "foo"},
XMLName: xml.Name{
Space: "http://example.com/",
Local: "foo",
},
}},
Status: "HTTP/1.1 200 OK",
}},
Expand All @@ -547,29 +551,68 @@ func TestMultistatusWriter(t *testing.T) {
// default of http.responseWriter
wantCode: http.StatusOK,
}}

loop:
for _, tc := range testCases {
rec := httptest.NewRecorder()
w := multistatusWriter{w: rec, responseDescription: tc.respdesc}
for _, r := range tc.responses {
if err := w.write(&r); err != nil {
if err != tc.wantErr {
t.Errorf("%s: got write error %v, want %v", tc.desc, err, tc.wantErr)
t.Errorf("%s: got write error %v, want %v",
tc.desc, err, tc.wantErr)
}
continue loop
}
}
if err := w.close(); err != tc.wantErr {
t.Errorf("%s: got close error %v, want %v", tc.desc, err, tc.wantErr)
t.Errorf("%s: got close error %v, want %v",
tc.desc, err, tc.wantErr)
continue
}
if rec.Code != tc.wantCode {
t.Errorf("%s: got HTTP status code %d, want %d\n", tc.desc, rec.Code, tc.wantCode)
t.Errorf("%s: got HTTP status code %d, want %d\n",
tc.desc, rec.Code, tc.wantCode)
continue
}
if gotXML := rec.Body.String(); gotXML != tc.wantXML {
t.Errorf("%s: XML body\ngot %q\nwant %q", tc.desc, gotXML, tc.wantXML)
continue

// normalize returns the normalized XML content of s. In contrast to
// the WebDAV specification, it ignores whitespace within property
// values of mixed XML content.
normalize := func(s string) string {
d := xml.NewDecoder(strings.NewReader(s))
var b bytes.Buffer
e := xml.NewEncoder(&b)
for {
tok, err := d.Token()
if err != nil {
if err == io.EOF {
break
}
t.Fatalf("%s: Token %v", tc.desc, err)
}
switch val := tok.(type) {
case xml.Comment, xml.Directive, xml.ProcInst:
continue
case xml.CharData:
if len(bytes.TrimSpace(val)) == 0 {
continue
}
}
if err := e.EncodeToken(tok); err != nil {
t.Fatalf("%s: EncodeToken: %v", tc.desc, err)
}
}
if err := e.Flush(); err != nil {
t.Fatalf("%s: Flush: %v", tc.desc, err)
}
return b.String()
}

gotXML := normalize(rec.Body.String())
wantXML := normalize(tc.wantXML)
if gotXML != wantXML {
t.Errorf("%s: XML body\ngot %q\nwant %q", tc.desc, gotXML, wantXML)
}
}
}

0 comments on commit 6dc0abc

Please sign in to comment.