forms
This commit is contained in:
parent
39723f8331
commit
3d61501eb9
136
core/http/forms.go
Normal file
136
core/http/forms.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Maximum form parse size (16MB)
|
||||
const maxFormSize = 16 << 20
|
||||
|
||||
// Common errors
|
||||
var (
|
||||
ErrFormSizeTooLarge = errors.New("form size too large")
|
||||
ErrInvalidFormType = errors.New("invalid form content type")
|
||||
)
|
||||
|
||||
// ParseForm parses a POST request body into a map of values
|
||||
// Supports both application/x-www-form-urlencoded and multipart/form-data content types
|
||||
func ParseForm(r *http.Request) (map[string]any, error) {
|
||||
// Only handle POST, PUT, PATCH
|
||||
if r.Method != http.MethodPost &&
|
||||
r.Method != http.MethodPut &&
|
||||
r.Method != http.MethodPatch {
|
||||
return make(map[string]any), nil
|
||||
}
|
||||
|
||||
// Check content type
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
if contentType == "" {
|
||||
return make(map[string]any), nil
|
||||
}
|
||||
|
||||
// Parse the media type
|
||||
mediaType, params, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidFormType
|
||||
}
|
||||
|
||||
result := make(map[string]any)
|
||||
|
||||
switch {
|
||||
case mediaType == "application/x-www-form-urlencoded":
|
||||
// Handle URL-encoded form
|
||||
if err := parseURLEncodedForm(r, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case strings.HasPrefix(mediaType, "multipart/form-data"):
|
||||
// Handle multipart form
|
||||
boundary := params["boundary"]
|
||||
if boundary == "" {
|
||||
return nil, ErrInvalidFormType
|
||||
}
|
||||
|
||||
if err := parseMultipartForm(r, boundary, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
// Unrecognized content type
|
||||
return make(map[string]any), nil
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// parseURLEncodedForm handles application/x-www-form-urlencoded forms
|
||||
func parseURLEncodedForm(r *http.Request, result map[string]any) error {
|
||||
// Enforce size limit
|
||||
r.Body = http.MaxBytesReader(nil, r.Body, maxFormSize)
|
||||
|
||||
// Read the entire body
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "http: request body too large") {
|
||||
return ErrFormSizeTooLarge
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse form values
|
||||
form, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Convert to map[string]any
|
||||
for key, values := range form {
|
||||
if len(values) == 1 {
|
||||
// Single value
|
||||
result[key] = values[0]
|
||||
} else if len(values) > 1 {
|
||||
// Multiple values
|
||||
result[key] = values
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseMultipartForm handles multipart/form-data forms
|
||||
func parseMultipartForm(r *http.Request, boundary string, result map[string]any) error {
|
||||
// Limit the form size
|
||||
if err := r.ParseMultipartForm(maxFormSize); err != nil {
|
||||
if strings.Contains(err.Error(), "http: request body too large") {
|
||||
return ErrFormSizeTooLarge
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Process form values
|
||||
for key, values := range r.MultipartForm.Value {
|
||||
if len(values) == 1 {
|
||||
// Single value
|
||||
result[key] = values[0]
|
||||
} else if len(values) > 1 {
|
||||
// Multiple values
|
||||
result[key] = values
|
||||
}
|
||||
}
|
||||
|
||||
// We don't handle file uploads here - could be extended in the future
|
||||
// if needed to support file uploads to Lua
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Usage:
|
||||
// After parsing the form with ParseForm, you can add it to the context with:
|
||||
// ctx.Set("form", formData)
|
||||
//
|
||||
// This makes the form data accessible in Lua as ctx.form.field_name
|
Loading…
Reference in New Issue
Block a user