package http import ( "crypto/rand" "encoding/base64" "fmt" "mime/multipart" "strings" "sync" "time" "Moonshark/utils/logger" "github.com/valyala/fasthttp" ) var emptyMap = make(map[string]any) var ( stringPool = sync.Pool{ New: func() any { return make([]string, 0, 4) }, } formDataPool = sync.Pool{ New: func() any { return make(map[string]any, 16) }, } ) // LogRequest logs an HTTP request with its status code and duration func LogRequest(statusCode int, method, path string, duration time.Duration) { var statusColor, methodColor string // Simplified color assignment switch { case statusCode < 300: statusColor = "\u001b[32m" // Green for 2xx case statusCode < 400: statusColor = "\u001b[36m" // Cyan for 3xx case statusCode < 500: statusColor = "\u001b[33m" // Yellow for 4xx default: statusColor = "\u001b[31m" // Red for 5xx+ } switch method { case "GET": methodColor = "\u001b[32m" case "POST": methodColor = "\u001b[34m" case "PUT": methodColor = "\u001b[33m" case "DELETE": methodColor = "\u001b[31m" default: methodColor = "\u001b[35m" } // Optimized duration formatting var durationStr string micros := duration.Microseconds() if micros < 1000 { durationStr = fmt.Sprintf("%.0fµs", float64(micros)) } else if micros < 1000000 { durationStr = fmt.Sprintf("%.1fms", float64(micros)/1000) } else { durationStr = fmt.Sprintf("%.2fs", duration.Seconds()) } logger.Server("%s%d\u001b[0m %s%s\u001b[0m %s %s", statusColor, statusCode, methodColor, method, path, durationStr) } // QueryToLua converts HTTP query args to a Lua-friendly map func QueryToLua(ctx *fasthttp.RequestCtx) map[string]any { args := ctx.QueryArgs() if args.Len() == 0 { return emptyMap } queryMap := make(map[string]any, args.Len()) // Pre-size args.VisitAll(func(key, value []byte) { k := string(key) v := string(value) if existing, exists := queryMap[k]; exists { // Handle multiple values more efficiently switch typed := existing.(type) { case []string: queryMap[k] = append(typed, v) case string: // Get slice from pool slice := stringPool.Get().([]string) slice = slice[:0] // Reset length slice = append(slice, typed, v) queryMap[k] = slice } } else { queryMap[k] = v } }) return queryMap } // ParseForm extracts form data from a request func ParseForm(ctx *fasthttp.RequestCtx) (map[string]any, error) { contentType := string(ctx.Request.Header.ContentType()) if strings.Contains(contentType, "multipart/form-data") { return parseMultipartForm(ctx) } args := ctx.PostArgs() if args.Len() == 0 { return emptyMap, nil } formData := formDataPool.Get().(map[string]any) // Clear the map (should already be clean from pool) for k := range formData { delete(formData, k) } args.VisitAll(func(key, value []byte) { k := string(key) v := string(value) if existing, exists := formData[k]; exists { switch typed := existing.(type) { case []string: formData[k] = append(typed, v) case string: slice := stringPool.Get().([]string) slice = slice[:0] slice = append(slice, typed, v) formData[k] = slice } } else { formData[k] = v } }) return formData, nil } // parseMultipartForm handles multipart/form-data requests 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) } // Process form values for key, values := range form.Value { switch len(values) { case 0: // Skip empty case 1: formData[key] = values[0] default: formData[key] = values } } // Process files if present if len(form.File) > 0 { files := make(map[string]any, len(form.File)) for fieldName, fileHeaders := range form.File { switch len(fileHeaders) { case 1: files[fieldName] = fileInfoToMap(fileHeaders[0]) default: 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 } // fileInfoToMap converts a FileHeader to a map for Lua func fileInfoToMap(fh *multipart.FileHeader) map[string]any { return map[string]any{ "filename": fh.Filename, "size": fh.Size, "mimetype": getMimeType(fh), } } // getMimeType gets the mime type from a file header func getMimeType(fh *multipart.FileHeader) string { if fh.Header != nil { contentType := fh.Header.Get("Content-Type") if contentType != "" { return contentType } } // Fallback to basic type detection from filename if strings.HasSuffix(fh.Filename, ".pdf") { return "application/pdf" } else if strings.HasSuffix(fh.Filename, ".png") { return "image/png" } else if strings.HasSuffix(fh.Filename, ".jpg") || strings.HasSuffix(fh.Filename, ".jpeg") { return "image/jpeg" } else if strings.HasSuffix(fh.Filename, ".gif") { return "image/gif" } else if strings.HasSuffix(fh.Filename, ".svg") { return "image/svg+xml" } return "application/octet-stream" } // GenerateSecureToken creates a cryptographically secure random token 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 }