Fix manage handlers ignoring multipart form bodies
The /manage page posts with FormData, i.e. multipart/form-data, but the edit, delete, tags, restore and empty-trash handlers parsed with r.ParseForm, which only reads urlencoded bodies. So every field came back empty: delete returned 200 with deleted:0 and moved nothing, edits saved nothing — while curl -d (urlencoded) worked, which is why tests and manual checks missed it. Parse with ParseMultipartForm (tolerating ErrNotMultipart so urlencoded still works), via a shared parseForm helper used by all manage handlers. handleUpload already did this. Also stop the UI reporting deleted:0 / updated:0 as success, and add a multipart regression test.
This commit is contained in:
1 parent
25384d3f3b
commit
bf75c9fe8e
4 files changed
+75
-9
No files matched your search
@@ -1,9 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"image"
|
||||
"image/png"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@@ -58,6 +60,20 @@ func form(v url.Values) *http.Request {
|
||||
return r
|
||||
}
|
||||
|
||||
// multipartForm builds a multipart/form-data request, as a browser's FormData
|
||||
// does — the content type the manage handlers must parse, not just urlencoded.
|
||||
func multipartForm(fields map[string]string) *http.Request {
|
||||
var b bytes.Buffer
|
||||
mw := multipart.NewWriter(&b)
|
||||
for k, v := range fields {
|
||||
mw.WriteField(k, v)
|
||||
}
|
||||
mw.Close()
|
||||
r := httptest.NewRequest(http.MethodPost, "/", &b)
|
||||
r.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
return r
|
||||
}
|
||||
|
||||
func readMeta(t *testing.T, content, slug string) PhotoMeta {
|
||||
t.Helper()
|
||||
var m PhotoMeta
|
||||
@@ -213,6 +229,37 @@ func TestHandleDeleteMovesToTrashAndPrunes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestMultipartBodies guards the regression where the manage handlers used
|
||||
// r.ParseForm (urlencoded only) and silently ignored the multipart bodies that
|
||||
// browsers send via FormData, returning 200 with deleted:0 / no change.
|
||||
func TestMultipartBodies(t *testing.T) {
|
||||
s, content, _ := newTestServer(t)
|
||||
|
||||
// Edit via multipart.
|
||||
rec := httptest.NewRecorder()
|
||||
s.handleEdit(rec, multipartForm(map[string]string{
|
||||
"slug": "foo", "caption": "Multipart", "date": "2021-02-03", "tags": "x,y",
|
||||
}))
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("multipart edit: code=%d", rec.Code)
|
||||
}
|
||||
if m := readMeta(t, content, "foo"); m.Caption != "Multipart" || strings.Join(m.Tags, ",") != "x,y" {
|
||||
t.Errorf("multipart edit not applied: %+v", m)
|
||||
}
|
||||
|
||||
// Delete via multipart actually moves the file.
|
||||
rec = httptest.NewRecorder()
|
||||
s.handleDelete(rec, multipartForm(map[string]string{"slug": "bar"}))
|
||||
var res map[string]int
|
||||
json.Unmarshal(rec.Body.Bytes(), &res)
|
||||
if res["deleted"] != 1 {
|
||||
t.Fatalf("multipart delete: deleted=%d, want 1 (body=%s)", res["deleted"], rec.Body)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(content, "bar.png")); !os.IsNotExist(err) {
|
||||
t.Error("bar.png should have moved to trash")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSRFGuard(t *testing.T) {
|
||||
s, _, _ := newTestServer(t) // generator.baseURL == "http://x"
|
||||
|
||||
|
||||
Reference in new issue
Block a user