update id
add tpl support reload on watched file changes
This commit is contained in:
parent
d416c61a6a
commit
7bb6938cb7
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,8 +2,6 @@
|
||||
!.gitignore
|
||||
go.sum
|
||||
/build
|
||||
/node_modules
|
||||
/package.json
|
||||
/bak
|
||||
node_modules
|
||||
package.json
|
||||
|
24
go.mod
24
go.mod
@ -3,29 +3,32 @@ module apigo.cc/gojs/service
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
apigo.cc/gojs v0.0.4
|
||||
apigo.cc/gojs/console v0.0.1
|
||||
apigo.cc/gojs v0.0.7
|
||||
apigo.cc/gojs/console v0.0.2
|
||||
apigo.cc/gojs/http v0.0.3
|
||||
apigo.cc/gojs/util v0.0.3
|
||||
apigo.cc/gojs/util v0.0.7
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/ssgo/config v1.7.8
|
||||
github.com/ssgo/config v1.7.9
|
||||
github.com/ssgo/discover v1.7.9
|
||||
github.com/ssgo/httpclient v1.7.8
|
||||
github.com/ssgo/log v1.7.7
|
||||
github.com/ssgo/redis v1.7.7
|
||||
github.com/ssgo/s v1.7.18
|
||||
github.com/ssgo/s v1.7.20
|
||||
github.com/ssgo/standard v1.7.7
|
||||
github.com/ssgo/u v1.7.9
|
||||
github.com/ssgo/u v1.7.12
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ZZMarquis/gm v1.3.2 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/emmansun/gmsm v0.29.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/gomodule/redigo v1.9.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
||||
github.com/obscuren/ecies v0.0.0-20150213224233-7c0f4a9b18d9 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
@ -33,8 +36,9 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.9.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
264
service.go
264
service.go
@ -1,15 +1,19 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"apigo.cc/gojs"
|
||||
@ -39,6 +43,16 @@ var poolActionRegistered = map[string]bool{}
|
||||
var poolsLock = sync.RWMutex{}
|
||||
var waitChan chan bool
|
||||
|
||||
type TplCache struct {
|
||||
FileModTime map[string]int64
|
||||
Tpl *template.Template
|
||||
}
|
||||
|
||||
var tplFunc = map[string]any{}
|
||||
var tplFuncLock = sync.RWMutex{}
|
||||
var tplCache = map[string]*TplCache{}
|
||||
var tplCacheLock = sync.RWMutex{}
|
||||
|
||||
type LimiterConfig struct {
|
||||
From string
|
||||
Time int
|
||||
@ -69,32 +83,28 @@ var onStop goja.Callable
|
||||
var limiters = map[string]*s.Limiter{}
|
||||
var configed = false
|
||||
|
||||
func init() {
|
||||
s.Config.KeepKeyCase = true
|
||||
obj := map[string]any{
|
||||
"config": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
func initConfig(opt *gojs.Obj, logger *log.Logger, vm *goja.Runtime) {
|
||||
configed = true
|
||||
s.InitConfig()
|
||||
if startPath, ok := vm.GoData["startPath"]; ok {
|
||||
s.SetWorkPath(u.String(startPath))
|
||||
}
|
||||
// 处理配置
|
||||
args := gojs.MakeArgs(&argsIn, vm)
|
||||
serviceConfig = Config{"Session", "Device", "Client", "userId", "", 3600, "auth failed", "verify failed", "too many requests", nil, "", map[string]string{}, map[string]string{}, map[string]string{}}
|
||||
if errs := config.LoadConfig("service", &serviceConfig); errs != nil && len(errs) > 0 {
|
||||
panic(vm.NewGoError(errs[0]))
|
||||
}
|
||||
config.LoadConfig("service", &discover.Config)
|
||||
// var auth goja.Callable
|
||||
if conf := args.Obj(0); conf != nil {
|
||||
u.Convert(conf.O.Export(), &serviceConfig)
|
||||
u.Convert(conf.O.Export(), &s.Config)
|
||||
u.Convert(conf.O.Export(), &discover.Config)
|
||||
if opt != nil {
|
||||
u.Convert(opt.O.Export(), &serviceConfig)
|
||||
u.Convert(opt.O.Export(), &s.Config)
|
||||
u.Convert(opt.O.Export(), &discover.Config)
|
||||
// auth = conf.Func("auth")
|
||||
onStop = conf.Func("onStop")
|
||||
onStop = opt.Func("onStop")
|
||||
|
||||
if serviceConfig.SessionProvider != "" {
|
||||
sessionRedis = redis.GetRedis(serviceConfig.SessionProvider, args.Logger)
|
||||
sessionRedis = redis.GetRedis(serviceConfig.SessionProvider, logger)
|
||||
}
|
||||
sessionTimeout = serviceConfig.SessionTimeout
|
||||
if sessionTimeout < 0 {
|
||||
@ -152,7 +162,7 @@ func init() {
|
||||
if serviceConfig.Limiters != nil {
|
||||
var limiterRedis *redis.Redis
|
||||
if serviceConfig.LimiterRedis != "" {
|
||||
limiterRedis = redis.GetRedis(serviceConfig.LimiterRedis, args.Logger)
|
||||
limiterRedis = redis.GetRedis(serviceConfig.LimiterRedis, logger)
|
||||
}
|
||||
for name, limiter := range serviceConfig.Limiters {
|
||||
switch limiter.From {
|
||||
@ -170,11 +180,20 @@ func init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
s.Config.KeepKeyCase = true
|
||||
obj := map[string]any{
|
||||
"config": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm)
|
||||
initConfig(args.Obj(0), args.Logger, vm)
|
||||
return nil
|
||||
},
|
||||
"start": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
if !configed {
|
||||
panic(vm.NewGoError(errors.New("must run service.config frist")))
|
||||
initConfig(nil, gojs.GetLogger(vm), vm)
|
||||
// panic(vm.NewGoError(errors.New("must run service.config frist")))
|
||||
}
|
||||
if server != nil {
|
||||
panic(vm.NewGoError(errors.New("server already started")))
|
||||
@ -192,6 +211,55 @@ func init() {
|
||||
s.SetProxyBy(proxy)
|
||||
}
|
||||
|
||||
// 处理Watch
|
||||
if vm.GoData["inWatch"] == true {
|
||||
onWatchConn := map[string]*websocket.Conn{}
|
||||
onWatchLock := sync.RWMutex{}
|
||||
vm.GoData["onWatch"] = func(filename string) {
|
||||
conns := map[string]*websocket.Conn{}
|
||||
onWatchLock.RLock()
|
||||
for id, conn := range onWatchConn {
|
||||
conns[id] = conn
|
||||
}
|
||||
onWatchLock.RUnlock()
|
||||
for id, conn := range conns {
|
||||
if err := conn.WriteMessage(websocket.TextMessage, []byte(filename)); err != nil {
|
||||
onWatchLock.Lock()
|
||||
delete(onWatchConn, id)
|
||||
onWatchLock.Unlock()
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
s.AddShutdownHook(func() {
|
||||
for _, conn := range onWatchConn {
|
||||
conn.Close()
|
||||
}
|
||||
})
|
||||
s.RegisterSimpleWebsocket(0, "/_watch", func(request *s.Request, conn *websocket.Conn) {
|
||||
onWatchLock.Lock()
|
||||
onWatchConn[request.Id] = conn
|
||||
onWatchLock.Unlock()
|
||||
}, "")
|
||||
s.SetOutFilter(func(in map[string]any, request *s.Request, response *s.Response, out any, logger *log.Logger) (newOut any, isOver bool) {
|
||||
if strings.HasPrefix(response.Header().Get("Content-Type"), "text/html") {
|
||||
outStr := u.String(out)
|
||||
// 注入自动刷新的代码
|
||||
outStr = strings.ReplaceAll(outStr, "</html>", `<script>
|
||||
function connect() {
|
||||
let ws = new WebSocket(location.protocol.replace('http', 'ws') + '//' + location.host + '/_watch')
|
||||
ws.onmessage = () => { location.reload() }
|
||||
ws.onclose = () => { setTimeout(connect, 1000) }
|
||||
}
|
||||
connect()
|
||||
</script>
|
||||
</html>`)
|
||||
return []byte(outStr), false
|
||||
}
|
||||
return nil, false
|
||||
})
|
||||
}
|
||||
|
||||
// 启动服务
|
||||
server = s.AsyncStart()
|
||||
waitChan = make(chan bool, 1)
|
||||
@ -203,6 +271,8 @@ func init() {
|
||||
server.OnStopped(func() {
|
||||
ClearRewritesAndProxies()
|
||||
pools = map[string]*gojs.Pool{}
|
||||
poolExists = map[string]bool{}
|
||||
poolActionRegistered = map[string]bool{}
|
||||
server = nil
|
||||
if waitChan != nil {
|
||||
waitChan <- true
|
||||
@ -217,6 +287,72 @@ func init() {
|
||||
server.Stop()
|
||||
return nil
|
||||
},
|
||||
"uniqueId": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm)
|
||||
size := args.Int(0)
|
||||
var id string
|
||||
if size >= 20 {
|
||||
id = s.UniqueId20()
|
||||
} else if size >= 16 {
|
||||
id = s.UniqueId16()
|
||||
} else if size >= 14 {
|
||||
id = s.UniqueId14()
|
||||
} else if size >= 12 {
|
||||
id = s.UniqueId14()[0:12]
|
||||
} else {
|
||||
id = s.UniqueId()
|
||||
}
|
||||
return vm.ToValue(id)
|
||||
},
|
||||
"uniqueIdL": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm)
|
||||
size := args.Int(0)
|
||||
var id string
|
||||
if size >= 20 {
|
||||
id = s.UniqueId20()
|
||||
} else if size >= 16 {
|
||||
id = s.UniqueId16()
|
||||
} else if size >= 14 {
|
||||
id = s.UniqueId14()
|
||||
} else if size >= 12 {
|
||||
id = s.UniqueId14()[0:12]
|
||||
} else {
|
||||
id = s.UniqueId()
|
||||
}
|
||||
return vm.ToValue(strings.ToLower(id))
|
||||
},
|
||||
"id": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
space := args.Str(0)
|
||||
size := args.Int(1)
|
||||
var id string
|
||||
if size >= 12 {
|
||||
id = s.Id12(space)
|
||||
} else if size >= 10 {
|
||||
id = s.Id10(space)
|
||||
} else if size >= 8 {
|
||||
id = s.Id8(space)
|
||||
} else {
|
||||
id = s.Id6(space)
|
||||
}
|
||||
return vm.ToValue(id)
|
||||
},
|
||||
"idL": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
space := args.Str(0)
|
||||
size := args.Int(1)
|
||||
var id string
|
||||
if size >= 12 {
|
||||
id = s.Id12(space)
|
||||
} else if size >= 10 {
|
||||
id = s.Id10(space)
|
||||
} else if size >= 8 {
|
||||
id = s.Id8(space)
|
||||
} else {
|
||||
id = s.Id6(space)
|
||||
}
|
||||
return vm.ToValue(strings.ToLower(id))
|
||||
},
|
||||
"register": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||
o := args.Obj(0)
|
||||
@ -391,6 +527,100 @@ func init() {
|
||||
})
|
||||
return nil
|
||||
},
|
||||
"setTplFunc": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
fnObj := args.Obj(0)
|
||||
if fnObj != nil {
|
||||
fnList := map[string]any{}
|
||||
for _, k := range fnObj.O.Keys() {
|
||||
if jsFunc := fnObj.Func(k); jsFunc != nil {
|
||||
fn := func(args ...any) any {
|
||||
jsArgs := make([]goja.Value, len(args))
|
||||
for i := 0; i < len(args); i++ {
|
||||
jsArgs[i] = vm.ToValue(args[i])
|
||||
}
|
||||
if r, err := jsFunc(argsIn.This, jsArgs...); err == nil {
|
||||
return r.Export()
|
||||
} else {
|
||||
panic(vm.NewGoError(err))
|
||||
}
|
||||
}
|
||||
fnList[k] = fn
|
||||
}
|
||||
}
|
||||
if len(fnList) > 0 {
|
||||
tplFuncLock.Lock()
|
||||
for k, v := range fnList {
|
||||
tplFunc[k] = v
|
||||
}
|
||||
tplFuncLock.Unlock()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"tpl": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||
filename := args.Path(0)
|
||||
info := u.GetFileInfo(filename)
|
||||
if info == nil {
|
||||
panic(vm.NewGoError(errors.New("tpl file " + filename + " not exists")))
|
||||
}
|
||||
|
||||
data := args.Any(1)
|
||||
tplCacheLock.RLock()
|
||||
t := tplCache[filename]
|
||||
tplCacheLock.RUnlock()
|
||||
if t != nil {
|
||||
for f, tm := range t.FileModTime {
|
||||
info := u.GetFileInfo(f)
|
||||
if info == nil || info.ModTime.UnixMilli() != tm {
|
||||
t = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if t == nil {
|
||||
tpl := template.New("main")
|
||||
if len(tplFunc) > 0 {
|
||||
tpl = tpl.Funcs(tplFunc)
|
||||
}
|
||||
|
||||
fileModTime := map[string]int64{
|
||||
filename: info.ModTime.UnixMilli(),
|
||||
}
|
||||
var err error
|
||||
for _, m := range tplIncludeMatcher.FindAllStringSubmatch(u.ReadFileN(filename), -1) {
|
||||
includeFilename := m[1]
|
||||
info2 := u.GetFileInfo(includeFilename)
|
||||
if info2 == nil {
|
||||
includeFilename = filepath.Join(filepath.Dir(filename), m[1])
|
||||
info2 = u.GetFileInfo(includeFilename)
|
||||
}
|
||||
if info2 != nil {
|
||||
tpl, err = tpl.Parse(`{{ define "` + m[1] + `" }}` + u.ReadFileN(includeFilename) + `{{ end }}`)
|
||||
if err != nil {
|
||||
panic(vm.NewGoError(err))
|
||||
}
|
||||
fileModTime[includeFilename] = info2.ModTime.UnixMilli()
|
||||
}
|
||||
}
|
||||
tpl, err = tpl.ParseFiles(filename)
|
||||
if err != nil {
|
||||
panic(vm.NewGoError(err))
|
||||
}
|
||||
t = &TplCache{
|
||||
Tpl: tpl,
|
||||
FileModTime: fileModTime,
|
||||
}
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(make([]byte, 0))
|
||||
err := t.Tpl.ExecuteTemplate(buf, filepath.Base(filename), data)
|
||||
if err != nil {
|
||||
panic(vm.NewGoError(err))
|
||||
}
|
||||
return vm.ToValue(buf.String())
|
||||
},
|
||||
"dataSet": DataSet,
|
||||
"dataGet": DataGet,
|
||||
"dataKeys": DataKeys,
|
||||
@ -413,6 +643,12 @@ func init() {
|
||||
server.Stop()
|
||||
}
|
||||
},
|
||||
OnSignal: func(sig os.Signal) {
|
||||
switch sig {
|
||||
case syscall.SIGUSR1:
|
||||
|
||||
}
|
||||
},
|
||||
WaitForStop: func() {
|
||||
if waitChan != nil {
|
||||
<-waitChan
|
||||
@ -421,6 +657,8 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
var tplIncludeMatcher = regexp.MustCompile(`{{\s*template\s+"([^"]+)"`)
|
||||
|
||||
func verifyRegexp(regexpStr string) func(any, *goja.Runtime) bool {
|
||||
if rx, err := regexp.Compile(regexpStr); err != nil {
|
||||
return func(value any, vm *goja.Runtime) bool {
|
||||
|
13
service.ts
13
service.ts
@ -18,6 +18,12 @@ export default {
|
||||
listPop,
|
||||
listCount,
|
||||
listRemove,
|
||||
id,
|
||||
idL,
|
||||
uniqueId,
|
||||
uniqueIdL,
|
||||
setTplFunc,
|
||||
tpl,
|
||||
}
|
||||
|
||||
function config(config?: Config): void { }
|
||||
@ -41,7 +47,12 @@ function listPush(scope: string, key: string, value: any): void { }
|
||||
function listPop(scope: string, key: string): any { return null }
|
||||
function listCount(scope: string): number { return 0 }
|
||||
function listRemove(scope: string): void { }
|
||||
|
||||
function id(space: string, size?: number): string { return '' }
|
||||
function idL(space: string, size?: number): string { return '' }
|
||||
function uniqueId(size?: number): string { return '' }
|
||||
function uniqueIdL(size?: number): string { return '' }
|
||||
function setTplFunc(fnList: Object): void { }
|
||||
function tpl(file: string, data: Object): string { return '' }
|
||||
|
||||
interface Config {
|
||||
// github.com/ssgo/s 的配置参数
|
||||
|
@ -1,6 +1,7 @@
|
||||
package service_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -9,6 +10,7 @@ import (
|
||||
_ "apigo.cc/gojs/http"
|
||||
_ "apigo.cc/gojs/service"
|
||||
_ "apigo.cc/gojs/util"
|
||||
"github.com/ssgo/httpclient"
|
||||
"github.com/ssgo/u"
|
||||
)
|
||||
|
||||
@ -41,80 +43,80 @@ func TestStatic(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// func TestJsEcho(t *testing.T) {
|
||||
// for i := 0; i < runTimes; i++ {
|
||||
// name := u.UniqueId()
|
||||
// r, err := rt.RunCode("test('" + name + "')")
|
||||
// if err != nil {
|
||||
// t.Fatal("test js get failed, got error", err)
|
||||
// } else if r != name {
|
||||
// t.Fatal("test js get failed, name not match", r, name)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
func TestJsEcho(t *testing.T) {
|
||||
for i := 0; i < runTimes; i++ {
|
||||
name := u.UniqueId()
|
||||
r, err := rt.RunCode("test('" + name + "')")
|
||||
if err != nil {
|
||||
t.Fatal("test js get failed, got error", err)
|
||||
} else if r != name {
|
||||
t.Fatal("test js get failed, name not match", r, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func TestGoEcho(t *testing.T) {
|
||||
// hc := httpclient.GetClientH2C(0)
|
||||
// for i := 0; i < runTimes; i++ {
|
||||
// name := u.UniqueId()
|
||||
// r := hc.Get("http://" + addr + "/echo?name=" + name)
|
||||
// if r.Error != nil {
|
||||
// t.Fatal("test go get failed, got error", r.Error)
|
||||
// } else if r.String() != name {
|
||||
// t.Fatal("test go get failed, name not match", r, name)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
func TestGoEcho(t *testing.T) {
|
||||
hc := httpclient.GetClientH2C(0)
|
||||
for i := 0; i < runTimes; i++ {
|
||||
name := u.UniqueId()
|
||||
r := hc.Get("http://" + addr + "/echo?name=" + name)
|
||||
if r.Error != nil {
|
||||
t.Fatal("test go get failed, got error", r.Error)
|
||||
} else if r.String() != name {
|
||||
t.Fatal("test go get failed, name not match", r, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func TestJsAsyncEcho(t *testing.T) {
|
||||
// ch := make(chan bool, runTimes)
|
||||
// t1 := time.Now().UnixMilli()
|
||||
// for i := 0; i < runTimes; i++ {
|
||||
// go func() {
|
||||
// name := u.UniqueId()
|
||||
// r, err := rt.RunCode("test('" + name + "')")
|
||||
// ch <- true
|
||||
// if err != nil {
|
||||
// t.Fatal("test js async get failed, got error", err)
|
||||
// } else if r != name {
|
||||
// t.Fatal("test js async get failed, name not match", r, name)
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
// for i := 0; i < runTimes; i++ {
|
||||
// <-ch
|
||||
// }
|
||||
// t2 := time.Now().UnixMilli() - t1
|
||||
// fmt.Println(u.BGreen("js async test time:"), t2, "ms")
|
||||
// }
|
||||
func TestJsAsyncEcho(t *testing.T) {
|
||||
ch := make(chan bool, runTimes)
|
||||
t1 := time.Now().UnixMilli()
|
||||
for i := 0; i < runTimes; i++ {
|
||||
go func() {
|
||||
name := u.UniqueId()
|
||||
r, err := rt.RunCode("test('" + name + "')")
|
||||
ch <- true
|
||||
if err != nil {
|
||||
t.Fatal("test js async get failed, got error", err)
|
||||
} else if r != name {
|
||||
t.Fatal("test js async get failed, name not match", r, name)
|
||||
}
|
||||
}()
|
||||
}
|
||||
for i := 0; i < runTimes; i++ {
|
||||
<-ch
|
||||
}
|
||||
t2 := time.Now().UnixMilli() - t1
|
||||
fmt.Println(u.BGreen("js async test time:"), t2, "ms")
|
||||
}
|
||||
|
||||
// func TestGoAsyncEcho(t *testing.T) {
|
||||
// hc := httpclient.GetClientH2C(0)
|
||||
// ch := make(chan bool, runTimes*10)
|
||||
// t1 := time.Now().UnixMilli()
|
||||
// lastName := ""
|
||||
// lastResult := ""
|
||||
// for i := 0; i < runTimes*10; i++ {
|
||||
// name := fmt.Sprint("N", i)
|
||||
// lastName = name
|
||||
// go func() {
|
||||
// r := hc.Get("http://" + addr + "/echo?name=" + name)
|
||||
// lastResult = r.String()
|
||||
// ch <- true
|
||||
// if r.Error != nil {
|
||||
// t.Fatal("test go async get failed, got error", r.Error)
|
||||
// } else if r.String() != name {
|
||||
// t.Fatal("test go async get failed, name not match", r, name)
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
// for i := 0; i < runTimes*10; i++ {
|
||||
// <-ch
|
||||
// }
|
||||
// t2 := time.Now().UnixMilli() - t1
|
||||
// fmt.Println(u.BGreen("go async test time:"), t2, "ms")
|
||||
// fmt.Println(u.BGreen("last name:"), lastName, lastResult)
|
||||
// }
|
||||
func TestGoAsyncEcho(t *testing.T) {
|
||||
hc := httpclient.GetClientH2C(0)
|
||||
ch := make(chan bool, runTimes*10)
|
||||
t1 := time.Now().UnixMilli()
|
||||
lastName := ""
|
||||
lastResult := ""
|
||||
for i := 0; i < runTimes*10; i++ {
|
||||
name := fmt.Sprint("N", i)
|
||||
lastName = name
|
||||
go func() {
|
||||
r := hc.Get("http://" + addr + "/echo?name=" + name)
|
||||
lastResult = r.String()
|
||||
ch <- true
|
||||
if r.Error != nil {
|
||||
t.Fatal("test go async get failed, got error", r.Error)
|
||||
} else if r.String() != name {
|
||||
t.Fatal("test go async get failed, name not match", r, name)
|
||||
}
|
||||
}()
|
||||
}
|
||||
for i := 0; i < runTimes*10; i++ {
|
||||
<-ch
|
||||
}
|
||||
t2 := time.Now().UnixMilli() - t1
|
||||
fmt.Println(u.BGreen("go async test time:"), t2, "ms")
|
||||
fmt.Println(u.BGreen("last name:"), lastName, lastResult)
|
||||
}
|
||||
|
||||
func TestStop(t *testing.T) {
|
||||
go func() {
|
||||
|
8
tests/tpl.js
Normal file
8
tests/tpl.js
Normal file
@ -0,0 +1,8 @@
|
||||
import s from "apigo.cc/gojs/service"
|
||||
|
||||
function main(args) {
|
||||
s.setTplFunc({
|
||||
bb: text => { return '<b>' + text + '</b>' }
|
||||
})
|
||||
return s.tpl('tpl/page.html', { title: 'Abc' })
|
||||
}
|
3
tests/tpl/header.html
Normal file
3
tests/tpl/header.html
Normal file
@ -0,0 +1,3 @@
|
||||
<header>
|
||||
<h1>Welcome to {{bb .title}}</h1>
|
||||
</header>
|
18
tests/tpl/page.html
Normal file
18
tests/tpl/page.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>{{.title}}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{template "header.html" .}}
|
||||
<pre>
|
||||
hello world
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
</html>
|
20
tests/tpl_test.go
Normal file
20
tests/tpl_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package service_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"apigo.cc/gojs"
|
||||
_ "apigo.cc/gojs/service"
|
||||
"github.com/ssgo/u"
|
||||
)
|
||||
|
||||
func TestTpl(t *testing.T) {
|
||||
r, err := gojs.RunFile("tpl.js")
|
||||
if err != nil {
|
||||
t.Fatal("test static failed, got error", err)
|
||||
}
|
||||
if !strings.Contains(u.String(r), "<h1>Welcome to <b>Abc</b></h1>") {
|
||||
t.Fatal("test tpl failed, name not match", r)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user