1
0
mirror of https://github.com/labstack/echo.git synced 2026-05-16 09:48:24 +02:00
Files
echo/middleware/util.go
T

108 lines
2.9 KiB
Go
Raw Normal View History

// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
package middleware
import (
2023-07-22 12:08:34 +08:00
"bufio"
"crypto/rand"
2025-12-29 22:47:42 +02:00
"fmt"
2023-07-22 12:08:34 +08:00
"io"
2025-12-29 22:47:42 +02:00
"net/url"
"strings"
2023-07-22 12:08:34 +08:00
"sync"
)
2025-12-26 16:21:08 +02:00
const (
_ = int64(1 << (10 * iota)) // ignore first value by assigning to blank identifier
// KB is 1 KiloByte = 1024 bytes
KB
// MB is 1 Megabyte = 1_048_576 bytes
MB
// GB is 1 Gigabyte = 1_073_741_824 bytes
GB
// TB is 1 Terabyte = 1_099_511_627_776 bytes
TB
// PB is 1 Petabyte = 1_125_899_906_842_624 bytes
PB
// EB is 1 Exabyte = 1_152_921_504_606_847_000 bytes
EB
)
func matchScheme(domain, pattern string) bool {
didx := strings.Index(domain, ":")
pidx := strings.Index(pattern, ":")
return didx != -1 && pidx != -1 && domain[:didx] == pattern[:pidx]
}
2025-12-26 16:21:08 +02:00
func createRandomStringGenerator(length uint8) func() string {
return func() string {
return randomString(length)
}
}
2023-07-22 12:08:34 +08:00
// https://tip.golang.org/doc/go1.19#:~:text=Read%20no%20longer%20buffers%20random%20data%20obtained%20from%20the%20operating%20system%20between%20calls
2025-12-26 16:21:08 +02:00
var randomReaderPool = sync.Pool{New: func() any {
2023-07-22 12:08:34 +08:00
return bufio.NewReader(rand.Reader)
}}
const randomStringCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
const randomStringCharsetLen = 52 // len(randomStringCharset)
const randomStringMaxByte = 255 - (256 % randomStringCharsetLen)
func randomString(length uint8) string {
2023-07-22 12:08:34 +08:00
reader := randomReaderPool.Get().(*bufio.Reader)
defer randomReaderPool.Put(reader)
2023-07-22 12:08:34 +08:00
b := make([]byte, length)
r := make([]byte, length+(length/4)) // perf: avoid read from rand.Reader many times
var i uint8 = 0
// security note:
// we can't just simply do b[i]=randomStringCharset[rb%len(randomStringCharset)],
// len(len(randomStringCharset)) is 52, and rb is [0, 255], 256 = 52 * 4 + 48.
// make the first 48 characters more possibly to be generated then others.
2025-12-26 16:21:08 +02:00
// So we have to skip bytes when rb > randomStringMaxByt
2023-07-22 12:08:34 +08:00
for {
_, err := io.ReadFull(reader, r)
if err != nil {
panic("unexpected error happened when reading from bufio.NewReader(crypto/rand.Reader)")
}
for _, rb := range r {
if rb > randomStringMaxByte {
// Skip this number to avoid bias.
continue
}
b[i] = randomStringCharset[rb%randomStringCharsetLen]
i++
if i == length {
return string(b)
}
}
}
}
2025-12-29 22:47:42 +02:00
func validateOrigins(origins []string, what string) error {
for _, o := range origins {
if err := validateOrigin(o, what); err != nil {
return err
}
}
return nil
}
func validateOrigin(origin string, what string) error {
u, err := url.Parse(origin)
if err != nil {
return fmt.Errorf("can not parse %s: %w", what, err)
}
if u.Scheme == "" || u.Host == "" {
return fmt.Errorf("%s is missing scheme or host: %s", what, origin)
}
if u.Path != "" || u.RawQuery != "" || u.Fragment != "" {
return fmt.Errorf("%s can not have path, query, and fragments: %s", what, origin)
}
return nil
}