Files
project-gifuu/backend/tools/http_requests.go
T

149 lines
3.2 KiB
Go
Raw Normal View History

2026-05-23 17:17:56 -07:00
package tools
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"html"
"io"
"net"
"net/http"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
var (
snowflakeMutex sync.Mutex
snowflakeMachineID int64
snowflakeSequence int64
snowflakeTimestamp int64
// Updates to normalizers here should be mirrored in `GET_Limits.go`!
RegexSpaces = regexp.MustCompile(`\s{2,}`)
RegexUnderscores = regexp.MustCompile(`_+`)
RegexNewlines = regexp.MustCompile(`\n{2,}`)
RegexMatcherTitle = regexp.MustCompile(`^[\S\s]{1,80}$`)
RegexMatcherTag = regexp.MustCompile(`^[\p{L}\p{N}_]{1,32}$`)
RegexMatcherComment = regexp.MustCompile(`^[\S\s]{10,240}$`)
)
func NormalizeTitle(str string) (string, bool) {
if str == "" {
return str, false
}
if !RegexMatcherTitle.MatchString(str) {
return str, false
}
str = RegexSpaces.ReplaceAllString(str, " ")
str = strings.TrimSpace(str)
str = html.EscapeString(str)
return str, true
}
func NormalizeTag(str string) (string, bool) {
if str == "" {
return str, false
}
if !RegexMatcherTag.MatchString(str) {
return str, false
}
str = RegexUnderscores.ReplaceAllString(str, "_")
str = strings.Trim(str, "_")
str = strings.ToUpper(str)
return str, true
}
func NormalizeComment(str string) (string, bool) {
if str == "" {
return str, false
}
if !RegexMatcherComment.MatchString(str) {
return str, false
}
str = RegexNewlines.ReplaceAllString(str, " ")
str = strings.TrimSpace(str)
str = html.EscapeString(str)
return str, true
}
func ParseLimit(str string) int {
v, _ := strconv.Atoi(str)
return min(100, max(1, v))
}
func ParseSnowflake(str string) int64 {
v, err := strconv.ParseInt(str, 10, 64)
if err != nil || v < 1 {
return 0
}
return v
}
func ParseJSON(r io.Reader, v any) error {
l := io.LimitReader(r, int64(LIMIT_JSON))
d := json.NewDecoder(l)
d.DisallowUnknownFields()
return d.Decode(v)
}
// Generate a Unique Snowflake
func RequestSnowflake() int64 {
snowflakeMutex.Lock()
defer snowflakeMutex.Unlock()
now := time.Now().UnixMilli()
if now != snowflakeTimestamp {
snowflakeSequence = 0
} else {
snowflakeSequence++
if snowflakeSequence > SNOWFLAKE_MAX_SEQUENCE {
for now <= snowflakeTimestamp {
time.Sleep(time.Millisecond)
now = time.Now().UnixMilli()
}
snowflakeSequence = 0
}
}
snowflakeTimestamp = now
return ((now - SNOWFLAKE_EPOCH_MILLI) << 22) | (snowflakeMachineID << 12) | snowflakeSequence
}
// Generate a Hex String out of random bytes
func RequestToken() string {
b := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
panic("failed to generate enough random bytes")
}
return hex.EncodeToString(b)
}
// Generate a SHA256 Hex String from a given string
func RequestHash(str string) string {
h := sha256.Sum256([]byte(str))
return hex.EncodeToString(h[:])
}
// Get the Client IP Address as a SHA256 Hex String
func RequestAddressHash(r *http.Request) string {
return RequestHash(RequestAddress(r))
}
// Get the Client IP Address
func RequestAddress(r *http.Request) string {
if HTTP_PROXY != "" {
return r.Header.Get(HTTP_PROXY)
}
addr, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return ""
}
return addr
}