Moonshark/core/http/Forms.go
2025-04-03 13:14:45 -05:00

117 lines
3.0 KiB
Go

package http
import (
"errors"
"mime/multipart"
"strings"
"github.com/valyala/fasthttp"
)
// 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(ctx *fasthttp.RequestCtx) (map[string]any, error) {
// Only handle POST, PUT, PATCH
method := string(ctx.Method())
if method != "POST" && method != "PUT" && method != "PATCH" {
return make(map[string]any), nil
}
// Check content type
contentType := string(ctx.Request.Header.ContentType())
if contentType == "" {
return make(map[string]any), nil
}
result := make(map[string]any)
// Check for content length to prevent DOS
if len(ctx.Request.Body()) > maxFormSize {
return nil, ErrFormSizeTooLarge
}
// Handle by content type
if strings.HasPrefix(contentType, "application/x-www-form-urlencoded") {
return parseURLEncodedForm(ctx)
} else if strings.HasPrefix(contentType, "multipart/form-data") {
return parseMultipartForm(ctx)
}
// Unrecognized content type
return result, nil
}
// parseURLEncodedForm handles application/x-www-form-urlencoded forms
func parseURLEncodedForm(ctx *fasthttp.RequestCtx) (map[string]any, error) {
result := make(map[string]any)
// Process form values directly from PostArgs()
ctx.PostArgs().VisitAll(func(key, value []byte) {
keyStr := string(key)
valStr := string(value)
// Check if we already have this key
if existing, ok := result[keyStr]; ok {
// If it's already a slice, append
if existingSlice, ok := existing.([]string); ok {
result[keyStr] = append(existingSlice, valStr)
} else {
// Convert to slice and append
result[keyStr] = []string{existing.(string), valStr}
}
} else {
// New key
result[keyStr] = valStr
}
})
return result, nil
}
// parseMultipartForm handles multipart/form-data forms
func parseMultipartForm(ctx *fasthttp.RequestCtx) (map[string]any, error) {
result := make(map[string]any)
// Parse multipart form
form, err := ctx.MultipartForm()
if err != nil {
if err == multipart.ErrMessageTooLarge || strings.Contains(err.Error(), "too large") {
return nil, ErrFormSizeTooLarge
}
return nil, err
}
// Process form values
for key, values := range form.Value {
if len(values) == 1 {
// Single value
result[key] = values[0]
} else if len(values) > 1 {
// Multiple values - store as string slice
strValues := make([]string, len(values))
copy(strValues, values)
result[key] = strValues
}
}
// We don't handle file uploads here - could be extended in the future
// if needed to support file uploads to Lua
return result, 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