Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions view/state/kind/locator/form.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,18 @@ func NewForm(opts ...Option) (kind.Locator, error) {
return ret, nil
}

// seedFormFromMultipart parses multipart/form-data (if needed) and copies textual values to the shared form
// seedFormFromMultipart parses multipart/form-data and copies values into shared maps.
// Mutex is required because multiple Form locators (one per parameter) can call this
// concurrently on the same request. Uses form.Values directly instead of form.Set to
// avoid deadlock (form.Set locks the same mutex).
func (r *Form) seedFormFromMultipart() {
if r.request == nil || r.form == nil {
return
}
if r.request.MultipartForm == nil && len(r.form.Values) == 0 {
// Only ParseMultipartForm for form-data; other multipart types aren't
// supported by ParseMultipartForm. If the shared form already has
// values, treat it as authoritative and avoid parsing.
ct := r.request.Header.Get("Content-Type")
if ct != "" {
if mediaType, _, err := mime.ParseMediaType(ct); err == nil && shared.IsFormData(mediaType) {
// Use the same default memory threshold as Body locator
const maxMultipartMemory = 32 << 20 // 32 MiB
_ = r.request.ParseMultipartForm(maxMultipartMemory)
}
Expand All @@ -118,14 +117,18 @@ func (r *Form) seedFormFromMultipart() {
if r.request.MultipartForm == nil {
return
}
if len(r.request.Form) == 0 {
// BUG FIX (concurrent map writes):
mu := r.form.Mutex()
mu.Lock()
defer mu.Unlock()
if r.request.Form == nil {
r.request.Form = url.Values{}
}
for k, vs := range r.request.MultipartForm.Value {
if len(vs) == 0 {
continue
}
r.form.Set(k, vs...)
r.form.Values[k] = vs
r.request.Form[k] = vs
}
}
Loading