rules: updated help message, make values optional, fixes tests

This commit is contained in:
yusing 2025-02-06 05:12:49 +08:00
parent bcf2dde091
commit 43ed860e18
6 changed files with 130 additions and 47 deletions

View file

@ -2,19 +2,19 @@ package http
import "net/http" import "net/http"
var validMethods = map[string]struct{}{
http.MethodGet: {},
http.MethodHead: {},
http.MethodPost: {},
http.MethodPut: {},
http.MethodPatch: {},
http.MethodDelete: {},
http.MethodConnect: {},
http.MethodOptions: {},
http.MethodTrace: {},
}
func IsMethodValid(method string) bool { func IsMethodValid(method string) bool {
_, ok := validMethods[method] switch method {
return ok case http.MethodGet,
http.MethodHead,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodConnect,
http.MethodOptions,
http.MethodTrace:
return true
default:
return false
}
} }

View file

@ -11,7 +11,8 @@ var (
ErrInvalidCommandSequence = E.New("invalid command sequence") ErrInvalidCommandSequence = E.New("invalid command sequence")
ErrInvalidSetTarget = E.New("invalid `rule.set` target") ErrInvalidSetTarget = E.New("invalid `rule.set` target")
ErrExpectNoArg = ErrInvalidArguments.Withf("expect no arg") ErrExpectNoArg = E.New("expect no arg")
ErrExpectOneArg = ErrInvalidArguments.Withf("expect 1 arg") ErrExpectOneArg = E.New("expect 1 arg")
ErrExpectTwoArgs = ErrInvalidArguments.Withf("expect 2 args") ErrExpectTwoArgs = E.New("expect 2 args")
ErrExpectKVOptionalV = E.New("expect 'key' or 'key value'")
) )

View file

