mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-09 04:48:16 +03:00
mitm: Refactor & Add url
This commit is contained in:
244
script/modules/url/url_search_params.go
Normal file
244
script/modules/url/url_search_params.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package url
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/script/jsc"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
)
|
||||
|
||||
type URLSearchParams struct {
|
||||
class jsc.Class[*Module, *URLSearchParams]
|
||||
params []searchParam
|
||||
}
|
||||
|
||||
func createURLSearchParams(module *Module) jsc.Class[*Module, *URLSearchParams] {
|
||||
class := jsc.NewClass[*Module, *URLSearchParams](module)
|
||||
class.DefineConstructor(newURLSearchParams)
|
||||
class.DefineField("size", (*URLSearchParams).getSize, nil)
|
||||
class.DefineMethod("append", (*URLSearchParams).append)
|
||||
class.DefineMethod("delete", (*URLSearchParams).delete)
|
||||
class.DefineMethod("entries", (*URLSearchParams).entries)
|
||||
class.DefineMethod("forEach", (*URLSearchParams).forEach)
|
||||
class.DefineMethod("get", (*URLSearchParams).get)
|
||||
class.DefineMethod("getAll", (*URLSearchParams).getAll)
|
||||
class.DefineMethod("has", (*URLSearchParams).has)
|
||||
class.DefineMethod("keys", (*URLSearchParams).keys)
|
||||
class.DefineMethod("set", (*URLSearchParams).set)
|
||||
class.DefineMethod("sort", (*URLSearchParams).sort)
|
||||
class.DefineMethod("toString", (*URLSearchParams).toString)
|
||||
class.DefineMethod("values", (*URLSearchParams).values)
|
||||
return class
|
||||
}
|
||||
|
||||
func newURLSearchParams(class jsc.Class[*Module, *URLSearchParams], call goja.ConstructorCall) *URLSearchParams {
|
||||
var (
|
||||
params []searchParam
|
||||
err error
|
||||
)
|
||||
switch argInit := call.Argument(0).Export().(type) {
|
||||
case *URLSearchParams:
|
||||
params = argInit.params
|
||||
case string:
|
||||
params, err = parseQuery(argInit)
|
||||
if err != nil {
|
||||
panic(class.Runtime().NewGoError(err))
|
||||
}
|
||||
case [][]string:
|
||||
for _, pair := range argInit {
|
||||
if len(pair) != 2 {
|
||||
panic(class.Runtime().NewTypeError("Each query pair must be an iterable [name, value] tuple"))
|
||||
}
|
||||
params = append(params, searchParam{pair[0], pair[1]})
|
||||
}
|
||||
case map[string]any:
|
||||
for name, value := range argInit {
|
||||
stringValue, isString := value.(string)
|
||||
if !isString {
|
||||
panic(class.Runtime().NewTypeError("Invalid query value"))
|
||||
}
|
||||
params = append(params, searchParam{name, stringValue})
|
||||
}
|
||||
}
|
||||
return &URLSearchParams{class, params}
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) getSize() any {
|
||||
return len(s.params)
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) append(call goja.FunctionCall) any {
|
||||
name := jsc.AssertString(s.class.Runtime(), call.Argument(0), "name", false)
|
||||
value := call.Argument(1).String()
|
||||
s.params = append(s.params, searchParam{name, value})
|
||||
return goja.Undefined()
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) delete(call goja.FunctionCall) any {
|
||||
name := jsc.AssertString(s.class.Runtime(), call.Argument(0), "name", false)
|
||||
argValue := call.Argument(1)
|
||||
if !jsc.IsNil(argValue) {
|
||||
value := argValue.String()
|
||||
for i, param := range s.params {
|
||||
if param.Key == name && param.Value == value {
|
||||
s.params = append(s.params[:i], s.params[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i, param := range s.params {
|
||||
if param.Key == name {
|
||||
s.params = append(s.params[:i], s.params[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return goja.Undefined()
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) entries(call goja.FunctionCall) any {
|
||||
return jsc.NewIterator[*Module, searchParam](s.class.Module().classURLSearchParamsIterator, s.params, func(this searchParam) any {
|
||||
return s.class.Runtime().NewArray(this.Key, this.Value)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) forEach(call goja.FunctionCall) any {
|
||||
callback := jsc.AssertFunction(s.class.Runtime(), call.Argument(0), "callbackFn")
|
||||
thisValue := call.Argument(1)
|
||||
for _, param := range s.params {
|
||||
for _, value := range param.Value {
|
||||
_, err := callback(thisValue, s.class.Runtime().ToValue(value), s.class.Runtime().ToValue(param.Key), call.This)
|
||||
if err != nil {
|
||||
panic(s.class.Runtime().NewGoError(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
return goja.Undefined()
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) get(call goja.FunctionCall) any {
|
||||
name := jsc.AssertString(s.class.Runtime(), call.Argument(0), "name", false)
|
||||
for _, param := range s.params {
|
||||
if param.Key == name {
|
||||
return param.Value
|
||||
}
|
||||
}
|
||||
return goja.Null()
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) getAll(call goja.FunctionCall) any {
|
||||
name := jsc.AssertString(s.class.Runtime(), call.Argument(0), "name", false)
|
||||
var values []any
|
||||
for _, param := range s.params {
|
||||
if param.Key == name {
|
||||
values = append(values, param.Value)
|
||||
}
|
||||
}
|
||||
return s.class.Runtime().NewArray(values...)
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) has(call goja.FunctionCall) any {
|
||||
name := jsc.AssertString(s.class.Runtime(), call.Argument(0), "name", false)
|
||||
argValue := call.Argument(1)
|
||||
if !jsc.IsNil(argValue) {
|
||||
value := argValue.String()
|
||||
for _, param := range s.params {
|
||||
if param.Key == name && param.Value == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, param := range s.params {
|
||||
if param.Key == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) keys(call goja.FunctionCall) any {
|
||||
return jsc.NewIterator[*Module, searchParam](s.class.Module().classURLSearchParamsIterator, s.params, func(this searchParam) any {
|
||||
return this.Key
|
||||
})
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) set(call goja.FunctionCall) any {
|
||||
name := jsc.AssertString(s.class.Runtime(), call.Argument(0), "name", false)
|
||||
value := call.Argument(1).String()
|
||||
for i, param := range s.params {
|
||||
if param.Key == name {
|
||||
s.params[i].Value = value
|
||||
return goja.Undefined()
|
||||
}
|
||||
}
|
||||
s.params = append(s.params, searchParam{name, value})
|
||||
return goja.Undefined()
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) sort(call goja.FunctionCall) any {
|
||||
sort.SliceStable(s.params, func(i, j int) bool {
|
||||
return s.params[i].Key < s.params[j].Key
|
||||
})
|
||||
return goja.Undefined()
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) toString(call goja.FunctionCall) any {
|
||||
return generateQuery(s.params)
|
||||
}
|
||||
|
||||
func (s *URLSearchParams) values(call goja.FunctionCall) any {
|
||||
return jsc.NewIterator[*Module, searchParam](s.class.Module().classURLSearchParamsIterator, s.params, func(this searchParam) any {
|
||||
return this.Value
|
||||
})
|
||||
}
|
||||
|
||||
type searchParam struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func parseQuery(query string) (params []searchParam, err error) {
|
||||
query = strings.TrimPrefix(query, "?")
|
||||
for query != "" {
|
||||
var key string
|
||||
key, query, _ = strings.Cut(query, "&")
|
||||
if strings.Contains(key, ";") {
|
||||
err = fmt.Errorf("invalid semicolon separator in query")
|
||||
continue
|
||||
}
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
key, value, _ := strings.Cut(key, "=")
|
||||
key, err1 := url.QueryUnescape(key)
|
||||
if err1 != nil {
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
continue
|
||||
}
|
||||
value, err1 = url.QueryUnescape(value)
|
||||
if err1 != nil {
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
continue
|
||||
}
|
||||
params = append(params, searchParam{key, value})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func generateQuery(params []searchParam) string {
|
||||
var parts []string
|
||||
for _, param := range params {
|
||||
parts = append(parts, F.ToString(param.Key, "=", url.QueryEscape(param.Value)))
|
||||
}
|
||||
return strings.Join(parts, "&")
|
||||
}
|
||||
Reference in New Issue
Block a user