Files
go-omni/contract/policy.go

108 lines
2.6 KiB
Go

package contract
import (
"context"
"errors"
"io"
"strings"
"unicode/utf8"
"git.yuntuo.hk/k2safe/go-omni/internal/configdiag"
)
// RequestPolicy describes protocol strictness, independently of business
// fields. Empty values inherit App defaults, then the historical binder policy.
type RequestPolicy struct {
UnknownQuery string `json:"unknownQuery"` // ignore or reject
UTF8 string `json:"utf8"` // replace or reject
Charset string `json:"charset"` // any or utf8
Body string `json:"body"` // auto, required, optional, none, emptyObject
}
func (p RequestPolicy) Validate() error {
for _, item := range []struct {
name, value string
allowed []string
}{
{"unknownQuery", p.UnknownQuery, []string{"ignore", "reject"}},
{"utf8", p.UTF8, []string{"replace", "reject"}},
{"charset", p.Charset, []string{"any", "utf8"}},
{"body", p.Body, []string{"auto", "required", "optional", "none", "emptyObject"}},
} {
if item.value == "" {
continue
}
ok := false
for _, value := range item.allowed {
ok = ok || value == item.value
}
if !ok {
return configdiag.New(item.name, "must be one of "+strings.Join(item.allowed, ", "))
}
}
return nil
}
func MergePolicy(base, override RequestPolicy) RequestPolicy {
if override.UnknownQuery != "" {
base.UnknownQuery = override.UnknownQuery
}
if override.UTF8 != "" {
base.UTF8 = override.UTF8
}
if override.Charset != "" {
base.Charset = override.Charset
}
if override.Body != "" {
base.Body = override.Body
}
return base
}
type policyKey struct{}
func WithRequestPolicy(ctx context.Context, p RequestPolicy) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, policyKey{}, p)
}
func RequestPolicyFromContext(ctx context.Context) RequestPolicy {
if ctx == nil {
return RequestPolicy{}
}
p, _ := ctx.Value(policyKey{}).(RequestPolicy)
return p
}
// utf8Reader checks original bytes in one pass, including runes crossing Read
// boundaries. It never replaces invalid bytes or buffers the complete body.
type utf8Reader struct {
r io.Reader
pending [4]byte
n int
}
var errUTF8 = errors.New("invalid UTF-8 in JSON body")
func (r *utf8Reader) Read(p []byte) (int, error) {
n, err := r.r.Read(p)
for _, b := range p[:n] {
if r.n == 0 && b < utf8.RuneSelf {
continue
}
r.pending[r.n] = b
r.n++
if utf8.FullRune(r.pending[:r.n]) {
ch, size := utf8.DecodeRune(r.pending[:r.n])
if ch == utf8.RuneError && size == 1 {
return n, errUTF8
}
r.n = 0
}
}
if err == io.EOF && r.n != 0 {
return n, errUTF8
}
return n, err
}