mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2026-02-19 20:15:37 +08:00
优化代码
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
package agents
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"net"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ package bfs
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -4,10 +4,10 @@ package bfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/linkedlist"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"log"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
package bfs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
import "github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
|
||||
// TODO 线程数可以根据硬盘数量动态调整?
|
||||
var readThreadsLimiter = make(chan zero.Zero, 8)
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
package cachehits
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/idles"
|
||||
memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"net"
|
||||
"runtime"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package connutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
package counters
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem"
|
||||
syncutils "github.com/TeaOSLab/EdgeNode/internal/utils/sync"
|
||||
"github.com/cespare/xxhash/v2"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
41
internal/utils/encrypt/magic_key.go
Normal file
41
internal/utils/encrypt/magic_key.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
)
|
||||
|
||||
const (
|
||||
MagicKey = "f1c8eafb543f03023e97b7be864a4e9b"
|
||||
)
|
||||
|
||||
// 加密特殊信息
|
||||
func MagicKeyEncode(data []byte) []byte {
|
||||
method, err := NewMethodInstance("aes-256-cfb", MagicKey, MagicKey[:16])
|
||||
if err != nil {
|
||||
logs.Println("[MagicKeyEncode]" + err.Error())
|
||||
return data
|
||||
}
|
||||
|
||||
dst, err := method.Encrypt(data)
|
||||
if err != nil {
|
||||
logs.Println("[MagicKeyEncode]" + err.Error())
|
||||
return data
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// 解密特殊信息
|
||||
func MagicKeyDecode(data []byte) []byte {
|
||||
method, err := NewMethodInstance("aes-256-cfb", MagicKey, MagicKey[:16])
|
||||
if err != nil {
|
||||
logs.Println("[MagicKeyEncode]" + err.Error())
|
||||
return data
|
||||
}
|
||||
|
||||
src, err := method.Decrypt(data)
|
||||
if err != nil {
|
||||
logs.Println("[MagicKeyEncode]" + err.Error())
|
||||
return data
|
||||
}
|
||||
return src
|
||||
}
|
||||
14
internal/utils/encrypt/magic_key_test.go
Normal file
14
internal/utils/encrypt/magic_key_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package encrypt_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/encrypt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMagicKeyEncode(t *testing.T) {
|
||||
var dst = encrypt.MagicKeyEncode([]byte("Hello,World"))
|
||||
t.Log("dst:", string(dst))
|
||||
|
||||
var src = encrypt.MagicKeyDecode(dst)
|
||||
t.Log("src:", string(src))
|
||||
}
|
||||
12
internal/utils/encrypt/method.go
Normal file
12
internal/utils/encrypt/method.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package encrypt
|
||||
|
||||
type MethodInterface interface {
|
||||
// Init 初始化
|
||||
Init(key []byte, iv []byte) error
|
||||
|
||||
// Encrypt 加密
|
||||
Encrypt(src []byte) (dst []byte, err error)
|
||||
|
||||
// Decrypt 解密
|
||||
Decrypt(dst []byte) (src []byte, err error)
|
||||
}
|
||||
73
internal/utils/encrypt/method_aes_128_cfb.go
Normal file
73
internal/utils/encrypt/method_aes_128_cfb.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
type AES128CFBMethod struct {
|
||||
iv []byte
|
||||
block cipher.Block
|
||||
}
|
||||
|
||||
func (this *AES128CFBMethod) Init(key, iv []byte) error {
|
||||
// 判断key是否为32长度
|
||||
l := len(key)
|
||||
if l > 16 {
|
||||
key = key[:16]
|
||||
} else if l < 16 {
|
||||
key = append(key, bytes.Repeat([]byte{' '}, 16-l)...)
|
||||
}
|
||||
|
||||
// 判断iv长度
|
||||
l2 := len(iv)
|
||||
if l2 > aes.BlockSize {
|
||||
iv = iv[:aes.BlockSize]
|
||||
} else if l2 < aes.BlockSize {
|
||||
iv = append(iv, bytes.Repeat([]byte{' '}, aes.BlockSize-l2)...)
|
||||
}
|
||||
|
||||
this.iv = iv
|
||||
|
||||
// block
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.block = block
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *AES128CFBMethod) Encrypt(src []byte) (dst []byte, err error) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = RecoverMethodPanic(recover())
|
||||
}()
|
||||
|
||||
dst = make([]byte, len(src))
|
||||
encrypter := cipher.NewCFBEncrypter(this.block, this.iv)
|
||||
encrypter.XORKeyStream(dst, src)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *AES128CFBMethod) Decrypt(dst []byte) (src []byte, err error) {
|
||||
if len(dst) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = RecoverMethodPanic(recover())
|
||||
}()
|
||||
|
||||
src = make([]byte, len(dst))
|
||||
encrypter := cipher.NewCFBDecrypter(this.block, this.iv)
|
||||
encrypter.XORKeyStream(src, dst)
|
||||
|
||||
return
|
||||
}
|
||||
90
internal/utils/encrypt/method_aes_128_cfb_test.go
Normal file
90
internal/utils/encrypt/method_aes_128_cfb_test.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package encrypt_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/encrypt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAES128CFBMethod_Encrypt(t *testing.T) {
|
||||
method, err := encrypt.NewMethodInstance("aes-128-cfb", "abc", "123")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var src = []byte("Hello, World")
|
||||
dst, err := method.Encrypt(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dst = dst[:len(src)]
|
||||
t.Log("dst:", string(dst))
|
||||
|
||||
src, err = method.Decrypt(dst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("src:", string(src))
|
||||
}
|
||||
|
||||
func TestAES128CFBMethod_Encrypt2(t *testing.T) {
|
||||
method, err := encrypt.NewMethodInstance("aes-128-cfb", "abc", "123")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var sources = [][]byte{}
|
||||
|
||||
{
|
||||
a := []byte{1}
|
||||
_, err = method.Encrypt(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
src := []byte(strings.Repeat("Hello", 1))
|
||||
dst, err := method.Encrypt(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sources = append(sources, dst)
|
||||
}
|
||||
|
||||
{
|
||||
a := []byte{1}
|
||||
_, err = method.Decrypt(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, dst := range sources {
|
||||
dst2 := append([]byte{}, dst...)
|
||||
src2, err := method.Decrypt(dst2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(string(src2))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAES128CFBMethod_Encrypt(b *testing.B) {
|
||||
runtime.GOMAXPROCS(1)
|
||||
|
||||
method, err := encrypt.NewMethodInstance("aes-128-cfb", "abc", "123")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
var src = []byte(strings.Repeat("Hello", 1024))
|
||||
for i := 0; i < b.N; i++ {
|
||||
dst, err := method.Encrypt(src)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
_ = dst
|
||||
}
|
||||
}
|
||||
74
internal/utils/encrypt/method_aes_192_cfb.go
Normal file
74
internal/utils/encrypt/method_aes_192_cfb.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
type AES192CFBMethod struct {
|
||||
block cipher.Block
|
||||
iv []byte
|
||||
}
|
||||
|
||||
func (this *AES192CFBMethod) Init(key, iv []byte) error {
|
||||
// 判断key是否为24长度
|
||||
l := len(key)
|
||||
if l > 24 {
|
||||
key = key[:24]
|
||||
} else if l < 24 {
|
||||
key = append(key, bytes.Repeat([]byte{' '}, 24-l)...)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.block = block
|
||||
|
||||
// 判断iv长度
|
||||
l2 := len(iv)
|
||||
if l2 > aes.BlockSize {
|
||||
iv = iv[:aes.BlockSize]
|
||||
} else if l2 < aes.BlockSize {
|
||||
iv = append(iv, bytes.Repeat([]byte{' '}, aes.BlockSize-l2)...)
|
||||
}
|
||||
|
||||
this.iv = iv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *AES192CFBMethod) Encrypt(src []byte) (dst []byte, err error) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = RecoverMethodPanic(recover())
|
||||
}()
|
||||
|
||||
dst = make([]byte, len(src))
|
||||
|
||||
encrypter := cipher.NewCFBEncrypter(this.block, this.iv)
|
||||
encrypter.XORKeyStream(dst, src)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *AES192CFBMethod) Decrypt(dst []byte) (src []byte, err error) {
|
||||
if len(dst) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = RecoverMethodPanic(recover())
|
||||
}()
|
||||
|
||||
src = make([]byte, len(dst))
|
||||
|
||||
decrypter := cipher.NewCFBDecrypter(this.block, this.iv)
|
||||
decrypter.XORKeyStream(src, dst)
|
||||
|
||||
return
|
||||
}
|
||||
46
internal/utils/encrypt/method_aes_192_cfb_test.go
Normal file
46
internal/utils/encrypt/method_aes_192_cfb_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package encrypt_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/encrypt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAES192CFBMethod_Encrypt(t *testing.T) {
|
||||
method, err := encrypt.NewMethodInstance("aes-192-cfb", "abc", "123")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
src := []byte("Hello, World")
|
||||
dst, err := method.Encrypt(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dst = dst[:len(src)]
|
||||
t.Log("dst:", string(dst))
|
||||
|
||||
src, err = method.Decrypt(dst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("src:", string(src))
|
||||
}
|
||||
|
||||
func BenchmarkAES192CFBMethod_Encrypt(b *testing.B) {
|
||||
runtime.GOMAXPROCS(1)
|
||||
|
||||
method, err := encrypt.NewMethodInstance("aes-192-cfb", "abc", "123")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
var src = []byte(strings.Repeat("Hello", 1024))
|
||||
for i := 0; i < b.N; i++ {
|
||||
dst, err := method.Encrypt(src)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
_ = dst
|
||||
}
|
||||
}
|
||||
72
internal/utils/encrypt/method_aes_256_cfb.go
Normal file
72
internal/utils/encrypt/method_aes_256_cfb.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
type AES256CFBMethod struct {
|
||||
block cipher.Block
|
||||
iv []byte
|
||||
}
|
||||
|
||||
func (this *AES256CFBMethod) Init(key, iv []byte) error {
|
||||
// 判断key是否为32长度
|
||||
l := len(key)
|
||||
if l > 32 {
|
||||
key = key[:32]
|
||||
} else if l < 32 {
|
||||
key = append(key, bytes.Repeat([]byte{' '}, 32-l)...)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.block = block
|
||||
|
||||
// 判断iv长度
|
||||
l2 := len(iv)
|
||||
if l2 > aes.BlockSize {
|
||||
iv = iv[:aes.BlockSize]
|
||||
} else if l2 < aes.BlockSize {
|
||||
iv = append(iv, bytes.Repeat([]byte{' '}, aes.BlockSize-l2)...)
|
||||
}
|
||||
this.iv = iv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *AES256CFBMethod) Encrypt(src []byte) (dst []byte, err error) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = RecoverMethodPanic(recover())
|
||||
}()
|
||||
|
||||
dst = make([]byte, len(src))
|
||||
|
||||
encrypter := cipher.NewCFBEncrypter(this.block, this.iv)
|
||||
encrypter.XORKeyStream(dst, src)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *AES256CFBMethod) Decrypt(dst []byte) (src []byte, err error) {
|
||||
if len(dst) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = RecoverMethodPanic(recover())
|
||||
}()
|
||||
|
||||
src = make([]byte, len(dst))
|
||||
decrypter := cipher.NewCFBDecrypter(this.block, this.iv)
|
||||
decrypter.XORKeyStream(src, dst)
|
||||
|
||||
return
|
||||
}
|
||||
45
internal/utils/encrypt/method_aes_256_cfb_test.go
Normal file
45
internal/utils/encrypt/method_aes_256_cfb_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package encrypt_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/encrypt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAES256CFBMethod_Encrypt(t *testing.T) {
|
||||
method, err := encrypt.NewMethodInstance("aes-256-cfb", "abc", "123")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var src = []byte("Hello, World")
|
||||
dst, err := method.Encrypt(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dst = dst[:len(src)]
|
||||
t.Log("dst:", string(dst))
|
||||
|
||||
src, err = method.Decrypt(dst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("src:", string(src))
|
||||
}
|
||||
|
||||
func TestAES256CFBMethod_Encrypt2(t *testing.T) {
|
||||
method, err := encrypt.NewMethodInstance("aes-256-cfb", "abc", "123")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var src = []byte("Hello, World")
|
||||
dst, err := method.Encrypt(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("dst:", string(dst))
|
||||
|
||||
src, err = method.Decrypt(dst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("src:", string(src))
|
||||
}
|
||||
26
internal/utils/encrypt/method_raw.go
Normal file
26
internal/utils/encrypt/method_raw.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package encrypt
|
||||
|
||||
type RawMethod struct {
|
||||
}
|
||||
|
||||
func (this *RawMethod) Init(key, iv []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *RawMethod) Encrypt(src []byte) (dst []byte, err error) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
dst = make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RawMethod) Decrypt(dst []byte) (src []byte, err error) {
|
||||
if len(dst) == 0 {
|
||||
return
|
||||
}
|
||||
src = make([]byte, len(dst))
|
||||
copy(src, dst)
|
||||
return
|
||||
}
|
||||
26
internal/utils/encrypt/method_raw_test.go
Normal file
26
internal/utils/encrypt/method_raw_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package encrypt_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/encrypt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRawMethod_Encrypt(t *testing.T) {
|
||||
method, err := encrypt.NewMethodInstance("raw", "abc", "123")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var src = []byte("Hello, World")
|
||||
dst, err := method.Encrypt(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dst = dst[:len(src)]
|
||||
t.Log("dst:", string(dst))
|
||||
|
||||
src, err = method.Decrypt(dst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("src:", string(src))
|
||||
}
|
||||
43
internal/utils/encrypt/method_utils.go
Normal file
43
internal/utils/encrypt/method_utils.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var methods = map[string]reflect.Type{
|
||||
"raw": reflect.TypeOf(new(RawMethod)).Elem(),
|
||||
"aes-128-cfb": reflect.TypeOf(new(AES128CFBMethod)).Elem(),
|
||||
"aes-192-cfb": reflect.TypeOf(new(AES192CFBMethod)).Elem(),
|
||||
"aes-256-cfb": reflect.TypeOf(new(AES256CFBMethod)).Elem(),
|
||||
}
|
||||
|
||||
func NewMethodInstance(method string, key string, iv string) (MethodInterface, error) {
|
||||
valueType, ok := methods[method]
|
||||
if !ok {
|
||||
return nil, errors.New("method '" + method + "' not found")
|
||||
}
|
||||
instance, ok := reflect.New(valueType).Interface().(MethodInterface)
|
||||
if !ok {
|
||||
return nil, errors.New("method '" + method + "' must implement MethodInterface")
|
||||
}
|
||||
err := instance.Init([]byte(key), []byte(iv))
|
||||
return instance, err
|
||||
}
|
||||
|
||||
func RecoverMethodPanic(err interface{}) error {
|
||||
if err != nil {
|
||||
s, ok := err.(string)
|
||||
if ok {
|
||||
return errors.New(s)
|
||||
}
|
||||
|
||||
e, ok := err.(error)
|
||||
if ok {
|
||||
return e
|
||||
}
|
||||
|
||||
return errors.New("unknown error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
11
internal/utils/encrypt/method_utils_test.go
Normal file
11
internal/utils/encrypt/method_utils_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package encrypt_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/encrypt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFindMethodInstance(t *testing.T) {
|
||||
t.Log(encrypt.NewMethodInstance("a", "b", ""))
|
||||
t.Log(encrypt.NewMethodInstance("aes-256-cfb", "123456", ""))
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package expires
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
package expires
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ package fasttime
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
|
||||
@@ -5,7 +5,7 @@ package fsutils
|
||||
import (
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
"os"
|
||||
|
||||
12
internal/utils/goman/instance.go
Normal file
12
internal/utils/goman/instance.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package goman
|
||||
|
||||
import "time"
|
||||
|
||||
type Instance struct {
|
||||
Id uint64
|
||||
CreatedTime time.Time
|
||||
File string
|
||||
Line int
|
||||
}
|
||||
90
internal/utils/goman/lib.go
Normal file
90
internal/utils/goman/lib.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package goman
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var locker = &sync.Mutex{}
|
||||
var instanceMap = map[uint64]*Instance{} // id => *Instance
|
||||
var instanceId = uint64(0)
|
||||
|
||||
// New 新创建goroutine
|
||||
func New(f func()) {
|
||||
if !teaconst.IsMain {
|
||||
return
|
||||
}
|
||||
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
|
||||
go func() {
|
||||
locker.Lock()
|
||||
instanceId++
|
||||
|
||||
var instance = &Instance{
|
||||
Id: instanceId,
|
||||
CreatedTime: time.Now(),
|
||||
}
|
||||
|
||||
instance.File = file
|
||||
instance.Line = line
|
||||
|
||||
instanceMap[instanceId] = instance
|
||||
locker.Unlock()
|
||||
|
||||
// run function
|
||||
f()
|
||||
|
||||
locker.Lock()
|
||||
delete(instanceMap, instanceId)
|
||||
locker.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
// NewWithArgs 创建带有参数的goroutine
|
||||
func NewWithArgs(f func(args ...interface{}), args ...interface{}) {
|
||||
if !teaconst.IsMain {
|
||||
return
|
||||
}
|
||||
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
|
||||
go func() {
|
||||
locker.Lock()
|
||||
instanceId++
|
||||
|
||||
var instance = &Instance{
|
||||
Id: instanceId,
|
||||
CreatedTime: time.Now(),
|
||||
}
|
||||
|
||||
instance.File = file
|
||||
instance.Line = line
|
||||
|
||||
instanceMap[instanceId] = instance
|
||||
locker.Unlock()
|
||||
|
||||
// run function
|
||||
f(args...)
|
||||
|
||||
locker.Lock()
|
||||
delete(instanceMap, instanceId)
|
||||
locker.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
// List 列出所有正在运行goroutine
|
||||
func List() []*Instance {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
|
||||
var result = []*Instance{}
|
||||
for _, instance := range instanceMap {
|
||||
result = append(result, instance)
|
||||
}
|
||||
return result
|
||||
}
|
||||
29
internal/utils/goman/lib_test.go
Normal file
29
internal/utils/goman/lib_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package goman_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
goman.New(func() {
|
||||
t.Log("Hello")
|
||||
|
||||
t.Log(goman.List())
|
||||
})
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
t.Log(goman.List())
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
func TestNewWithArgs(t *testing.T) {
|
||||
goman.NewWithArgs(func(args ...interface{}) {
|
||||
t.Log(args[0], args[1])
|
||||
}, 1, 2)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
52
internal/utils/goman/task_group.go
Normal file
52
internal/utils/goman/task_group.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package goman
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type TaskGroup struct {
|
||||
semi chan zero.Zero
|
||||
wg *sync.WaitGroup
|
||||
locker *sync.RWMutex
|
||||
}
|
||||
|
||||
func NewTaskGroup() *TaskGroup {
|
||||
var concurrent = runtime.NumCPU()
|
||||
if concurrent <= 1 {
|
||||
concurrent = 2
|
||||
}
|
||||
return &TaskGroup{
|
||||
semi: make(chan zero.Zero, concurrent),
|
||||
wg: &sync.WaitGroup{},
|
||||
locker: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (this *TaskGroup) Run(f func()) {
|
||||
this.wg.Add(1)
|
||||
go func() {
|
||||
defer this.wg.Done()
|
||||
|
||||
this.semi <- zero.Zero{}
|
||||
|
||||
f()
|
||||
|
||||
<-this.semi
|
||||
}()
|
||||
}
|
||||
|
||||
func (this *TaskGroup) Wait() {
|
||||
this.wg.Wait()
|
||||
}
|
||||
|
||||
func (this *TaskGroup) Lock() {
|
||||
this.locker.Lock()
|
||||
}
|
||||
|
||||
func (this *TaskGroup) Unlock() {
|
||||
this.locker.Unlock()
|
||||
}
|
||||
30
internal/utils/goman/task_group_test.go
Normal file
30
internal/utils/goman/task_group_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package goman_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewTaskGroup(t *testing.T) {
|
||||
var group = goman.NewTaskGroup()
|
||||
var m = map[int]bool{}
|
||||
|
||||
for i := 0; i < runtime.NumCPU()*2; i++ {
|
||||
var index = i
|
||||
group.Run(func() {
|
||||
t.Log("task", index)
|
||||
|
||||
group.Lock()
|
||||
_, ok := m[index]
|
||||
if ok {
|
||||
t.Error("duplicated:", index)
|
||||
}
|
||||
m[index] = true
|
||||
group.Unlock()
|
||||
})
|
||||
}
|
||||
group.Wait()
|
||||
}
|
||||
@@ -5,8 +5,8 @@ package idles
|
||||
import (
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
"math"
|
||||
|
||||
@@ -4,7 +4,7 @@ package memutils
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ package percpu_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/percpu"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
||||
298
internal/utils/re/regexp.go
Normal file
298
internal/utils/re/regexp.go
Normal file
@@ -0,0 +1,298 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package re
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
"regexp/syntax"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var prefixReg = regexp.MustCompile(`^\(\?([\w\s]+)\)`) // (?x)
|
||||
var braceZeroReg = regexp.MustCompile(`^{\s*0*\s*}`) // {0}
|
||||
var braceZeroReg2 = regexp.MustCompile(`^{\s*0*\s*,`) // {0, x}
|
||||
|
||||
var lastId uint64
|
||||
|
||||
type Regexp struct {
|
||||
exp string
|
||||
rawRegexp *regexp.Regexp
|
||||
|
||||
isStrict bool
|
||||
isCaseInsensitive bool
|
||||
keywords []string
|
||||
keywordsMap RuneMap
|
||||
|
||||
id uint64
|
||||
idString string
|
||||
}
|
||||
|
||||
func MustCompile(exp string) *Regexp {
|
||||
var reg = &Regexp{
|
||||
exp: exp,
|
||||
rawRegexp: regexp.MustCompile(exp),
|
||||
}
|
||||
reg.init()
|
||||
return reg
|
||||
}
|
||||
|
||||
func Compile(exp string) (*Regexp, error) {
|
||||
reg, err := regexp.Compile(exp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewRegexp(reg), nil
|
||||
}
|
||||
|
||||
func NewRegexp(rawRegexp *regexp.Regexp) *Regexp {
|
||||
var reg = &Regexp{
|
||||
exp: rawRegexp.String(),
|
||||
rawRegexp: rawRegexp,
|
||||
}
|
||||
reg.init()
|
||||
return reg
|
||||
}
|
||||
|
||||
func (this *Regexp) init() {
|
||||
this.id = atomic.AddUint64(&lastId, 1)
|
||||
this.idString = "re:" + types.String(this.id)
|
||||
|
||||
if len(this.exp) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var exp = strings.TrimSpace(this.exp)
|
||||
|
||||
// 去掉前面的(?...)
|
||||
if prefixReg.MatchString(exp) {
|
||||
var matches = prefixReg.FindStringSubmatch(exp)
|
||||
var modifiers = matches[1]
|
||||
if strings.Contains(modifiers, "i") {
|
||||
this.isCaseInsensitive = true
|
||||
}
|
||||
exp = exp[len(matches[0]):]
|
||||
}
|
||||
|
||||
var keywords = this.ParseKeywords(exp)
|
||||
|
||||
var filteredKeywords = []string{}
|
||||
var minLength = 1
|
||||
var isValid = true
|
||||
for _, keyword := range keywords {
|
||||
if len(keyword) <= minLength {
|
||||
isValid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if isValid {
|
||||
filteredKeywords = keywords
|
||||
}
|
||||
|
||||
this.keywords = filteredKeywords
|
||||
if len(filteredKeywords) > 0 {
|
||||
this.keywordsMap = NewRuneTree(filteredKeywords)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Regexp) Keywords() []string {
|
||||
return this.keywords
|
||||
}
|
||||
|
||||
func (this *Regexp) Raw() *regexp.Regexp {
|
||||
return this.rawRegexp
|
||||
}
|
||||
|
||||
func (this *Regexp) IsCaseInsensitive() bool {
|
||||
return this.isCaseInsensitive
|
||||
}
|
||||
|
||||
func (this *Regexp) MatchString(s string) bool {
|
||||
if this.keywordsMap != nil {
|
||||
var b = this.keywordsMap.Lookup(s, this.isCaseInsensitive)
|
||||
if !b {
|
||||
return false
|
||||
}
|
||||
if this.isStrict {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return this.rawRegexp.MatchString(s)
|
||||
}
|
||||
|
||||
func (this *Regexp) Match(s []byte) bool {
|
||||
if this.keywordsMap != nil {
|
||||
var b = this.keywordsMap.Lookup(string(s), this.isCaseInsensitive)
|
||||
if !b {
|
||||
return false
|
||||
}
|
||||
if this.isStrict {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return this.rawRegexp.Match(s)
|
||||
}
|
||||
|
||||
func (this *Regexp) FindStringSubmatch(s string) []string {
|
||||
return this.rawRegexp.FindStringSubmatch(s)
|
||||
}
|
||||
|
||||
// ParseKeywords 提取表达式中的关键词
|
||||
func (this *Regexp) ParseKeywords(exp string) (keywords []string) {
|
||||
if len(exp) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
reg, err := syntax.Parse(exp, syntax.Perl)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(reg.Sub) == 0 {
|
||||
var keywordRunes = this.parseKeyword(reg.String())
|
||||
if len(keywordRunes) > 0 {
|
||||
keywords = append(keywords, string(keywordRunes))
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(reg.Sub) == 1 {
|
||||
if reg.Op == syntax.OpStar || reg.Op == syntax.OpQuest || reg.Op == syntax.OpRepeat {
|
||||
return nil
|
||||
}
|
||||
return this.ParseKeywords(reg.Sub[0].String())
|
||||
}
|
||||
|
||||
const maxComposedKeywords = 32
|
||||
|
||||
switch reg.Op {
|
||||
case syntax.OpConcat:
|
||||
var prevKeywords = []string{}
|
||||
var isStarted bool
|
||||
for _, sub := range reg.Sub {
|
||||
if sub.String() == `\b` {
|
||||
if isStarted {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if sub.Op != syntax.OpLiteral && sub.Op != syntax.OpCapture && sub.Op != syntax.OpAlternate {
|
||||
if isStarted {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
var subKeywords = this.ParseKeywords(sub.String())
|
||||
if len(subKeywords) > 0 {
|
||||
if !isStarted {
|
||||
prevKeywords = subKeywords
|
||||
isStarted = true
|
||||
} else {
|
||||
for _, prevKeyword := range prevKeywords {
|
||||
for _, subKeyword := range subKeywords {
|
||||
keywords = append(keywords, prevKeyword+subKeyword)
|
||||
|
||||
// 限制不能超出最大关键词
|
||||
if len(keywords) > maxComposedKeywords {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
prevKeywords = keywords
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(prevKeywords) > 0 && len(keywords) == 0 {
|
||||
keywords = prevKeywords
|
||||
}
|
||||
case syntax.OpAlternate:
|
||||
for _, sub := range reg.Sub {
|
||||
var subKeywords = this.ParseKeywords(sub.String())
|
||||
if len(subKeywords) == 0 {
|
||||
keywords = nil
|
||||
return
|
||||
}
|
||||
keywords = append(keywords, subKeywords...)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *Regexp) IdString() string {
|
||||
return this.idString
|
||||
}
|
||||
|
||||
func (this *Regexp) parseKeyword(subExp string) (result []rune) {
|
||||
if len(subExp) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 去除开始和结尾的()
|
||||
if subExp[0] == '(' && subExp[len(subExp)-1] == ')' {
|
||||
subExp = subExp[1 : len(subExp)-1]
|
||||
if len(subExp) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var runes = []rune(subExp)
|
||||
|
||||
for index, r := range runes {
|
||||
if r == '[' || r == '{' || r == '.' || r == '+' || r == '$' {
|
||||
if index == 0 {
|
||||
return
|
||||
}
|
||||
if runes[index-1] != '\\' {
|
||||
if r == '{' && (braceZeroReg.MatchString(subExp[index:])) || braceZeroReg2.MatchString(subExp[index:]) { // r {0, ...}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
return result[:len(result)-1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
if r == '?' || r == '*' {
|
||||
if index == 0 {
|
||||
return
|
||||
}
|
||||
if runes[index-1] != '\\' {
|
||||
if len(result) > 0 {
|
||||
return result[:len(result)-1]
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (r == 'n' || r == 't' || r == 'a' || r == 'f' || r == 'r' || r == 'v' || r == 'x') && index > 0 && runes[index-1] == '\\' {
|
||||
switch r {
|
||||
case 'n':
|
||||
r = '\n'
|
||||
case 't':
|
||||
r = '\t'
|
||||
case 'f':
|
||||
r = '\f'
|
||||
case 'r':
|
||||
r = '\r'
|
||||
case 'v':
|
||||
r = '\v'
|
||||
case 'a':
|
||||
r = '\a'
|
||||
case 'x':
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if r == '\\' {
|
||||
continue
|
||||
}
|
||||
result = append(result, r)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
263
internal/utils/re/regexp_test.go
Normal file
263
internal/utils/re/regexp_test.go
Normal file
@@ -0,0 +1,263 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package re_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/re"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegexp(t *testing.T) {
|
||||
for _, s := range []string{"(?i)(abc|efg)", "abc|efg", "abc(.+)"} {
|
||||
var reg = regexp.MustCompile(s)
|
||||
t.Log("===" + s + "===")
|
||||
t.Log(reg.LiteralPrefix())
|
||||
t.Log(reg.NumSubexp())
|
||||
t.Log(reg.SubexpNames())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexp_MatchString(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
{
|
||||
var r = re.MustCompile("abc")
|
||||
a.IsTrue(r.MatchString("abc"))
|
||||
a.IsFalse(r.MatchString("ab"))
|
||||
a.IsFalse(r.MatchString("ABC"))
|
||||
}
|
||||
|
||||
{
|
||||
var r = re.MustCompile("(?i)abc|def|ghi")
|
||||
a.IsTrue(r.MatchString("DEF"))
|
||||
a.IsFalse(r.MatchString("ab"))
|
||||
a.IsTrue(r.MatchString("ABC"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexp_Sub(t *testing.T) {
|
||||
{
|
||||
reg := regexp.MustCompile(`(a|b|c)(e|f|g)`)
|
||||
for _, subName := range reg.SubexpNames() {
|
||||
t.Log(subName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexp_ParseKeywords(t *testing.T) {
|
||||
var r = re.MustCompile("")
|
||||
|
||||
{
|
||||
var keywords = r.ParseKeywords(`\n\t\n\f\r\v\x123`)
|
||||
t.Log(keywords)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexp_Special(t *testing.T) {
|
||||
for _, s := range []string{
|
||||
`\\s`,
|
||||
`\s\W`,
|
||||
`aaaa/\W`,
|
||||
`aaaa\/\W`,
|
||||
`aaaa\=\W`,
|
||||
`aaaa\\=\W`,
|
||||
`aaaa\\\=\W`,
|
||||
`aaaa\\\\=\W`,
|
||||
} {
|
||||
var es = testUnescape(t, s)
|
||||
t.Log(s, "=>", es)
|
||||
_, err := re.Compile(es)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexp_Special2(t *testing.T) {
|
||||
r, err := re.Compile(testUnescape(t, `/api/ios/a
|
||||
/api/ios/b
|
||||
/api/ios/c
|
||||
/report`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(r.Keywords())
|
||||
}
|
||||
|
||||
func TestRegexp_ParseKeywords2(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
var r = re.MustCompile("")
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("(abc)def"), []string{"abcdef"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("(abc)|(?:def)"), []string{"abc", "def"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("(abc)"), []string{"abc"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("(abc|def|ghi)"), []string{"abc", "def", "ghi"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("(?i:abc)"), []string{}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`\babc`), []string{"abc"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(` \babc`), []string{" "}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`\babc\b`), []string{"abc"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`\b(abc)`), []string{"abc"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("abc"), []string{"abc"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("abc|efg|hij"), []string{"abc", "efg", "hij"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`abc\|efg|hij`), []string{"abc|efg", "hij"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`abc\|efg*|hij`), []string{"abc|ef", "hij"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`abc\|efg?|hij`), []string{"abc|ef", "hij"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`abc\|efg+|hij`), []string{"abc|ef", "hij"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`abc\|efg{2,10}|hij`), []string{"abc|ef", "hij"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`abc\|efg{0,10}|hij`), []string{"abc|ef", "hij"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`abc\|efg.+|hij`), []string{"abc|efg", "hij"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("A(abc|bcd)"), []string{"Aabc", "Abcd"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("^abc"), []string{"abc"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("abc$"), []string{"abc"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`abc$`), []string{"abc"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("abc\\d"), []string{"abc"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("abc{0,4}"), []string{"ab"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("{0,4}"), []string{}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("{1,4}"), []string{}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords("中文|北京|上海|golang"), []string{"中文", "北京", "上海", "golang"}))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`(onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick)\s*=`), strings.Split("onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick", "|")))
|
||||
a.IsTrue(testCompareStrings(r.ParseKeywords(`/\*(!|\x00)`), []string{"/*"}))
|
||||
}
|
||||
|
||||
func TestRegexp_ParseKeywords3(t *testing.T) {
|
||||
var r = re.MustCompile("")
|
||||
|
||||
var policy = firewallconfigs.HTTPFirewallTemplate()
|
||||
for _, group := range policy.Inbound.Groups {
|
||||
for _, set := range group.Sets {
|
||||
for _, rule := range set.Rules {
|
||||
if rule.Operator == firewallconfigs.HTTPFirewallRuleOperatorMatch || rule.Operator == firewallconfigs.HTTPFirewallRuleOperatorNotMatch {
|
||||
t.Log(set.Name+":", rule.Value, "=>", r.ParseKeywords(rule.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexp_MatchString(b *testing.B) {
|
||||
var r = re.MustCompile("(?i)(onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick|onkeydown|onkeyup|onkeypress)(\\s|%09|%0A|(\\+|%20))*(=|%3D)")
|
||||
b.ResetTimer()
|
||||
|
||||
//b.Log("keywords:", r.Keywords())
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.MatchString("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexp_MatchString2(b *testing.B) {
|
||||
var r = regexp.MustCompile(`(?i)(onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick|onkeydown|onkeyup|onkeypress)(\s|%09|%0A|(\+|%20))*(=|%3D)`)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.MatchString("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexp_MatchString_CaseSensitive(b *testing.B) {
|
||||
var r = re.MustCompile("(abc|def|ghi)")
|
||||
b.Log("keywords:", r.Keywords())
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.MatchString("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexp_MatchString_CaseSensitive2(b *testing.B) {
|
||||
var r = regexp.MustCompile("(abc|def|ghi)")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.MatchString("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexp_MatchString_VS_FindSubString1(b *testing.B) {
|
||||
var r = re.MustCompile("(?i)(chrome)")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = r.Raw().MatchString("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexp_MatchString_VS_FindSubString2(b *testing.B) {
|
||||
var r = re.MustCompile("(?i)(chrome)")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = r.Raw().FindStringSubmatch("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitAndJoin(t *testing.T) {
|
||||
var pieces = strings.Split(`/api/ios/a
|
||||
/api/ios/b
|
||||
/api/ios/c
|
||||
/report`, "/")
|
||||
t.Log(strings.Join(pieces, `(/|%2F)`))
|
||||
}
|
||||
|
||||
func testCompareStrings(s1 []string, s2 []string) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for index, s := range s1 {
|
||||
if s != s2[index] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func testUnescape(t *testing.T, v string) string {
|
||||
// replace urlencoded characters
|
||||
var unescapeChars = [][2]string{
|
||||
{`\s`, `(\s|%09|%0A|\+)`},
|
||||
{`\(`, `(\(|%28)`},
|
||||
{`=`, `(=|%3D)`},
|
||||
{`<`, `(<|%3C)`},
|
||||
{`\*`, `(\*|%2A)`},
|
||||
{`\\`, `(\\|%2F)`},
|
||||
{`!`, `(!|%21)`},
|
||||
{`/`, `(/|%2F)`},
|
||||
{`;`, `(;|%3B)`},
|
||||
{`\+`, `(\+|%20)`},
|
||||
}
|
||||
|
||||
for _, c := range unescapeChars {
|
||||
if !strings.Contains(v, c[0]) {
|
||||
continue
|
||||
}
|
||||
var pieces = strings.Split(v, c[0])
|
||||
|
||||
// 修复piece中错误的\
|
||||
for pieceIndex, piece := range pieces {
|
||||
var l = len(piece)
|
||||
if l == 0 {
|
||||
continue
|
||||
}
|
||||
if piece[l-1] != '\\' {
|
||||
continue
|
||||
}
|
||||
|
||||
// 计算\的数量
|
||||
var countBackSlashes = 0
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
if piece[i] == '\\' {
|
||||
countBackSlashes++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if countBackSlashes%2 == 1 {
|
||||
// 去掉最后一个
|
||||
pieces[pieceIndex] = piece[:len(piece)-1]
|
||||
}
|
||||
}
|
||||
|
||||
v = strings.Join(pieces, c[1])
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
74
internal/utils/re/rune_tree.go
Normal file
74
internal/utils/re/rune_tree.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package re
|
||||
|
||||
type RuneMap map[rune]*RuneTree
|
||||
|
||||
func (this RuneMap) Lookup(s string, caseInsensitive bool) bool {
|
||||
return this.lookup([]rune(s), caseInsensitive, 0)
|
||||
}
|
||||
|
||||
func (this RuneMap) lookup(runes []rune, caseInsensitive bool, depth int) bool {
|
||||
if len(runes) == 0 {
|
||||
return false
|
||||
}
|
||||
for i, r := range runes {
|
||||
tree, ok := this[r]
|
||||
if !ok {
|
||||
if caseInsensitive {
|
||||
if r >= 'a' && r <= 'z' {
|
||||
r -= 32
|
||||
tree, ok = this[r]
|
||||
} else if r >= 'A' && r <= 'Z' {
|
||||
r += 32
|
||||
tree, ok = this[r]
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
if depth > 0 {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
if tree.IsEnd {
|
||||
return true
|
||||
}
|
||||
b := tree.Children.lookup(runes[i+1:], caseInsensitive, depth+1)
|
||||
if b {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type RuneTree struct {
|
||||
Children RuneMap
|
||||
IsEnd bool
|
||||
}
|
||||
|
||||
func NewRuneTree(list []string) RuneMap {
|
||||
var rootMap = RuneMap{}
|
||||
for _, s := range list {
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var lastMap = rootMap
|
||||
var runes = []rune(s)
|
||||
for index, r := range runes {
|
||||
tree, ok := lastMap[r]
|
||||
if !ok {
|
||||
tree = &RuneTree{
|
||||
Children: RuneMap{},
|
||||
}
|
||||
lastMap[r] = tree
|
||||
}
|
||||
if index == len(runes)-1 {
|
||||
tree.IsEnd = true
|
||||
}
|
||||
lastMap = tree.Children
|
||||
}
|
||||
}
|
||||
return rootMap
|
||||
}
|
||||
52
internal/utils/re/rune_tree_test.go
Normal file
52
internal/utils/re/rune_tree_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package re_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/re"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewRuneTree(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
var tree = re.NewRuneTree([]string{"abc", "abd", "def", "GHI", "中国", "@"})
|
||||
a.IsTrue(tree.Lookup("ABC", true))
|
||||
a.IsTrue(tree.Lookup("ABC1", true))
|
||||
a.IsTrue(tree.Lookup("1ABC", true))
|
||||
a.IsTrue(tree.Lookup("def", true))
|
||||
a.IsTrue(tree.Lookup("ghI", true))
|
||||
a.IsFalse(tree.Lookup("d ef", true))
|
||||
a.IsFalse(tree.Lookup("de", true))
|
||||
a.IsFalse(tree.Lookup("de f", true))
|
||||
a.IsTrue(tree.Lookup("我是中国人", true))
|
||||
a.IsTrue(tree.Lookup("iwind.liu@gmail.com", true))
|
||||
}
|
||||
|
||||
func TestNewRuneTree2(t *testing.T) {
|
||||
var tree = re.NewRuneTree([]string{"abc", "abd", "def", "GHI", "中国", "@"})
|
||||
tree.Lookup("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", true)
|
||||
}
|
||||
|
||||
func BenchmarkRuneMap_Lookup(b *testing.B) {
|
||||
var tree = re.NewRuneTree([]string{"abc", "abd", "def", "ghi", "中国"})
|
||||
for i := 0; i < b.N; i++ {
|
||||
tree.Lookup("我来自中国", true)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRuneMap_Lookup2_NOT_FOUND(b *testing.B) {
|
||||
var tree = re.NewRuneTree([]string{"abc", "abd", "cde", "GHI"})
|
||||
for i := 0; i < b.N; i++ {
|
||||
tree.Lookup("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", true)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRune_Regexp_FOUND(b *testing.B) {
|
||||
var reg = regexp.MustCompile("(?i)abc|abd|cde|GHI")
|
||||
for i := 0; i < b.N; i++ {
|
||||
reg.MatchString("HELLO WORLD ABC 123 456 abc HELLO WORLD HELLO WORLD ABC 123 456 abc HELLO WORLD HELLO WORLD ABC 123 456 abc HELLO WORLD")
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
package runes_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/re"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/re"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/runes"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"regexp"
|
||||
@@ -136,7 +136,6 @@ func BenchmarkContainsAnyWordRunes(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
func BenchmarkContainsAnyWord_Regexp(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
var reg = regexp.MustCompile("(?i)" + strings.ReplaceAll("python\npycurl\nhttp-client\nhttpclient\napachebench\nnethttp\nhttp_request\njava\nperl\nruby\nscrapy\nphp\nrust", "\n", "|"))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package setutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
32
internal/utils/trackers/label.go
Normal file
32
internal/utils/trackers/label.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package trackers
|
||||
|
||||
import "time"
|
||||
|
||||
type tracker struct {
|
||||
label string
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
func Begin(label string) *tracker {
|
||||
return &tracker{label: label, startTime: time.Now()}
|
||||
}
|
||||
|
||||
func Run(label string, f func()) {
|
||||
var tr = Begin(label)
|
||||
f()
|
||||
tr.End()
|
||||
}
|
||||
|
||||
func (this *tracker) End() {
|
||||
SharedManager.Add(this.label, time.Since(this.startTime).Seconds()*1000)
|
||||
}
|
||||
|
||||
func (this *tracker) Begin(subLabel string) *tracker {
|
||||
return Begin(this.label + ":" + subLabel)
|
||||
}
|
||||
|
||||
func (this *tracker) Add(duration time.Duration) {
|
||||
this.startTime = this.startTime.Add(-duration)
|
||||
}
|
||||
47
internal/utils/trackers/manager.go
Normal file
47
internal/utils/trackers/manager.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package trackers
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var SharedManager = NewManager()
|
||||
|
||||
type Manager struct {
|
||||
m map[string][]float64 // label => time costs ms
|
||||
locker sync.Mutex
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
return &Manager{m: map[string][]float64{}}
|
||||
}
|
||||
|
||||
func (this *Manager) Add(label string, costMs float64) {
|
||||
this.locker.Lock()
|
||||
costs, ok := this.m[label]
|
||||
if ok {
|
||||
costs = append(costs, costMs)
|
||||
if len(costs) > 5 { // 只取最近的N条
|
||||
costs = costs[1:]
|
||||
}
|
||||
this.m[label] = costs
|
||||
} else {
|
||||
this.m[label] = []float64{costMs}
|
||||
}
|
||||
this.locker.Unlock()
|
||||
}
|
||||
|
||||
func (this *Manager) Labels() map[string]float64 {
|
||||
var result = map[string]float64{}
|
||||
this.locker.Lock()
|
||||
for label, costs := range this.m {
|
||||
var sum float64
|
||||
for _, cost := range costs {
|
||||
sum += cost
|
||||
}
|
||||
result[label] = sum / float64(len(costs))
|
||||
}
|
||||
this.locker.Unlock()
|
||||
return result
|
||||
}
|
||||
56
internal/utils/trackers/manager_test.go
Normal file
56
internal/utils/trackers/manager_test.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package trackers_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/trackers"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewManager(t *testing.T) {
|
||||
{
|
||||
var tr = trackers.Begin("a")
|
||||
tr.End()
|
||||
}
|
||||
{
|
||||
var tr = trackers.Begin("a")
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
tr.End()
|
||||
}
|
||||
{
|
||||
var tr = trackers.Begin("a")
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
tr.End()
|
||||
}
|
||||
{
|
||||
var tr = trackers.Begin("a")
|
||||
time.Sleep(3 * time.Millisecond)
|
||||
tr.End()
|
||||
}
|
||||
{
|
||||
var tr = trackers.Begin("a")
|
||||
time.Sleep(4 * time.Millisecond)
|
||||
tr.End()
|
||||
}
|
||||
{
|
||||
var tr = trackers.Begin("a")
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
tr.End()
|
||||
}
|
||||
{
|
||||
var tr = trackers.Begin("b")
|
||||
tr.End()
|
||||
}
|
||||
|
||||
logs.PrintAsJSON(trackers.SharedManager.Labels(), t)
|
||||
}
|
||||
|
||||
func TestTrackers_Add(t *testing.T) {
|
||||
var tr = trackers.Begin("a")
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
tr.Add(-10 * time.Millisecond)
|
||||
tr.End()
|
||||
t.Log(trackers.SharedManager.Labels())
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
package ttlcache
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ package ttlcache_test
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/ttlcache"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
9
internal/utils/zero/zero.go
Normal file
9
internal/utils/zero/zero.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package zero
|
||||
|
||||
type Zero = struct{}
|
||||
|
||||
func New() Zero {
|
||||
return Zero{}
|
||||
}
|
||||
42
internal/utils/zero/zero_test.go
Normal file
42
internal/utils/zero/zero_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package zero_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestZero_Chan(t *testing.T) {
|
||||
var stat1 = &runtime.MemStats{}
|
||||
runtime.ReadMemStats(stat1)
|
||||
|
||||
var m = make(chan zero.Zero, 2_000_000)
|
||||
for i := 0; i < 1_000_000; i++ {
|
||||
m <- zero.New()
|
||||
}
|
||||
|
||||
var stat2 = &runtime.MemStats{}
|
||||
runtime.ReadMemStats(stat2)
|
||||
t.Log(stat2.HeapInuse, stat1.HeapInuse, stat2.HeapInuse-stat1.HeapInuse, "B")
|
||||
t.Log(len(m))
|
||||
}
|
||||
|
||||
func TestZero_Map(t *testing.T) {
|
||||
var stat1 = &runtime.MemStats{}
|
||||
runtime.ReadMemStats(stat1)
|
||||
|
||||
var m = map[int]zero.Zero{}
|
||||
for i := 0; i < 1_000_000; i++ {
|
||||
m[i] = zero.New()
|
||||
}
|
||||
|
||||
var stat2 = &runtime.MemStats{}
|
||||
runtime.ReadMemStats(stat2)
|
||||
t.Log((stat2.HeapInuse-stat1.HeapInuse)/1024/1024, "MB")
|
||||
t.Log(len(m))
|
||||
|
||||
_, ok := m[1024]
|
||||
t.Log(ok)
|
||||
}
|
||||
Reference in New Issue
Block a user