Moonshark/http/utils.go
2025-05-26 13:03:29 -05:00

148 lines
2.9 KiB
Go

package http
import (
"crypto/rand"
"encoding/base64"
"mime/multipart"
"strings"
"sync"
"github.com/valyala/fasthttp"
)
var (
emptyMap = make(map[string]any)
formDataPool = sync.Pool{
New: func() any {
return make(map[string]any, 16)
},
}
)
func QueryToLua(ctx *fasthttp.RequestCtx) map[string]any {
args := ctx.QueryArgs()
if args.Len() == 0 {
return emptyMap
}
queryMap := make(map[string]any, args.Len())
args.VisitAll(func(key, value []byte) {
k := string(key)
v := string(value)
appendValue(queryMap, k, v)
})
return queryMap
}
func ParseForm(ctx *fasthttp.RequestCtx) (map[string]any, error) {
if strings.Contains(string(ctx.Request.Header.ContentType()), "multipart/form-data") {
return parseMultipartForm(ctx)
}
args := ctx.PostArgs()
if args.Len() == 0 {
return emptyMap, nil
}
formData := formDataPool.Get().(map[string]any)
for k := range formData {
delete(formData, k)
}
args.VisitAll(func(key, value []byte) {
k := string(key)
v := string(value)
appendValue(formData, k, v)
})
return formData, nil
}
func parseMultipartForm(ctx *fasthttp.RequestCtx) (map[string]any, error) {
form, err := ctx.MultipartForm()
if err != nil {
return nil, err
}
formData := formDataPool.Get().(map[string]any)
for k := range formData {
delete(formData, k)
}
for key, values := range form.Value {
if len(values) == 1 {
formData[key] = values[0]
} else if len(values) > 1 {
formData[key] = values
}
}
if len(form.File) > 0 {
files := make(map[string]any, len(form.File))
for fieldName, fileHeaders := range form.File {
if len(fileHeaders) == 1 {
files[fieldName] = fileInfoToMap(fileHeaders[0])
} else {
fileInfos := make([]map[string]any, len(fileHeaders))
for i, fh := range fileHeaders {
fileInfos[i] = fileInfoToMap(fh)
}
files[fieldName] = fileInfos
}
}
formData["_files"] = files
}
return formData, nil
}
func fileInfoToMap(fh *multipart.FileHeader) map[string]any {
ct := fh.Header.Get("Content-Type")
if ct == "" {
ct = getMimeType(fh.Filename)
}
return map[string]any{
"filename": fh.Filename,
"size": fh.Size,
"mimetype": ct,
}
}
func getMimeType(filename string) string {
if i := strings.LastIndex(filename, "."); i >= 0 {
switch filename[i:] {
case ".pdf":
return "application/pdf"
case ".png":
return "image/png"
case ".jpg", ".jpeg":
return "image/jpeg"
case ".gif":
return "image/gif"
case ".svg":
return "image/svg+xml"
}
}
return "application/octet-stream"
}
func appendValue(m map[string]any, k, v string) {
if existing, exists := m[k]; exists {
switch typed := existing.(type) {
case []string:
m[k] = append(typed, v)
case string:
m[k] = []string{typed, v}
}
} else {
m[k] = v
}
}
func GenerateSecureToken(length int) (string, error) {
b := make([]byte, length)
if _, err := rand.Read(b); err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(b)[:length], nil
}