Skip to content

Commit ac99879

Browse files
vvatanabegopherbot
authored andcommitted
webdav: return 409 for PUT without parent collection
Aligning with the WebDAV RFC specification, changes the server response for PUT operations that would create a resource without an existing parent collection from HTTP 404 Not Found to HTTP 409 Conflict. RFC RFC4918, section 9.7.1 Fixes golang/go#67150 Change-Id: I5ce6c5ea147a6a450ef5ae2e0077eb0dce409743 Reviewed-on: https://go-review.googlesource.com/c/net/+/583055 Reviewed-by: Cherry Mui <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Damien Neil <[email protected]>
1 parent 7fa635b commit ac99879

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

webdav/webdav.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request) (status int,
267267

268268
f, err := h.FileSystem.OpenFile(ctx, reqPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
269269
if err != nil {
270+
if os.IsNotExist(err) {
271+
return http.StatusConflict, err
272+
}
270273
return http.StatusNotFound, err
271274
}
272275
_, copyErr := io.Copy(f, r.Body)

webdav/webdav_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,63 @@ func TestFilenameEscape(t *testing.T) {
347347
}
348348
}
349349
}
350+
351+
func TestPutRequest(t *testing.T) {
352+
h := &Handler{
353+
FileSystem: NewMemFS(),
354+
LockSystem: NewMemLS(),
355+
}
356+
srv := httptest.NewServer(h)
357+
defer srv.Close()
358+
359+
do := func(method, urlStr string, body string) (*http.Response, error) {
360+
bodyReader := strings.NewReader(body)
361+
req, err := http.NewRequest(method, urlStr, bodyReader)
362+
if err != nil {
363+
return nil, err
364+
}
365+
res, err := http.DefaultClient.Do(req)
366+
if err != nil {
367+
return nil, err
368+
}
369+
return res, nil
370+
}
371+
372+
testCases := []struct {
373+
name string
374+
urlPrefix string
375+
want int
376+
}{{
377+
name: "put",
378+
urlPrefix: "/res",
379+
want: http.StatusCreated,
380+
}, {
381+
name: "put_utf8_segment",
382+
urlPrefix: "/res-%e2%82%ac",
383+
want: http.StatusCreated,
384+
}, {
385+
name: "put_empty_segment",
386+
urlPrefix: "",
387+
want: http.StatusNotFound,
388+
}, {
389+
name: "put_root_segment",
390+
urlPrefix: "/",
391+
want: http.StatusNotFound,
392+
}, {
393+
name: "put_no_parent [RFC4918:S9.7.1]",
394+
urlPrefix: "/409me/noparent.txt",
395+
want: http.StatusConflict,
396+
}}
397+
398+
for _, tc := range testCases {
399+
urlStr := srv.URL + tc.urlPrefix
400+
res, err := do("PUT", urlStr, "ABC\n")
401+
if err != nil {
402+
t.Errorf("name=%q: PUT: %v", tc.name, err)
403+
continue
404+
}
405+
if res.StatusCode != tc.want {
406+
t.Errorf("name=%q: got status code %d, want %d", tc.name, res.StatusCode, tc.want)
407+
}
408+
}
409+
}

0 commit comments

Comments
 (0)