@ -20,9 +20,8 @@ func (h *Help) String() string {
sb.WriteString(h.command) sb.WriteString(h.command)
sb.WriteString(" ") sb.WriteString(" ")
for arg := range h.args { for arg := range h.args {
sb.WriteRune('<') sb.WriteString(strings.ToUpper(arg))
sb.WriteString(arg) sb.WriteRune(' ')
sb.WriteString("> ")
} }
if h.description != "" { if h.description != "" {
sb.WriteString("\n\t") sb.WriteString("\n\t")
@ -32,7 +31,7 @@ func (h *Help) String() string {
sb.WriteRune('\n') sb.WriteRune('\n')
for arg, desc := range h.args { for arg, desc := range h.args {
sb.WriteRune('\t') sb.WriteRune('\t')
sb.WriteString(arg) sb.WriteString(strings.ToUpper(arg))
sb.WriteString(": ") sb.WriteString(": ")
sb.WriteString(desc) sb.WriteString(desc)
sb.WriteRune('\n') sb.WriteRune('\n')

View file

@ -34,15 +34,25 @@ var checkers = map[string]struct {
help: Help{ help: Help{
command: OnHeader, command: OnHeader,
args: map[string]string{ args: map[string]string{
"key": "the header key", "key": "the header key",
"value": "the header value", "[value]": "the header value",
}, },
}, },
validate: toStrTuple, validate: toKVOptionalV,
builder: func(args any) CheckFunc { builder: func(args any) CheckFunc {
k, v := args.(*StrTuple).Unpack() k, v := args.(*StrTuple).Unpack()
if v == "" {
return func(cached Cache, r *http.Request) bool {
return len(r.Header[k]) > 0
}
}
return func(cached Cache, r *http.Request) bool { return func(cached Cache, r *http.Request) bool {
return r.Header.Get(k) == v for _, vv := range r.Header[k] {
if v == vv {
return true
}
}
return false
} }
}, },
}, },
@ -50,13 +60,18 @@ var checkers = map[string]struct {
help: Help{ help: Help{
command: OnQuery, command: OnQuery,
args: map[string]string{ args: map[string]string{
"key": "the query key", "key": "the query key",
"value": "the query value", "[value]": "the query value",
}, },
}, },
validate: toStrTuple, validate: toKVOptionalV,
builder: func(args any) CheckFunc { builder: func(args any) CheckFunc {
k, v := args.(*StrTuple).Unpack() k, v := args.(*StrTuple).Unpack()
if v == "" {
return func(cached Cache, r *http.Request) bool {
return len(cached.GetQueries(r)[k]) > 0
}
}
return func(cached Cache, r *http.Request) bool { return func(cached Cache, r *http.Request) bool {
queries := cached.GetQueries(r)[k] queries := cached.GetQueries(r)[k]
for _, query := range queries { for _, query := range queries {
@ -72,13 +87,24 @@ var checkers = map[string]struct {
help: Help{ help: Help{
command: OnCookie, command: OnCookie,
args: map[string]string{ args: map[string]string{
"key": "the cookie key", "key": "the cookie key",
"value": "the cookie value", "[value]": "the cookie value",
}, },
}, },
validate: toStrTuple, validate: toKVOptionalV,
builder: func(args any) CheckFunc { builder: func(args any) CheckFunc {
k, v := args.(*StrTuple).Unpack() k, v := args.(*StrTuple).Unpack()
if v == "" {
return func(cached Cache, r *http.Request) bool {
cookies := cached.GetCookies(r)
for _, cookie := range cookies {
if cookie.Name == k {
return true
}
}
return false
}
}
return func(cached Cache, r *http.Request) bool { return func(cached Cache, r *http.Request) bool {
cookies := cached.GetCookies(r) cookies := cached.GetCookies(r)
for _, cookie := range cookies { for _, cookie := range cookies {
@ -95,13 +121,18 @@ var checkers = map[string]struct {
help: Help{ help: Help{
command: OnForm, command: OnForm,
args: map[string]string{ args: map[string]string{
"key": "the form key", "key": "the form key",
"value": "the form value", "[value]": "the form value",
}, },
}, },
validate: toStrTuple, validate: toKVOptionalV,
builder: func(args any) CheckFunc { builder: func(args any) CheckFunc {
k, v := args.(*StrTuple).Unpack() k, v := args.(*StrTuple).Unpack()
if v == "" {
return func(cached Cache, r *http.Request) bool {
return r.FormValue(k) != ""
}
}
return func(cached Cache, r *http.Request) bool { return func(cached Cache, r *http.Request) bool {
return r.FormValue(k) == v return r.FormValue(k) == v
} }
@ -111,13 +142,18 @@ var checkers = map[string]struct {
help: Help{ help: Help{
command: OnPostForm, command: OnPostForm,
args: map[string]string{ args: map[string]string{
"key": "the form key", "key": "the form key",
"value": "the form value", "[value]": "the form value",
}, },
}, },
validate: toStrTuple, validate: toKVOptionalV,
builder: func(args any) CheckFunc { builder: func(args any) CheckFunc {
k, v := args.(*StrTuple).Unpack() k, v := args.(*StrTuple).Unpack()
if v == "" {
return func(cached Cache, r *http.Request) bool {
return r.PostFormValue(k) != ""
}
}
return func(cached Cache, r *http.Request) bool { return func(cached Cache, r *http.Request) bool {
return r.PostFormValue(k) == v return r.PostFormValue(k) == v
} }

View file

@ -15,25 +15,50 @@ func TestParseOn(t *testing.T) {
}{ }{
// header // header
{ {
name: "header_valid", name: "header_valid_kv",
input: "header Connection Upgrade", input: "header Connection Upgrade",
wantErr: nil, wantErr: nil,
}, },
{ {
name: "header_invalid", name: "header_valid_k",
input: "header Connection", input: "header Connection",
wantErr: ErrInvalidArguments, wantErr: nil,
},
{
name: "header_missing_arg",
input: "header",
wantErr: ErrExpectKVOptionalV,
}, },
// query // query
{ {
name: "query_valid", name: "query_valid_kv",
input: "query key value", input: "query key value",
wantErr: nil, wantErr: nil,
}, },
{ {
name: "query_invalid", name: "query_valid_k",
input: "query key", input: "query key",
wantErr: ErrInvalidArguments, wantErr: nil,
},
{
name: "query_missing_arg",
input: "query",
wantErr: ErrExpectKVOptionalV,
},
{
name: "cookie_valid_kv",
input: "cookie key value",
wantErr: nil,
},
{
name: "cookie_valid_k",
input: "cookie key",
wantErr: nil,
},
{
name: "cookie_missing_arg",
input: "cookie",
wantErr: ErrExpectKVOptionalV,
}, },
// method // method
{ {
@ -43,9 +68,14 @@ func TestParseOn(t *testing.T) {
}, },
{ {
name: "method_invalid", name: "method_invalid",
input: "method", input: "method invalid",
wantErr: ErrInvalidArguments, wantErr: ErrInvalidArguments,
}, },
{
name: "method_missing_arg",
input: "method",
wantErr: ErrExpectOneArg,
},
// path // path
{ {
name: "path_valid", name: "path_valid",
@ -53,9 +83,9 @@ func TestParseOn(t *testing.T) {
wantErr: nil, wantErr: nil,
}, },
{ {
name: "path_invalid", name: "path_missing_arg",
input: "path", input: "path",
wantErr: ErrInvalidArguments, wantErr: ErrExpectOneArg,
}, },
// remote // remote
{ {
@ -65,9 +95,14 @@ func TestParseOn(t *testing.T) {
}, },
{ {
name: "remote_invalid", name: "remote_invalid",
input: "remote", input: "remote abcd",
wantErr: ErrInvalidArguments, wantErr: ErrInvalidArguments,
}, },
{
name: "remote_missing_arg",
input: "remote",
wantErr: ErrExpectOneArg,
},
{ {
name: "unknown_target", name: "unknown_target",
input: "unknown", input: "unknown",

View file

@ -36,6 +36,18 @@ func toStrTuple(args []string) (any, E.Error) {
return &StrTuple{args[0], args[1]}, nil return &StrTuple{args[0], args[1]}, nil
} }
// toKVOptionalV returns *StrTuple that value is optional.
func toKVOptionalV(args []string) (any, E.Error) {
switch len(args) {
case 1:
return &StrTuple{args[0], ""}, nil
case 2:
return &StrTuple{args[0], args[1]}, nil
default:
return nil, ErrExpectKVOptionalV
}
}
// validateURL returns types.URL with the URL validated. // validateURL returns types.URL with the URL validated.
func validateURL(args []string) (any, E.Error) { func validateURL(args []string) (any, E.Error) {
if len(args) != 1 { if len(args) != 1 {